rlsm 1.0.0 → 1.1.0

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