Don't expose InitialRule to RuleDiff. Add first test for rm algo stage.

This commit is contained in:
Grigorii Kirgizov 2019-06-12 14:56:57 +03:00
parent 0bfe9e8a47
commit d7b7d40f8a
4 changed files with 102 additions and 25 deletions

View File

@ -66,9 +66,16 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
private data class ExecPos(val pos: MatchJournal.Pos, val activeOcc: Occurrence)
private data class MatchCandidate(val rule: Rule, val occ: Occurrence, val occParentId: Int)
// fixme: wtf, why idea's compiler complains???
override fun index(): MatchJournal.Index = journalIndex
// Invalidation includes several activities:
// - removing chunks (i.e. principal matches) corresponding to removed rules and chunks depending on them from journal
// - reactivating occurrences that led to invalidated matches
// - pruning invalidated occurrences and matches from Dispatcher's state
fun invalidateByRules(ruleIds: Set<Any>) {
val justificationRoots = mutableListOf<Int>()
@ -86,23 +93,25 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
// We removed the match, so need to reactivate all still valid occurrences from the head
// by definition of Chunk and principal rule, all occurrences from the head are principal
// todo: check/assert it
val matchedOccs = chunk.match.allHeads() as Iterable<Occurrence>
assert(matchedOccs.all { it.isPrincipal() })
val (invalidatedOccs, validOccs) = matchedOccs.partition { occ ->
occ.justifications().intersects(justificationRoots)
}
// todo: do need to reactivate only the main, matching~activating match?
// (i.e. don't reactivate additional, inactive heads that only completed the match?)
execQueue.addAll(validOccs.map { occ ->
val chunkPos = journalIndex.activatingChunkOf(Id(occ))
if (chunkPos != null) ExecPos(chunkPos, occ) else null
}.filterNotNull())
execQueue.addAll(validOccs.mapNotNull { occ ->
journalIndex.activatingChunkOf(Id(occ))?.let { chunkPos ->
ExecPos(chunkPos, occ)
}
})
continue
}
// Invalidating dependent chunks
val toInvalidate = chunk.justifications.intersects(justificationRoots)
if (toInvalidate) {
// Remove the chunk from the journal
@ -123,8 +132,6 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
}
}
private data class MatchCandidate(val rule: Rule, val occ: Occurrence, val occParentId: Int)
private fun canMatch(rule: Rule, occ: Occurrence): Boolean =
(rule.headKept() + rule.headReplaced()).find { it.symbol() == occ.constraint.symbol() } != null
@ -220,7 +227,7 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
}
fun snapshot(): SessionToken {
return SessionToken(view(), ruleOrdering.ruleTags(), dispatchingFront.state())
return SessionToken(view(), ruleOrdering.ruleTags, dispatchingFront.state())
}
/**

View File

@ -22,13 +22,13 @@ import jetbrains.mps.logic.reactor.program.Rule
internal class RuleOrdering(order: Iterable<Rule>): Comparator<Rule> {
private val ruleOrder: Map<Any, Int> = HashMap<Any, Int>().apply {
put(MatchJournalImpl.InitRuleMatch.rule().uniqueTag(), -1)
put(MatchJournalImpl.InitRuleMatch.rule().uniqueTag(), -1) // less than anything
order.forEachIndexed { index, rule -> put(rule.uniqueTag(), index) }
}
fun orderOf(rule: Rule): Int? = ruleOrder[rule.uniqueTag()]
val ruleTags: Set<Any> = order.map { it.uniqueTag() }.toHashSet() // NB: without initial rule
fun ruleTags(): Set<Any> = ruleOrder.keys
fun orderOf(rule: Rule): Int? = ruleOrder[rule.uniqueTag()]
override fun compare(lhs: Rule, rhs: Rule): Int {
val lhsRuleOrder = orderOf(lhs)

View File

@ -1,5 +1,3 @@
import gnu.trove.set.TIntSet
import gnu.trove.set.hash.TIntHashSet
import jetbrains.mps.logic.reactor.core.*
import jetbrains.mps.logic.reactor.core.internal.FeedbackStatus
import jetbrains.mps.logic.reactor.evaluation.PredicateInvocation
@ -84,6 +82,22 @@ private fun insertRulesInHandler(at: Int, name: String, rulesList: RulesList, va
hb.toHandler()
}
private fun removeRulesByIndices(at: Iterable<Int>, name: String, rulesList: RulesList): () -> RulesList = {
val hb = HandlerBuilder(name)
rulesList.rules().forEachIndexed { index, rule ->
if (!at.contains(index)) hb.appendRule(rule)
}
hb.toHandler()
}
private fun removeRulesByTags(tags: Iterable<Any>, name: String, rulesList: RulesList): () -> RulesList = {
val hb = HandlerBuilder(name)
rulesList.rules().forEach { rule ->
if (!tags.contains(rule.uniqueTag())) hb.appendRule(rule)
}
hb.toHandler()
}
private fun insertRulesInHandlerWhen(at: (Rule) -> Boolean, name: String, rulesList: RulesList, vararg ruleBlocks: () -> Rule): () -> RulesList =
insertRulesInHandler(rulesList.rules().indexOfFirst(at), name, rulesList, * ruleBlocks)
@ -95,6 +109,12 @@ fun programWithRules(vararg ruleBuilders: () -> Rule): Builder =
fun Builder.programWithRules(vararg ruleBuilders: () -> Rule): Builder =
updateBuilder(this, arrayOf(updateHandler("test", rulesLists.first(), * ruleBuilders)))
fun Builder.removeRulesAt(at: Iterable<Int>): Builder =
updateBuilder(this, arrayOf(removeRulesByIndices(at, "test", rulesLists.first())))
fun Builder.removeRules(tags: Iterable<Any>): Builder =
updateBuilder(this, arrayOf(removeRulesByTags(tags, "test", rulesLists.first())))
fun Builder.insertRulesAt(at: Int, vararg ruleBuilders: () -> Rule): Builder =
updateBuilder(this, arrayOf(insertRulesInHandler(at, "test", rulesLists.first(), * ruleBuilders)))

View File

@ -83,6 +83,8 @@ class TestIncrementalProgram {
private fun Iterable<Occurrence>.constraintSymbols() = this.map { it.constraint.symbol() }
private fun EvaluationResult.lastChunkSymbols() = this.lastChunk().activatedLog().constraintSymbols()
@Test
fun addNewMatchAtTheEnd() {
@ -100,7 +102,7 @@ class TestIncrementalProgram {
))
).launch("addRule", progSpec) { result ->
result.storeView().constraintSymbols() shouldBe setOf(sym0("foo"))
result.lastChunk().activatedLog().constraintSymbols() shouldBe listOf(sym0("foo"))
result.lastChunkSymbols() shouldBe listOf(sym0("foo"))
}.also { (builder, evalRes) ->
builder.programWithRules(
@ -114,7 +116,7 @@ class TestIncrementalProgram {
)
).relaunch("test1", progSpec, evalRes.token()) { result ->
result.storeView().constraintSymbols() shouldBe setOf(sym0("bar"))
result.lastChunk().activatedLog().constraintSymbols() shouldBe listOf(sym0("bar"))
result.lastChunkSymbols() shouldBe listOf(sym0("bar"))
}
}
}
@ -143,7 +145,7 @@ class TestIncrementalProgram {
)
).launch("addRule", progSpec) { result ->
result.storeView().constraintSymbols() shouldBe setOf(sym0("foo"), sym0("bar"))
result.lastChunk().activatedLog().constraintSymbols() shouldBe listOf(sym0("bar"))
result.lastChunkSymbols() shouldBe listOf(sym0("bar"))
}.also { (builder, evalRes) ->
builder.programWithRules(
@ -157,7 +159,7 @@ class TestIncrementalProgram {
)
).relaunch("test1", progSpec, evalRes.token()) { result ->
result.storeView().constraintSymbols() shouldBe setOf(sym0("foo"), sym0("bar"), sym0("baz"))
result.lastChunk().activatedLog().constraintSymbols() shouldBe listOf(sym0("baz"))
result.lastChunkSymbols() shouldBe listOf(sym0("baz"))
}
}
}
@ -199,7 +201,7 @@ class TestIncrementalProgram {
)
).relaunch("test1", progSpec, evalRes.token()) { result ->
result.storeView().constraintSymbols() shouldBe setOf(sym0("foo"), sym0("bar"), sym0("baz"))
result.lastChunk().activatedLog().constraintSymbols() shouldBe listOf(sym0("baz"))
result.lastChunkSymbols() shouldBe listOf(sym0("baz"))
}
}
}
@ -223,7 +225,7 @@ class TestIncrementalProgram {
result.storeView().constraintSymbols() shouldBe setOf(sym0("foo"), sym0("main"))
result.countChunks() shouldBe 2
result.lastChunk().activatedLog().constraintSymbols() shouldBe listOf(sym0("foo"))
result.lastChunkSymbols() shouldBe listOf(sym0("foo"))
}.also { (builder, evalRes) ->
builder.programWithRules(
@ -239,7 +241,7 @@ class TestIncrementalProgram {
result.storeView().constraintSymbols() shouldBe setOf(sym0("foo"), sym0("main"), sym0("bar"))
result.countChunks() shouldBe 3
result.lastChunk().activatedLog().constraintSymbols() shouldBe listOf(sym0("bar"))
result.lastChunkSymbols() shouldBe listOf(sym0("bar"))
}
}
}
@ -315,7 +317,7 @@ class TestIncrementalProgram {
).launch("initial run", progSpec) { result ->
result.storeView().constraintSymbols() shouldBe setOf(sym0("baz"), sym0("dummy"))
result.lastChunk().activatedLog().constraintSymbols() shouldBe listOf(sym0("dummy"))
result.lastChunkSymbols() shouldBe listOf(sym0("dummy"))
}.also { (builder, evalRes) ->
val nPrincipalMatches = evalRes.countChunks()
@ -332,7 +334,7 @@ class TestIncrementalProgram {
).relaunch("test1", progSpec, evalRes.token()) { result ->
result.storeView().constraintSymbols() shouldBe setOf(sym0("bar"), sym0("baz"), sym0("dummy"))
result.lastChunk().activatedLog().constraintSymbols() shouldBe listOf(sym0("dummy"))
result.lastChunkSymbols() shouldBe listOf(sym0("dummy"))
result.countChunks() shouldBe (1 + nPrincipalMatches)
}
}
@ -509,7 +511,7 @@ class TestIncrementalProgram {
// if "foobar" happens too early, "1st" occ won't be produced
result.storeView().constraintSymbols() shouldBe setOf(sym0("start"), sym0("1st"), sym0("2nd"))
// ensure right rule match order: the last chunk must contain "2nd"
result.lastChunk().activatedLog().constraintSymbols() shouldBe listOf(sym0("2nd"))
result.lastChunkSymbols() shouldBe listOf(sym0("2nd"))
}
}
}
@ -568,10 +570,58 @@ class TestIncrementalProgram {
// if "foobar" happens too early, "1st" occ won't be produced
result.storeView().constraintSymbols() shouldBe setOf(sym0("foo"), sym0("bar"), sym0("marker"))
result.lastChunk().activatedLog().constraintSymbols() shouldBe listOf(sym0("marker"))
result.lastChunkSymbols() shouldBe listOf(sym0("marker"))
result.countChunks() shouldBe (2 + nPrincipalMatches) // +[.bar, foobar]
}
}
}
@Test
fun rmSingleKeptMatch() {
val progSpec = MockIncrProgSpec(
setOf("main", "foo.bar", "foo.baz", "baz.qux"),
setOf(sym0("foo"), sym0("baz"))
)
programWithRules(
rule("main",
headReplaced(
constraint("main")
),
body(
princConstraint("foo")
)),
rule("foo.baz",
headReplaced(
princConstraint("foo")
),
body(
princConstraint("baz")
)),
rule("baz.qux",
headKept(
princConstraint("baz")
),
body(
constraint("qux")
)
)
).launch("initial run", progSpec) { result ->
result.storeView().constraintSymbols() shouldBe setOf(sym0("baz"), sym0("qux"))
result.lastChunkSymbols() shouldBe listOf(sym0("qux"))
}.also { (builder, evalRes) ->
val nPrincipalMatches = evalRes.countChunks()
// Insert rule before foo.baz: produce bar before discarding foo in foo.baz
builder.removeRules(listOf("baz.qux"))
.relaunch("removed", progSpec, evalRes.token()) { result ->
result.storeView().constraintSymbols() shouldBe setOf(sym0("baz"))
result.lastChunkSymbols() shouldBe listOf(sym0("baz"))
result.countChunks() shouldBe (nPrincipalMatches - 1)
}
}
}
}