Skip to content

Commit e6b7781

Browse files
committed
New "Monoids" benchmark
1 parent 858383c commit e6b7781

14 files changed

+1635
-0
lines changed

benchmark/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,12 +220,24 @@ set(SWIFT_BENCH_MODULES
220220

221221
set(SWIFT_MULTISOURCE_SWIFT_BENCHES
222222
multi-source/PrimsSplit
223+
multi-source/Monoids
223224
)
224225

225226
set(PrimsSplit_sources
226227
multi-source/PrimsSplit/Prims.swift
227228
multi-source/PrimsSplit/Prims_main.swift)
228229

230+
set(Monoids_sources
231+
multi-source/Monoids/Automaton.swift
232+
multi-source/Monoids/Benchmark.swift
233+
multi-source/Monoids/Enumeration.swift
234+
multi-source/Monoids/Monoids.swift
235+
multi-source/Monoids/Presentation.swift
236+
multi-source/Monoids/RewritingSystem.swift
237+
multi-source/Monoids/Solver.swift
238+
multi-source/Monoids/Strategy.swift
239+
multi-source/Monoids/Trie.swift)
240+
229241
set(BENCH_DRIVER_LIBRARY_FLAGS)
230242
if (SWIFT_RUNTIME_ENABLE_LEAK_CHECKER)
231243
set(BENCH_DRIVER_LIBRARY_FLAGS -DSWIFT_RUNTIME_ENABLE_LEAK_CHECKER)
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/// This file implements an algorithm to compute the number of elements in a
2+
/// monoid (or determine it is infinite), given a complete presentation.
3+
4+
/// A finite state automaton, given by a set of vertices and edges.
5+
struct Automaton {
6+
var states: [Word] = []
7+
var transitions: [(Word, Symbol, Word)] = []
8+
}
9+
10+
extension Automaton {
11+
var hasStar: Bool {
12+
for state in states {
13+
var visited = Set<Word>()
14+
15+
func rec(_ state: Word) -> Bool {
16+
for (from, _, to) in transitions {
17+
if from == state {
18+
if visited.contains(to) {
19+
return true
20+
} else {
21+
visited.insert(to)
22+
if rec(to) { return true }
23+
visited.remove(to)
24+
}
25+
}
26+
}
27+
28+
return false
29+
}
30+
31+
visited.insert(state)
32+
if rec(state) { return true }
33+
visited.remove(state)
34+
}
35+
36+
return false
37+
}
38+
39+
/// If this automaton is star-free, count the number of unique words accepted.
40+
var countWords: Int {
41+
func R(_ q: Word) -> [Word] {
42+
var result: [Word] = []
43+
44+
for (from, _, to) in transitions {
45+
if to == q {
46+
result.append(from)
47+
}
48+
}
49+
50+
return result
51+
}
52+
53+
func T(_ q: Word, _ p: Word) -> Int {
54+
var letters = Set<Symbol>()
55+
for (from, x, to) in transitions {
56+
if from == q && to == p {
57+
letters.insert(x)
58+
}
59+
}
60+
return letters.count
61+
}
62+
63+
func N(_ q: Word) -> Int {
64+
if q == [] {
65+
return 1
66+
}
67+
68+
var result = 0
69+
for p in R(q) {
70+
result += N(p) * T(p, q)
71+
}
72+
return result
73+
}
74+
75+
var result = 0
76+
77+
for q in states {
78+
result += N(q)
79+
}
80+
81+
return result
82+
}
83+
}
84+
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+
}
110+
111+
return true
112+
}
113+
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)
119+
}
120+
}
121+
122+
result.states = prefixes.sorted { compare($0, $1, order: .shortlex) == .lessThan }
123+
124+
for prefix in prefixes {
125+
for x in 0 ..< UInt8(alphabet) {
126+
let word = prefix + [x]
127+
128+
if prefixes.contains(word) {
129+
result.transitions.append((prefix, x, word))
130+
continue
131+
}
132+
133+
if !isIrreducible(word) {
134+
continue
135+
}
136+
137+
for i in 1 ... word.count {
138+
let suffix = Word(word[i...])
139+
140+
if prefixes.contains(suffix) {
141+
result.transitions.append((prefix, x, suffix))
142+
break
143+
}
144+
}
145+
}
146+
}
147+
148+
return result
149+
}
150+
151+
extension Presentation {
152+
/// The Irr(R) automaton.
153+
func automaton(alphabet: Int) -> Automaton {
154+
return buildAutomaton(rules.map { $0.lhs }, alphabet)
155+
}
156+
157+
/// Returns the number of irreducible words in this monoid presentation, or
158+
/// 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)
164+
if automaton.hasStar {
165+
return nil
166+
}
167+
return automaton.countWords
168+
}
169+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import TestsUtils
2+
import Dispatch
3+
4+
public let benchmarks = [
5+
BenchmarkInfo(
6+
name: "Monoids",
7+
runFunction: run_Monoids,
8+
tags: [.algorithm])
9+
]
10+
11+
func run_Monoids(_ n: Int) {
12+
let semaphore = DispatchSemaphore(value: 0)
13+
for _ in 0 ... n {
14+
Task {
15+
await run(output: false)
16+
semaphore.signal()
17+
}
18+
semaphore.wait()
19+
}
20+
}
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/// This file implements algorithms for enumerating all monoid presentations
2+
/// up to a given length.
3+
4+
func nextSymbol(_ s: inout Symbol, alphabet: Int) -> Bool {
5+
precondition(alphabet > 0)
6+
if s == alphabet - 1 {
7+
s = 0
8+
return true
9+
}
10+
s += 1
11+
return false
12+
}
13+
14+
func nextWord(_ word: inout Word, alphabet: Int) -> Bool {
15+
var carry = true
16+
for i in word.indices.reversed() {
17+
carry = nextSymbol(&word[i], alphabet: alphabet)
18+
if !carry { break }
19+
}
20+
return carry
21+
}
22+
23+
func nextRule(_ rule: inout Rule, alphabet: Int) -> Bool {
24+
if nextWord(&rule.rhs, alphabet: alphabet) {
25+
rule.rhs = Word(repeating: 0, count: rule.rhs.count)
26+
if nextWord(&rule.lhs, alphabet: alphabet) {
27+
rule.lhs = Word(repeating: 0, count: rule.lhs.count)
28+
return true
29+
}
30+
}
31+
return false
32+
}
33+
34+
func nextPresentation(_ p: inout Presentation, alphabet: Int) -> Bool {
35+
precondition(!p.rules.isEmpty)
36+
var carry = true
37+
for i in p.rules.indices.reversed() {
38+
carry = nextRule(&p.rules[i], alphabet: alphabet)
39+
if !carry { break }
40+
}
41+
return carry
42+
}
43+
44+
struct RuleShape {
45+
var lhs: Int
46+
var rhs: Int
47+
48+
var rule: Rule {
49+
return Rule(lhs: Word(repeating: 0, count: lhs),
50+
rhs: Word(repeating: 0, count: rhs))
51+
}
52+
}
53+
54+
struct PresentationShape {
55+
var rules: [RuleShape]
56+
57+
var presentation: Presentation {
58+
return Presentation(rules: rules.map { $0.rule })
59+
}
60+
}
61+
62+
func enumerateAll(alphabet: Int, shapes: [PresentationShape], output: Bool)
63+
-> [Presentation] {
64+
var filteredLHS = 0
65+
var filteredRHS = 0
66+
var filteredSymmetry = 0
67+
var total = 0
68+
69+
var instances: [Presentation] = []
70+
var unique: Set<Presentation> = []
71+
72+
let perms = allPermutations(alphabet)
73+
74+
for shape in shapes {
75+
var p = shape.presentation
76+
var done = false
77+
78+
loop: while !done {
79+
defer {
80+
done = nextPresentation(&p, alphabet: alphabet)
81+
}
82+
83+
total += 1
84+
85+
for n in 0 ..< p.rules.count - 1 {
86+
if compare(p.rules[n].lhs, p.rules[n + 1].lhs, order: .shortlex) != .lessThan {
87+
filteredLHS += 1
88+
continue loop
89+
}
90+
}
91+
92+
for rule in p.rules {
93+
if compare(rule.rhs, rule.lhs, order: .shortlex) != .lessThan {
94+
filteredRHS += 1
95+
continue loop
96+
}
97+
}
98+
99+
if unique.contains(p.sorted(order: .shortlex)) {
100+
filteredSymmetry += 1
101+
continue
102+
}
103+
104+
for perm in perms {
105+
let permuted = p.permuted(perm)
106+
unique.insert(permuted.sorted(order: .shortlex))
107+
}
108+
109+
precondition(p == p.sorted(order: .shortlex))
110+
instances.append(p)
111+
}
112+
}
113+
114+
if output {
115+
print("# Total \(total)")
116+
print("# Discarded lhs:\(filteredLHS),rhs:\(filteredRHS),"
117+
+ "symmetry:\(filteredSymmetry)")
118+
}
119+
120+
return instances
121+
}
122+
123+
func ruleShapes(_ n: Int) -> [RuleShape] {
124+
precondition(n > 0)
125+
var result: [RuleShape] = []
126+
for i in 0 ..< n {
127+
let j = n - i
128+
129+
// Don't generate rules with shorter left-hand side.
130+
if j < i { continue }
131+
132+
result.append(RuleShape(lhs: j, rhs: i))
133+
}
134+
return result
135+
}
136+
137+
func presentationShapes(rules: Int, ofLength n: Int) -> [PresentationShape] {
138+
precondition(n > 0)
139+
precondition(rules > 0)
140+
141+
if rules == 1 {
142+
return ruleShapes(n).map {
143+
PresentationShape(rules: [$0])
144+
}
145+
}
146+
147+
var result: [PresentationShape] = []
148+
for i in 1 ..< n {
149+
let next = presentationShapes(rules: rules - 1, ofLength: i)
150+
for x in ruleShapes(n - i) {
151+
for y in next {
152+
if x.lhs <= y.rules.first!.lhs {
153+
result.append(PresentationShape(rules: [x] + y.rules))
154+
}
155+
}
156+
}
157+
}
158+
return result
159+
}
160+
161+
func presentationShapes(rules: Int, upToLength n: Int) -> [PresentationShape] {
162+
var shapes: [PresentationShape] = []
163+
for i in 1 ... n {
164+
shapes.append(contentsOf: presentationShapes(rules: rules, ofLength: i))
165+
}
166+
return shapes
167+
}

0 commit comments

Comments
 (0)