Skip to content

Commit

Permalink
precompute state table
Browse files Browse the repository at this point in the history
  • Loading branch information
breandan committed Apr 14, 2024
1 parent 47d19fe commit 7e4ff07
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,24 @@ private infix fun CFG.intersectLevFSAP(fsa: FSA): CFG {
val lengthBoundsCache = lengthBounds.let { lb -> ntLst.map { lb[it] ?: 0..0 } }
val validTriples: List<Triple<STC, STC, STC>> = fsa.validTriples

val ct = prods.map { it.first }.toSet().flatMap { fsa.validPairs * setOf(it) }
val ct1: Map<Triple<Int, Int, Int>, Boolean> =
ct.associate { Pair(it.π11 to it.π3 to it.π21, lengthBoundsCache[it.π3].overlaps(fsa.SPLP(it.π1, it.π2))) }
val ct2: Map<Triple<Int, Int, Int>, Boolean> =
ct.associate { Pair(it.π11 to it.π3 to it.π21, fsa.obeys(it.π1, it.π2, it.π3, parikhMap)) }
val ct = (fsa.validPairs * nonterminals.indices.toSet()).toList()
// val ct1 = Array(fsa.states.size) { Array(nonterminals.size) { Array(fsa.states.size) { false } } }
// ct.filter { lengthBoundsCache[it.π3].overlaps(fsa.SPLP(it.π1, it.π2)) }
// .forEach { ct1[it.π1.π1][it.π3][it.π2.π1] = true }
val ct2 = Array(fsa.states.size) { Array(nonterminals.size) { Array(fsa.states.size) { false } } }
ct.filter { fsa.obeys(it.π1, it.π2, it.π3, parikhMap) }
.forEach { ct2[it.π11][it.π3][it.π21] = true }

val binaryProds =
prods.map {
// if (i % 100 == 0) println("Finished ${i}/${nonterminalProductions.size} productions")
val (A, B, C) = it.π1 to it.π2[0] to it.π2[1]
val trip = A to B to C
validTriples
// CFG ∩ FSA - in general we are not allowed to do this, but it works
// because we assume a Levenshtein FSA, which is monotone and acyclic.
.filter { it.checkCT(A to B to C, ct1) }
.filter { it.checkCT(A to B to C, ct2) }
// .filter { it.checkCT(trip, ct1) }
.filter { it.checkCT(trip, ct2) }
// .filter { it.obeysLevenshteinParikhBounds(A to B to C, fsa, parikhMap) }
.map { (a, b, c) ->
val (p, q, r) = fsa.stateLst[a.π1] to fsa.stateLst[b.π1] to fsa.stateLst[c.π1]
Expand Down Expand Up @@ -288,7 +291,7 @@ fun Π3A<STC>.isCompatibleWith(nts: Π3A<Int>, fsa: FSA, lengthBounds: List<IntR
&& lengthBounds[nts.second].overlaps(fsa.SPLP(first, second))
&& lengthBounds[nts.third].overlaps(fsa.SPLP(second, third))

fun Π3A<STC>.checkCT(nts: Π3A<Int>, ct: Map<Π3A<Int>, Boolean>): Boolean =
true == ct[π11 to nts.π1 to π31] &&
true == ct[π11 to nts.π2 to π21] &&
true == ct[π21 to nts.π3 to π31]
fun Π3A<STC>.checkCT(nts: Π3A<Int>, ct: Array<Array<Array<Boolean>>>): Boolean =
ct[π11][nts.π1][π31] &&
ct[π11][nts.π2][π21] &&
ct[π21][nts.π3][π31]
Original file line number Diff line number Diff line change
Expand Up @@ -171,14 +171,9 @@ private fun CFG.jvmIntersectLevFSAP(fsa: FSA, parikhMap: ParikhMap): CFG {
val validTriples: List<Triple<STC, STC, STC>> = fsa.validTriples

val ct = (fsa.validPairs * nonterminals.indices.toSet()).toList()
val ct1: Map<Triple<Int, Int, Int>, Boolean> = ct.parallelStream()
.filter { lengthBoundsCache[it.π3].overlaps(fsa.SPLP(it.π1, it.π2)) }
.map { Pair(it.π11 to it.π3 to it.π21, true) }
.collect(Collectors.toMap({ it.first }, { it.second }))
val ct2: Map<Triple<Int, Int, Int>, Boolean> = ct.parallelStream()
.filter { fsa.obeys(it.π1, it.π2, it.π3, parikhMap) }
.map { Pair(it.π11 to it.π3 to it.π21, true) }
.collect(Collectors.toMap({ it.first }, { it.second }))
val ct2 = Array(fsa.states.size) { Array(nonterminals.size) { Array(fsa.states.size) { false } } }
ct.filter { fsa.obeys(it.π1, it.π2, it.π3, parikhMap) }
.forEach { ct2[it.π11][it.π3][it.π21] = true }

val elimCounter = AtomicInteger(0)
val counter = AtomicInteger(0)
Expand All @@ -187,13 +182,14 @@ private fun CFG.jvmIntersectLevFSAP(fsa: FSA, parikhMap: ParikhMap): CFG {
prods.parallelStream().flatMap {
if (BH_TIMEOUT < clock.elapsedNow()) throw Exception("Timeout: ${nts.size} nts")
val (A, B, C) = it.π1 to it.π2[0] to it.π2[1]
val trip = A to B to C
validTriples.stream()
// CFG ∩ FSA - in general we are not allowed to do this, but it works
// because we assume a Levenshtein FSA, which is monotone and acyclic.
// .filter { it.isCompatibleWith(A to B to C, fsa, lengthBoundsCache).also { if (!it) elimCounter.incrementAndGet() } }
.filter { it.checkCT(A to B to C, ct1).also { if (!it) elimCounter.incrementAndGet() } }
// .filter { it.checkCT(trip, ct1).also { if (!it) elimCounter.incrementAndGet() } }
// .filter { it.obeysLevenshteinParikhBounds(A to B to C, fsa, parikhMap).also { if (!it) elimCounter.incrementAndGet() } }
.filter { it.checkCT(A to B to C, ct2).also { if (!it) elimCounter.incrementAndGet() } }
.filter { it.checkCT(trip, ct2).also { if (!it) elimCounter.incrementAndGet() } }
.map { (a, b, c) ->
if (MAX_PRODS < counter.incrementAndGet()) throw Exception("∩-grammar has too many productions! (>$MAX_PRODS)")
val (p, q, r) = fsa.stateLst[a.π1] to fsa.stateLst[b.π1] to fsa.stateLst[c.π1]
Expand Down

0 comments on commit 7e4ff07

Please sign in to comment.