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.
This commit is contained in:
Grigorii Kirgizov 2020-03-25 18:55:42 +03:00
parent 6a53d15a21
commit a2675351b5
9 changed files with 62 additions and 66 deletions

View File

@ -46,7 +46,6 @@ class Occurrence (observable: LogicalStateObservable,
val identity = System.identityHashCode(this)
init {
justifications.add(evidence) // ensure reflexivity of justifications
revive(observable)
}

View File

@ -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()}") {

View File

@ -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<FeedbackStatus>("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<FeedbackStatus>("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)
}

View File

@ -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<Occurrence>): 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
}

View File

@ -118,7 +118,7 @@ interface MatchJournal : MutableIterable<MatchJournal.Chunk>, EvidenceSource {
* Returns [Chunk] where provided principal occurrence was activated.
* Returns null for non-principal occurrences.
*/
fun activatingChunkOf(occId: Id<Occurrence>): OccChunk?
fun activatingChunkOf(occ: Occurrence): OccChunk?
/**
* Returns [Pos] at which provided [RuleMatch] is triggered
@ -161,11 +161,6 @@ interface MatchJournal : MutableIterable<MatchJournal.Chunk>, EvidenceSource {
*/
fun entries(): List<Entry>
/**
* 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])
*/

View File

@ -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<Chunk>): MatchJournal.Index
{
private val chunkOrder = HashMap<Evidence, Int>()
private val occChunks = HashMap<Id<Occurrence>, OccChunk>()
private val chunkOrder = HashMap<Id<Chunk>, Int>()
private val occChunks = HashMap<Int, OccChunk>()
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<Occurrence>) = 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<Pos>{ chunkOrder[it.chunk.evidence] }
compareBy<Pos>{ orderOf(it.chunk) }
.thenComparingInt { it.entriesCount }
.compare(lhs, rhs)
private fun orderOf(chunk: Chunk): Int? = chunkOrder[Id(chunk)]
}

View File

@ -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<Int>, 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)

View File

@ -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")
),

View File

@ -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()