Skip to content

Commit

Permalink
AddressSet.unify: pay off technical debt
Browse files Browse the repository at this point in the history
The perfect implementation of this algorithm would be to perform a full
circuit minimization, ala Quine–McCluskey.  However, that would require
expanding out to every addres, very exponential time!

The previous heuristic algorithm was O(n^2) for n AddressSet terms.
However, it did not always merge sets by the same bit in order.
This led to poor outcomes on common inputs.

The new heuristic merges by lowest bits first.
This is O(n * bits), which has better worst-case performance.
On contiguous ranges it also finds better results.
  • Loading branch information
terpstra committed May 12, 2020
1 parent 88cf2b1 commit 63becef
Showing 1 changed file with 13 additions and 17 deletions.
30 changes: 13 additions & 17 deletions src/main/scala/diplomacy/Parameters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -219,24 +219,20 @@ object AddressSet
}
}

def unify(seq: Seq[AddressSet], bit: BigInt): Seq[AddressSet] = {
// Pair terms up by ignoring 'bit'
seq.groupBy(x => x.copy(base = x.base & ~bit)).map { case (key, seq) =>
if (seq.size == 1) {
seq.head // singleton -> unaffected
} else {
key.copy(mask = key.mask | bit) // pair - widen mask by bit
}
}.toList
}

def unify(seq: Seq[AddressSet]): Seq[AddressSet] = {
val n = seq.size
val array = Array(seq:_*)
var filter = Array.fill(n) { false }
for (i <- 0 until n-1) { if (!filter(i)) {
for (j <- i+1 until n) { if (!filter(j)) {
val a = array(i)
val b = array(j)
if (a.mask == b.mask && isPow2(a.base ^ b.base)) {
val c_base = a.base & ~(a.base ^ b.base)
val c_mask = a.mask | (a.base ^ b.base)
filter.update(j, true)
array.update(i, AddressSet(c_base, c_mask))
}
}}
}}
val out = (array zip filter) flatMap { case (a, f) => if (f) None else Some(a) }
if (out.size != n) unify(out) else out.toList
val bits = seq.map(_.base).foldLeft(BigInt(0))(_ | _)
AddressSet.enumerateBits(bits).foldLeft(seq) { case (acc, bit) => unify(acc, bit) }.sorted
}

def enumerateMask(mask: BigInt): Seq[BigInt] = {
Expand Down

0 comments on commit 63becef

Please sign in to comment.