rlsm 0.2.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,778 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'rlsm'))
2
+ require 'dfa'
3
+
4
+ =begin rdoc
5
+ =The RLSM::Monoid class
6
+ ===Definition of a monoid
7
+ A _monoid_ is a tuple <tt>(M,B)</tt> where
8
+ * +M+ is a set of _elements_
9
+ * <tt>B:M x M -> M</tt> is a _binary_ _operation_
10
+ with the following properties
11
+ * the binary operation is associative ( <tt>B(a,B(b,c)) = B(B(a,b),c)</tt> for all <tt>a,b,c</tt> in +M+)
12
+ * It exists an element +e+ in +M+ with <tt>B(a,e) = B(e,a) = a</tt> for all +a+ in +M+ (the neutral element).
13
+
14
+ In theory the set +M+ may be of infinite size, but for obvious reasons we consider here only the finite case. The size of +M+ is called the _order_ of the monoid.
15
+
16
+ Also we denote the product of two elements by
17
+
18
+ B(x,y) =: xy
19
+
20
+
21
+ ===A word on the binary operation
22
+ Suppose that <tt>M = {e,b,c}</tt>. We can describe the binary operation as a table, for example
23
+
24
+ | e | a | b | <-- this row gives only a correspondence between
25
+ ---+---+---+---+ columns and monoid elements (analog the first column,
26
+ e | e | a | b | which gives a correspondence between rows and elements).
27
+ ---+---+---+---+
28
+ a | a | a | b |
29
+ ---+---+---+---+
30
+ b | b | a | b |
31
+ ---+---+---+---+
32
+
33
+ For two elements <tt>x,y</tt>, the product +xy+ can be looked up in the table as follows: Say <tt>x = a</tt> and <tt>y = b</tt> the product +ab+ is then the entry in row +a+ and column +b+ (in this case it is +b+).
34
+
35
+ This gives us an easy way to express a binary operation in code. It is simply a 2D-array. We see immediatly two restrictions:
36
+ * Ignoring the descreptive first row and first column, the array must be quadratic
37
+ * Each entry must be a monoid element
38
+
39
+ If we now agree upon the convention that the first row and column belongs to the neutral element, we can discard the row and column description because of the fact, that in this case the first row and column are identical with the descriptions (compare above example).
40
+
41
+ ===Basic properties of monoids and monoid elements
42
+ Given two monoids <tt>(M,B), (N,C)</tt> we say the monoids are _isomorph_ _to_ each other iff there exists a bijective map <tt>I : M -> N</tt> such that the equality
43
+ I(B(x,y)) = C(I(x),I(y))
44
+ holds for all <tt>x,y</tt> in +M+. The map +I+ is then called an _isomorphism_.
45
+
46
+ The monoids are _anti_-_isomorph_ _to_ each other iff there exists a bijective map <tt>A : M -> N </tt> such that the equality
47
+ A(B(x,y)) = C(A(y),A(x))
48
+ holds for all <tt>x,y</tt> in +M+. The map +A+ is then called an _anti_-_isomorphism_.
49
+
50
+ We say a monoid <tt>(M,B)</tt> is _commutative_ iff the equality
51
+ B(x,y) = B(y,x)
52
+ holds for all <tt>x,y</tt> in +M+.
53
+
54
+ An element +l+ of +M+ is called a _left_-_zero_ iff for all +x+ in +M+
55
+ lx = l
56
+ holds. An element +r+ of +M+ is called a _right_-_zero_ iff for all +x+ in +M+
57
+ xr = r
58
+ holds. An element +z+ of +M+ is called a _zero_ _element_ iff for all +x+ in +M+
59
+ zx = xz = z
60
+ holds. If it exists, the zero element is unique.
61
+
62
+ An element +a+ of +M+ is called a _idempotent_ iff <tt>xx = x</tt>. A monoid +M+ is called _idempotent_ if all elements are idempotent.
63
+
64
+ It is easy to see that a monoid is a group (i.e. for all elements +x+ there exists an element +y+ such that <tt>xy = yx = 1</tt> where +1+ is the neutral element) if the neutral element is the only idempotent element.
65
+
66
+ ===Submonoids and generators
67
+ Given two monoids <tt>(M,B), (N,C)</tt> we say +M+ is a _submonoid_ of +N+ iff there exists an injective map <tt>S : M -> N</tt> such that the equality
68
+ S(B(x,y)) = C(S(x),S(y))
69
+ holds for all <tt>x,y</tt> in +M+ and +M+ is a subset of +N+.
70
+
71
+ A submonoid +N+ of +M+ is called a _proper_ _submonoid_ if <tt>N != M</tt> and +N+ has more than one element.
72
+
73
+ Let <tt>(M,B)</tt> with <tt>M = {m1,m2,m3,...}</tt> be a monoid and <tt>N = {n1,n2,...}</tt> be a subset of +M+. Then the set
74
+ <N> := { x | x is the product of arbitary powers of elements in N }
75
+ is a submonoid of +M+ with +B+ restricted to <tt>NxN</tt>. It is called the submonoid _generated_ _by_ +N+ and +N+ is called the _generator_.
76
+
77
+ A _generating_ _subset_ of a Monoid +M+ is a subset +N+ of +M+ such that <tt><N>=M</tt>.
78
+
79
+ ===Ideals of a monoid and Green Relations
80
+ Let +M+ be a monoid and +a+ in +M+. We define
81
+ Ma := {xa | x in M}
82
+ aM := {ax | x in M}
83
+ MaM := {xay | x,y in M}
84
+ and call +Ma+ the _left_ _ideal_ of +a+, +aM+ the _right_ _ideal_ of +a+ and +MaM+ the (_two_-_sided_) _ideal_ of +a+.
85
+
86
+ We can now define some equivalent relations on +M+:
87
+ a =L= b :<=> Ma = Mb
88
+ a =R= b :<=> aM = bM
89
+ a =J= b :<=> MaM = MbM
90
+ a =H= b :<=> a =L= b and a =R= b
91
+ a =D= b :<=> it exists a c in +M+ such that a =L= c and c =R= b
92
+ These relations are called the _Green_-_relations_ of the monoid +M+.
93
+
94
+ The relations =J= and =D= are the same for finite monoids, so we consider here only the relation =D=.
95
+
96
+ The equivalence classes of these relations are called _L_-_class_, _R_-_class_,_J_-_class_,_H_-_class_ and _D_-_class_.
97
+
98
+ A monoid is called _L_-_trivial_ iff all L-classes contains only one element. Analog for _R_,_J_,_H_,_D_-_trivial_.
99
+
100
+ ===Disjunctive subsets and syntactic monoids.
101
+ A subset +D+ of a monoid +M+ is called a _disjunctive_ _subset_ iff for all <tt>a,b</tt> in +M+ with <tt>a != b</tt> a 'context' <tt>x,y</tt> in +M+ exists such that
102
+ (xay in N and xby not in N) or vice versa
103
+
104
+ A monoid is called a _syntactic_ _monoid_ iff it has a disjunctive subset.
105
+
106
+ These definitions are motivated by the formal language theory in theoretical computer science. There one can define the _syntactic_ _monoid_ of a language as the factor monoid given by a congruence relation which depends on the language. It is shown that a monoid is syntactic in this sense iff it has a disjunctive subset.
107
+
108
+ Also it is shown that the syntactic monoid of a language is finite iff the language is regular.
109
+ =end
110
+
111
+ class RLSM::Monoid
112
+ =begin rdoc
113
+ The new method takes two parameters: the binary_operation and an (more or less) optional options hash.
114
+
115
+ The +binary_operation+ parameter should be either an array or a string.
116
+ If it is an array it must satisfy the following conditions:
117
+
118
+ * It is a two dimensional array and the rows are also arrays.
119
+ * It is quadratic.
120
+ * Each entry is an element of the monoid
121
+ * The first row and column belongs to the neutral element
122
+
123
+ If +binary_operation+ is a string, it must be of the form
124
+
125
+ 1abc aabc babc cabc
126
+
127
+ Such a string will be transformed in a 2D-array in the following way:
128
+ 1. Each row is seperated by a space
129
+ 2. In a row elements are seperated by ',' (comma) or each element consists of exactly one character (as in the above example)
130
+
131
+ The above example will be transformed to
132
+ [['1','a','b','c'],['a','a','b','c'],['b','a','b','c'],['c','a','b','c']]
133
+ After the transformation, the same rules as for an array parameter applies.
134
+
135
+ Remark: The elements will always converted to a string, even if given an array with only numeric values. So multiplication will always be performed on strings.
136
+
137
+ The optional options hash knows the following keys.
138
+ [<tt>:elements</tt>] Takes an array as value and calls elements= with this array after the construction is complete.
139
+ [<tt>:normalize</tt>] If given non-nil and non-false value, the normalize method will be called after construction.
140
+ [<tt>:rename</tt>]If given non-nil and non-false value, the rename_elements method will be called after construction.
141
+
142
+ Other keys will be ignored and the order in which the methods will be called is
143
+ normalize rename elements
144
+ =end
145
+ def initialize(binary_operation, options = {})
146
+ @binary_operation = get_binary_operation_from binary_operation
147
+ @elements = @binary_operation.first.uniq unless @binary_operation.empty?
148
+ @order = @binary_operation.size
149
+
150
+ validate
151
+
152
+ normalize if options[:normalize]
153
+ rename_elements if options[:rename]
154
+ self.elements = options[:elements] if options[:elements]
155
+ end
156
+
157
+ attr_reader :binary_operation, :elements, :order
158
+
159
+ =begin rdoc
160
+ Returns the product of the given elements. Raises a MonoidException if one of the arguments isn't a monoid element.
161
+ =end
162
+ def [](*args)
163
+ args.flatten!
164
+ check_args(args)
165
+
166
+ if args.size == 2
167
+ x,y = args[0], args[1]
168
+ return @binary_operation[@elements.index(x)][@elements.index(y)]
169
+ else
170
+ args[0,2] = self[args[0,2]]
171
+ return self[*args]
172
+ end
173
+ end
174
+
175
+ =begin rdoc
176
+ Checks if this monoid is isomorph to +other+, if so returns true.
177
+ =end
178
+ def isomorph_to?(other)
179
+ #First a trivial check
180
+ return false if @order != other.order
181
+
182
+ #Search now an isomorphism
183
+ iso = @elements.permutations.find do |p|
184
+ @elements.product(@elements).all? do |x,y|
185
+ px, py = other.elements[p.index(x)], other.elements[p.index(y)]
186
+
187
+ other.elements[p.index(self[x,y])] == other[px,py]
188
+ end
189
+ end
190
+
191
+ #Did we found an isomorphism?
192
+ !iso.nil?
193
+ end
194
+
195
+ =begin rdoc
196
+ Checks if this monoid is anti-isomorph to +other+, if so returns true.
197
+ =end
198
+ def anti_isomorph_to?(other)
199
+ transposed = (0...@order).map do |i|
200
+ @binary_operation.map { |row| row[i].clone }
201
+ end
202
+
203
+ RLSM::Monoid.new(transposed).isomorph_to?(other)
204
+ end
205
+
206
+ =begin rdoc
207
+ Checks if the monoid is equal to +other+, i.e. the identity map is an isomorphism.
208
+ =end
209
+ def ==(other)
210
+ return false unless @elements == other.elements
211
+ return false unless @binary_operation == other.binary_operation
212
+
213
+ true
214
+ end
215
+
216
+ =begin rdoc
217
+ Checks if the monoid is commutative, if so returns true.
218
+ =end
219
+ def commutative?
220
+ @elements.product(@elements).all? { |x,y| self[x,y] == self[y,x] }
221
+ end
222
+
223
+ =begin rdoc
224
+ Returns the submonoid which is generated by the given elements. If one of the given elements isn't a monoid element, an MonoidException is raised.
225
+ =end
226
+ def get_submonoid(*args)
227
+ element_indices = get_closure_of(args).map { |x| @elements.index(x) }
228
+
229
+ RLSM::Monoid.new(@binary_operation.values_at(*element_indices).
230
+ map { |r| r.values_at *element_indices } )
231
+ end
232
+
233
+ =begin rdoc
234
+ Returns an array of all submonoids of this monoid. The array is sorted in lexicographical order of the submonoid elements.
235
+ =end
236
+ def submonoids
237
+ @elements.powerset.map { |s| get_closure_of(s) }.uniq.sort_lex.map do |s|
238
+ get_submonoid(s)
239
+ end
240
+ end
241
+
242
+ =begin rdoc
243
+ Returns an array of all proper submonoids of this monoid. The array is sorted in lexicographical order of the submonoid elements.
244
+ =end
245
+ def proper_submonoids
246
+ submonoids.reject { |m| [1,@order].include? m.order }
247
+ end
248
+
249
+ =begin rdoc
250
+ Returns true if this monoid is a submonoid of +other+.
251
+ =end
252
+ def submonoid_of?(other)
253
+ other.submonoids.include? self
254
+ end
255
+
256
+ #A synonym for submonoid_of?
257
+ def <=(other)
258
+ submonoid_of?(other)
259
+ end
260
+
261
+ =begin rdoc
262
+ Returns true if this monoid is a proper submonoid of +other+.
263
+ =end
264
+ def proper_submonoid_of?(other)
265
+ other.proper_submonoids.include? self
266
+ end
267
+
268
+ #A synonym for proper_submonoid_of?
269
+ def <(other)
270
+ proper_submonoid_of?(other)
271
+ end
272
+
273
+ =begin rdoc
274
+ Returns true if this monoid has +other+ as a submonoid
275
+ =end
276
+ def has_as_submonoid?(other)
277
+ other.submonoid_of?(self)
278
+ end
279
+
280
+ #A synonym for has_as_submonoid?
281
+ def >=(other)
282
+ has_as_submonoid?(other)
283
+ end
284
+
285
+ =begin rdoc
286
+ Returns true if this monoid has +other+ as a proper submonoid
287
+ =end
288
+ def has_as_proper_submonoid?(other)
289
+ other.proper_submonoid_of?(self)
290
+ end
291
+
292
+ #A synonym for has_as_proper_submonoid?
293
+ def >(other)
294
+ has_as_proper_submonoid?(other)
295
+ end
296
+
297
+ =begin rdoc
298
+ Returns the lexicographical smallest subset which generates the monoid
299
+ =end
300
+ def generating_subset
301
+ @elements.powerset.find { |s| get_closure_of(s).size == @order }
302
+ end
303
+
304
+ =begin rdoc
305
+ Returns the right ideal of the given element. Raises a MonoidException if the given element isn't in the monoid.
306
+ =end
307
+ def right_ideal_of(element)
308
+ check_args(element)
309
+
310
+ @binary_operation[@elements.index(element)].uniq.sort
311
+ end
312
+
313
+ =begin rdoc
314
+ Returns the left ideal of the given element. Raises a MonoidException if the given element isn't in the monoid.
315
+ =end
316
+ def left_ideal_of(element)
317
+ check_args(element)
318
+
319
+ i = @elements.index(element)
320
+ @binary_operation.map { |row| row[i] }.uniq.sort
321
+ end
322
+
323
+ =begin rdoc
324
+ Returns the (two-sided) ideal of the given element. Raises a MonoidException if the given element isn't in the monoid.
325
+ =end
326
+ def ideal_of(element)
327
+ @elements.product(@elements).inject([]) do |res,xy|
328
+ x,y = xy.first, xy.last
329
+ res << self[x,element,y] unless res.include? self[x,element,y]
330
+ res.sort
331
+ end
332
+ end
333
+
334
+ =begin rdoc
335
+ Returns the L-class of the given element. Raises a MonoidException if the given element isn't a monoid element.
336
+ =end
337
+ def l_class_of(element)
338
+ l = left_ideal_of(element)
339
+ @elements.select { |x| left_ideal_of(x) == l }
340
+ end
341
+
342
+ =begin rdoc
343
+ Returns all different L-classes of the monoid ordered by the lexicographical smallest element of each class
344
+ =end
345
+ def l_classes
346
+ @elements.map { |x| l_class_of(x) }.uniq
347
+ end
348
+
349
+ =begin rdoc
350
+ Returns true if the monoid is L-trivial.
351
+ =end
352
+ def l_trivial?
353
+ l_classes.all? { |l| l.size == 1 }
354
+ end
355
+
356
+ =begin rdoc
357
+ Returns the R-class of the given element. Raises a MonoidException if the given element isn't a monoid element.
358
+ =end
359
+ def r_class_of(element)
360
+ r = right_ideal_of(element)
361
+ @elements.select { |x| right_ideal_of(x) == r }
362
+ end
363
+ =begin rdoc
364
+ Returns all different R-classes of the monoid ordered by the lexicographical smallest element of each class
365
+ =end
366
+ def r_classes
367
+ @elements.map { |x| r_class_of(x) }.uniq
368
+ end
369
+
370
+ =begin rdoc
371
+ Returns true if the monoid is R-trivial.
372
+ =end
373
+ def r_trivial?
374
+ r_classes.all? { |r| r.size == 1 }
375
+ end
376
+
377
+ =begin rdoc
378
+ Returns the H-class of the given element. Raises a MonoidException if the given element isn't a monoid element.
379
+ =end
380
+ def h_class_of(element)
381
+ l_class_of(element) & r_class_of(element)
382
+ end
383
+
384
+ =begin rdoc
385
+ Returns all different H-classes of the monoid ordered by the lexicographical smallest element of each class
386
+ =end
387
+ def h_classes
388
+ @elements.map { |x| h_class_of(x) }.uniq
389
+ end
390
+
391
+ =begin rdoc
392
+ Returns true if the monoid is H-trivial.
393
+ =end
394
+ def h_trivial?
395
+ h_classes.all? { |h| h.size == 1 }
396
+ end
397
+
398
+ =begin rdoc
399
+ Returns the D-class of the given element. Raises a MonoidException if the given element isn't a monoid element.
400
+ =end
401
+ def d_class_of(element)
402
+ d = ideal_of(element)
403
+ @elements.select { |x| ideal_of(x) == d }
404
+ end
405
+
406
+ =begin rdoc
407
+ Returns all different D-classes of the monoid ordered by the lexicographical smallest element of each class
408
+ =end
409
+ def d_classes
410
+ @elements.map { |x| d_class_of(x) }.uniq
411
+ end
412
+
413
+ =begin rdoc
414
+ Returns true if the monoid is D-trivial.
415
+ =end
416
+ def d_trivial?
417
+ d_classes.all? { |d| d.size == 1 }
418
+ end
419
+
420
+ #Synonym for d_class_of (in a finite monoid =D= is the same as =J=)
421
+ def j_class_of(element)
422
+ d_class_of(element)
423
+ end
424
+
425
+ #Synonym for d_classes (in a finite monoid =D= is the same as =J=)
426
+ def j_classes
427
+ d_classes
428
+ end
429
+
430
+ #Synonym for d_trivial? (in a finite monoid =D= is the same as =J=)
431
+ def j_trivial?
432
+ d_trivial?
433
+ end
434
+
435
+ =begin rdoc
436
+ Returns true if the given element is idempotent.
437
+ =end
438
+ def idempotent?(x = nil)
439
+ x ? x == self[x,x] : @elements.all? { |x| idempotent?(x) }
440
+ end
441
+
442
+ =begin rdoc
443
+ Returns all idempotent elements of the monoid.
444
+ =end
445
+ def idempotents
446
+ @elements.select { |x| idempotent?(x) }
447
+ end
448
+
449
+ =begin rdoc
450
+ Returns true if the monoid is also a group.
451
+ =end
452
+ def group?
453
+ idempotents.size == 1
454
+ end
455
+
456
+ =begin rdoc
457
+ Returns true if the given element is a left zero. Raises a MonoidException if the given element isn't a monoid element.
458
+ =end
459
+ def left_zero?(element)
460
+ return false if @order == 1
461
+ @elements.all? { |x| self[element,x] == element }
462
+ end
463
+
464
+ =begin rdoc
465
+ Returns all left zeros of the monoid.
466
+ =end
467
+ def left_zeros
468
+ @elements.select { |x| left_zero?(x) }
469
+ end
470
+
471
+ =begin rdoc
472
+ Returns true if the given element is a right zero. Raises a MonoidException if the given element isn't a monoid element.
473
+ =end
474
+ def right_zero?(element)
475
+ return false if @order == 1
476
+ @elements.all? { |x| self[x,element] == element }
477
+ end
478
+
479
+ =begin rdoc
480
+ Returns all right zeros of the monoid.
481
+ =end
482
+ def right_zeros
483
+ @elements.select { |x| right_zero?(x) }
484
+ end
485
+
486
+ =begin rdoc
487
+ Returns the neutral element.
488
+ =end
489
+ def neutral_element
490
+ @elements.first.dup
491
+ end
492
+
493
+ =begin rdoc
494
+ Returns the zero element if it exists, nil otherwise.
495
+ =end
496
+ def zero_element
497
+ @elements.find { |x| left_zero?(x) and right_zero?(x) }
498
+ end
499
+
500
+
501
+ =begin rdoc
502
+ Returns true if the given set (as an array) is disjunctive.
503
+ =end
504
+ def subset_disjunctive?(set)
505
+ check_args(set)
506
+
507
+ tup = @elements.product(@elements)
508
+
509
+ tup.all? do |a,b|
510
+ a == b or tup.any? do |x,y|
511
+ set.include?(self[x,a,y]) ^ set.include?(self[x,b,y])
512
+ end
513
+ end
514
+ end
515
+
516
+ =begin rdoc
517
+ Returns the lexicographical smallest subset which is disjunctive.
518
+ =end
519
+ def disjunctive_subset
520
+ @elements.powerset.find { |s| subset_disjunctive? s }
521
+ end
522
+
523
+ =begin rdoc
524
+ Returns all disjunctive subsets of the monoid in lexicographical order.
525
+ =end
526
+ def all_disjunctive_subsets
527
+ @elements.powerset.select { |s| subset_disjunctive? s }
528
+ end
529
+
530
+ =begin rdoc
531
+ Returns true if the monoid is syntactic.
532
+ =end
533
+ def syntactic?
534
+ !disjunctive_subset.nil?
535
+ end
536
+
537
+ =begin rdoc
538
+ Returns true if the monoid is aperiodic. (A synonym for h_trivial?)
539
+ =end
540
+ def aperiodic?
541
+ h_trivial?
542
+ end
543
+
544
+ def to_s # :nodoc:
545
+ sep = ''
546
+ sep = ',' if @elements.any? { |x| x.length > 1 }
547
+ @binary_operation.map { |row| row.join(sep) }.join(' ')
548
+ end
549
+
550
+ def inspect # :nodoc:
551
+ "<#{self.class} : {#{@elements.join(',')}}; #{to_s}>"
552
+ end
553
+
554
+ =begin rdoc
555
+ Arranges the elements in such a way that the generators follows directly the neutral element.
556
+ =end
557
+ def normalize
558
+ #new element order
559
+ elements =
560
+ [@elements.first] +
561
+ generating_subset +
562
+ (@elements[(1..-1)] - generating_subset)
563
+
564
+ indices = elements.map { |x| @elements.index(x) }
565
+
566
+ #Adjust the binaray operation
567
+ @binary_operation = @binary_operation.values_at(*indices).map do |row|
568
+ row.values_at(*indices)
569
+ end
570
+
571
+ #Adjust the elements
572
+ @elements = elements
573
+
574
+ self
575
+ end
576
+
577
+ =begin rdoc
578
+ Renames the elements to 1,a,b,c,d,e,... (see also elements=). It may be a little bit confusing if the monoid has more than 27 elements, because then the 28th element is named 'aa' which should not confused with the product in the monoid.
579
+ =end
580
+ def rename_elements
581
+ #Create the new elements
582
+ eles = ['1']
583
+ if @order > 1
584
+ eles << 'a'
585
+ eles << eles.last.succ while eles.size < @order
586
+ end
587
+
588
+ self.elements = eles
589
+ self
590
+ end
591
+
592
+ =begin rdoc
593
+ Renames the elements to the given array. Each array entry will be converted to a string.
594
+
595
+ A MonoidException will be raised if
596
+ * the given array has the wrong size
597
+ * the given array has duplicated elements (e.g. ['a','b','a'])
598
+ =end
599
+ def elements=(els)
600
+ els.map! { |x| x.to_s }
601
+
602
+ if els.size != @order
603
+ raise MonoidException, "Wrong number of elements given!"
604
+ elsif els.uniq!
605
+ raise MonoidException, "Given elements aren't unique!"
606
+ end
607
+
608
+
609
+
610
+ @binary_operation.map! do |row|
611
+ row.map { |x| els[@elements.index(x)] }
612
+ end
613
+
614
+ @elements = els
615
+ end
616
+ =begin rdoc
617
+ Returns a DFA which has the elements as states, the binary operation as transitions and the neutral element as initial state.
618
+
619
+ As optional parameter one may pass an array of elements which should become the final states. If the monoid is syntactic, these finals must be a disjunctive subset.
620
+
621
+ Also if the monoid is syntactic the set returned by disjunctive subset will be used as the finals as default.
622
+ =end
623
+ def to_dfa(finals = [])
624
+ check_args *finals
625
+ if generating_subset == []
626
+ return RLSM::DFA.new(:alphabet => [], :states => @elements,
627
+ :initial => neutral_element,
628
+ :finals => finals, :transitions => [])
629
+ end
630
+
631
+ if syntactic?
632
+ if finals.empty?
633
+ finals = disjunctive_subset
634
+ else
635
+ unless all_disjunctive_subsets.include? finals.sort
636
+ raise MonoidException, "Given finals aren't a disjunctive subset."
637
+ end
638
+ end
639
+ end
640
+
641
+ RLSM::DFA.create(:initial => neutral_element,
642
+ :finals => finals,
643
+ :transitions => get_transitions)
644
+ end
645
+
646
+ private
647
+ def get_transitions
648
+ trans = []
649
+ generating_subset.each do |l|
650
+ @elements.each do |s|
651
+ trans << [l,s,self[s,l]]
652
+ end
653
+ end
654
+ trans
655
+ end
656
+
657
+ def check_args(*args)
658
+ args.flatten!
659
+ bad = args.find_all { |x| !@elements.include? x }
660
+
661
+ if bad.size == 1
662
+ raise MonoidException, "Bad argument: #{bad[0]}"
663
+ elsif bad.size > 1
664
+ raise MonoidException, "Bad arguments: #{bad.join(',')}"
665
+ end
666
+ end
667
+
668
+ def get_closure_of(*args)
669
+ args.flatten!
670
+ check_args(args)
671
+
672
+ #Add the neutral element if necassary
673
+ args.unshift @elements.first unless args.include? @elements.first
674
+
675
+ searching = true
676
+ while searching
677
+ searching = false
678
+
679
+ args.product(args).each do |x,y|
680
+ unless args.include? self[x,y]
681
+ args << self[x,y]
682
+ searching = true
683
+ end
684
+ end
685
+ end
686
+
687
+ args.sort { |x,y| @elements.index(x) <=> @elements.index(y) }
688
+ end
689
+
690
+ def get_binary_operation_from(bo)
691
+ if bo.class == String
692
+ return from_string_to_array(bo)
693
+ else
694
+ begin
695
+ return bo.map { |r| r.map { |x| x.to_s } }
696
+ rescue
697
+ raise MonoidException, "Something went wrong."
698
+ end
699
+ end
700
+ end
701
+
702
+ def from_string_to_array(bo)
703
+ #Reduce multiple spaces and spaces before or after commas
704
+ bo.squeeze!(' ')
705
+ bo.gsub!(', ', ',')
706
+ bo.gsub!(' ,', ',')
707
+
708
+ bo.split.map { |row| split_rows(row) }
709
+ end
710
+
711
+ def split_rows(r)
712
+ if r.include? ','
713
+ return r.split(',')
714
+ else
715
+ return r.scan(/./)
716
+ end
717
+ end
718
+
719
+ def validate
720
+ validate_form_of_binary_operation
721
+ validate_elements
722
+ validate_neutral_element
723
+ validate_associativity
724
+ end
725
+
726
+ def validate_form_of_binary_operation
727
+ if @binary_operation.empty?
728
+ raise(MonoidException,
729
+ "No binary operation given!")
730
+ end
731
+
732
+ unless @binary_operation.all? { |r| r.size == @binary_operation.size }
733
+ raise(MonoidException,
734
+ "A binary operation must be quadratic!")
735
+ end
736
+ end
737
+
738
+ def validate_elements
739
+ #Have we enough elements
740
+ unless @elements.size == @order
741
+ raise(MonoidException,
742
+ "Expected #@order elements, but got #{@elements.join(',')}")
743
+ end
744
+
745
+ #All elements of the table are known?
746
+ unless @binary_operation.flatten.all? { |x| @elements.include? x }
747
+ raise(MonoidException,
748
+ "There are too many elements in the binary operation.")
749
+ end
750
+ end
751
+
752
+ def validate_neutral_element
753
+ #By convention 0 is the index of the neutral element, check this
754
+ unless @elements.all? do |x|
755
+ @binary_operation[0][@elements.index(x)] == x and
756
+ @binary_operation[@elements.index(x)][0] == x
757
+ end
758
+ raise(MonoidException,
759
+ "Convention violated. #{@elements.first} is not a neutral element.")
760
+ end
761
+ end
762
+
763
+ def validate_associativity
764
+ #Search for a triple which violates the associativity
765
+ nat = @elements.product(@elements,@elements).find do |triple|
766
+ x,y,z = triple.map { |a| @elements.index(a) }
767
+ @binary_operation[x][@elements.index(@binary_operation[y][z])] !=
768
+ @binary_operation[@elements.index(@binary_operation[x][y])][z]
769
+ end
770
+
771
+ #Found one?
772
+ if nat
773
+ err_str = "#{nat[0]}(#{nat[1]}#{nat[2]}) != (#{nat[0]}#{nat[1]})#{nat[2]}"
774
+ raise(MonoidException,
775
+ "Given binary operation is not associative: #{err_str}")
776
+ end
777
+ end
778
+ end