Properly handle non-principal rules with origins in incremental algo

Fixes IncrClassHierarchy.modifyClsHierarchyTypeParams test.

Key point is that changes to rules causing their
regeneration must be reflected in program.
There appeared to be cases when it's not true.
It's a case of non-principal rules with origins.
Such rules are called "weak principal" rules.

Matches of weak principal rules are not recorded
in Journal (as is for principal rules), but instead
they become part of their parent MatchChunk.
(In essense, tags of weak principal rules are
recorded in parent chunk).
With this, when a weak principal rule changes,
matches of all principal rules whose computation
depends on them are correctly invalidated.
(Example rules: findHierarchy_Classifier)

Relevant for MPSCR-62.
This commit is contained in:
Grigorii Kirgizov 2020-06-24 17:59:15 +03:00
parent a37bd44de0
commit 8629e5abfc
6 changed files with 32 additions and 2 deletions

View File

@ -86,7 +86,7 @@ internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.Di
while (it.hasNext()) {
val chunk = it.next()
if (chunk is MatchJournal.MatchChunk && ruleIds.contains(chunk.ruleUniqueTag)) {
if (chunk is MatchJournal.MatchChunk && chunk.dependsOnAny(ruleIds)) {
justificationRoots.add(chunk)
}
@ -341,4 +341,8 @@ internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.Di
private fun RuleMatch.isPrincipal() = ispec.isPrincipal(this.rule())
private fun Occurrence.isPrincipal() = ispec.isPrincipal(this.constraint())
private fun MatchJournal.MatchChunk.dependsOnAny(utags: Iterable<Any>): Boolean =
utags.contains(this.ruleUniqueTag) || utags.any { utag -> dependsOnRule(utag) }
}

View File

@ -189,7 +189,17 @@ interface MatchJournal : MutableIterable<MatchJournal.Chunk>, EvidenceSource {
* [Chunk] corresponding to a [RuleMatch] of a principal [Rule].
*/
interface MatchChunk : Chunk {
/**
* Returns true if this [Chunk] depends on changes to specified rule.
* Relevant for rules with origin. Doesn't include rule from [match].
*/
fun dependsOnRule(utag: Any): Boolean
/**
* [RuleMatch] which defines this [Chunk]
*/
val match: RuleMatch
val ruleUniqueTag: Any get() = match.rule().uniqueTag()
}

View File

@ -40,10 +40,14 @@ internal open class MatchJournalImpl(
}
private class MatchChunkImpl(override val evidence: Evidence, override val match: RuleMatch) : ChunkImpl(), MatchChunk {
override fun dependsOnRule(utag: Any): Boolean = rulesWithOrigin.contains(utag)
override fun justifications(): Justifications = justifications
private val justifications = match.collectJustifications(evidence)
val rulesWithOrigin = HashSet<Any>(4)
override fun toString() = "(id=$evidence, ${justifications()}, ${match.rule().tag()}, $entries)"
}
@ -132,7 +136,7 @@ internal open class MatchJournalImpl(
}
private fun logJustificationsFrom(match: RuleMatch) {
val parent: Chunk = parentChunk()
val parent: MatchChunk = parentChunk()
val moreJustified = match.allHeads().filter {
// Filter to avoid justifying parent by its child!
@ -144,6 +148,12 @@ internal open class MatchJournalImpl(
child.justifyByAll(moreJustified)
}
}
match.rule().let {
if (it.isWeakPrincipal()) {
(parent as MatchChunkImpl).rulesWithOrigin.add(it.uniqueTag())
}
}
}
override fun logActivation(occ: Occurrence): OccChunk? {
@ -352,6 +362,7 @@ internal open class MatchJournalImpl(
}
private fun RuleMatch.isPrincipal() = ispec.isPrincipal(this.rule())
private fun Rule.isWeakPrincipal() = ispec.isWeakPrincipal(this)
private fun Occurrence.isPrincipal() = ispec.isPrincipal(this.constraint())
/**

View File

@ -20,12 +20,15 @@ public interface IncrementalProgramSpec {
boolean isPrincipal(Constraint ctr);
boolean isPrincipal(Rule rule);
boolean isWeakPrincipal(Rule rule);
class NonIncrSpec implements IncrementalProgramSpec {
@Override
public boolean isPrincipal(Constraint ctr) { return false; }
@Override
public boolean isPrincipal(Rule rule) { return false; }
@Override
public boolean isWeakPrincipal(Rule rule) { return false; }
}
NonIncrSpec DefaultSpec = new NonIncrSpec();

View File

@ -22,4 +22,5 @@ import jetbrains.mps.logic.reactor.program.Rule
class MockIncrProgSpec(val principalRuleTags: Set<Any>, val principalCtrSyms: Set<ConstraintSymbol>) : IncrementalProgramSpec {
override fun isPrincipal(ctr: Constraint): Boolean = principalCtrSyms.contains(ctr.symbol())
override fun isPrincipal(rule: Rule): Boolean = principalRuleTags.contains(rule.uniqueTag())
override fun isWeakPrincipal(rule: Rule): Boolean = false
}

View File

@ -30,6 +30,7 @@ class TestStoreAwareJournal {
private object LegacyMockIncrProgSpec : IncrementalProgramSpec {
override fun isPrincipal(ctr: Constraint): Boolean = ctr.isPrincipal
override fun isPrincipal(rule: Rule): Boolean = rule.all().any { it is Constraint && it.isPrincipal }
override fun isWeakPrincipal(rule: Rule): Boolean = false
}
private class JournalDispatcherHelper(