rlsm 1.1.0 → 1.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +26 -0
- data/README +10 -10
- data/Rakefile +9 -55
- data/data/monoids.db +0 -0
- data/examples/benchmark.rb +14 -0
- data/examples/creating_db.rb +52 -0
- data/examples/creating_lists.rb +185 -0
- data/examples/numbers.rb +41 -0
- data/examples/order8.rb +13 -0
- data/examples/presenting_monoids_in_tex.rb +373 -0
- data/examples/regular_monoids.rb +144 -0
- data/ext/array/array_c_ext.c +21 -12
- data/ext/monoid/monoid_c_ext.c +76 -85
- data/lib/rlsm.rb +1 -1
- data/lib/rlsm/dfa.rb +9 -8
- data/lib/rlsm/helper.rb +12 -6
- data/lib/rlsm/monoid.rb +627 -217
- data/lib/rlsm/regexp.rb +2 -1
- data/lib/rlsm/regexp_parser.rb +2 -1
- data/rlsm.gemspec +26 -0
- data/test/test_dfa.rb +16 -14
- data/test/test_monoid.rb +43 -43
- data/test/test_regexp.rb +2 -2
- metadata +51 -83
- data/ext/binop/binop_c_ext.c +0 -57
- data/ext/binop/extconf.rb +0 -2
- data/lib/rlsm/binary_operation.rb +0 -151
- data/test/test_binop.rb +0 -119
data/lib/rlsm/helper.rb
CHANGED
@@ -1,11 +1,17 @@
|
|
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
1
|
|
8
2
|
module RLSM
|
3
|
+
# The exception class which is raised by the code under the RLSM module.
|
4
|
+
# So somthing like
|
5
|
+
# begin
|
6
|
+
# ...
|
7
|
+
# rescue RLSM::Error => e
|
8
|
+
# ... #Error from RLSM code
|
9
|
+
# rescue
|
10
|
+
# ... #Some external error
|
11
|
+
# end
|
12
|
+
# works.
|
13
|
+
class Error < StandardError; end
|
14
|
+
|
9
15
|
def self.require_extension(extension)
|
10
16
|
require File.join(File.dirname(__FILE__), '..', '..', 'ext', extension, extension + '_cext')
|
11
17
|
end
|
data/lib/rlsm/monoid.rb
CHANGED
@@ -1,90 +1,237 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), 'helper')
|
2
|
-
require File.join(File.dirname(__FILE__), 'binary_operation')
|
3
2
|
require File.join(File.dirname(__FILE__), 'dfa')
|
3
|
+
|
4
4
|
RLSM::require_extension 'array'
|
5
5
|
RLSM::require_extension 'monoid'
|
6
6
|
|
7
7
|
module RLSM
|
8
|
+
|
9
|
+
# Implements the mathematical idea of a finite monoid.
|
8
10
|
class Monoid
|
9
|
-
private_class_method :new
|
10
11
|
|
11
|
-
|
12
|
-
|
12
|
+
class << self
|
13
|
+
# Iterates over each monoid of the given order.
|
14
|
+
#
|
15
|
+
# @param [Integer] order Specifies over which monoids the method iterates.
|
16
|
+
#
|
17
|
+
# @raise [RLSM::Error] The given parameter must be greater than zero.
|
18
|
+
#
|
19
|
+
# @yield [monoid] A monoid of the given order.
|
20
|
+
def each(order)
|
21
|
+
each_table(order) { |table| yield new(table,false) }
|
22
|
+
end
|
13
23
|
|
14
|
-
|
15
|
-
|
16
|
-
|
24
|
+
# Iterates over each transition table of a monoid of the given order.
|
25
|
+
#
|
26
|
+
# @param [Integer] order Specifies over which monoids the method iterates.
|
27
|
+
#
|
28
|
+
# @raise [RLSM::Error] The given parameter must be greater than zero.
|
29
|
+
#
|
30
|
+
# @yield [table] An array describing the transition table of a monoid.
|
31
|
+
def each_table(order)
|
32
|
+
raise RLSM::Error, "Given order must be > 0" if order <= 0
|
33
|
+
|
34
|
+
if order == 1 #trivial case
|
35
|
+
yield [0]
|
36
|
+
return
|
37
|
+
end
|
38
|
+
|
39
|
+
#calculate the permutations once
|
40
|
+
permutations =
|
41
|
+
RLSM::ArrayExt::permutations((1...order).to_a).map { |p| p.unshift 0 }
|
42
|
+
|
43
|
+
each_diagonal(order,permutations) do |diagonal|
|
44
|
+
each_with_diagonal(diagonal,permutations) do |table|
|
45
|
+
yield table
|
46
|
+
end
|
47
|
+
end
|
17
48
|
end
|
18
49
|
|
19
|
-
|
20
|
-
|
21
|
-
|
50
|
+
# Creates a new monoid.
|
51
|
+
# @see RLSM::Monoid#initialize
|
52
|
+
def [](table)
|
53
|
+
new(table)
|
54
|
+
end
|
22
55
|
|
23
|
-
#
|
24
|
-
|
56
|
+
# Creates the syntactic monoid to the given regular language.
|
57
|
+
#
|
58
|
+
# @param [String] regexp a regular expression.
|
59
|
+
#
|
60
|
+
# @raise [RLSM::Error] if given string isn't a valid regexp.
|
61
|
+
#
|
62
|
+
# @return [Monoid] the syntactic monoid of the given regular language.
|
63
|
+
def from_regexp(regexp)
|
64
|
+
RLSM::RegExp[regexp].to_monoid
|
65
|
+
end
|
25
66
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
67
|
+
# Creates the syntactic monoid to the given regular language.
|
68
|
+
#
|
69
|
+
# @param [String] dfa a deterministic finite automaton
|
70
|
+
#
|
71
|
+
# @raise [RLSM::Error] if given string isn't a valid DFA description.
|
72
|
+
#
|
73
|
+
# @return [Monoid] the syntactic monoid of the given regular language.
|
74
|
+
def from_dfa(dfa)
|
75
|
+
RLSM::DFA[dfa].to_monoid
|
30
76
|
end
|
77
|
+
|
31
78
|
end
|
32
79
|
|
80
|
+
#The elements of the monoid.
|
81
|
+
attr_reader :elements
|
33
82
|
|
34
|
-
|
83
|
+
#The order of the monoid.
|
84
|
+
attr_reader :order
|
35
85
|
|
36
|
-
#
|
37
|
-
|
38
|
-
new RLSM::BinaryOperation.new!(description)
|
39
|
-
end
|
86
|
+
#The transition table of the monoid.
|
87
|
+
attr_reader :table
|
40
88
|
|
41
|
-
#Creates a Monoid
|
42
|
-
#
|
43
|
-
#
|
89
|
+
# Creates a new Monoid from the given table.
|
90
|
+
# The table is interpreted as follows:
|
91
|
+
#
|
92
|
+
# * Case 1: Table is an Array.
|
93
|
+
#
|
94
|
+
# It is assumed, that the array is flat and that every entry
|
95
|
+
# is an entry in the transition table. The neutral element must
|
96
|
+
# be described in the first row and column.
|
97
|
+
#
|
98
|
+
# * Case 2: Table is a String.
|
99
|
+
#
|
100
|
+
# The string will be parsed into a flat Array.
|
101
|
+
# If commas are present, the String will be splitted at these,
|
102
|
+
# otherwise it will be splitted at each character.
|
103
|
+
# Whitespaces will be ignored or threted as commas.
|
104
|
+
# After parsing, the resulting array will be treated as in case 1.
|
105
|
+
#
|
106
|
+
# @example Some different ways to create a Monoid
|
107
|
+
# RLSM::Monoid.new [0,1,1,1]
|
108
|
+
# RLSM::Monoid.new "0110"
|
109
|
+
# RLSM::Monoid.new "01 10"
|
110
|
+
# RLSM::Monoid.new "0,1,1,0"
|
111
|
+
# RLSM::Monoid.new "0,1 1,0"
|
44
112
|
#
|
45
|
-
#
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
113
|
+
# @param [Array,String] table The transition table of the monoid, either as
|
114
|
+
# a flat Array or a string.
|
115
|
+
#
|
116
|
+
# @param [Boolean] validate If true, the given table will be validated.
|
117
|
+
#
|
118
|
+
# @raise [RLSM::Error] If validate is true and the given table isn't
|
119
|
+
# associative or has no neutral element or the neutral
|
120
|
+
# element isn't in the first row and column.
|
121
|
+
def initialize(table, validate = true)
|
122
|
+
@table = parse(table)
|
123
|
+
@order = Math::sqrt(@table.size).to_i
|
124
|
+
@elements = @table[0,@order].clone
|
125
|
+
@internal = {}
|
126
|
+
@elements.each_with_index { |x,i| @internal[x] = i }
|
127
|
+
@table.map! { |x| @internal[x] }
|
128
|
+
|
129
|
+
if validate
|
130
|
+
if @order == 0
|
131
|
+
raise RLSM::Error, "No elements given."
|
132
|
+
end
|
133
|
+
|
134
|
+
unless @table.size == @order**2
|
135
|
+
raise RLSM::Error, "Binary operation must be quadratic."
|
136
|
+
end
|
50
137
|
|
51
|
-
|
138
|
+
unless @table.uniq.size == @order
|
139
|
+
raise RLSM::Error, "Number of different elements is wrong."
|
140
|
+
end
|
141
|
+
|
142
|
+
enforce_identity_position(@table, @order)
|
143
|
+
|
144
|
+
nat = non_associative_triple
|
145
|
+
unless nat.nil?
|
146
|
+
err_str = "(#{nat[0]}#{nat[0]})#{nat[0]} != #{nat[0]}(#{nat[1]}#{nat[2]})"
|
147
|
+
raise RLSM::Error, "Associativity required, but #{err_str}."
|
148
|
+
end
|
149
|
+
end
|
52
150
|
end
|
53
151
|
|
54
152
|
|
55
|
-
|
56
|
-
|
57
|
-
#
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
@order = @elements.size
|
153
|
+
# Parses a transition table description.
|
154
|
+
#
|
155
|
+
# @param [Array,String] table the transition table description.
|
156
|
+
# @return [Array] The parsed description.
|
157
|
+
def parse(table)
|
158
|
+
return table if Array === table
|
62
159
|
|
63
|
-
|
160
|
+
if table.include?(',')
|
161
|
+
table.gsub(/\W/,',').squeeze(',').split(',').reject { |x| x.empty? }
|
162
|
+
else
|
163
|
+
table.gsub(/\W/,'').scan(/./)
|
164
|
+
end
|
64
165
|
end
|
65
|
-
|
166
|
+
|
66
167
|
#Calculates the product of the given elements.
|
168
|
+
# @return The result of the operation
|
169
|
+
# @raise [RLSM::Error] If at least one of the given elements isn't a element
|
170
|
+
# of the monoid or too few elements are given.
|
67
171
|
def [](*args)
|
68
172
|
case args.size
|
69
173
|
when 0,1
|
70
|
-
raise
|
174
|
+
raise RLSM::Error, "At least two elements must be provided."
|
71
175
|
when 2
|
72
|
-
|
176
|
+
begin
|
177
|
+
@elements[ @table[ @order*@internal[args[0]] + @internal[args[1]] ] ]
|
178
|
+
rescue
|
179
|
+
raise RLSM::Error, "Given arguments aren't monoid elements."
|
180
|
+
end
|
73
181
|
else
|
74
|
-
args[0,2] =
|
182
|
+
args[0,2] = self[ *args[0,2] ]
|
75
183
|
self[*args]
|
76
184
|
end
|
77
185
|
end
|
78
186
|
|
79
|
-
|
187
|
+
|
188
|
+
# Transforms monoid into a string.
|
189
|
+
#
|
190
|
+
# @return [String] The string representation of the monoid.
|
191
|
+
def to_s
|
192
|
+
result = ""
|
193
|
+
sep = @elements.any? { |x| x.to_s.length > 1 } ? ',' : ''
|
194
|
+
@table.each_with_index do |el,i|
|
195
|
+
result += @elements[el].to_s
|
196
|
+
if (i+1) % (@order) == 0
|
197
|
+
result += ' '
|
198
|
+
else
|
199
|
+
result += sep unless i == @order**2 - 1
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
result
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
# Transforms monoid into a string for debug purposes.
|
208
|
+
#
|
209
|
+
# @return [String] The string representation of the monoid.
|
210
|
+
def inspect
|
211
|
+
"<#{self.class}: #{to_s}>"
|
212
|
+
end
|
213
|
+
|
214
|
+
# Tests for monoid equality. Two monoids are equal if the have the same
|
215
|
+
# set of elements and the same transition table.
|
216
|
+
#
|
217
|
+
# @param [Monoid] other The righthand side of the equality test.
|
218
|
+
#
|
219
|
+
# @return [nil] if other isn't a monoid.
|
220
|
+
# @return [Boolean] the result of the equality check.
|
80
221
|
def ==(other)
|
81
222
|
return nil unless RLSM::Monoid === other
|
82
223
|
|
83
|
-
@
|
84
|
-
@
|
224
|
+
@table == other.table and
|
225
|
+
@elements == other.elements
|
85
226
|
end
|
86
227
|
|
87
|
-
#Checks if
|
228
|
+
# Checks if this monoid is a proper submonoid (i.e. not equal)
|
229
|
+
# of the other one.
|
230
|
+
#
|
231
|
+
# @param [Monoid] other Righthand side of the inequality test.
|
232
|
+
#
|
233
|
+
# @return [nil] if other isn't a monoid.
|
234
|
+
# @return [Boolean] the result of the inequality test.
|
88
235
|
def <(other)
|
89
236
|
return nil unless RLSM::Monoid === other
|
90
237
|
return false if @order >= other.order
|
@@ -93,7 +240,7 @@ module RLSM
|
|
93
240
|
@elements.each do |e2|
|
94
241
|
begin
|
95
242
|
return false if self[e1,e2] != other[e1,e2]
|
96
|
-
rescue
|
243
|
+
rescue RLSM::Error
|
97
244
|
return false
|
98
245
|
end
|
99
246
|
end
|
@@ -102,29 +249,82 @@ module RLSM
|
|
102
249
|
true
|
103
250
|
end
|
104
251
|
|
105
|
-
#Checks if
|
252
|
+
# Checks if this monoid is a submonoid of the other one.
|
253
|
+
#
|
254
|
+
# @param [Monoid] other Righthand side of the inequality test.
|
255
|
+
#
|
256
|
+
# @return [nil] if other isn't a monoid.
|
257
|
+
# @return [Boolean] the result of the inequality test.
|
106
258
|
def <=(other)
|
107
259
|
(self == other) || (self < other)
|
108
260
|
end
|
109
261
|
|
110
|
-
|
262
|
+
# Checks if the other monoid is a proper submonoid (i.e. not equal)
|
263
|
+
# of this one.
|
264
|
+
#
|
265
|
+
# @param [Monoid] other Righthand side of the inequality test.
|
266
|
+
#
|
267
|
+
# @return [nil] if other isn't a monoid.
|
268
|
+
# @return [Boolean] the result of the inequality test.
|
269
|
+
def >(other)
|
111
270
|
other < self
|
112
271
|
end
|
113
272
|
|
114
|
-
|
273
|
+
# Checks if the other monoid is a submonoid of this one.
|
274
|
+
#
|
275
|
+
# @param [Monoid] other Righthand side of the inequality test.
|
276
|
+
#
|
277
|
+
# @return [nil] if other isn't a monoid.
|
278
|
+
# @return [Boolean] the result of the inequality test.
|
279
|
+
def >=(other)
|
115
280
|
other <= self
|
281
|
+
endy
|
116
282
|
end
|
117
283
|
|
118
|
-
#
|
284
|
+
# @see RLSM::Monoid#isomorph?
|
285
|
+
def =~(other)
|
286
|
+
isomorph?(other)
|
287
|
+
end
|
288
|
+
|
289
|
+
# Checks if this monoid is isomorph to the other one.
|
119
290
|
#
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
291
|
+
# @param [Monoid] other Righthand side of the isomorphism check.
|
292
|
+
#
|
293
|
+
# @return [nil] if other isn't a monoid.
|
294
|
+
# @return [Boolean] the result of the isomorphism check.
|
295
|
+
def isomorph?(other)
|
296
|
+
return nil unless RLSM::Monoid === other
|
297
|
+
isomorphisms(other) { return true }
|
298
|
+
|
299
|
+
false
|
300
|
+
end
|
301
|
+
|
302
|
+
# Checks if this monoid is antiisomorph to the other one.
|
303
|
+
#
|
304
|
+
# @param [Monoid] other Righthand side of the isomorphism check.
|
305
|
+
#
|
306
|
+
# @return [nil] if other isn't a monoid.
|
307
|
+
# @return [Boolean] the result of the antiisomorphism check.
|
308
|
+
def antiisomorph?(other)
|
309
|
+
return nil unless RLSM::Monoid === other
|
310
|
+
|
311
|
+
antiisomorphisms(other) { return true }
|
312
|
+
|
313
|
+
false
|
314
|
+
end
|
127
315
|
|
316
|
+
# Calculates the set of elements which will be generated by the given set.
|
317
|
+
#
|
318
|
+
# @param [Array] set The elements which act as generators
|
319
|
+
#
|
320
|
+
# @return [Array] the generated set.
|
321
|
+
#
|
322
|
+
# @raise [RLSM::Error] if one of the elements isn't a monoid element.
|
323
|
+
#
|
324
|
+
# @see RLSM::Monoid#get_submonoid.
|
325
|
+
def generated_set(set)
|
326
|
+
gen_set = set | @elements[0,1]
|
327
|
+
|
128
328
|
unfinished = true
|
129
329
|
|
130
330
|
while unfinished
|
@@ -144,20 +344,33 @@ module RLSM
|
|
144
344
|
gen_set.sort(&element_sorter)
|
145
345
|
end
|
146
346
|
|
147
|
-
#
|
347
|
+
# Calculates the set of elements which will be generated by the given set.
|
348
|
+
#
|
349
|
+
# @param [Array] set The elements which act as generators
|
350
|
+
#
|
351
|
+
# @return [Monoid] the monoid generated by the given set.
|
352
|
+
#
|
353
|
+
# @raise [RLSM::Error] if one of the elements isn't a monoid element.
|
354
|
+
#
|
355
|
+
# @see RLSM::Monoid#get_generated_set.
|
148
356
|
def get_submonoid(set)
|
149
357
|
elements = generated_set(set)
|
150
358
|
|
151
359
|
set_to_monoid(elements)
|
152
360
|
end
|
153
361
|
|
154
|
-
#
|
362
|
+
# Calculates all submonoids.
|
363
|
+
#
|
364
|
+
# @return [Array] List with all submonoids in it.
|
155
365
|
def submonoids
|
156
366
|
candidates = get_submonoid_candidates
|
157
367
|
candidates.map { |set| set_to_monoid(set) }
|
158
368
|
end
|
159
369
|
|
160
|
-
#
|
370
|
+
# Calculates all proper submonoids (i.e. all submonoids without the
|
371
|
+
# monoid itself and the trivial one).
|
372
|
+
#
|
373
|
+
# @return [Array] List with all proper submonoids in it.
|
161
374
|
def proper_submonoids
|
162
375
|
candidates = get_submonoid_candidates.select do |cand|
|
163
376
|
cand.size > 1 and cand.size < @order
|
@@ -166,27 +379,21 @@ module RLSM
|
|
166
379
|
candidates.map { |set| set_to_monoid(set) }
|
167
380
|
end
|
168
381
|
|
169
|
-
#
|
382
|
+
# Finds the smallest set which generates the whole monoid
|
383
|
+
# (smallest in the sense of cardinality of the set).
|
384
|
+
#
|
385
|
+
# @return [Array] A set of elements which generates the whole monoid.
|
170
386
|
def generating_subset
|
171
387
|
sorted_subsets.find { |set| generated_set(set).size == @order }
|
172
388
|
end
|
173
389
|
|
174
|
-
#
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
#
|
180
|
-
|
181
|
-
self =~ other
|
182
|
-
end
|
183
|
-
|
184
|
-
#Checks if +self+ is antiisomorph to +other+.
|
185
|
-
def antiisomorph?(other)
|
186
|
-
bijective_maps_to(other).any? { |map| antiisomorphism?(map,other) }
|
187
|
-
end
|
188
|
-
|
189
|
-
#If an argument is given, checks if this element is idempotent. Otherwise checks if the monoid itself is idempotent.
|
390
|
+
# @overload idempotent?
|
391
|
+
# Checks if the monoid is idempotent (i.e. all elements are idempotent).
|
392
|
+
#
|
393
|
+
# @overload idempotent?(element)
|
394
|
+
# Checks if given element is idempotent.
|
395
|
+
#
|
396
|
+
# @return [Boolean] result of the check
|
190
397
|
def idempotent?(element = nil)
|
191
398
|
if element
|
192
399
|
self[element,element] == element
|
@@ -195,22 +402,46 @@ module RLSM
|
|
195
402
|
end
|
196
403
|
end
|
197
404
|
|
198
|
-
#
|
405
|
+
# Calculates the order of the monoid.
|
406
|
+
#
|
407
|
+
# @param element An element of the monoid.
|
408
|
+
#
|
409
|
+
# @raise [RLSM::Error] if the element isn't a monoid element.
|
410
|
+
#
|
411
|
+
# @return [Integer] Order of the given element.
|
199
412
|
def order_of(element)
|
200
413
|
generated_set([element]).size
|
201
414
|
end
|
202
415
|
|
203
|
-
#
|
416
|
+
# Calculates the principal right ideal of the given element.
|
417
|
+
#
|
418
|
+
# @param element An element of the monoid.
|
419
|
+
#
|
420
|
+
# @raise [RLSM::Error] if the element isn't a monoid element.
|
421
|
+
#
|
422
|
+
# @return [Array] Principle right ideal of the given element.
|
204
423
|
def right_ideal(element)
|
205
424
|
@elements.map { |el| self[element,el] }.uniq.sort(&element_sorter)
|
206
425
|
end
|
207
426
|
|
208
|
-
#
|
427
|
+
# Calculates the principal left ideal of the given element.
|
428
|
+
#
|
429
|
+
# @param element An element of the monoid.
|
430
|
+
#
|
431
|
+
# @raise [RLSM::Error] if the element isn't a monoid element.
|
432
|
+
#
|
433
|
+
# @return [Array] Principle left ideal of the given element.
|
209
434
|
def left_ideal(element)
|
210
435
|
@elements.map { |el| self[el,element] }.uniq.sort(&element_sorter)
|
211
436
|
end
|
212
437
|
|
213
|
-
#
|
438
|
+
# Calculates the principal (twosided) ideal of the given element.
|
439
|
+
#
|
440
|
+
# @param element An element of the monoid.
|
441
|
+
#
|
442
|
+
# @raise [RLSM::Error] if the element isn't a monoid element.
|
443
|
+
#
|
444
|
+
# @return [Array] Principle ideal of the given element.
|
214
445
|
def ideal(element)
|
215
446
|
result = []
|
216
447
|
@elements.each do |el1|
|
@@ -222,17 +453,33 @@ module RLSM
|
|
222
453
|
result.uniq.sort(&element_sorter)
|
223
454
|
end
|
224
455
|
|
225
|
-
#
|
456
|
+
# The neutral element of the monoid.
|
457
|
+
#
|
458
|
+
# @return neutral element of the monoid.
|
226
459
|
def identity
|
227
460
|
@elements.first
|
228
461
|
end
|
229
462
|
|
230
|
-
#Checks if
|
463
|
+
# Checks if given element is the neutral element.
|
464
|
+
#
|
465
|
+
# @param element An element of the monoid.
|
466
|
+
#
|
467
|
+
# @return [Boolean] Result of the check.
|
231
468
|
def identity?(element)
|
232
469
|
element == identity
|
233
470
|
end
|
234
471
|
|
235
|
-
#
|
472
|
+
# @overload zero?
|
473
|
+
# Checks if the monoid has a zero element.
|
474
|
+
#
|
475
|
+
# @overload zero?(element)
|
476
|
+
# Checks if the given element is the zero element.
|
477
|
+
#
|
478
|
+
# @param element An element of the monoid.
|
479
|
+
#
|
480
|
+
# @raise [RLSM::Error] if element isn't a monoid element
|
481
|
+
#
|
482
|
+
# @return [Boolean] Result of the check.
|
236
483
|
def zero?(element = nil)
|
237
484
|
if element
|
238
485
|
return false if @order == 1
|
@@ -244,140 +491,257 @@ module RLSM
|
|
244
491
|
end
|
245
492
|
end
|
246
493
|
|
247
|
-
#
|
494
|
+
# Calculates the zero element of the monoid (if it exists).
|
495
|
+
#
|
496
|
+
# @return [nil] if the monoid has no zero element
|
497
|
+
# @return [Object] the zero element
|
248
498
|
def zero
|
249
499
|
@elements.find { |el| zero?(el) }
|
250
500
|
end
|
251
501
|
|
252
|
-
#Checks if
|
502
|
+
# Checks if the given element is a left zero element.
|
503
|
+
#
|
504
|
+
# @param element An element of the monoid.
|
505
|
+
#
|
506
|
+
# @raise [RLSM::Error] if element isn't a monoid element
|
507
|
+
#
|
508
|
+
# @return [Boolean] Result of the check.
|
253
509
|
def left_zero?(element)
|
254
510
|
return false if @order == 1
|
255
511
|
@elements.all? { |x| self[element,x] == element }
|
256
512
|
end
|
257
513
|
|
258
|
-
#Checks if
|
514
|
+
# Checks if the given element is a right zero element.
|
515
|
+
#
|
516
|
+
# @param element An element of the monoid.
|
517
|
+
#
|
518
|
+
# @raise [RLSM::Error] if element isn't a monoid element
|
519
|
+
#
|
520
|
+
# @return [Boolean] Result of the check.
|
259
521
|
def right_zero?(element)
|
260
522
|
return false if @order == 1
|
261
523
|
@elements.all? { |x| self[x,element] == element }
|
262
524
|
end
|
263
525
|
|
264
|
-
#
|
526
|
+
# Calculates all right zero elements of the monoid.
|
527
|
+
#
|
528
|
+
# @return [Array] the right zero elements of the monoid.
|
265
529
|
def right_zeros
|
266
530
|
@elements.select { |el| right_zero?(el) }
|
267
531
|
end
|
268
532
|
|
269
|
-
#
|
533
|
+
# Calculates all left zero elements of the monoid.
|
534
|
+
#
|
535
|
+
# @return [Array] the left zero elements of the monoid.
|
270
536
|
def left_zeros
|
271
537
|
@elements.select { |el| left_zero?(el) }
|
272
538
|
end
|
273
539
|
|
274
|
-
#
|
540
|
+
# Calculates all idempotent elements of the monoid.
|
541
|
+
#
|
542
|
+
# @return [Array] the idempotent elements of the monoid.
|
275
543
|
def idempotents
|
276
544
|
@elements.select { |el| idempotent?(el) }
|
277
545
|
end
|
278
546
|
|
279
|
-
#Checks if the monoid is a group.
|
547
|
+
# Checks if the monoid is a group.
|
548
|
+
#
|
549
|
+
# @return [Boolean] Result of the check.
|
280
550
|
def group?
|
281
551
|
idempotents.size == 1
|
282
552
|
end
|
283
553
|
|
284
|
-
#Checks if the monoid is commutative.
|
554
|
+
# Checks if the monoid is commutative.
|
555
|
+
#
|
556
|
+
# @return [Boolean] Result of the check.
|
285
557
|
def commutative?
|
286
|
-
|
558
|
+
is_commutative
|
287
559
|
end
|
288
560
|
|
289
|
-
#Checks if the monoid is monogenic
|
561
|
+
# Checks if the monoid is monogenic (i.e. generated by a single element).
|
562
|
+
#
|
563
|
+
# @return [Boolean] Result of the check.
|
290
564
|
def monogenic?
|
291
565
|
generating_subset.size == 1
|
292
566
|
end
|
293
567
|
|
294
|
-
#Calculates the L-class of an element.
|
568
|
+
# Calculates the L-class of an element.
|
569
|
+
#
|
570
|
+
# @param element An element of the monoid.
|
571
|
+
#
|
572
|
+
# @raise [RLSM::Error] if the element isn't a monoid element.
|
573
|
+
#
|
574
|
+
# @return [Array] L-class of the given element.
|
295
575
|
def l_class(element)
|
296
576
|
li = left_ideal(element)
|
297
577
|
@elements.select { |el| left_ideal(el) == li }
|
298
578
|
end
|
299
579
|
|
300
|
-
#Calculates the R-class of an element.
|
580
|
+
# Calculates the R-class of an element.
|
581
|
+
#
|
582
|
+
# @param element An element of the monoid.
|
583
|
+
#
|
584
|
+
# @raise [RLSM::Error] if the element isn't a monoid element.
|
585
|
+
#
|
586
|
+
# @return [Array] R-class of the given element.
|
301
587
|
def r_class(element)
|
302
588
|
r = right_ideal(element)
|
303
589
|
@elements.select { |el| right_ideal(el) == r }
|
304
590
|
end
|
305
591
|
|
306
|
-
#Calculates the J-class of an element.
|
592
|
+
# Calculates the J-class of an element.
|
593
|
+
#
|
594
|
+
# @param element An element of the monoid.
|
595
|
+
#
|
596
|
+
# @raise [RLSM::Error] if the element isn't a monoid element.
|
597
|
+
#
|
598
|
+
# @return [Array] J-class of the given element.
|
307
599
|
def j_class(element)
|
308
600
|
d = ideal(element)
|
309
601
|
@elements.select { |el| ideal(el) == d }
|
310
602
|
end
|
311
603
|
|
312
|
-
#Calculates the H-class of an element.
|
604
|
+
# Calculates the H-class of an element.
|
605
|
+
#
|
606
|
+
# @param element An element of the monoid.
|
607
|
+
#
|
608
|
+
# @raise [RLSM::Error] if the element isn't a monoid element.
|
609
|
+
#
|
610
|
+
# @return [Array] H-class of the given element.
|
313
611
|
def h_class(element)
|
314
612
|
l_class(element) & r_class(element)
|
315
613
|
end
|
316
|
-
|
317
|
-
#
|
614
|
+
|
615
|
+
# Calculates the D-class of an element.
|
616
|
+
# Synonym for j_class (J and D relation are the same for a finite monoid).
|
617
|
+
#
|
618
|
+
# @param element An element of the monoid.
|
619
|
+
#
|
620
|
+
# @raise [RLSM::Error] if the element isn't a monoid element.
|
621
|
+
#
|
622
|
+
# @return [Array] D-class of the given element.
|
318
623
|
def d_class(element)
|
319
624
|
j_class(element)
|
320
625
|
end
|
321
626
|
|
322
|
-
#
|
323
|
-
|
324
|
-
|
627
|
+
# Calculates all R-classes of the monoid.
|
628
|
+
#
|
629
|
+
# @return [Array] List of all R-classes.
|
630
|
+
def r_classes
|
631
|
+
not_tested = @elements.dup
|
632
|
+
classes = []
|
633
|
+
|
634
|
+
until not_tested.empty?
|
635
|
+
classes << r_class(not_tested.first)
|
636
|
+
not_tested = not_tested.reject { |el| classes.last.include? el }
|
637
|
+
end
|
638
|
+
|
639
|
+
classes.sort(&subset_sorter)
|
325
640
|
end
|
326
641
|
|
327
|
-
|
328
|
-
#
|
329
|
-
#
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
# Returns all H-classes of the monoid.
|
342
|
-
|
343
|
-
##
|
344
|
-
# :method: d_classes
|
345
|
-
# Returns all D-classes of the monoid.
|
642
|
+
# Calculates all L-classes of the monoid.
|
643
|
+
#
|
644
|
+
# @return [Array] List of all L-classes.
|
645
|
+
def l_classes
|
646
|
+
not_tested = @elements.dup
|
647
|
+
classes = []
|
648
|
+
|
649
|
+
until not_tested.empty?
|
650
|
+
classes << l_class(not_tested.first)
|
651
|
+
not_tested = not_tested.reject { |el| classes.last.include? el }
|
652
|
+
end
|
653
|
+
|
654
|
+
classes.sort(&subset_sorter)
|
655
|
+
end
|
346
656
|
|
347
|
-
|
348
|
-
#
|
349
|
-
#
|
657
|
+
# Calculates all J-classes of the monoid.
|
658
|
+
#
|
659
|
+
# @return [Array] List of all J-classes.
|
660
|
+
def j_classes
|
661
|
+
not_tested = @elements.dup
|
662
|
+
classes = []
|
663
|
+
|
664
|
+
until not_tested.empty?
|
665
|
+
classes << j_class(not_tested.first)
|
666
|
+
not_tested = not_tested.reject { |el| classes.last.include? el }
|
667
|
+
end
|
668
|
+
|
669
|
+
classes.sort(&subset_sorter)
|
670
|
+
end
|
350
671
|
|
351
|
-
|
352
|
-
#
|
353
|
-
#
|
672
|
+
# Calculates all H-classes of the monoid.
|
673
|
+
#
|
674
|
+
# @return [Array] List of all H-classes.
|
675
|
+
def h_classes
|
676
|
+
not_tested = @elements.dup
|
677
|
+
classes = []
|
678
|
+
|
679
|
+
until not_tested.empty?
|
680
|
+
classes << h_class(not_tested.first)
|
681
|
+
not_tested = not_tested.reject { |el| classes.last.include? el }
|
682
|
+
end
|
683
|
+
|
684
|
+
classes.sort(&subset_sorter)
|
685
|
+
end
|
354
686
|
|
355
|
-
|
356
|
-
#
|
357
|
-
#
|
687
|
+
# Calculates all D-classes of the monoid.
|
688
|
+
#
|
689
|
+
# @return [Array] List of all D-classes.
|
690
|
+
def d_classes
|
691
|
+
j_classes
|
692
|
+
end
|
693
|
+
|
694
|
+
# Checks if all R-classes of the monoid are singleton sets.
|
695
|
+
#
|
696
|
+
# @return [Boolean] Result of the check.
|
697
|
+
def r_trivial?
|
698
|
+
@elements.all? { |el| r_class(el).size == 1 }
|
699
|
+
end
|
700
|
+
|
701
|
+
# Checks if all L-classes of the monoid are singleton sets.
|
702
|
+
#
|
703
|
+
# @return [Boolean] Result of the check.
|
704
|
+
def l_trivial?
|
705
|
+
@elements.all? { |el| l_class(el).size == 1 }
|
706
|
+
end
|
358
707
|
|
359
|
-
|
360
|
-
#
|
361
|
-
#
|
708
|
+
# Checks if all J-classes of the monoid are singleton sets.
|
709
|
+
#
|
710
|
+
# @return [Boolean] Result of the check.
|
711
|
+
def j_trivial?
|
712
|
+
@elements.all? { |el| j_class(el).size == 1 }
|
713
|
+
end
|
362
714
|
|
363
|
-
|
364
|
-
#
|
365
|
-
#
|
715
|
+
# Checks if all H-classes of the monoid are singleton sets.
|
716
|
+
#
|
717
|
+
# @return [Boolean] Result of the check.
|
718
|
+
def h_trivial?
|
719
|
+
@elements.all? { |el| h_class(el).size == 1 }
|
720
|
+
end
|
366
721
|
|
367
|
-
|
368
|
-
#
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
722
|
+
# Checks if all D-classes of the monoid are singleton sets.
|
723
|
+
#
|
724
|
+
# @return [Boolean] Result of the check.
|
725
|
+
def d_trivial?
|
726
|
+
@elements.all? { |el| d_class(el).size == 1 }
|
727
|
+
end
|
728
|
+
|
729
|
+
# Checks if the monoid is aperiodic (i.e. H-trivial).
|
730
|
+
#
|
731
|
+
# @return [Boolean] Result of the check.
|
732
|
+
#
|
733
|
+
# @see RLSM::Monoid#h_trivial?
|
734
|
+
def aperiodic?
|
735
|
+
h_trivial?
|
378
736
|
end
|
379
737
|
|
380
|
-
#Checks if the given set is a disjunctive subset.
|
738
|
+
# Checks if the given set is a disjunctive subset.
|
739
|
+
#
|
740
|
+
# @param [Array] set A set of monoid elements
|
741
|
+
#
|
742
|
+
# @raise [RLSM::Error] If one of the given elements isn't a monoid element.
|
743
|
+
#
|
744
|
+
# @return [Boolean] Result of the check.
|
381
745
|
def subset_disjunctive?(set)
|
382
746
|
tupels = []
|
383
747
|
@elements.each do |el1|
|
@@ -393,51 +757,59 @@ module RLSM
|
|
393
757
|
end
|
394
758
|
end
|
395
759
|
|
396
|
-
#
|
760
|
+
# Calculate a disjunctive subset if one exists.
|
761
|
+
#
|
762
|
+
# @return [nil] if no disjunctive subset exists.
|
763
|
+
# @return [Array] a disjunctive subset.
|
397
764
|
def disjunctive_subset
|
398
|
-
@elements.
|
765
|
+
RLSM::ArrayExt::powerset(@elements).find { |s| subset_disjunctive? s }
|
399
766
|
end
|
400
767
|
|
401
|
-
#
|
768
|
+
# Calculate all disjunctive subsets.
|
769
|
+
#
|
770
|
+
# @return [Array] all disjunctive subsets.
|
402
771
|
def all_disjunctive_subsets
|
403
|
-
@elements.
|
772
|
+
RLSM::ArrayExt::powerset(@elements).select { |s| subset_disjunctive? s }
|
404
773
|
end
|
405
774
|
|
406
|
-
#Checks if the monoid is syntactic, i.e. if it has a disjunctive subset.
|
775
|
+
# Checks if the monoid is syntactic, i.e. if it has a disjunctive subset.
|
776
|
+
#
|
777
|
+
# @return [Boolean] Result of the check.
|
407
778
|
def syntactic?
|
408
779
|
!!disjunctive_subset
|
409
780
|
end
|
410
781
|
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
782
|
+
# Transforms the monoid to a regexp which has this monoid as its syntactic
|
783
|
+
# monoid. Each disjunctive subset may yield a different language.
|
784
|
+
# If no argument is given, the smallest disjunctive subset is used.
|
785
|
+
#
|
786
|
+
# @raise [RLSM::Error] If given set isn't a disjunctive subset or the monoid
|
787
|
+
# isn't syntactic.
|
788
|
+
#
|
789
|
+
# @param [Array] set A disjunctive subset of the monoid.
|
790
|
+
#
|
791
|
+
# @return [RLSM::RegExp] A regular expression which has this monoi as the
|
792
|
+
# syntactic monoid.
|
793
|
+
def to_regexp(set = nil)
|
794
|
+
unless syntactic?
|
795
|
+
raise RLSM::Error, "Only syntactic monoids may be transformed to regexp"
|
421
796
|
end
|
422
|
-
|
423
|
-
|
424
|
-
end
|
425
|
-
|
426
|
-
def inspect # :nodoc:
|
427
|
-
"<#{self.class}: #{to_s}>"
|
428
|
-
end
|
429
|
-
|
430
|
-
#Returns the monoid.
|
431
|
-
def to_monoid
|
432
|
-
self
|
433
|
-
end
|
434
|
-
|
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
|
797
|
+
|
798
|
+
to_dfa(set).to_regexp
|
438
799
|
end
|
439
800
|
|
440
|
-
#
|
801
|
+
# Transform this monoid into a minimal DFA, which has this monoid as
|
802
|
+
# transition monoid.
|
803
|
+
#
|
804
|
+
# @param [Array] finals The set of final states in the resulting DFA.
|
805
|
+
# Must be a disjunctive subset. If no subset is given,
|
806
|
+
# the smallest disjunctive subset is used.
|
807
|
+
#
|
808
|
+
# @raise [RLSM::Error] If given set isn't a disjunctive subset of the monoid
|
809
|
+
# or the monoid isn't syntactic.
|
810
|
+
#
|
811
|
+
# @return [RLSM::DFA] A minimal DFA which has this monoid as its transition
|
812
|
+
# monoid.
|
441
813
|
def to_dfa(finals = nil)
|
442
814
|
finals = finals || disjunctive_subset || []
|
443
815
|
|
@@ -462,7 +834,37 @@ module RLSM
|
|
462
834
|
RLSM::DFA.new string
|
463
835
|
end
|
464
836
|
|
465
|
-
|
837
|
+
# @overload regular?
|
838
|
+
# Checks if the monoid is regular.
|
839
|
+
#
|
840
|
+
# @overload regular?(a)
|
841
|
+
# Checks if given element is regular.
|
842
|
+
#
|
843
|
+
# @param a an element of the monoid
|
844
|
+
# @raise [RLSM::Error] if given element isn't a monoid element.
|
845
|
+
# @return [Boolean] Result of the check.
|
846
|
+
def regular?(a=nil)
|
847
|
+
if a.nil?
|
848
|
+
@elements.all? { |x| regular?(x) }
|
849
|
+
else
|
850
|
+
@elements.any? { |x| self[a,x,a] == a}
|
851
|
+
end
|
852
|
+
end
|
853
|
+
|
854
|
+
# Checks if the monoid is inverse.
|
855
|
+
#
|
856
|
+
# @return [Boolean] Result of the check.
|
857
|
+
def inverse?
|
858
|
+
regular? and
|
859
|
+
idempotents.all? { |x| idempotents.all? { |y| self[x,y] == self[y,x] } }
|
860
|
+
end
|
861
|
+
|
862
|
+
|
863
|
+
# Transforms a given subset of the elements to a submonoid.
|
864
|
+
#
|
865
|
+
# @raise [RLSM::Error] if set isn't a subset of the monoid elements or the
|
866
|
+
# set isn't a submonoid.
|
867
|
+
# @return [Monoid] the submonoid formed by the given set.
|
466
868
|
def set_to_monoid(set)
|
467
869
|
description = set.map do |el1|
|
468
870
|
set.map { |el2| self[el1,el2] }.join(",")
|
@@ -471,10 +873,19 @@ module RLSM
|
|
471
873
|
RLSM::Monoid[ description.join(' ') ]
|
472
874
|
end
|
473
875
|
|
876
|
+
|
877
|
+
# The order in the transition table is the natural order for the elements.
|
878
|
+
#
|
879
|
+
# @return [Proc] As argument for the sort method, elements will be compared
|
880
|
+
# by their indices rather than by their names.
|
474
881
|
def element_sorter
|
475
882
|
Proc.new { |el1,el2| @elements.index(el1) <=> @elements.index(el2)}
|
476
883
|
end
|
477
884
|
|
885
|
+
# Subsets are first ordered by size and then lexicographically.
|
886
|
+
#
|
887
|
+
# @return [Proc] As argument for the sort method, subsets will be sorted
|
888
|
+
# first by size, then lexicographically.
|
478
889
|
def subset_sorter
|
479
890
|
Proc.new do |set1,set2|
|
480
891
|
if set1.size == set2.size
|
@@ -486,16 +897,23 @@ module RLSM
|
|
486
897
|
end
|
487
898
|
end
|
488
899
|
|
900
|
+
# Calculates a list of all subsets and sorts them.
|
901
|
+
#
|
902
|
+
# @return [Array] List of subsets sorted by size and lexicographically
|
489
903
|
def sorted_subsets
|
490
|
-
subsets = @elements
|
904
|
+
subsets = RLSM::ArrayExt::powerset(@elements)
|
491
905
|
|
492
906
|
subsets.sort(&subset_sorter)
|
493
907
|
end
|
494
|
-
|
908
|
+
|
909
|
+
# Calculates a list of sets of elements which form a submonoid.
|
910
|
+
# Sorts the result.
|
911
|
+
#
|
912
|
+
# @return [Array] List of sets of elements which form a submonoid.
|
495
913
|
def get_submonoid_candidates
|
496
914
|
submons = []
|
497
915
|
|
498
|
-
@elements.
|
916
|
+
RLSM::ArrayExt::powerset(@elements).each do |set|
|
499
917
|
candidate = generated_set(set)
|
500
918
|
submons << candidate unless submons.include? candidate
|
501
919
|
end
|
@@ -503,48 +921,40 @@ module RLSM
|
|
503
921
|
submons.sort(&subset_sorter)
|
504
922
|
end
|
505
923
|
|
506
|
-
|
924
|
+
# Iterates over all isomorphisms form this monoid to the other.
|
925
|
+
#
|
926
|
+
# @yield [isomorphism] An isomorphism from this monoid to the other.
|
927
|
+
def isomorphisms(other)
|
507
928
|
return [] if @order != other.order
|
508
929
|
|
509
|
-
other.elements.
|
510
|
-
Hash[*@elements.zip(perm).flatten]
|
511
|
-
end
|
512
|
-
end
|
930
|
+
RLSM::ArrayExt::permutations(other.elements).map do |perm|
|
931
|
+
map = Hash[*@elements.zip(perm).flatten]
|
513
932
|
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
933
|
+
if @elements.all? { |x| @elements.all? { |y|
|
934
|
+
map[self[x,y]] == other[map[x],map[y]]
|
935
|
+
} }
|
936
|
+
yield map
|
518
937
|
end
|
519
938
|
end
|
520
|
-
|
521
|
-
true
|
522
939
|
end
|
523
940
|
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
end
|
941
|
+
# Iterates over all antiisomorphisms form this monoid to the other.
|
942
|
+
#
|
943
|
+
# @yield [isomorphism] An antiisomorphism from this monoid to the other.
|
944
|
+
def antiisomorphisms(other)
|
945
|
+
return [] if @order != other.order
|
530
946
|
|
531
|
-
|
532
|
-
|
947
|
+
RLSM::ArrayExt::permutations(other.elements).map do |perm|
|
948
|
+
map = Hash[*@elements.zip(perm).flatten]
|
533
949
|
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
classes << self.send((type + '_class').to_sym, not_tested.first)
|
540
|
-
not_tested = not_tested.reject { |el| classes.last.include? el }
|
950
|
+
if @elements.all? { |x| @elements.all? { |y|
|
951
|
+
map[self[x,y]] == other[map[y],map[x]]
|
952
|
+
} }
|
953
|
+
yield map
|
954
|
+
end
|
541
955
|
end
|
542
|
-
|
543
|
-
classes.sort(&subset_sorter)
|
544
956
|
end
|
957
|
+
|
545
958
|
|
546
|
-
def green_trivial?(type)
|
547
|
-
@elements.all? { |el| self.send((type + '_class').to_sym, el).size == 1 }
|
548
|
-
end
|
549
959
|
end # of class Monoid
|
550
960
|
end # of module RLSM
|