mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
293 lines
8.1 KiB
Swift
293 lines
8.1 KiB
Swift
/// This file implements the driver loop which attempts Knuth-Bendix completion
|
|
/// with various strategies on all instances in parallel.
|
|
|
|
struct Dispatcher {
|
|
let subset: [Int]
|
|
let strategies: [Strategy]
|
|
var currentStrategy = 0
|
|
var currentInstance = 0
|
|
|
|
mutating func next() -> (instance: Int, strategy: Int)? {
|
|
if subset.isEmpty || currentStrategy == strategies.count { return nil }
|
|
|
|
defer {
|
|
currentInstance += 1
|
|
if currentInstance == subset.count {
|
|
currentInstance = 0
|
|
currentStrategy += 1
|
|
}
|
|
}
|
|
|
|
return (instance: subset[currentInstance],
|
|
strategy: currentStrategy)
|
|
}
|
|
}
|
|
|
|
struct Solver {
|
|
let alphabet: Int
|
|
let instances: [Presentation]
|
|
|
|
// This is a list of indices of all remaining unsolved instances.
|
|
var subset: [Int]
|
|
|
|
// Histogram of finite monoid cardinality.
|
|
var finite: [Int: Int] = [:]
|
|
|
|
var factors: [Order: [Int: [Word]]] = [:]
|
|
var maxFactors: [Int] = []
|
|
|
|
var output: Bool
|
|
|
|
init(alphabet: Int, instances: [Presentation], output: Bool) {
|
|
self.alphabet = alphabet
|
|
self.instances = instances
|
|
self.subset = Array(instances.indices)
|
|
self.output = output
|
|
}
|
|
|
|
mutating func solve() async {
|
|
if output {
|
|
print("# Remaining \(subset.count)")
|
|
print("# n:\tpresentation:\tcardinality:\tcomplete presentation:\tstrategy:")
|
|
}
|
|
|
|
// The shortlex order with identity permutation of generators solves
|
|
// almost everything.
|
|
await attempt([Strategy()])
|
|
|
|
if output {
|
|
print("# Remaining \(subset.count)")
|
|
print("# Attempting more reduction orders")
|
|
}
|
|
|
|
var orderMix: [Int: [Order]] = [:]
|
|
for i in [0, 1] {
|
|
orderMix[alphabet + i] = getExhaustiveOrderMix(alphabet, i)
|
|
}
|
|
|
|
do {
|
|
var strategies: [Strategy] = []
|
|
|
|
let strategy = Strategy()
|
|
let orders = orderMix[alphabet]!
|
|
|
|
// We already did the first one.
|
|
for order in orders[1...] {
|
|
strategies.append(strategy.withOrder(order))
|
|
}
|
|
|
|
await attempt(strategies)
|
|
}
|
|
|
|
if output {
|
|
print("# Remaining \(subset.count)")
|
|
print("# Attempting to add a generator")
|
|
}
|
|
|
|
do {
|
|
collectFactors(orderMix[alphabet]!)
|
|
|
|
var strategies: [Strategy] = []
|
|
|
|
for factorLength in 2 ..< maxFactors.count {
|
|
for frequency in [0, 1] {
|
|
let strategy = Strategy(factorLength: factorLength,
|
|
frequency: frequency)
|
|
for order in orderMix[alphabet + 1]! {
|
|
strategies.append(strategy.withOrder(order))
|
|
}
|
|
}
|
|
}
|
|
|
|
await attempt(strategies)
|
|
}
|
|
|
|
if output {
|
|
print("# Remaining \(subset.count)")
|
|
|
|
for n in subset {
|
|
let instance = instances[n]
|
|
print("\(n + 1)\t\(instance)\thard")
|
|
}
|
|
|
|
print("# Finite monoids: ", terminator: "")
|
|
print(finite.sorted { $0.0 < $1.0 }
|
|
.map { "\($0): \($1)" }
|
|
.joined(separator: ", "))
|
|
}
|
|
}
|
|
|
|
mutating func collectFactors(_ orders: [Order]) {
|
|
for order in orders {
|
|
for n in subset {
|
|
factors[order, default: [:]][n] =
|
|
collectFactors(n, instances[n], order: order.simplified)
|
|
}
|
|
}
|
|
}
|
|
|
|
mutating func collectFactors(_ n: Int, _ p: Presentation, order: Order) -> [Word] {
|
|
// FIXME: The 6 is a magic number.
|
|
let words = p.collectFactors(order: order, upToLength: 6)
|
|
if !words.isEmpty {
|
|
let longestFactor = words.map { $0.count }.max()!
|
|
for i in 2 ... longestFactor {
|
|
let factorsOfLength = words.filter { $0.count == i }
|
|
while maxFactors.count <= i {
|
|
maxFactors.append(0)
|
|
}
|
|
maxFactors[i] = max(maxFactors[i], factorsOfLength.count)
|
|
}
|
|
}
|
|
return words
|
|
}
|
|
|
|
func prepare(_ instance: Int, _ strategy: Strategy) -> Strategy? {
|
|
var strategy = strategy
|
|
|
|
var factorsOfLength: [Word] = []
|
|
if let length = strategy.factorLength {
|
|
let order = strategy.order.removeGenerator(Symbol(alphabet))
|
|
factorsOfLength = (factors[order]!)[instance]!
|
|
|
|
let factorsOfLength = factorsOfLength.filter { $0.count == length }
|
|
if strategy.frequency >= factorsOfLength.count { return nil }
|
|
|
|
// Add a new generator 'c' and a rule 'c=x' for a magic factor 'x'.
|
|
let newFactor = factorsOfLength[strategy.frequency]
|
|
let newGenerator = [Symbol(alphabet)]
|
|
|
|
// If 'c' is just going to reduce to 'x' there's no point in
|
|
// considering it further.
|
|
if compare(newFactor, newGenerator, order: strategy.order) == .lessThan {
|
|
return nil
|
|
}
|
|
|
|
strategy.extra = [Rule(lhs: newFactor, rhs: newGenerator)]
|
|
}
|
|
|
|
strategy.order = strategy.order.simplified
|
|
return strategy
|
|
}
|
|
|
|
mutating func attempt(_ strategies: [Strategy]) async {
|
|
if subset.isEmpty { return }
|
|
|
|
let solved = await withTaskGroup(of: (Int, Int, Solution?).self) { group in
|
|
var dispatcher = Dispatcher(subset: subset,
|
|
strategies: strategies)
|
|
var solved: [Int: (Int, Solution)] = [:]
|
|
var pending: [Int: Int] = [:]
|
|
|
|
func startTask() {
|
|
while true {
|
|
guard let (instance, strategyIndex) = dispatcher.next() else {
|
|
return
|
|
}
|
|
|
|
if solved[instance] != nil {
|
|
continue
|
|
}
|
|
|
|
guard let strategy = prepare(instance, strategies[strategyIndex]) else {
|
|
continue
|
|
}
|
|
|
|
pending[strategyIndex, default: 0] += 1
|
|
|
|
let p = instances[instance]
|
|
|
|
group.addTask { () -> (Int, Int, Solution?) in
|
|
if let solution = try? p.complete(strategy) {
|
|
return (instance, strategyIndex, solution)
|
|
}
|
|
return (instance, strategyIndex, nil)
|
|
}
|
|
|
|
return
|
|
}
|
|
}
|
|
|
|
func completeTask(_ instance: Int, _ strategyIndex: Int, _ solution: Solution?) {
|
|
pending[strategyIndex, default: 0] -= 1
|
|
precondition(pending[strategyIndex]! >= 0)
|
|
|
|
if let solution {
|
|
// The lowest-numbered strategy is the 'official' solution for the instance.
|
|
var betterStrategy = true
|
|
if let (oldStrategyIndex, _) = solved[instance] {
|
|
if oldStrategyIndex < strategyIndex {
|
|
betterStrategy = false
|
|
}
|
|
}
|
|
|
|
if betterStrategy {
|
|
solved[instance] = (strategyIndex, solution)
|
|
}
|
|
}
|
|
|
|
retireStrategies()
|
|
}
|
|
|
|
func retireStrategies() {
|
|
var retired: [Int: [Int]] = [:]
|
|
let pendingInOrder = pending.sorted { $0.key < $1.key }
|
|
for (strategyIndex, numTasks) in pendingInOrder {
|
|
precondition(strategyIndex <= dispatcher.currentStrategy)
|
|
if dispatcher.currentStrategy == strategyIndex { break }
|
|
if numTasks > 0 { break }
|
|
|
|
pending[strategyIndex] = nil
|
|
retired[strategyIndex] = []
|
|
}
|
|
|
|
if retired.isEmpty { return }
|
|
|
|
// If we are done dispatching a strategy, look at all instances solved
|
|
// by that strategy and print them out.
|
|
for n in subset {
|
|
if let (strategyIndex, solution) = solved[n] {
|
|
if retired[strategyIndex] != nil {
|
|
retired[strategyIndex]!.append(n)
|
|
|
|
// Print the instance and solution.
|
|
var str = "\(n + 1)\t\(instances[n])\t"
|
|
|
|
if let cardinality = solution.cardinality {
|
|
str += "finite:\(cardinality)"
|
|
finite[cardinality, default: 0] += 1
|
|
} else {
|
|
str += "infinite"
|
|
}
|
|
str += "\tfcrs:\(solution.presentation)"
|
|
|
|
// Print the extra generators that were added, if any.
|
|
if !solution.extra.isEmpty {
|
|
str += "\t\(printRules(solution.extra))"
|
|
}
|
|
|
|
if output {
|
|
print(str)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// We run 32 tasks at a time.
|
|
for _ in 0 ..< 32 {
|
|
startTask()
|
|
}
|
|
|
|
for await (instance, strategyIndex, solution) in group {
|
|
startTask()
|
|
completeTask(instance, strategyIndex, solution)
|
|
}
|
|
|
|
return solved
|
|
}
|
|
|
|
subset = subset.filter { solved[$0] == nil }
|
|
}
|
|
}
|