Skip to content

Commit 4d7adea

Browse files
committed
One final round of small cleanups for Monoids benchmark
1 parent 46677a5 commit 4d7adea

File tree

8 files changed

+104
-115
lines changed

8 files changed

+104
-115
lines changed

benchmark/multi-source/Monoids/Automaton.swift

Lines changed: 46 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -82,85 +82,69 @@ extension Automaton {
8282
}
8383
}
8484

85-
/// Constructs an automaton to recognize the complement of this regular set:
86-
///
87-
/// .*(x1|x2|...).*
88-
///
89-
/// where 'words' is [x1, x2, ...].
90-
///
91-
/// This is Lemma 2.1.3 in:
92-
///
93-
/// String Rewriting Systems, R.V. Book, F. Otto 1993. Springer New York.
94-
func buildAutomaton(_ words: [Word], _ alphabet: Int) -> Automaton {
95-
// Proper prefixes of each word.
96-
var prefixes = Set<Word>()
97-
98-
var result = Automaton()
99-
100-
func isIrreducible(_ word: Word) -> Bool {
101-
for i in 0 ..< word.count {
102-
for other in words {
103-
if i + other.count <= word.count {
104-
if Word(word[i ..< (i + other.count)]) == other {
105-
return false
106-
}
107-
}
108-
}
109-
}
85+
extension RewritingSystem {
86+
/// Constructs an automaton to recognize the complement of this regular set:
87+
///
88+
/// .*(x1|x2|...).*
89+
///
90+
/// where 'words' is [x1, x2, ...].
91+
///
92+
/// This is Lemma 2.1.3 in:
93+
///
94+
/// String Rewriting Systems, R.V. Book, F. Otto 1993. Springer New York.
95+
func buildAutomaton() -> Automaton {
96+
// Proper prefixes of each word.
97+
var prefixes = Set<Word>()
11098

111-
return true
112-
}
99+
var result = Automaton()
113100

114-
prefixes.insert([])
115-
for word in words {
116-
for i in 0 ..< word.count {
117-
let prefix = Word(word[0 ..< i])
118-
prefixes.insert(prefix)
101+
func isIrreducible(_ word: Word) -> Bool {
102+
return reduceOne(word) == nil
119103
}
120-
}
121104

122-
result.states = prefixes.sorted { compare($0, $1, order: .shortlex) == .lessThan }
105+
prefixes.insert([])
106+
for rule in presentation.rules {
107+
let word = rule.lhs
108+
for i in 0 ..< word.count {
109+
let prefix = Word(word[0 ..< i])
110+
prefixes.insert(prefix)
111+
}
112+
}
123113

124-
for prefix in prefixes {
125-
for x in 0 ..< UInt8(alphabet) {
126-
let word = prefix + [x]
114+
result.states = prefixes.sorted { compare($0, $1, order: .shortlex) == .lessThan }
127115

128-
if prefixes.contains(word) {
129-
result.transitions.append((prefix, x, word))
130-
continue
131-
}
116+
for prefix in prefixes {
117+
for x in 0 ..< UInt8(alphabet) {
118+
let word = prefix + [x]
132119

133-
if !isIrreducible(word) {
134-
continue
135-
}
120+
if prefixes.contains(word) {
121+
result.transitions.append((prefix, x, word))
122+
continue
123+
}
136124

137-
for i in 1 ... word.count {
138-
let suffix = Word(word[i...])
125+
if !isIrreducible(word) {
126+
continue
127+
}
128+
129+
for i in 1 ... word.count {
130+
let suffix = Word(word[i...])
139131

140-
if prefixes.contains(suffix) {
141-
result.transitions.append((prefix, x, suffix))
142-
break
132+
if prefixes.contains(suffix) {
133+
result.transitions.append((prefix, x, suffix))
134+
break
135+
}
143136
}
144137
}
145138
}
146-
}
147-
148-
return result
149-
}
150139

151-
extension Presentation {
152-
/// The Irr(R) automaton.
153-
func automaton(alphabet: Int) -> Automaton {
154-
return buildAutomaton(rules.map { $0.lhs }, alphabet)
140+
return result
155141
}
156142

157143
/// Returns the number of irreducible words in this monoid presentation, or
158144
/// nil if this set is infinite.
159-
///
160-
/// If the presentation is complete, this is the cardinality of the
161-
/// presented monoid. Otherwise, it is an upper bound.
162-
func cardinality(alphabet: Int) -> Int? {
163-
let automaton = automaton(alphabet: alphabet)
145+
var cardinality: Int? {
146+
precondition(state == .complete)
147+
let automaton = buildAutomaton()
164148
if automaton.hasStar {
165149
return nil
166150
}

benchmark/multi-source/Monoids/Enumeration.swift

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ struct RuleShape {
5454
struct PresentationShape {
5555
var rules: [RuleShape]
5656

57-
var presentation: Presentation {
58-
return Presentation(rules: rules.map { $0.rule })
57+
func presentation(alphabet: Int) -> Presentation {
58+
return Presentation(alphabet: alphabet,
59+
rules: rules.map { $0.rule })
5960
}
6061
}
6162

@@ -72,7 +73,7 @@ func enumerateAll(alphabet: Int, shapes: [PresentationShape], output: Bool)
7273
let perms = allPermutations(alphabet)
7374

7475
for shape in shapes {
75-
var p = shape.presentation
76+
var p = shape.presentation(alphabet: alphabet)
7677
var done = false
7778

7879
loop: while !done {
@@ -90,13 +91,14 @@ loop: while !done {
9091
}
9192

9293
for rule in p.rules {
94+
precondition(rule.rhs.count <= rule.lhs.count)
9395
if compare(rule.rhs, rule.lhs, order: .shortlex) != .lessThan {
9496
filteredRHS += 1
9597
continue loop
9698
}
9799
}
98100

99-
if unique.contains(p.sorted(order: .shortlex)) {
101+
if unique.contains(p) {
100102
filteredSymmetry += 1
101103
continue
102104
}
@@ -106,7 +108,6 @@ loop: while !done {
106108
unique.insert(permuted.sorted(order: .shortlex))
107109
}
108110

109-
precondition(p == p.sorted(order: .shortlex))
110111
instances.append(p)
111112
}
112113
}
@@ -126,9 +127,12 @@ func ruleShapes(_ n: Int) -> [RuleShape] {
126127
for i in 0 ..< n {
127128
let j = n - i
128129

129-
// Don't generate rules with shorter left-hand side.
130+
// Don't consider rules with a shorter left-hand side.
130131
if j < i { continue }
131132

133+
// Don't consider rules of the form x=1 or x=y where x, y are generators.
134+
if j == 1 && i <= 1 { continue }
135+
132136
result.append(RuleShape(lhs: j, rhs: i))
133137
}
134138
return result

benchmark/multi-source/Monoids/Monoids.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,18 @@ func run(output: Bool) async {
1616
await solver.solve()
1717

1818
// These we know we can't solve.
19-
let expect: [Presentation] = ["bab=aaa,bbbb=1", "aaaa=1,abbba=b", "aaa=a,abba=bb"]
19+
let expectUnsolved: [Presentation] = ["bab=aaa,bbbb=1", "aaaa=1,abbba=b", "aaa=a,abba=bb"]
2020

2121
// Make sure everything else was solved.
2222
let unsolved = solver.subset.map { instances[$0] }
23-
if unsolved != expect {
24-
fatalError("Expected \(expect), but got \(unsolved)")
23+
if unsolved != expectUnsolved {
24+
fatalError("Expected \(expectUnsolved), but got \(unsolved)")
25+
}
26+
27+
// Also check finite monoid results.
28+
let expectFinite = [1: 1188, 2: 2059, 3: 1233, 4: 1644, 5: 1019, 6: 1630, 7: 686, 8: 884, 9: 493, 10: 615, 11: 191, 12: 317, 13: 79, 14: 134, 15: 62, 16: 158, 17: 16, 18: 60, 19: 2, 20: 52, 21: 38, 22: 12, 24: 31, 25: 5, 26: 11, 27: 42, 28: 10, 30: 52, 32: 5, 34: 8, 36: 17, 39: 4, 42: 4, 44: 8, 48: 12, 50: 12, 52: 4, 55: 4, 56: 8, 60: 18, 64: 14, 81: 6, 84: 22, 96: 1, 100: 4, 105: 2, 120: 7, 129: 2, 147: 6, 160: 2, 165: 2, 192: 2, 195: 2, 320: 2, 324: 2, 339: 4, 605: 2, 1083: 2]
29+
let finite = solver.finite
30+
if finite != expectFinite {
31+
fatalError("Expected \(expectFinite), but got \(finite)")
2532
}
2633
}

benchmark/multi-source/Monoids/Presentation.swift

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,9 @@ extension Rule: ExpressibleByStringLiteral, CustomStringConvertible {
4949
}
5050

5151
struct Presentation: Hashable {
52+
var alphabet: Int
5253
var rules: [Rule]
5354

54-
var alphabet: Int {
55-
var result: Int = 0
56-
for rule in rules {
57-
for s in rule.lhs { result = max(result, Int(s)) }
58-
for s in rule.rhs { result = max(result, Int(s)) }
59-
}
60-
return result + 1
61-
}
62-
6355
var longestRule: Int {
6456
var result = 0
6557
for rule in rules {
@@ -70,8 +62,13 @@ struct Presentation: Hashable {
7062
}
7163
}
7264

65+
func printRules(_ rules: [Rule]) -> String {
66+
return rules.map { $0.description }.joined(separator: ",")
67+
}
68+
7369
extension Presentation: ExpressibleByStringLiteral, CustomStringConvertible {
7470
init(_ str: String) {
71+
self.alphabet = 2
7572
self.rules = str.split(separator: ",").map { Rule(String($0)) }
7673
}
7774

@@ -80,8 +77,7 @@ extension Presentation: ExpressibleByStringLiteral, CustomStringConvertible {
8077
}
8178

8279
var description: String {
83-
if rules.isEmpty { return "," }
84-
return rules.map { $0.description }.joined(separator: ",")
80+
return printRules(rules)
8581
}
8682
}
8783

@@ -145,7 +141,8 @@ extension Rule {
145141

146142
extension Presentation {
147143
func permuted(_ perm: Permutation) -> Presentation {
148-
return Presentation(rules: rules.map { $0.permuted(perm) })
144+
return Presentation(alphabet: alphabet,
145+
rules: rules.map { $0.permuted(perm) })
149146
}
150147
}
151148

@@ -367,6 +364,6 @@ extension Presentation {
367364
let sortedRules =
368365
rules.map { $0.oriented(order: order)! }
369366
.sorted { compare($0, $1, order: order) == .lessThan }
370-
return Presentation(rules: sortedRules)
367+
return Presentation(alphabet: alphabet, rules: sortedRules)
371368
}
372369
}

benchmark/multi-source/Monoids/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,5 @@ In addition to Knuth-Bendix completion, there are some other interesting algorit
2222
The Knuth-Bendix implementation is pretty fast. It uses a trie to speed up reduction and finding overlaps.
2323

2424
The main "entry point" is `func run()` in Monoids.swift.
25+
26+
A formal write-up is here: https://factorcode.org/slava/aaaaabbabb.pdf

benchmark/multi-source/Monoids/RewritingSystem.swift

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ struct RewritingSystem {
3333
// Limits for completion
3434
struct Limits: Hashable {
3535
var maxRounds = 100
36-
var maxRules = 180
36+
var maxRules = 200
3737
var maxLength = 100
3838
var maxReductionLength = 100
3939
var maxReductionSteps = 1 << 24
4040
}
4141

42-
var limits: Limits
42+
var limits = Limits()
4343

4444
var checkedRulesUpTo = 0 // Completion progress
4545
var reducedRules: [UInt32] = [] // Bitmap of reduced rules
@@ -55,10 +55,9 @@ struct RewritingSystem {
5555
var numReductionSteps = 0
5656
}
5757

58-
init(alphabet: Int, limits: Limits) {
58+
init(alphabet: Int) {
5959
self.alphabet = alphabet
6060
self.trie = Trie(alphabet: self.alphabet)
61-
self.limits = limits
6261

6362
criticalPairs.reserveCapacity(128)
6463
}
@@ -288,6 +287,6 @@ struct RewritingSystem {
288287
result.append(rule)
289288
}
290289
}
291-
return Presentation(rules: result)
290+
return Presentation(alphabet: alphabet, rules: result)
292291
}
293292
}

benchmark/multi-source/Monoids/Solver.swift

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ struct Solver {
3030
// This is a list of indices of all remaining unsolved instances.
3131
var subset: [Int]
3232

33+
// Histogram of finite monoid cardinality.
34+
var finite: [Int: Int] = [:]
35+
3336
var factors: [Order: [Int: [Word]]] = [:]
3437
var maxFactors: [Int] = []
3538

@@ -86,8 +89,8 @@ struct Solver {
8689

8790
var strategies: [Strategy] = []
8891

89-
for frequency in [0, 1] {
90-
for factorLength in 2 ..< maxFactors.count {
92+
for factorLength in 2 ..< maxFactors.count {
93+
for frequency in [0, 1] {
9194
let strategy = Strategy(factorLength: factorLength,
9295
frequency: frequency)
9396
for order in orderMix[alphabet + 1]! {
@@ -104,8 +107,13 @@ struct Solver {
104107

105108
for n in subset {
106109
let instance = instances[n]
107-
print("\(n)\t\(instance)\thard")
110+
print("\(n + 1)\t\(instance)\thard")
108111
}
112+
113+
print("# Finite monoids: ", terminator: "")
114+
print(finite.sorted { $0.0 < $1.0 }
115+
.map { "\($0): \($1)" }
116+
.joined(separator: ", "))
109117
}
110118
}
111119

@@ -119,8 +127,8 @@ struct Solver {
119127
}
120128

121129
mutating func collectFactors(_ n: Int, _ p: Presentation, order: Order) -> [Word] {
122-
// FIXME: The 2 is a magic number
123-
let words = p.collectFactors(order: order, upToLength: p.longestRule + 2)
130+
// FIXME: The 6 is a magic number.
131+
let words = p.collectFactors(order: order, upToLength: 6)
124132
if !words.isEmpty {
125133
let longestFactor = words.map { $0.count }.max()!
126134
for i in 2 ... longestFactor {
@@ -243,18 +251,19 @@ struct Solver {
243251
retired[strategyIndex]!.append(n)
244252

245253
// Print the instance and solution.
246-
var str = "\(n)\t\(instances[n])\t"
254+
var str = "\(n + 1)\t\(instances[n])\t"
247255

248256
if let cardinality = solution.cardinality {
249257
str += "finite:\(cardinality)"
258+
finite[cardinality, default: 0] += 1
250259
} else {
251260
str += "infinite"
252261
}
253262
str += "\tfcrs:\(solution.presentation)"
254263

255264
// Print the extra generators that were added, if any.
256265
if !solution.extra.isEmpty {
257-
str += "\t\(Presentation(rules: solution.extra))"
266+
str += "\t\(printRules(solution.extra))"
258267
}
259268

260269
if output {

0 commit comments

Comments
 (0)