From a2675351b5d96f2f92f69799852d037246f9236a Mon Sep 17 00:00:00 2001 From: Grigorii Kirgizov Date: Wed, 25 Mar 2020 18:55:42 +0300 Subject: [PATCH] Track justifications for all occurrences, but weaken information they carry. Now Evidence isn't unique for each Chunk: Occurrence Chunks fully share evidence and justifications with their activating match. So, less collections of justifications are created, approx. 2 times less. --- .../mps/logic/reactor/core/Occurrence.kt | 1 - .../core/internal/ConstraintsProcessing.kt | 3 +- .../reactor/core/internal/ControllerImpl.kt | 18 ++++---- .../reactor/core/internal/ExecutionQueue.kt | 9 ++-- .../reactor/core/internal/MatchJournal.kt | 7 +--- .../reactor/core/internal/MatchJournalImpl.kt | 42 ++++++++++--------- reactor/Test/test/RulesHelper.kt | 18 ++++---- reactor/Test/test/TestIncrementalProgram.kt | 3 -- reactor/Test/test/TestStoreAwareJournal.kt | 27 ++++++------ 9 files changed, 62 insertions(+), 66 deletions(-) diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Occurrence.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Occurrence.kt index b1ca3fd2..5a1b8940 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Occurrence.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Occurrence.kt @@ -46,7 +46,6 @@ class Occurrence (observable: LogicalStateObservable, val identity = System.identityHashCode(this) init { - justifications.add(evidence) // ensure reflexivity of justifications revive(observable) } diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ConstraintsProcessing.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ConstraintsProcessing.kt index b29eb35f..c4f0b6dc 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ConstraintsProcessing.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ConstraintsProcessing.kt @@ -221,7 +221,8 @@ internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.Di activationChunk = logActivation(active) active.revive(logicalState) } else { - activationChunk = journalIndex.activatingChunkOf(Id(active)) + // defined (not null) & needed only for incremental execution + activationChunk = journalIndex.activatingChunkOf(active) } profiler.profile("dispatch_${active.constraint().symbol()}") { diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ControllerImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ControllerImpl.kt index edbe569a..3d45cdee 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ControllerImpl.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ControllerImpl.kt @@ -80,7 +80,7 @@ internal class ControllerImpl ( // FIXME noLogicalContext val context = Context(NORMAL(), noLogicalContext, null, trace) - activateConstraint(constraint, processing.initialChunk(), justsCopy(processing.justifications()), context) + activateConstraint(constraint, processing.initialChunk(), processing.evidence(), processing.justifications(), context) return context.currentStatus() } @@ -105,7 +105,6 @@ internal class ControllerImpl ( profiler.profile("reactivate_${occ.constraint.symbol()}") { trace.reactivate(occ) - // FIXME: propagate parent MatchChunk through call to reactivate() processing.processActivated(this, occ, processing.parentChunk(), NORMAL()) } @@ -166,15 +165,13 @@ internal class ControllerImpl ( } assert(newParent === processing.parentChunk()) - val justifications = processing.justifications() + val currentJustifications = processing.justifications() + val currentEvidence = processing.evidence() for (item in body) { val itemOk = when (item) { - is Constraint -> { - // track justifications only for principal constraints - val js = if (ispec.isPrincipal(item)) justsCopy(justifications) else emptyJustifications() - activateConstraint(item, newParent, js, context) - } + // Occurrence just inherits evidence and justifications of its activating rule match + is Constraint -> activateConstraint(item, newParent, currentEvidence, currentJustifications, context) is Predicate -> tellPredicate(item, context) else -> throw IllegalArgumentException("unknown item ${item}") } @@ -230,14 +227,13 @@ internal class ControllerImpl ( return context.currentStatus() } - private fun activateConstraint(constraint: Constraint, parent: MatchJournal.MatchChunk, justifications: Justifications, context: Context) : Boolean { + private fun activateConstraint(constraint: Constraint, parent: MatchJournal.MatchChunk, evidence: Evidence, justifications: Justifications, context: Context) : Boolean { val args = supervisor.instantiateArguments(constraint.arguments(), context.logicalContext, context) return context.eval { status -> profiler.profile("activate_${constraint.symbol()}") { - // fixme: maybe provide no evidence for non-principal constraints? - constraint.occurrence(logicalStateObservable(), args, processing.nextEvidence(), justifications, context.logicalContext, context.ruleUniqueTag).let { occ -> + constraint.occurrence(logicalStateObservable(), args, evidence, justifications, context.logicalContext, context.ruleUniqueTag).let { occ -> trace.activate(occ) processing.processActivated(this, occ, parent, status) } diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ExecutionQueue.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ExecutionQueue.kt index ff201e2b..467a8da9 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ExecutionQueue.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ExecutionQueue.kt @@ -128,12 +128,15 @@ internal class ExecutionQueue( */ fun canBeInserted(candidateRule: Rule, parentChunk: MatchJournal.OccChunk, beforeChunk: MatchJournal.Chunk): Boolean { // Place to try activating candidate rule is: - // either according to the ordering between rules. + // either according to the ordering between rules // or as the last one, after all existing activations val placeToInsertFound = beforeChunk is MatchJournal.MatchChunk && ruleOrdering.isEarlierThan(candidateRule, beforeChunk.match.rule()) - val childChunksEnded = !beforeChunk.isDescendantOf(parentChunk) + + val isDescendant = beforeChunk.justifiedBy(parentChunk) + val isSibling = beforeChunk.evidence == parentChunk.evidence && beforeChunk != parentChunk + val childChunksEnded = !isDescendant || isSibling return (childChunksEnded || placeToInsertFound) } @@ -143,7 +146,7 @@ internal class ExecutionQueue( // (i.e. don't reactivate additional, inactive heads that only completed the match?) fun offerAll(occs: Iterable): Boolean = execQueue.addAll(occs.mapNotNull { occ -> - journalIndex.activatingChunkOf(Id(occ))?.let { occChunk -> + journalIndex.activatingChunkOf(occ)?.let { occChunk -> ExecPos(occChunk).let { if (seen.add(it)) it else null } diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt index e52cf85c..68c466c4 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt @@ -118,7 +118,7 @@ interface MatchJournal : MutableIterable, EvidenceSource { * Returns [Chunk] where provided principal occurrence was activated. * Returns null for non-principal occurrences. */ - fun activatingChunkOf(occId: Id): OccChunk? + fun activatingChunkOf(occ: Occurrence): OccChunk? /** * Returns [Pos] at which provided [RuleMatch] is triggered @@ -161,11 +161,6 @@ interface MatchJournal : MutableIterable, EvidenceSource { */ fun entries(): List - /** - * Check whether this [Chunk] depends on another, which is specified by justifications. - */ - fun isDescendantOf(chunk: Chunk): Boolean = this.justifiedBy(chunk) - /** * Checks whether this [Chunk] has no ancestors (not counting [MatchJournal.initialChunk]) */ diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournalImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournalImpl.kt index 999c35a0..c097ff8b 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournalImpl.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournalImpl.kt @@ -96,7 +96,7 @@ internal open class MatchJournalImpl( override fun logMatch(match: RuleMatch): MatchChunk? { - var added: MatchChunk? = null + val added: MatchChunk? if (match.isPrincipal()) { added = MatchChunkImpl(nextEvidence(), match) @@ -105,11 +105,11 @@ internal open class MatchJournalImpl( trackAncestor(added) logAncestor(added) } else { - val dummy: Justified = MatchChunkImpl(initEvidence, match) + added = null + val dummy: Justified = MatchChunkImpl(initEvidence, match) // collects justifications trackAncestor(dummy) - // If non-principal match has any - // principal occurrences in head - // --- then they must be tracked + // If a non-principal match has any principal + // occurrences in head -- they must be tracked logJustificationsFrom(match) } // Log discarded occurrences @@ -134,26 +134,28 @@ internal open class MatchJournalImpl( private fun logJustificationsFrom(match: RuleMatch) { val parent: Chunk = parentChunk() - val moreJustifications = match.allHeads().filter { + val moreJustified = match.allHeads().filter { // Filter to avoid justifying parent by its child! it.isPrincipal() && !it.justifiedBy(parent) }.toList() - if (moreJustifications.isNotEmpty()) { + if (moreJustified.isNotEmpty()) { forEachChunkFrom(parent.toPos()) { child -> - child.justifyByAll(moreJustifications) + child.justifyByAll(moreJustified) } } } override fun logActivation(occ: Occurrence): OccChunk? { - var added: OccChunk? = null + val added: OccChunk? -// trackAncestor(occ) + trackAncestor(occ) if (occ.isPrincipal()) { added = OccChunkImpl(occ) current = added posPtr.add(current) + } else { + added = null } current.entries.add(Chunk.Entry(occ)) @@ -275,7 +277,7 @@ internal open class MatchJournalImpl( while (posPtr.hasNext()) { current = posPtr.next() - if (!current.isDescendantOf(ancestor)) { + if (!current.justifiedBy(ancestor)) { break } @@ -364,35 +366,37 @@ internal open class MatchJournalImpl( private class IndexImpl(chunks: Iterable): MatchJournal.Index { - private val chunkOrder = HashMap() - private val occChunks = HashMap, OccChunk>() + private val chunkOrder = HashMap, Int>() + private val occChunks = HashMap() init { chunks.forEachIndexed { index, chunk -> - chunkOrder[chunk.evidence] = index + chunkOrder[Id(chunk)] = index if (chunk is OccChunk) { - occChunks[Id(chunk.occ)] = chunk + occChunks[chunk.occ.identity] = chunk } } } override val size: Int = chunks.count() - override fun activatingChunkOf(occId: Id) = occChunks[occId] + override fun activatingChunkOf(occ: Occurrence) = occChunks[occ.identity] override fun activationPos(match: RuleMatchEx): OccChunk? = // The latest matched occurrence from match's head is (by definition) // the occurrence which activated this match. match.signature().mapNotNull { occSig -> - occSig?.let { activatingChunkOf(it) } - }.maxBy { chunkOrder[it.evidence]!! } // compare positions: find latest + occSig?.let { activatingChunkOf(it.wrapped) } + }.maxBy { orderOf(it)!! } // compare positions: find latest // todo: throw for invalid positions? override fun compare(lhs: Pos, rhs: Pos): Int = - compareBy{ chunkOrder[it.chunk.evidence] } + compareBy{ orderOf(it.chunk) } .thenComparingInt { it.entriesCount } .compare(lhs, rhs) + + private fun orderOf(chunk: Chunk): Int? = chunkOrder[Id(chunk)] } diff --git a/reactor/Test/test/RulesHelper.kt b/reactor/Test/test/RulesHelper.kt index 293bbf63..26f957b5 100644 --- a/reactor/Test/test/RulesHelper.kt +++ b/reactor/Test/test/RulesHelper.kt @@ -1,6 +1,5 @@ import jetbrains.mps.logic.reactor.core.* -import jetbrains.mps.logic.reactor.core.internal.FeedbackStatus -import jetbrains.mps.logic.reactor.core.internal.MatchJournal +import jetbrains.mps.logic.reactor.core.internal.* import jetbrains.mps.logic.reactor.evaluation.PredicateInvocation import jetbrains.mps.logic.reactor.evaluation.StoreView import jetbrains.mps.logic.reactor.logical.Logical @@ -181,15 +180,18 @@ fun taggedOccurrence(ruleUniqueTag: Any, id: String, vararg args: Any): Occurren MockConstraint(ConstraintSymbol.symbol(id, args.size)) .occurrence(MockController().logicalStateObservable(), listOf(* args), 0, justsOf(0), noLogicalContext, ruleUniqueTag) -fun justifiedOccurrence(id: String, evidence: Evidence, justifications: Justifications, vararg args: Any): Occurrence = - MockConstraint(ConstraintSymbol.symbol(id, args.size), true) +fun justifiedOccurrence(id: String, evidence: Evidence, justifications: Justifications, principal: Boolean, vararg args: Any): Occurrence = + MockConstraint(ConstraintSymbol.symbol(id, args.size), principal) .occurrence(MockController().logicalStateObservable(), listOf(* args), evidence, justifications, noLogicalContext) -fun justifiedOccurrence(id: String, evidence: Evidence, justs: Collection, vararg args: Any): Occurrence = - justifiedOccurrence(id, evidence, justsFromCollection(justs), * args) +fun principalOccurrence(id: String, hist: MatchJournal, vararg args: Any): Occurrence = + justifiedOccurrence(id, hist.evidence(), hist.justifications(), true, * args) -fun justifiedOccurrenceInit(id: String, vararg args: Any): Occurrence = - justifiedOccurrence(id, 1, justsFromCollection(setOf(0, 1)), * args) +fun justifiedOccurrence(id: String, hist: MatchJournal, vararg args: Any): Occurrence = + justifiedOccurrence(id, hist.evidence(), hist.justifications(), false, * args) + +fun principalOccurrenceInit(id: String, vararg args: Any): Occurrence = + justifiedOccurrence(id, 0, justsFromCollection(setOf(0)), true, * args) fun sym0(id: String): ConstraintSymbol = ConstraintSymbol(id, 0) diff --git a/reactor/Test/test/TestIncrementalProgram.kt b/reactor/Test/test/TestIncrementalProgram.kt index cc1f0fdb..7ad82655 100644 --- a/reactor/Test/test/TestIncrementalProgram.kt +++ b/reactor/Test/test/TestIncrementalProgram.kt @@ -626,8 +626,6 @@ class TestIncrementalProgram { )) ).relaunch("withBar", progSpec, evalRes.token()) { result -> - println(result.token().chunks()) - // 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" @@ -1385,7 +1383,6 @@ class TestIncrementalProgram { body( )), rule("influenceResult_default", - // note: non-principal rule which nonetheless matches on a principal constraint headReplaced( constraint("doInfluence") ), diff --git a/reactor/Test/test/TestStoreAwareJournal.kt b/reactor/Test/test/TestStoreAwareJournal.kt index 616f3656..32cdc2ec 100644 --- a/reactor/Test/test/TestStoreAwareJournal.kt +++ b/reactor/Test/test/TestStoreAwareJournal.kt @@ -45,13 +45,13 @@ class TestStoreAwareJournal { } fun logExpand(id: String, vararg args: Any) = - logExpand(occurrence(id, * args)) + logExpand(justifiedOccurrence(id, hist, * args)) fun logFirstMatch() = hist.logMatch(d.matches().first()) // log and expand occurrence while tracking its justifications fun logExpandJustified(id: String, vararg args: Any) = - logExpand(justifiedOccurrence(id, hist.nextEvidence(), justsCopy(hist.justifications()), * args)) + logExpand(principalOccurrence(id, hist, * args)) } @Test @@ -83,7 +83,7 @@ class TestStoreAwareJournal { hist.justifications() shouldBe justsOf(0) // initial chunk - logExpand(justifiedOccurrenceInit("foo")) + logExpand(principalOccurrenceInit("foo")) val fooMatches = d.matches() fooMatches.count() shouldBe 2 @@ -98,13 +98,13 @@ class TestStoreAwareJournal { // log second 'foo' match hist.logMatch(fooMatches.elementAt(1)) - hist.justifications() shouldBe justsOf(0,1,3) + hist.justifications() shouldBe justsOf(0,2) logExpandJustified("qux") d.matches().count() shouldBe 1 logFirstMatch() - hist.justifications() shouldBe justsOf(0,1,2,3,4,5) + hist.justifications() shouldBe justsOf(0,1,2,3) } } } @@ -142,7 +142,7 @@ class TestStoreAwareJournal { val initPos = hist.currentPos() - logExpand(justifiedOccurrenceInit("foo")) + logExpand(principalOccurrenceInit("foo")) val fooPos = hist.currentPos() @@ -224,7 +224,7 @@ class TestStoreAwareJournal { val initPos = hist.currentPos() - logExpand(justifiedOccurrenceInit("foo")) + logExpand(principalOccurrenceInit("foo")) val fooPos = hist.currentPos() @@ -313,7 +313,7 @@ class TestStoreAwareJournal { val initPos = hist.currentPos() - logExpand(justifiedOccurrenceInit("foo")) + logExpand(principalOccurrenceInit("foo")) val fooPos = hist.currentPos() logFirstMatch() @@ -392,7 +392,7 @@ class TestStoreAwareJournal { { with(JournalDispatcherHelper(Dispatcher(RuleIndex(rulesLists)))) { - logExpand(justifiedOccurrenceInit("foo")) + logExpand(principalOccurrenceInit("foo")) logFirstMatch() logExpandJustified("bar") @@ -454,8 +454,7 @@ class TestStoreAwareJournal { logExpand("foo") logFirstMatch() - // provide initial justification - logExpand(justifiedOccurrenceInit("bar")) + logExpandJustified("bar") logFirstMatch() logExpandJustified("qux") @@ -509,14 +508,14 @@ class TestStoreAwareJournal { { with(JournalDispatcherHelper(Dispatcher(RuleIndex(rulesLists)))) { - logExpand(justifiedOccurrenceInit("foo")) + logExpand(principalOccurrenceInit("foo")) // rule2 logFirstMatch() logExpand("bar") logExpand("bazz") // use this production later, but create it now to get relevant justs - val quxOcc = justifiedOccurrence("qux", hist.nextEvidence(), hist.justifications()) + val quxOcc = principalOccurrence("qux", hist) val curChunk = hist.currentPos().chunk @@ -614,7 +613,7 @@ class TestStoreAwareJournal { with(JournalDispatcherHelper(Dispatcher(RuleIndex(rulesLists)))) { - logExpand(justifiedOccurrenceInit("foo")) + logExpand(principalOccurrenceInit("foo")) // rule1 logFirstMatch()