Alternative solution for invalidating feedback
Introduce feedback basis to track dependencies on rules. Index journal chunks by evidence for quick lookup. The feedback basis is the collection of principal rules tags from the justifications of the current match.
This commit is contained in:
parent
7c89dfdffd
commit
c51a984648
|
|
@ -33,12 +33,12 @@ class CompositeFeedback private constructor(private val elements: List<Feedback>
|
|||
this.severity = maxSeverity(elements)
|
||||
}
|
||||
|
||||
override fun handle(currentRuleMatch: RuleMatch, feedbackKey: Any, supervisor: Supervisor): Boolean {
|
||||
override fun handle(currentRuleMatch: RuleMatch, feedbackKey: Any, feedbackBasis: List<Any>, supervisor: Supervisor): Boolean {
|
||||
var unhandled = 0
|
||||
for (feedback in elements) {
|
||||
if (!feedback.alreadyHandled()) {
|
||||
unhandled += 1
|
||||
if (supervisor.handleFeedback(currentRuleMatch, feedbackKey, feedback)) {
|
||||
if (supervisor.handleFeedback(currentRuleMatch, feedbackKey, feedbackBasis, feedback)) {
|
||||
feedback.setHandled()
|
||||
unhandled -= 1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ abstract class Feedback : EvaluationFeedback() {
|
|||
/**
|
||||
* Returns true if the feedback has been handled.
|
||||
*/
|
||||
open fun handle(currentRuleMatch: RuleMatch, feedbackKey: Any, supervisor: Supervisor): Boolean {
|
||||
if (!alreadyHandled() && supervisor.handleFeedback(currentRuleMatch, feedbackKey, this)) {
|
||||
open fun handle(currentRuleMatch: RuleMatch, feedbackKey: Any, feedbackBasis: List<Any>, supervisor: Supervisor): Boolean {
|
||||
if (!alreadyHandled() && supervisor.handleFeedback(currentRuleMatch, feedbackKey, feedbackBasis, this)) {
|
||||
setHandled()
|
||||
}
|
||||
return alreadyHandled()
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ typealias Evidence = Int
|
|||
*/
|
||||
typealias Justifications = TIntSet
|
||||
|
||||
|
||||
fun emptyJustifications(): Justifications = TIntHashSet(1)
|
||||
fun justsOf(vararg elements: Evidence): Justifications = TIntHashSet(elements)
|
||||
fun justsFromCollection(collection: Collection<Evidence>): Justifications = TIntHashSet(collection)
|
||||
|
|
|
|||
|
|
@ -167,7 +167,10 @@ internal class ControllerImpl (
|
|||
if (itemOk) {
|
||||
context.withStatus { status ->
|
||||
if (status.feedback?.alreadyHandled() == false) {
|
||||
status.feedback.handle(match, newParent.match.feedbackKey, supervisor)
|
||||
status.feedback.handle(match,
|
||||
newParent.match.feedbackKey,
|
||||
processing.principalRuleTags(newParent),
|
||||
supervisor)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -188,7 +191,10 @@ internal class ControllerImpl (
|
|||
|
||||
// if failure can be handled here then recover
|
||||
} else if (status.feedback?.alreadyHandled() == false
|
||||
&& status.failure.handle(match, newParent.match.feedbackKey, supervisor)) {
|
||||
&& status.failure.handle(match,
|
||||
newParent.match.feedbackKey,
|
||||
processing.principalRuleTags(newParent),
|
||||
supervisor)) {
|
||||
|
||||
status.recover()
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package jetbrains.mps.logic.reactor.core.internal
|
||||
|
||||
import gnu.trove.TIntObjectHashMap
|
||||
import jetbrains.mps.logic.reactor.core.*
|
||||
import jetbrains.mps.logic.reactor.evaluation.*
|
||||
import jetbrains.mps.logic.reactor.program.*
|
||||
|
|
@ -23,8 +24,10 @@ import jetbrains.mps.logic.reactor.util.Id
|
|||
import java.util.*
|
||||
import kotlin.Comparator
|
||||
|
||||
typealias ChunkIndex = TIntObjectHashMap<MatchJournal.Chunk>
|
||||
|
||||
interface MatchJournal : Iterable<MatchJournal.Chunk>, EvidenceSource {
|
||||
|
||||
interface MatchJournal : EvidenceSource {
|
||||
|
||||
/**
|
||||
* Add new [Chunk] for matches of principal rules.
|
||||
|
|
@ -68,7 +71,7 @@ interface MatchJournal : Iterable<MatchJournal.Chunk>, EvidenceSource {
|
|||
/**
|
||||
* Returns read-only [JournalIterator] that allows to remove future [Chunk]s.
|
||||
*/
|
||||
override fun iterator(): JournalIterator
|
||||
fun iterator(): JournalIterator
|
||||
|
||||
/**
|
||||
* Returns internal [JournalIterator] that allows to remove future [Chunk]s.
|
||||
|
|
@ -118,6 +121,7 @@ interface MatchJournal : Iterable<MatchJournal.Chunk>, EvidenceSource {
|
|||
*/
|
||||
fun index(): Index
|
||||
|
||||
fun principalRuleTags(chunk: Chunk): List<Any>
|
||||
|
||||
/**
|
||||
* Simplifies some search operations on [MatchJournal].
|
||||
|
|
@ -165,9 +169,9 @@ interface MatchJournal : Iterable<MatchJournal.Chunk>, EvidenceSource {
|
|||
* Length of the indexed [MatchJournal]
|
||||
*/
|
||||
val size: Int
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Immutable snapshot of [MatchJournal].
|
||||
*/
|
||||
|
|
@ -178,7 +182,6 @@ interface MatchJournal : Iterable<MatchJournal.Chunk>, EvidenceSource {
|
|||
override fun getStoreView(): StoreView = StoreViewImpl(
|
||||
chunks.flatMap { it.entries() }.allOccurrences().asSequence()
|
||||
)
|
||||
|
||||
override fun getPreamble(info: PreambleInfo): View {
|
||||
val cornerChunk = chunks.first() // corner chunk at beginning
|
||||
val initialChunk = chunks[1]
|
||||
|
|
@ -209,6 +212,7 @@ interface MatchJournal : Iterable<MatchJournal.Chunk>, EvidenceSource {
|
|||
* the next [Chunk] was logged.
|
||||
*/
|
||||
interface Chunk : Justified {
|
||||
|
||||
/**
|
||||
* Corresponds to event of activated or discarded [Occurrence]
|
||||
*/
|
||||
|
|
@ -235,7 +239,6 @@ interface MatchJournal : Iterable<MatchJournal.Chunk>, EvidenceSource {
|
|||
* Same as [entries], but returns only discarded [Occurrence]s.
|
||||
*/
|
||||
fun discardedLog(): List<Occurrence> = entries().filter { it.discarded }.map { it.occ }
|
||||
|
||||
fun toPos(): Pos = Pos(this, entries().size)
|
||||
}
|
||||
|
||||
|
|
@ -243,6 +246,7 @@ interface MatchJournal : Iterable<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].
|
||||
|
|
@ -253,7 +257,6 @@ interface MatchJournal : Iterable<MatchJournal.Chunk>, EvidenceSource {
|
|||
* [RuleMatch] which defines this [Chunk]
|
||||
*/
|
||||
val match: RuleMatch
|
||||
|
||||
val ruleUniqueTag: Any get() = match.rule().uniqueTag()
|
||||
}
|
||||
|
||||
|
|
@ -265,16 +268,15 @@ interface MatchJournal : Iterable<MatchJournal.Chunk>, EvidenceSource {
|
|||
}
|
||||
|
||||
open class Pos(val chunk: Chunk, val entriesCount: Int) {
|
||||
|
||||
override fun equals(other: Any?) =
|
||||
other is Pos
|
||||
&& other.chunk === chunk
|
||||
&& other.entriesCount == entriesCount
|
||||
|
||||
override fun hashCode(): Int = 31 * chunk.hashCode() + entriesCount
|
||||
|
||||
override fun toString(): String = "($chunk, $entriesCount)"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,8 @@ internal open class MatchJournalImpl(
|
|||
// invariant: never empty
|
||||
private val hist: IteratorMutableList<ChunkImpl>
|
||||
|
||||
private val chunkIndex = ChunkIndex()
|
||||
|
||||
// pointer to current position where logging (chunk additions) and log erasing (chunk removals) happen
|
||||
// initially points at initial chunk
|
||||
private val __cursor: Cursor
|
||||
|
|
@ -133,6 +135,13 @@ internal open class MatchJournalImpl(
|
|||
return added
|
||||
}
|
||||
|
||||
private fun indexChunk(chunk: Chunk) {
|
||||
chunkIndex.put(chunk.evidence, chunk)
|
||||
}
|
||||
|
||||
private fun lookupChunkByEvidence(evidence: Evidence) =
|
||||
chunkIndex.get(evidence)
|
||||
|
||||
private fun trackAncestor(logEvent: Justified) {
|
||||
while (!logEvent.justifiedBy(parentChunk())) {
|
||||
ancestorChunksStack.pop()
|
||||
|
|
@ -234,6 +243,20 @@ internal open class MatchJournalImpl(
|
|||
|
||||
override fun index(): MatchJournal.Index = IndexImpl(hist)
|
||||
|
||||
override fun principalRuleTags(chunk: Chunk): List<Any> {
|
||||
val ptags = mutableListOf<Any>()
|
||||
chunk.justifications().forEach { jn ->
|
||||
// hist is sequential, random access can be expensive
|
||||
(lookupChunkByEvidence(jn) as? MatchChunk)?.let {
|
||||
if (it.match.isPrincipal) {
|
||||
ptags.add(it.ruleUniqueTag)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
return ptags
|
||||
}
|
||||
|
||||
private fun allOccurrences(): Sequence<Occurrence> {
|
||||
// the following loop doesn't handle this case of starting posPtr, when 'current' isn't valid (e.g. just right after resetPos())
|
||||
if (__cursor.atStart()) return emptySequence()
|
||||
|
|
@ -320,6 +343,7 @@ internal open class MatchJournalImpl(
|
|||
override fun add(chunk: Chunk) {
|
||||
it.add(chunk as ChunkImpl)
|
||||
__current = chunk
|
||||
indexChunk(chunk)
|
||||
}
|
||||
|
||||
override fun removeNext() {
|
||||
|
|
@ -328,7 +352,6 @@ internal open class MatchJournalImpl(
|
|||
updateNext()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Walk in journal in direct order ([from] -> [current])
|
||||
* while applying [action] to each visited [Chunk]
|
||||
|
|
@ -380,7 +403,6 @@ internal open class MatchJournalImpl(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Immutable view for MutableList that provides mutability only through its iterators
|
||||
*/
|
||||
|
|
@ -453,7 +475,6 @@ internal open class MatchJournalImpl(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Mock RuleMatch for use only in initial Chunk
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -34,8 +34,5 @@ public interface Supervisor {
|
|||
* Override this method in order to "handle" the feedback.
|
||||
* Returns true if the method has handled (consumed) the feedback.
|
||||
*/
|
||||
default boolean handleFeedback(RuleMatch ruleMatch, Object feedbackKey, EvaluationFeedback feedback) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean handleFeedback(RuleMatch ruleMatch, Object feedbackKey, List<Object> feedbackBasis, EvaluationFeedback feedback);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@
|
|||
*/
|
||||
|
||||
import jetbrains.mps.logic.reactor.core.RulesDiff
|
||||
import jetbrains.mps.logic.reactor.evaluation.EvaluationFeedback
|
||||
import jetbrains.mps.logic.reactor.evaluation.InvocationContext
|
||||
import jetbrains.mps.logic.reactor.evaluation.RuleMatch
|
||||
import jetbrains.mps.logic.reactor.evaluation.Supervisor
|
||||
import jetbrains.mps.logic.reactor.logical.LogicalContext
|
||||
import jetbrains.mps.logic.reactor.logical.MetaLogical
|
||||
|
|
@ -128,7 +130,7 @@ open class MockSupervisor : Supervisor {
|
|||
else a
|
||||
}
|
||||
|
||||
// override fun handleFeedback(rule: Rule?, feedback: EvaluationFeedback?): Boolean = false
|
||||
override fun handleFeedback(ruleMatch: RuleMatch, feedbackKey: Any, feedbackBasis: MutableList<Any>, feedback: EvaluationFeedback): Boolean = false
|
||||
}
|
||||
|
||||
class MockConstraintRegistry() {
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ class TestController {
|
|||
vararg occurrences: ConstraintOccurrence): Controller {
|
||||
val program = MockProgram("test", rulesLists, registry = MockConstraintRegistry())
|
||||
val supervisor = object : MockSupervisor() {
|
||||
override fun handleFeedback(ruleMatch: RuleMatch, feedbackKey: Any, feedback: EvaluationFeedback): Boolean =
|
||||
override fun handleFeedback(ruleMatch: RuleMatch, feedbackKey: Any, feedbackBasis: MutableList<Any>, feedback: EvaluationFeedback): Boolean =
|
||||
feedbackHandler(ruleMatch, feedback)
|
||||
}
|
||||
MockSession.init(program, supervisor)
|
||||
|
|
|
|||
Loading…
Reference in New Issue