rlsm 1.1.0 → 1.8.1
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.
- 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
|