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:
parent
6a53d15a21
commit
a2675351b5
|
|
@ -46,7 +46,6 @@ class Occurrence (observable: LogicalStateObservable,
|
|||
val identity = System.identityHashCode(this)
|
||||
|
||||
init {
|
||||
justifications.add(evidence) // ensure reflexivity of justifications
|
||||
revive(observable)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()}") {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in New Issue