diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Dispatcher.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Dispatcher.kt index c6846bee..e7c45535 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Dispatcher.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Dispatcher.kt @@ -109,30 +109,7 @@ class Dispatcher (val ruleIndex: RuleIndex, prevState: DispatchingFrontState = e occId2tags.remove(discarded.identity) ?.mapNotNull { ruleTag -> ruletag2probe[ruleTag] } ?.map { probe -> probe.contract(discarded) }) - - /** - * Returns a [DispatchingFront] instance which "forgot" that it already expanded the occurrence. - * After that the occurrence can be expanded again without triggering observer reactivation logic. - * Needed to discern incremental reactivation from observers reactivation. - * In other words, the state of [DispatchingFront] connected with this occurrence - * transitions from "fully-expanded" to "partially-expanded". - */ - internal fun forgetExpanded(dropped: Occurrence): DispatchingFront = DispatchingFront(this, - ruleIndex.forOccurrence(dropped) - .mapNotNull { rule -> ruletag2probe[rule.uniqueTag()] } - .map { probe -> probe.forgetExpanded(dropped) } - ) - - /** - * Returns a new [DispatchingFront] instance which contracts state with this occurrence - * and "forgets" that has seen it or that consumed any matches involving it. - * Needed for pruning outdated unrelevant state on incremental reactivations. - */ - internal fun forget(dropped: Occurrence): DispatchingFront = DispatchingFront(this, - ruleIndex.forOccurrence(dropped) - .mapNotNull { rule -> ruletag2probe[rule.uniqueTag()] } - .map { probe -> probe.forget(dropped) }) - + /** * Serves to indicate that the specified [RuleMatchEx] has been processed (consumed) and has to * be excluded from any further "match" set returned by [matches]. @@ -147,19 +124,6 @@ class Dispatcher (val ruleIndex: RuleIndex, prevState: DispatchingFrontState = e return DispatchingFront(this) } - /** - * Forgets that the specified [RuleMatchEx] has been consumed. - */ - internal fun forget(consumedMatch: RuleMatchEx): DispatchingFront { - ruletag2probe[consumedMatch.rule().uniqueTag()]?.let { - val probe = it.forget(consumedMatch) - if (RULE_MATCHER_PROBE_PERSISTENT) { - ruletag2probe[consumedMatch.rule().uniqueTag()] = probe - } - } - return DispatchingFront(this) - } - } } diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleMatchingProbe.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleMatchingProbe.kt index 3a9aa0e3..1b2ac22b 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleMatchingProbe.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleMatchingProbe.kt @@ -54,19 +54,6 @@ interface RuleMatchingProbe { */ fun consume(ruleMatch: RuleMatchEx): RuleMatchingProbe - /** - * Clears all internal state related to [ruleMatch]. - * Effect is as if [ruleMatch] was never seen. - */ - fun forget(ruleMatch: RuleMatchEx): RuleMatchingProbe - - /** - * Clears all state related to [Occurrence] [occ]. - * Same as [contract], but also clears internal state. - * Effect is as if [occ] was never seen. - */ - fun forget(occ: Occurrence): RuleMatchingProbe - fun expand(occ: Occurrence): RuleMatchingProbe fun expand(occ: Occurrence, mask: BitSet, profiler: Profiler? = null): RuleMatchingProbe @@ -75,22 +62,4 @@ interface RuleMatchingProbe { * Tells the probe that [occ] can't be used for finding matches. */ fun contract(occ: Occurrence): RuleMatchingProbe - - /** - * Turns the Probe's processing-related state linked with - * [occ] from "fully-expanded" to "partially-expanded". - * Doesn't modify internal state related to [matches], instead - * modifies how the next [expand] of [occ] will be processed. - * - * Notion of "partially-expanded" state is important - * only for incremental execution. - */ - fun forgetExpanded(occ: Occurrence): RuleMatchingProbe - - /** - * Clears internal state related to [consume] - * as if [occ] were never seen by this probe. - */ - fun forgetConsumed(occ: Occurrence): RuleMatchingProbe - } \ No newline at end of file diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/RulesDiff.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/RulesDiff.kt deleted file mode 100644 index d36f2671..00000000 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/RulesDiff.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2014-2019 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package jetbrains.mps.logic.reactor.core - -import jetbrains.mps.logic.reactor.program.Rule - - -@Deprecated("superfluous") -class RulesDiff( - preserved: Iterable, - val added: Iterable, - val removed: Set -) { - private val preserved: Map = HashMap().apply { - preserved.forEach { put(it.uniqueTag(), it) } - } - - fun getPreservedRule(utag: Any): Rule? = preserved[utag] - - fun getPreserved(): Set = preserved.keys - - companion object { - @JvmStatic - fun emptyDiff() = RulesDiff(emptyList(), emptyList(), emptySet()) - - @JvmStatic - fun findDiff(old: Iterable, new: Iterable): RulesDiff { - val oldTagsSet = old.map { it.uniqueTag() }.toHashSet() - val newTagsSet = new.map { it.uniqueTag() }.toHashSet() - - val added = new.filter { !oldTagsSet.contains(it.uniqueTag()) } - val (preserved, removed) = old.partition { newTagsSet.contains(it.uniqueTag()) } - val removedTags: Set = removed.map { it.uniqueTag() }.toSet() - - return RulesDiff(preserved, added, removedTags) - } - } -} \ No newline at end of file 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 d47e14b3..1646d707 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 @@ -51,8 +51,6 @@ internal class ConstraintsProcessing( fun setStrategy(strategy: ProcessingStrategy) { this.incrementalProcessing = strategy } - fun getStateCleaner(): ProgramStateCleaner = ProgramStateCleaner() - fun getFrontState(): DispatchingFrontState = dispatchingFront.state() fun engage(controller: Controller) { @@ -63,18 +61,6 @@ internal class ConstraintsProcessing( logicalState.clearController(controller) } - fun activateContinue(controller: Controller, activeOcc: Occurrence, parent: MatchJournal.MatchChunk): FeedbackStatus { - assert(activeOcc.stored) - - // Forget that occurrence was seen. Otherwise it will be - // processed as with reactivation through observers. - this.dispatchingFront = dispatchingFront.forgetExpanded(activeOcc) - - trace.activateContinue(activeOcc) - - return processActivated(controller, activeOcc, parent, FeedbackStatus.NORMAL()) - } - fun evaluate(controller: Controller, prototype: Occurrence, inStatus: FeedbackStatus) : FeedbackStatus = profiler.profile("activate_${prototype.constraint().symbol()}") { // fixme: ensure justifications are tracked (incremented) correctly in processing & creator @@ -185,17 +171,11 @@ internal class ConstraintsProcessing( profiler.profile("discardOccurrence") { match.forEachReplaced { occ -> - // Principal occurrences must be preserved for future incremental evaluation sessions - if (!occ.isPrincipal) { - this.dispatchingFront = dispatchingFront.contract(occ) - } - + this.dispatchingFront = dispatchingFront.contract(occ) occ.stored = false profiler.profile("terminateOccurrence") { - occ.terminate(logicalState) - } trace.discard(occ) @@ -203,19 +183,7 @@ internal class ConstraintsProcessing( } } - - inner class ProgramStateCleaner{ - fun erase(occurrence: Occurrence) { - dispatchingFront = dispatchingFront.forget(occurrence) - occurrence.terminate(logicalState) - incrementalProcessing.processInvalidated(occurrence, logicalState) - } - - fun erase(match: RuleMatchEx) { - dispatchingFront = dispatchingFront.forget(match) - } - } - + /** * Encapsulates logic for deriving [Evidence] and [Justifications] for a new [Occurrence]. */ diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ContinuedActivationSink.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ContinuedActivationSink.kt deleted file mode 100644 index 070fa4e7..00000000 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ContinuedActivationSink.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2014-2019 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package jetbrains.mps.logic.reactor.core.internal - -import jetbrains.mps.logic.reactor.core.Occurrence - -internal interface ContinuedActivationSink { - - fun offerAll(continueFromPos: MatchJournal.Pos, occs: Iterable) - - fun offer(continueFromPos: MatchJournal.Pos, ancestor: MatchJournal.OccChunk): Boolean -} diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/EvaluationSessionImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/EvaluationSessionImpl.kt index 75c36133..12ff5451 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/EvaluationSessionImpl.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/EvaluationSessionImpl.kt @@ -65,7 +65,6 @@ internal interface SessionManager { * Bundle of all entities involved in a session. */ internal data class SessionParts( - val preambleInfo: PreambleInfo, val ruleIndex: RuleIndex, val journal: MatchJournal, val logicalState: LogicalState, @@ -89,45 +88,10 @@ internal class EvaluationSessionImpl private constructor ( @Suppress("UNCHECKED_CAST") override fun parameter(key: ParameterKey): T? = params ?.get(key) as T - private fun SessionToken?.hasBindingObservers() = - (this as? SessionTokenImpl)?.principalObservers?.isNotEmpty() ?: false - - private fun launch(token: SessionToken?, store: OccurrenceStore, main: Constraint): EvaluationResult { - val sessionProcessing: SessionManager = - with(incrementality) { - when { - ability().allowed() -> when { - // it's a too restrictive place for fallback, happens almost always - //token.hasBindingObservers() -> FallbackProcessingSession() - incrLevel() == IncrementalSpec.IncrLevel.Full -> IncrementalProcessingSession() - incrLevel() == IncrementalSpec.IncrLevel.Preamble -> PreambleProcessingSession() - else -> DefaultProcessingSession() - } - else -> DefaultProcessingSession() - } - } - - val session = - with(sessionProcessing) { - if (token != null) { - (token as? SessionTokenImpl)?.setStore(store) - nextSession(token) - } else { - firstSession() - } - } - - try { - return sessionProcessing.runSession(session, main) - - // fixme: exception is a dirty way to abort incremental processing. - } catch (e: IncrementalContractViolationException) { - with(FallbackProcessingSession(session)) { - val fallbackSession = - if (token != null) nextSession(token) else firstSession() - return runSession(fallbackSession, main) - } - } + private fun launch(token: SessionToken?, main: Constraint): EvaluationResult { + val sessionProcessing: SessionManager = DefaultProcessingSession() + val session = sessionProcessing.firstSession() + return sessionProcessing.runSession(session, main) } open inner class DefaultProcessingSession: SessionManager { @@ -152,11 +116,11 @@ internal class EvaluationSessionImpl private constructor ( val controller = ControllerImpl(supervisor, processing, incrementality, trace, profiler) - return SessionParts(program.preambleInfo(), ruleIndex, journal, logicalState, controller, processing, processingStrategy, PrincipalObserverDispatcher.EMPTY) + return SessionParts(ruleIndex, journal, logicalState, controller, processing, processingStrategy, PrincipalObserverDispatcher.EMPTY) } override fun endSession(session: SessionParts): SessionToken = with(session) { - SessionTokenImpl(journal.view(), emptyList(), ruleIndex.toRules(), emptyFrontState(), ruleIndex, logicalState, principalObservers.apply { clearTriggerReceiver() }) + SessionTokenImpl(journal.view(), ruleIndex.toRules(), emptyFrontState(), ruleIndex, logicalState, principalObservers.apply { clearTriggerReceiver() }) } override fun runSession(session: SessionParts, main: Constraint): EvaluationResult = with(session) { @@ -169,190 +133,7 @@ internal class EvaluationSessionImpl private constructor ( protected fun SessionParts.run(main: Constraint): FeedbackStatus = strategy.run(processing, controller, main) } - - /** - * Same as [DefaultProcessingSession], but also returns - * [EvaluationResult.invalidFeedbackKeys] & [EvaluationResult.invalidRules] - * to signify that results of a previous session must be cleared. - */ - inner class FallbackProcessingSession(private val abortedSession: SessionParts?): DefaultProcessingSession() { - - constructor(): this(null) - - val invalidatedFeedback: MutableFeedbackKeySet = mutableSetOf() - val invalidatedRules: MutableList = mutableListOf() - - - override fun nextSession(token: SessionToken): SessionParts { - - invalidateToken(token.journalView as MatchJournal.View) - invalidateAborted() - - return super.nextSession(token) - } - - override fun runSession(session: SessionParts, main: Constraint): EvaluationResult = with(session) { - val status = run(main) - controller.shutDown() - val newToken = endSession(session) - - invalidatedFeedback.addAll(strategy.invalidatedFeedback()) - invalidatedRules.addAll(strategy.invalidatedRules()) - - return EvaluationResultImpl(newToken, status, invalidatedFeedback, invalidatedRules) - } - - private fun invalidateAborted() { - if(abortedSession != null) { - with (abortedSession) { - invalidatedFeedback.addAll(strategy.invalidatedFeedback()) - invalidatedRules.addAll(strategy.invalidatedRules()) - } - } - } - - private fun invalidateToken(view: MatchJournal.View) { - for(chunk in view.chunks) { - if (chunk is MatchJournal.MatchChunk) { - invalidatedFeedback.add(chunk.match.feedbackKey) - invalidatedRules.add(chunk.ruleUniqueTag) - } - } - } - } - - open inner class IncrementalProcessingSession(): DefaultProcessingSession() { - - /** - * Same as [DefaultProcessingSession.firstSession], - * but uses [GroundProcessing] instead of [EmptyProcessing] - */ - override fun firstSession(): SessionParts { - val ruleIndex = RuleIndex(program.rules()) - val journal = MatchJournalImpl(incrementality) - val logicalState = LogicalState() - val dispatchingFront = Dispatcher(ruleIndex).front() - - val principalObservers = LogicalBindObserverDispatcher() - val processingStrategy = GroundProcessing(incrementality, principalObservers) - - val processing = ConstraintsProcessing(dispatchingFront, journal, logicalState, incrementality, trace, profiler) - processing.setStrategy(processingStrategy) - - val controller = ControllerImpl(supervisor, processing, incrementality, trace, profiler) - - return SessionParts(program.preambleInfo(), ruleIndex, journal, logicalState, controller, processing, processingStrategy, principalObservers) - } - - override fun nextSession(token: SessionToken): SessionParts { - val tkn = token as SessionTokenImpl - val logicalState = tkn.logicalState - - val ruleIndex = tkn.ruleIndex.apply { updateIndexFromRules(program.rules()) } - val journal = MatchJournalImpl(incrementality, tkn.journalView as MatchJournal.View) - val front = Dispatcher(ruleIndex, tkn.getFrontState()).front() - val processing = ConstraintsProcessing(front, journal, logicalState, incrementality, trace, profiler) - - val principalObservers = tkn.principalObservers - val processingStrategy = IncrementalProcessing( - incrementality, journal, program.newRules(), program.droppedRules(), - processing.getStateCleaner(), ruleIndex, principalObservers, trace - ) - processing.setStrategy(processingStrategy) - - val controller = ControllerImpl(supervisor, processing, incrementality, trace, profiler) - - return SessionParts(program.preambleInfo(), ruleIndex, journal, logicalState, controller, processing, processingStrategy, principalObservers) - } - - override fun endSession(session: SessionParts): SessionToken = with(session) { - val histView = journal.view() - processing.resetStore() // clear observers - val principalState = sessionState(frontState).resetLookup() - principalObservers.clearTriggerReceiver() - return SessionTokenImpl(histView, emptyList(), ruleIndex.toRules(), principalState, ruleIndex, logicalState, principalObservers) - } - - /** - * Preserve data needed between sessions: - * preserve only relevant and non-empty RuleMatchers - */ - protected fun sessionState(frontState: DispatchingFrontState) = - frontState.filterValues { ruleMatcher -> - incrementality.isPrincipal(ruleMatcher.rule()) || ruleMatcher.probe().hasOccurrences() - } - } - - inner class PreambleProcessingSession(): IncrementalProcessingSession() { - private var inputStore: OccurrenceStore = emptyStore() - - override fun nextSession(token: SessionToken): SessionParts { - val tkn = token as SessionTokenImpl - val logicalState = LogicalState() - - // fixme: ensure why ruleIndex.update here leads to errors - val ruleIndex = tkn.ruleIndex.apply{ updateIndexFromRules(program.rules()) } -// val ruleIndex = RuleIndex(program.rules()) - val front = Dispatcher(ruleIndex).front() // new dispatcher front - val journal = MatchJournalImpl(incrementality) // new journal - val processing = ConstraintsProcessing(front, journal, logicalState, incrementality, trace, profiler) - - this.inputStore = tkn.principalStore - val processingStrategy = CachedOccurrencesProcessing(incrementality, inputStore) - processing.setStrategy(processingStrategy) - - val controller = ControllerImpl(supervisor, processing, incrementality, trace, profiler) - - return SessionParts(program.preambleInfo(), ruleIndex, journal, logicalState, controller, processing, processingStrategy, tkn.principalObservers) - } - - override fun endSession(session: SessionParts): SessionToken = with(session) { - val histView = journal.view() - val outputOccurrences = histView.filterOccurrences(inputStore) - - outputOccurrences.forEach{ it.terminate(logicalState) } - processing.resetStore() // clear observers - logicalState.reset() - principalObservers.clearTriggerReceiver() - - val rules = ruleIndex.toRules().filter(preambleInfo::inPreamble) - - SessionTokenImpl(histView, outputOccurrences, rules, emptyFrontState(), ruleIndex, LogicalState()) - } - - private fun MatchJournal.View.filterOccurrences(without: OccurrenceStore = emptyStore()): OccurrenceStore { - val withoutSources = without.asSequence().map { it.cacheKey() }.toHashSet() - return chunks.asSequence() - .mapNotNull { (it as? MatchJournal.OccChunk)?.occ } - .filter { it.stored } - .filter { !withoutSources.contains(it.cacheKey()) } - .toList() - } - - private fun ConstraintOccurrence.cacheKey() = sourceRule() to constraint().symbol() - - private fun getPreamble(session: SessionParts): MatchJournal.View = with(session) { - val journalView = journal.view() - val preambleView = journalView.getPreamble(preambleInfo) - // unnecessary work if dispatcher state isn't passed in SessionToken - // cleanNonPreamble(processing, journalView.chunks, preambleView) - preambleView - } - - private fun cleanNonPreamble(processing: ConstraintsProcessing, chunks: Iterable, preambleView: MatchJournal.View) { - val preambleSet = preambleView.chunks.toSet() - val cleaner = processing.getStateCleaner() - - chunks.asSequence().filterNot(preambleSet::contains).forEach { - it.activatedLog().forEach(cleaner::erase) - if (it is MatchJournal.MatchChunk) { - cleaner.erase(it.match as RuleMatchEx) - } - } - } - } - - + private class Config(val program: Program) : EvaluationSession.Config() { val parameters = HashMap, Any>() @@ -363,22 +144,11 @@ internal class EvaluationSessionImpl private constructor ( var token: SessionToken? = null - var principalStore: Collection = emptyList() - override fun withTrace(computingTracer: EvaluationTrace): EvaluationSession.Config { this.evaluationTrace = computingTracer return this } - override fun withStoreView(storeView: StoreView): EvaluationSession.Config { - return this - } - - override fun withStore(store: Collection): EvaluationSession.Config { - this.principalStore = store as Collection - return this - } - override fun withSessionToken(token: SessionToken?): EvaluationSession.Config { this.token = token return this @@ -407,7 +177,7 @@ internal class EvaluationSessionImpl private constructor ( Backend.ourBackend.ourSession.set(session) try { val main = parameters[ParameterKey.of("main", Constraint::class.java)] as Constraint - return session.launch(token, principalStore, main) + return session.launch(token, main) } finally { try { diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/IncrSpecHolder.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/IncrSpecHolder.kt index ed90daa0..ef696501 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/IncrSpecHolder.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/IncrSpecHolder.kt @@ -22,6 +22,7 @@ import jetbrains.mps.logic.reactor.program.IncrementalContractViolationException import jetbrains.mps.logic.reactor.program.IncrementalSpec import jetbrains.mps.logic.reactor.program.Rule +@Deprecated("obsolete class") interface IncrSpecHolder { val ispec: IncrementalSpec @@ -34,18 +35,3 @@ interface IncrSpecHolder { val Rule.isWeakPrincipal get() = ispec.isWeakPrincipal(this) } - - -inline fun checkContract(value: Boolean) { - checkContract(value) { "Contract assertion failed" } -} -inline fun checkContract(value: Boolean, lazyMsg: () -> String) { - if (!value) throw IncrementalContractViolationException(lazyMsg()) -} - -inline fun IncrSpecHolder.assertContract(lazyValue: () -> Boolean) { - assertContract(lazyValue) { "Contract assertion failed" } -} -inline fun IncrSpecHolder.assertContract(lazyValue: () -> Boolean, noinline lazyMsg: () -> String) { - if (ispec.assertLevel().assertContracts()) checkContract(lazyValue(), lazyMsg) -} diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/IncrementalStage.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/IncrementalStage.kt deleted file mode 100644 index ff7276d5..00000000 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/IncrementalStage.kt +++ /dev/null @@ -1,489 +0,0 @@ -/* - * Copyright 2014-2020 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package jetbrains.mps.logic.reactor.core.internal - -import jetbrains.mps.logic.reactor.core.* -import jetbrains.mps.logic.reactor.evaluation.EvaluationTrace -import jetbrains.mps.logic.reactor.program.IncrementalSpec -import jetbrains.mps.logic.reactor.program.Rule -import java.util.PriorityQueue - - -/** - * Incremental stages handle different parts of incremental processing. - * - * Each incremental stage is applied at each journal cursor position - * with read-only rights (see [ChunkReader]) while traversing [MatchJournal]. - */ -internal interface IncrementalStage: IncrSpecHolder { - -// fun receive(data: Iterable): Boolean - -// fun onNext(reader: MatchJournal.ChunkReader): Collection - -} - - -/** - * Invalidation stage includes several activities: - * - removing from chunks (i.e. principal matches) that correspond - * to rules removed from program, their dependent chunks, and those - * dependent on additional invalidated [Justified] entities from [receive]; - * - reactivating occurrences that led to invalidated matches (through [activationSink]); - * - pruning invalidated occurrences and matches from [Dispatcher.DispatchingFront]'s state. - */ -internal class InvalidationStage( - override val ispec: IncrementalSpec, - private val posTracker: PosTracking, - private val invalidRuleIds: Set, - private val activationSink: ContinuedActivationSink, - private val stateCleaner: ConstraintsProcessing.ProgramStateCleaner, - private val trace: EvaluationTrace -): IncrementalStage { - - private val invalidJustifications = mutableListOf() - - private val invalidFeedbackKeys: MutableSet = mutableSetOf() - - private val invalidRuleIdsAll: MutableList = mutableListOf() - - - fun invalidatedFeedback(): FeedbackKeySet = invalidFeedbackKeys.toHashSet() - - fun invalidatedRules(): List = invalidRuleIdsAll.toList() - - fun receive(invalid: Iterable) { invalidJustifications.addAll(invalid) } - - fun receive(invalid: Justified) { invalidJustifications.add(invalid) } - - /** - * Invalidates next chunk, if needed. - * Doesn't remove the [Chunk] from the [Journal]. - * Returns [true] if chunk is invalidated and must be removed. - */ - fun onNext(reader: ChunkReader): Boolean { - val chunk = reader.next - if (chunk is MatchJournal.MatchChunk && chunk.dependsOnAny(invalidRuleIds)) { - invalidJustifications.add(chunk) - } - - // Invalidating dependent chunks - if (chunk.justifiedByAny(invalidJustifications)) { - - val validOccs = invalidateChunk(chunk) - activationSink.offerAll(reader.current.toPos(), validOccs) - - // Remove chunk from the journal - return true - } - return false - } - - private fun invalidateChunk(chunk: MatchJournal.Chunk): Iterable { - // 'Undo' all activated in this chunk occurrences: clear Dispatcher & LogicalState - chunk.activatedLog().forEach(stateCleaner::erase) - - val validOccs: Sequence - if (chunk is MatchJournal.MatchChunk) { - with (chunk.match) { - trace.invalidate(this) - - // Don't accidentaly invalidate new rules. - // New rules themselves remain valid, only their effects must be cleared. - if (posTracker.isOld(chunk)) { - invalidRuleIdsAll.add(rule().uniqueTag()) - } - // So invalidate feedback they produced - invalidFeedbackKeys.add(feedbackKey) - - stateCleaner.erase(this as RuleMatchEx) - - // Valid head occurrences could match more rules - // without this match, so need to reactivate them. - // E.g. occurrences discarded in this match on - // previous run but revived here can match more rules. - validOccs = allHeads().filter { occ -> - !occ.justifiedByAny(invalidJustifications) - } - // By definition of Chunk and principal rule, - // all occurrences from the head are principal. - assert(allHeads().all { it.isPrincipal }) - } - } else validOccs = emptySequence() - return validOccs.asIterable() - } - - private fun MatchJournal.MatchChunk.dependsOnAny(utags: Iterable): Boolean = - utags.contains(ruleUniqueTag) || utags.any(::dependsOnRule) - -} - - -/** - * Addition stage includes: - * - searching for potential matches for new [addedRules] (see [addRuleCandidates]) - * - receiving additional [MatchCandidates] (e.g. postponed matches) - * - offering both to [ContinuedActivationSink] at right positions in [MatchJournal] - * and in correct order according to rule priorities (see [offerCandidates]) - */ -internal class AdditionStage( - override val ispec: IncrementalSpec, - private val posTracker: PosTracking, - private val addedRules: Iterable, - private val activationSink: ContinuedActivationSink, - private val ruleOrdering: RuleOrdering, - private val ruleIndex: RuleIndex, - private val trace: EvaluationTrace -): IncrementalStage { - - interface MatchCandidate { - val rule: Rule - val occChunk: MatchJournal.OccChunk - } - - private data class PotentialMatch( - override val rule: Rule, - override val occChunk: MatchJournal.OccChunk - ): MatchCandidate - - - private val activationCandidates = PriorityQueue{ - lhs, rhs -> ruleOrdering.compare(lhs.rule, rhs.rule) - } - - private val hasRules = addedRules.iterator().hasNext() // it's constant - - - fun onNext(reader: ChunkReader) { - val chunk = reader.current - if (hasRules && chunk is MatchJournal.OccChunk) { - addRuleCandidates(chunk) - } - offerCandidates(reader) - } - - fun onRewind(reader: ChunkReader) = with(posTracker) { - activationCandidates.removeIf { - isNew(it.occChunk) || isFuture(it.occChunk) - } - } - - fun receive(candidates: Iterable) = - activationCandidates.addAll(candidates) - - private fun addRuleCandidates(chunk: MatchJournal.OccChunk) { - // filters out rules using occurrence's arguments - val allRuleCandidates = ruleIndex.forOccurrence(chunk.occ).map { it.uniqueTag() }.toHashSet() - - for (rule in addedRules) { - if (allRuleCandidates.contains(rule.uniqueTag()) && rule.canMatch(chunk.occ.constraint)) { - // Can this rule be matched by principal occurrence? - // Then we will need to find the place among existing child chunks - // (i.e. among some number of following ones) - // to activate this occurrence, to (possibly) match this rule. - // Also remember the parent justification of this rule candidate - // to drop it from monitoring when child chunks end. - activationCandidates.add(PotentialMatch(rule, chunk)) - // todo: also use the rule to help Dispatcher in future? - // i.e. try matching only on the candidate rule - } - } - } - - private fun offerCandidates(reader: ChunkReader) { - val aIt = activationCandidates.iterator() - while (aIt.hasNext()) { - val candidate = aIt.next() - val candidateRule = candidate.rule - val occChunk = candidate.occChunk - - val pos = - if (ruleOrdering.canBeInserted(candidateRule, occChunk, reader.next) || reader.atEnd()) - reader.current.toPos() - else - continue - - activationSink.offer(pos, occChunk) - trace.potentialMatch(occChunk.occ, candidateRule) - // Drop the candidate if appropriate activation place is found. - aIt.remove() - } - } - -} - - -// todo; extract more restricted interface (w/o rewind) for use in stages -internal class PosTracking( - val index: MatchJournal.Index, - private val journal: MatchJournal, - initPos: MatchJournal.Pos = journal.initialChunk().toPos() -) { - - /** - * Serves as a reference point for determining [isFuture] and [isPast]. - * Contract: [MatchJournal.Index.isKnown] is always `true` for [lastVisited]. - * Updated on each [onNext]. - */ - private var lastVisited: MatchJournal.Pos = initPos - - /** - * Contract: [MatchJournal.Index.isKnown] is always `true` for [front]. - * Updated only on [rewind], not on each [onNext]. - */ - private var front: MatchJournal.Pos = initPos - - - private fun updateReferencePos(current: MatchJournal.Chunk) { - if (index.isKnown(current)) { - lastVisited = current.toPos() - } - } - - fun rewind(pos: MatchJournal.Pos) = with(index) { - assert(isKnown(pos.chunk)) - assert(pos before lastVisited) - - journal.resetCursor(pos) - - if (lastVisited after front) { // rewind can happen inside another rewind and appear inferior - front = lastVisited - } - lastVisited = pos - } - - /** - * Tracks [MatchJournal] position as a reference point for [isFuture]. - */ - fun onNext(reader: ChunkReader): Unit = - updateReferencePos(reader.current) - - - fun isOld(chunk: MatchJournal.Chunk): Boolean = - index.isKnown(chunk) - fun isOld(occ: Occurrence): Boolean = - index.isKnown(occ) - - fun isNew(chunk: MatchJournal.Chunk): Boolean = - !index.isKnown(chunk) - fun isNew(occ: Occurrence): Boolean = - !index.isKnown(occ) - - - fun isFront(): Boolean = - with(index) { lastVisited afterOrEq front } - - fun isFuture(pos: MatchJournal.Pos): Boolean = - with(index) { pos after lastVisited } - fun isFuture(chunk: MatchJournal.Chunk) = isFuture(chunk.toPos()) - - fun isPast(pos: MatchJournal.Pos): Boolean = - with(index) { pos before lastVisited } - fun isPast(chunk: MatchJournal.Chunk) = isPast(chunk.toPos()) -} - - -internal class PostponeMatchesStage( - override val ispec: IncrementalSpec, - private val posTracker: PosTracking, - private val ruleOrdering: RuleOrdering -): IncrementalStage { - - private data class PostponedMatch( - val match: RuleMatchEx, - override val occChunk: MatchJournal.OccChunk - ): AdditionStage.MatchCandidate { - override val rule: Rule = match.rule() - - init { assert(match.allHeads().contains(occChunk.occ)) } - } - - - private val postponedMatches: MutableMap> = hashMapOf() - - - fun onNext(reader: ChunkReader): Collection { - return (reader.current as? MatchJournal.OccChunk)?.let { occChunk -> - postponedMatches.remove(occChunk.identity)?.map { PostponedMatch(it, occChunk) } - } ?: emptyList() - } - - /** - * Postpones future matches from [matches] on [active] - * and adds postponed matches to the resulting list (if there're any). - * - * @return matches ready for processing, sorted according to rule priorities. - */ - fun process(active: Occurrence, matches: List): List = - postponeFutureMatches(matches).withPostponedMatches(active) - - /** - * Determines, filters out, and postpones future matches. - * Returns only current matches. - * - * Future match is a match, which has heads that are not yet - * activated according to current [MatchJournal] position. - */ - fun postponeFutureMatches(matches: List): List { - val currentMatches = mutableListOf() - for (m in matches) { - // Returns null for matches with occurrences only from this session - // because journalIndex indexes only previous session. - val occChunk = posTracker.index.activationPos(m) - if (occChunk != null && posTracker.isFuture(occChunk.toPos())) { - postponedMatches.getOrPut(occChunk.identity, ::mutableListOf).add(m) - - } else { - currentMatches.add(m) - } - } - return currentMatches - } - - /** - * Adds previously postponed matches on [active] [Occurrence], if any, or returns original list. - * - * @return matches sorted according to rule priorities or original list. - */ - private fun List.withPostponedMatches(active: Occurrence): List = - postponedMatches.remove(active.identity)?.let { postponed -> - (this + postponed).sortedWith(ruleOrdering.matchComparator) - } ?: this - - private val MatchJournal.OccChunk.identity get() = this.occ.identity -} - - -internal class ContinueOccurrencesStage( - override val ispec: IncrementalSpec, - private val journalIndex: MatchJournal.Index -): IncrementalStage, ContinuedActivationSink { - - /** - * Specifies position in [MatchJournal] for continuing program evaluation. - * Position of [reactivated] in journal must precede execution position [continueFrom]. - * (see contract in [assertValid]). - */ - private data class ExecPos(val continueFrom: MatchJournal.Pos, val reactivated: MatchJournal.OccChunk) { - val reactivatedOcc: Occurrence get() = reactivated.occ - } - - private infix fun ChunkReader.at(execPos: ExecPos) = - this at execPos.continueFrom.chunk - - private fun ExecPos.assertValid() { - assert(journalIndex.isKnown(reactivated)) { - "only occurrences from previous session can be incrementally continued" - } - - assert({ // lazily - if (!isNew(continueFrom.chunk)) { // can't assert it for new chunks (i.e. those from this session) - val isAncestor = continueFrom.chunk.justifiedBy(reactivated) - val isPredecessor = journalIndex.compare(continueFrom, reactivated.toPos()) >= 0 - isAncestor || isPredecessor - } else true - }()) - } - - - private val queue: MutableList = ArrayList(1 + journalIndex.size / 8) // rough estimate - - private val seen: MutableSet = HashSet() - - - fun onNext(reader: ChunkReader) = mutableListOf().apply { - while (queue.isNotEmpty() && reader at queue.top()) { - add(queue.pop().reactivatedOcc) - } - } - - fun onRewind(reader: ChunkReader): Unit = with(journalIndex) { - val rewindPos = reader.next.toPos() - - // clear queue - queue.removeIf { - it.reactivated.toPos() afterOrEq rewindPos - } - - // clear seen - seen.removeIf { - isNew(it.continueFrom.chunk) || it.continueFrom afterOrEq rewindPos - } - } - - override fun offerAll(continueFromPos: MatchJournal.Pos, occs: Iterable) = - occs.mapNotNull(journalIndex::activatingChunkOf) - .sortedWith(journalIndex.chunkComparator) - .forEach { offer(continueFromPos, it) } - - override fun offer(continueFromPos: MatchJournal.Pos, ancestor: MatchJournal.OccChunk): Boolean = - ExecPos(continueFromPos, ancestor).let { - when { - seen.add(it) -> { - it.assertValid() - queue.push(it) - } - else -> false - } - } - - private fun isNew(chunk: MatchJournal.Chunk) = !journalIndex.isKnown(chunk) - - private fun MutableList.top(): E = this.last() - private fun MutableList.pop(): E = this.removeAt(this.size - 1) - private fun MutableList.push(element: E): Boolean = this.add(element) -} - - -internal class RewindStage( - override val ispec: IncrementalSpec, - private val posTracker: PosTracking, - private val principalObserver: PrincipalObserverDispatcher -): IncrementalStage { - - private val toRewind: PriorityQueue = PriorityQueue(posTracker.index.chunkComparator) - - private val seen = hashSetOf() - - // precondition: all received occurrences are principal and valid (i.e. not invalidated) - fun receive(occs: Sequence): Boolean = - occs.filter(::takeVolatile) - .mapNotNull(posTracker.index::activatingChunkOf) // get chunk corresponding to occurrence activation - .mapNotNull(posTracker.index::matchChunkOf) // get chunk that activated it - .filter(seen::add) // filter already seen - .toList().let(toRewind::addAll) - - /** - * Returns collection of past chunks for rewind - * in sorted order from earliest to latest. - */ - fun onNext(reader: ChunkReader): Collection = - if (needRewind()) { - toRewind.toList().also { toRewind.clear() } - } else emptyList() - - fun needRewind(): Boolean = toRewind.peek()?.let { - posTracker.isPast(it) - } ?: false - - - private fun takeVolatile(occ: Occurrence) = - // NB: only occurrences from previous session are considered - posTracker.isOld(occ) && - with(principalObserver) { removeTriggered(occ) } - -} 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 a4a56223..468ed2f8 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 @@ -182,27 +182,6 @@ interface MatchJournal : 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] - val preambleChunks = arrayListOf(cornerChunk, initialChunk) - - for (chunk in chunks.asSequence().drop(preambleChunks.size)) { - // chunks which depend only on preamble are added to preamble - if (chunk.justifiedOnlyBy(preambleChunks)) { - if (chunk is MatchChunk && info.inPreamble(chunk.match.rule()) - || chunk is OccChunk) - { - preambleChunks.add(chunk) - } - } - } - val evidenceSeed = preambleChunks.last().evidence; - preambleChunks.add(chunks.last()) // corner chunk at end - assert(evidenceSeed == preambleChunks.maxBy { it.evidence }?.evidence) - - return View(preambleChunks, evidenceSeed + 1) - } } /** diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/PrincipalObserverDispatcher.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/PrincipalObserverDispatcher.kt index 705a0935..5f8d382f 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/PrincipalObserverDispatcher.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/PrincipalObserverDispatcher.kt @@ -51,130 +51,3 @@ interface PrincipalObserverDispatcher { override fun removeTriggered(occ: Occurrence): Boolean = false } } - -/** - * Ensures important contract of incremental algorithm. - * - * Contract states that arguments of principal [Occurrence]s - * must be immutable, that is, logicals can't be unified - * with either ground or free other logicals. - */ -internal class LogicalBindObserverDispatcher : PrincipalObserverDispatcher { - - private var receiver: ObserverTriggeredHandler = { false } - - private val observing: HashMap = hashMapOf() - private val triggered: HashSet = hashSetOf() - - override fun toString(): String = - "${javaClass.name}(observed: ${observing.size}, triggered: ${triggered.size})" - - override fun setTriggerReceiver(receiver: ObserverTriggeredHandler) { this.receiver = receiver } - - override fun clearTriggerReceiver() { receiver = { false } } - - override fun onActivated(occ: Occurrence, observable: LogicalStateObservable) { - if (observing.containsKey(occ.identity)) return - - LogicalBindObserver(this, occ, observable).let { - if (it.observes) { - observing[occ.identity] = it - } - } - } - - override fun onInvalidated(occ: Occurrence, observable: LogicalStateObservable) { - // NB: works between incremental sessions only if this instance is preserved between them - observing.remove(occ.identity)?.removeObservers(observable) - } - - override fun isEmpty(): Boolean = triggered.isEmpty() //&& observing.isEmpty() - - override fun isObserving(occ: Occurrence): Boolean = observing.containsKey(occ.identity) - - override fun isTriggered(occ: Occurrence): Boolean = triggered.contains(occ.identity) - - override fun removeTriggered(occ: Occurrence): Boolean = triggered.remove(occ.identity) - - - fun onTrigger(source: Occurrence, logical: Logical<*>) { - observing.remove(source.identity) // logical observers are already removed - if (!receiver(source)) { - // if receiver didn't accept occurrence, - // then remember it for later requests - triggered.add(source.identity) - } - } - - private inner class LogicalBindObserver( - private val logicalDispatcher: LogicalBindObserverDispatcher, - val source: Occurrence, - observable: LogicalStateObservable - ): ForwardingLogicalObserver { - - private val observed: MutableSet> = hashSetOf() - - init { - for (unboundLogical in source.usedUnboundLogicals()) { - observe(unboundLogical, observable) - } - } - - private fun observe(arg: Logical<*>, observable: LogicalStateObservable) { - if (!observed.contains(arg)) { - observable.addForwardingObserver(arg, this) - observed.add(arg) - } - } - - fun removeObservers(observable: LogicalStateObservable) { - for (logical in observed) { - observable.removeForwardingObserver(logical, this) - } - observed.clear() - } - - val observes: Boolean = observed.isNotEmpty() - - - override fun valueUpdated(logical: Logical<*>, controller: Controller) = onUpdated(logical, controller) - - override fun parentUpdated(logical: Logical<*>, controller: Controller) = onUpdated(logical, controller) - - private fun onUpdated(logical: Logical<*>, controller: Controller) { - removeObservers(controller.logicalStateObservable()) - logicalDispatcher.onTrigger(source, logical) - } - } - - - - private fun Occurrence.removeContractObservers(observable: LogicalStateObservable) { - for (observedLogical in this.usedUnboundLogicals()) { - observable.removeForwardingObserversWhere(observedLogical) { observer -> - observer is LogicalBindObserver && observer.source.identity == this.identity - } - } - } -} - - -internal fun Occurrence.usedUnboundLogicals(): Set> { - val unique = hashSetOf>() - for (arg in arguments) { - - val argLogicals = when (arg) { - is Logical<*> -> - if (!arg.isBound) listOf(arg) - else when(val value = arg.findRoot().value()) { - is Term -> value.unboundLogicals() - else -> emptyList() - } - is Term -> arg.unboundLogicals() - else -> emptyList() - } - - unique.addAll(argLogicals) - } - return unique -} diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingStrategy.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingStrategy.kt index 97996142..5620901d 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingStrategy.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingStrategy.kt @@ -148,269 +148,3 @@ internal open class EmptyProcessing: ProcessingStrategy { override fun processInvalidated(occ: Occurrence, observable: LogicalStateObservable) {} } - - -/** - * Strategy that observes logical vars used in principal constraints - * with a help of [PrincipalObserverDispatcher]. - * - * Required for [IncrementalProcessing] for an initial session run. - */ -internal open class GroundProcessing( - override val ispec: IncrementalSpec, - private val principalObservers: PrincipalObserverDispatcher = PrincipalObserverDispatcher.EMPTY -): EmptyProcessing(), IncrSpecHolder { - - override fun processActivated(active: Occurrence, observable: LogicalStateObservable) { - if (active.isPrincipal) { - principalObservers.onActivated(active, observable) - } - } - - override fun processInvalidated(occ: Occurrence, observable: LogicalStateObservable) { - if (occ.isPrincipal) { - principalObservers.onInvalidated(occ, observable) - } - } - -} - - -/** - * Facade implementation for incremental processing algorithm. - * - * It includes 5 stages that operate on [MatchJournalImpl.Cursor]. - * Stages are: - * - [RewindStage] - * - [InvalidationStage] - * - [AdditionStage] - * - [PostponeMatchesStage] - * - [ContinueOccurrencesStage] - * - * Main loop [run] defines relations between stages. - * After invalidation and addition control flow is passed to - * general processing in [Controller] & [ConstraintsProcessing]. - * - * Methods [processMatch] & [processOccurrenceMatches] serve as - * a bridge back from [ConstraintsProcessing] to specific stages. - */ -internal class IncrementalProcessing( - ispec: IncrementalSpec, - val journal: MatchJournal, - newRules: Iterable, - droppedRules: Iterable, - stateCleaner: ConstraintsProcessing.ProgramStateCleaner, - ruleIndex: RuleIndex, - principalObservers: PrincipalObserverDispatcher, - trace: EvaluationTrace -): GroundProcessing(ispec, principalObservers) { - - private val journalIndex = journal.index() - private val ruleOrdering = RuleOrdering(ruleIndex) - - private val posTracker = PosTracking(journalIndex, journal) - private val continuator = ContinueOccurrencesStage(ispec, journalIndex) - private val invalidator = InvalidationStage(ispec, posTracker, droppedRules.toSet(), continuator, stateCleaner, trace) - private val adder = AdditionStage(ispec, posTracker, newRules, continuator, ruleOrdering, ruleIndex, trace) - private val postponer = PostponeMatchesStage(ispec, posTracker, ruleOrdering) - private val rewinder = RewindStage(ispec, posTracker, principalObservers) - - init { - principalObservers.setTriggerReceiver(this::receiveBindTriggered) - } - - - override fun invalidatedFeedback(): FeedbackKeySet = - invalidator.invalidatedFeedback() - - override fun invalidatedRules(): List = - invalidator.invalidatedRules() - - override fun offerMatch(match: RuleMatchEx) = - !match.allHeads().filter { it.isPrincipal }.toList().let { - if (it.isNotEmpty()) { - rewinder.receiveStrict(it.asSequence(), match) - } else false - } - - override fun processMatch(match: RuleMatchEx) = - continueReplacedHeadsImpl(match) - - override fun processOccurrenceMatches(active: Occurrence, matches: List) = - postponeFutureMatchesImpl(active, matches) - - - override fun run(processing: ConstraintsProcessing, controller: Controller, main: Constraint): FeedbackStatus { - var status: FeedbackStatus = FeedbackStatus.NORMAL() - val cursor = journal.cursor - while (true) { - posTracker.onNext(cursor) - - rewind(cursor) - invalidate(cursor) - - val postponedMatches = postponer.onNext(cursor) - adder.receive(postponedMatches) - // Adder step must work on the incremental front. - // If rewind happened, then must skip this. - if (posTracker.isFront()) { adder.onNext(cursor) } - - // fixme: pass inStatus? - status = continuator.runContinued(processing, controller, cursor) - // continuator may request invalidating more chunks - val haveChanges = invalidate(cursor) - - if (!status.operational) break - if (!rewinder.needRewind() && cursor.atEnd()) break - // These changes (if present) must operate on - // current cursor position, so don't advance it. - if (!rewinder.needRewind() && !haveChanges) cursor.next() - } - return status - } - - private fun receiveBindTriggered(occ: Occurrence): Boolean = - if (journalIndex.isKnown(occ)) { - invalidator.receive(occ) - true - } else false - - private fun continueReplacedHeadsImpl(match: RuleMatchEx) { - if (requiresIncrementalProcessing(match)) { - val invalidJustifications = match.matchHeadReplaced().filter { it.isPrincipal } - invalidator.receive(invalidJustifications) - } - } - - private fun postponeFutureMatchesImpl(active: Occurrence, matches: List) = - if (requiresIncrementalProcessing(active)) { - postponer.postponeFutureMatches(matches) - } else matches - - private fun RewindStage.receiveStrict(occs: Sequence, match: RuleMatchEx) = - rewinder.receive(occs).also { - if (ispec.assertLevel() == IncrementalSpec.AssertLevel.AssertContracts) { - throw IncrementalContractViolationException( - "Incremental processing can't guarantee correctness for match ${match.rule().uniqueTag()}" - ) - } - } - - private fun rewind(cursor: ChunkReader) { - val toRewind = rewinder.onNext(cursor) - - toRewind.firstOrNull()?.let { earliest -> - // This is the only place where journal can be reset to past - // fixme: modifying not through cursor; not the cleanest way - posTracker.rewind(earliest.toPos()) - - // Will invalidate these chunks and reevaluate them - invalidator.receive(toRewind) - - // Some stages on rewind need to clear internal state - adder.onRewind(cursor) - continuator.onRewind(cursor) - } - } - - private fun invalidate(cursor: RemovingJournalIterator): Boolean { - var haveChanges = false - do { - // handle all new chunks that were invalidated by rewind - if (posTracker.isNew(cursor.next)) { - invalidator.receive(cursor.next) - } - - if (invalidator.onNext(cursor)) { - cursor.removeNext() - haveChanges = true - } else break - } while (true) - return haveChanges - } - - private fun requiresIncrementalProcessing(match: RuleMatchEx) = !journal.isFront() && ispec.ability().allowed() && match.isPrincipal - - private fun requiresIncrementalProcessing(occ: Occurrence) = !journal.isFront() && ispec.ability().allowed() && occ.isPrincipal - -} - - -/** - * Strategy that works with occurrence store. - * Doesn't directly work with [MatchJournal] except for default logging. - * Strategy simply activates [Occurrence]s passed as input store - * - * Caller is responsible for handling new computed principal [Occurrences]: - * it can get them from [MatchJournal] and output for putting into cache. - * - * It requires that program in question adheres to incremental contracts. - * Importantly, one ensured by [LogicalBindObserverDispatcher]. - */ -internal class CachedOccurrencesProcessing( - ispec: IncrementalSpec, - private val occurrences: OccurrenceStore -): GroundProcessing(ispec) { - - private var inPreamble = true - - private val postponedMatches: MutableList>> = mutableListOf() - - - override fun processOccurrenceMatches(active: Occurrence, matches: List) = - if (inPreamble && matches.isNotEmpty()) { - postponedMatches.add(active to matches) - emptyList() - } else matches - - - override fun run(processing: ConstraintsProcessing, controller: Controller, main: Constraint): FeedbackStatus { - var status: FeedbackStatus = FeedbackStatus.NORMAL() - - // NB: assume evaluation order doesn't matter for these occurrences - - // first activate cached occurrences, - // but don't process their matches right away - this.inPreamble = true - for (occ in occurrences) { - if (occ.constraint().symbol() == main.symbol()) - continue - status = processing.evaluate(controller, occ, status) - if (!status.operational) - return status - } - this.inPreamble = false - - // then continue matches caused by cached occurrences - status = continueMatches(processing, controller, status) - - // then proceed with normal execution - if (status.operational) - status = controller.activate(main) - - return status - } - - private fun continueMatches(processing: ConstraintsProcessing, controller: Controller, inStatus: FeedbackStatus): FeedbackStatus { - val parentChunk = processing.initialChunk() // fixme: get activation chunk of active occ? - val status = postponedMatches.fold(inStatus) { status, (active, matches) -> - processing.processMatches(controller, active, matches, parentChunk, status) - } - return status - } -} - - -private fun ContinueOccurrencesStage.runContinued(processing: ConstraintsProcessing, controller: Controller, chunkReader: ChunkReader): FeedbackStatus { - var status: FeedbackStatus = FeedbackStatus.NORMAL() - val parentChunk = processing.parentChunk() - - for (continuedOcc in this.onNext(chunkReader)) { - if (continuedOcc.stored) { - status = processing.activateContinue(controller, continuedOcc, parentChunk) - - if (!status.operational) break - } - } - return status -} diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ReteRuleMatcherImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ReteRuleMatcherImpl.kt index 99298feb..db3234a1 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ReteRuleMatcherImpl.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ReteRuleMatcherImpl.kt @@ -26,7 +26,7 @@ import kotlin.NoSuchElementException import kotlin.collections.ArrayList /** - * An alternative implementation of RuleMatcherImpl. Has similar asymptotic characteristics as the "naïve" implementation. + * An alternative implementation of RuleMatcher. Has similar asymptotic characteristics as the "naïve" implementation. * * Loosely based on "Rete network" algorithm. * @@ -77,6 +77,7 @@ internal class ReteRuleMatcherImpl(private var ruleLookup: RuleLookup?, var lastGeneration = Generation(Layer(InitialNode())) + /* FIXME MEMLEAK */ val consumedSignatures = IndexedSignatureSet() abstract inner class ReteNode @@ -234,8 +235,10 @@ internal class ReteRuleMatcherImpl(private var ruleLookup: RuleLookup?, */ inner class Layer() { + /* FIXME MEMLEAK */ private val introTrail: Trail = trailOf() + /* FIXME MEMLEAK */ private val introNodes = HashMap>() private val nodeList = UnionFindLinkedList() @@ -255,12 +258,7 @@ internal class ReteRuleMatcherImpl(private var ruleLookup: RuleLookup?, fun containsOccurrence(occ: Occurrence): Boolean { return introTrail.contains(occ.identity) } - - fun forgetContains(occ: Occurrence) { - // FIXME this breaks the internal invariant - introTrail.remove(occ.identity) - } - + fun iterate(): MutableIterator = nodeList.iterator() fun nextNode(it: MutableIterator): ReteNode? { @@ -449,23 +447,7 @@ internal class ReteRuleMatcherImpl(private var ruleLookup: RuleLookup?, consumedSignatures.removeAllWith(occurrence.identity) return nextGeneration().reset() } - - /* - * Allows to avoid triggering reactivation logic in the next call of "introduce" for this occurrence. - */ - fun forgetIntroduced(occurrence: Occurrence): Generation { - for (layer in layers) { - layer.forgetContains(occurrence) - } - if (lastIntroduced === occurrence) lastIntroduced = null - return nextGeneration() - } - - fun erase(occurrence: Occurrence): Generation { - if (lastIntroduced === occurrence) lastIntroduced = null - return drop(occurrence).clearInvalidNodes() - } - + private fun clearInvalidNodes(): Generation { while (nodesIt.hasNext()) { val n = nodesIt.next() @@ -528,21 +510,6 @@ internal class ReteRuleMatcherImpl(private var ruleLookup: RuleLookup?, return this } - override fun forgetExpanded(occ: Occurrence): ReteNetwork { - this.lastGeneration.forgetIntroduced(occ) - return this - } - - override fun forgetConsumed(occ: Occurrence): ReteNetwork { - consumedSignatures.removeAllWith(occ.identity) - return this - } - - override fun forget(occ: Occurrence): RuleMatchingProbe { - this.lastGeneration = lastGeneration.erase(occ) - return this - } - override fun hasOccurrences(): Boolean { return this.lastGeneration.hasOccurrences() } @@ -556,11 +523,6 @@ internal class ReteRuleMatcherImpl(private var ruleLookup: RuleLookup?, return this } - override fun forget(ruleMatch: RuleMatchEx): RuleMatchingProbe { - consumedSignatures.remove(ruleMatch.signatureArray().toSignature()) - return this - } - } } diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/RuleOrdering.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/RuleOrdering.kt deleted file mode 100644 index 94ff4afb..00000000 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/RuleOrdering.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2014-2019 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package jetbrains.mps.logic.reactor.core.internal - -import jetbrains.mps.logic.reactor.core.Occurrence -import jetbrains.mps.logic.reactor.core.canMatch -import jetbrains.mps.logic.reactor.evaluation.RuleMatch -import jetbrains.mps.logic.reactor.program.Rule - - -internal class RuleOrdering(order: Iterable): ComparatorExt { - - private val ruleOrder: Map = HashMap().apply { - put(MatchJournalImpl.InitRuleMatch.rule().uniqueTag(), -1) // less than anything - order.forEachIndexed { index, rule -> put(rule.uniqueTag(), index) } - } - - val ruleTags: Set = order.map { it.uniqueTag() }.toHashSet() // NB: without initial rule - - override fun compare(lhs: Rule, rhs: Rule): Int = compareBy(this::orderOfThrow).compare(lhs, rhs) - - val matchComparator: Comparator get() = compareBy{ orderOfThrow(it.rule()) } - - - private fun orderOf(rule: Rule): Int? = ruleOrder[rule.uniqueTag()] - - private fun orderOfThrow(rule: Rule): Int = when (val res = orderOf(rule)) { - null -> throw IllegalStateException("Compared rule (${rule.uniqueTag()}) must be present in rule index!") - else -> res - } -} - - -/** - * Checks whether [candidateRule] can be inserted in journal as a child - * of [parentChunk] before one of its child chunks, [beforeChunk]. - * It is assumed that [candidateRule] can be matched by [Occurrence] from [parentChunk]. - */ -internal fun RuleOrdering.canBeInserted(candidateRule: Rule, parentChunk: MatchJournal.OccChunk, beforeChunk: MatchJournal.Chunk): Boolean { - // Place to try activating candidate rule is: - // either according to the ordering between rules - // or as the last one, after all existing activations - assert(candidateRule.canMatch(parentChunk.occ.constraint)) - - val placeToInsertFound = beforeChunk is MatchJournal.MatchChunk - && candidateRule before beforeChunk.match.rule() - - val isDescendant = beforeChunk.justifiedBy(parentChunk) - val isSibling = beforeChunk.evidence == parentChunk.evidence && beforeChunk != parentChunk - val childChunksEnded = !isDescendant || isSibling - - return (childChunksEnded || placeToInsertFound) -} diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/SessionTokenImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/SessionTokenImpl.kt index cf43d097..f0fc6d49 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/SessionTokenImpl.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/SessionTokenImpl.kt @@ -25,7 +25,6 @@ import jetbrains.mps.logic.reactor.program.Rule data class SessionTokenImpl( private val journalView: MatchJournal.View, - private var store: Collection, private val rules: Iterable, private val frontState: DispatchingFrontState, val ruleIndex: RuleIndex, @@ -35,8 +34,4 @@ data class SessionTokenImpl( { override fun getJournalView(): MatchJournalView = journalView override fun getRules(): Iterable = rules - override fun getPrincipalStore(): Collection = store - - fun getFrontState(): DispatchingFrontState = frontState - fun setStore(newStore: Collection) { this.store = newStore } } diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/evaluation/EvaluationSession.java b/reactor/Core/src/jetbrains/mps/logic/reactor/evaluation/EvaluationSession.java index 030f2b7a..d06294a7 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/evaluation/EvaluationSession.java +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/evaluation/EvaluationSession.java @@ -78,14 +78,6 @@ public abstract class EvaluationSession { public abstract Config withTrace(EvaluationTrace computingTracer); - /** - * @deprecated passing store view is deprecated and doesn't have an effect - */ - @Deprecated - public abstract Config withStoreView(StoreView storeView); - - public Config withStore(Collection store) { return this; } - public Config withSessionToken(SessionToken token) { return this; } public Config withIncrSpec(IncrementalSpec ispec) { return this; } diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/evaluation/MatchJournalView.java b/reactor/Core/src/jetbrains/mps/logic/reactor/evaluation/MatchJournalView.java index 5adb8c39..2086fa21 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/evaluation/MatchJournalView.java +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/evaluation/MatchJournalView.java @@ -16,9 +16,6 @@ package jetbrains.mps.logic.reactor.evaluation; -import jetbrains.mps.logic.reactor.program.PreambleInfo; - public interface MatchJournalView { StoreView getStoreView(); - MatchJournalView getPreamble(PreambleInfo info); } diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/evaluation/SessionToken.java b/reactor/Core/src/jetbrains/mps/logic/reactor/evaluation/SessionToken.java index 10972d7b..1605ef23 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/evaluation/SessionToken.java +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/evaluation/SessionToken.java @@ -25,6 +25,4 @@ public interface SessionToken { MatchJournalView getJournalView(); @NotNull() Iterable getRules(); - @NotNull - Collection getPrincipalStore(); } diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/program/PreambleInfo.java b/reactor/Core/src/jetbrains/mps/logic/reactor/program/PreambleInfo.java deleted file mode 100644 index 24a07fe2..00000000 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/program/PreambleInfo.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2014-2020 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package jetbrains.mps.logic.reactor.program; - -/** - * Provides required but incomplete information to determine preamble. - * Aimed at filtering rules with origins which do belong to preamble. - * - * Together with justifications from journal provides complete information. - * See implementation for details. - */ -public interface PreambleInfo { - - boolean inPreamble(Rule rule); - - static final PreambleInfo EMPTY = new PreambleInfo() { - @Override - public boolean inPreamble(Rule rule) { - return false; - } - }; -} diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/program/Program.java b/reactor/Core/src/jetbrains/mps/logic/reactor/program/Program.java index 65e8a4b7..27c64398 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/program/Program.java +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/program/Program.java @@ -16,15 +16,8 @@ package jetbrains.mps.logic.reactor.program; -import javaslang.collection.List; -import javaslang.collection.Stream; -import jetbrains.mps.logic.reactor.core.RulesDiff; - -import java.util.ArrayList; -import java.util.stream.Collectors; - /** - * A collection of rulesLists that constitute a constraint rules program. + * A collection of rules that constitute a constraint rules program. * * @author Fedor Isakov */ @@ -32,39 +25,6 @@ public abstract class Program { public abstract String name(); - @Deprecated - public abstract Iterable rulesLists(); - - public abstract PreambleInfo preambleInfo(); - - @Deprecated - public Program withRulesDiff(RulesDiff diff) { return this; }; - - @Deprecated - public RulesDiff incrementalDiff() { return RulesDiff.emptyDiff(); }; - - public Iterable rules() { - ArrayList allRules = new ArrayList(); - for (RulesList rulesList : rulesLists()) { - for (Rule rule : rulesList.rules()) { - allRules.add(rule); - } - } - return allRules; - }; - - /** - * Returns rules that have been created since the last evaluation of this program. - */ - public Iterable newRules () { - return incrementalDiff().getAdded(); - } - - /** - * Returns objects that identify rules removed from the previous invocation. - */ - public Iterable droppedRules() { - return incrementalDiff().getRemoved(); - } + abstract public Iterable rules(); } diff --git a/reactor/Test/src/program/MockProgram.kt b/reactor/Test/src/program/MockProgram.kt index 7fc8fdd3..26d41c31 100644 --- a/reactor/Test/src/program/MockProgram.kt +++ b/reactor/Test/src/program/MockProgram.kt @@ -2,7 +2,6 @@ * @author Fedor Isakov */ -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 @@ -103,21 +102,8 @@ class MockRule( } class MockProgram(val name: String, val rulesLists: List, val registry: MockConstraintRegistry) : Program() { - private var rulesDiff: RulesDiff = RulesDiff.emptyDiff() - - override fun preambleInfo(): PreambleInfo = PreambleInfo.EMPTY - - override fun incrementalDiff(): RulesDiff = rulesDiff - - override fun withRulesDiff(diff: RulesDiff): MockProgram { - this.rulesDiff = diff - return this - } - override fun name(): String = name - override fun rulesLists(): Iterable = unmodifiableCollection(rulesLists) - override fun rules(): MutableIterable = unmodifiableCollection(rulesLists.flatMap { it.rules() }) } diff --git a/reactor/Test/test/TestIncrementalProgram.kt b/reactor/Test/test/TestIncrementalProgram.kt index 4959a8a2..33e70f9c 100644 --- a/reactor/Test/test/TestIncrementalProgram.kt +++ b/reactor/Test/test/TestIncrementalProgram.kt @@ -1,13 +1,11 @@ import jetbrains.mps.logic.reactor.program.IncrementalSpec import jetbrains.mps.logic.reactor.core.Occurrence import jetbrains.mps.logic.reactor.core.ReactorLifecycle -import jetbrains.mps.logic.reactor.core.RulesDiff import jetbrains.mps.logic.reactor.core.internal.MatchJournal import jetbrains.mps.logic.reactor.evaluation.* import jetbrains.mps.logic.reactor.logical.Logical import jetbrains.mps.logic.reactor.program.Constraint import jetbrains.mps.logic.reactor.program.ConstraintSymbol -import jetbrains.mps.logic.reactor.program.IncrementalContractViolationException import org.junit.* import org.junit.Assert.* import program.MockConstraint @@ -33,6 +31,7 @@ import solver.tellEquals * @author Fedor Isakov */ +@Ignore class TestIncrementalProgram { companion object { @@ -64,9 +63,7 @@ class TestIncrementalProgram { private fun Builder.relaunch(name: String, incrSpec: IncrementalSpec, sessionToken: SessionToken, resultHandler: (EvaluationResult) -> Unit ) : Pair { - val prog = program(name).withRulesDiff( - RulesDiff.findDiff(sessionToken.rules, rules) - ) + val prog = program(name) val result = EvaluationSession.newSession(prog) .withParameter(EvaluationSession.ParameterKey.of("main", Constraint::class.java), MockConstraint(ConstraintSymbol("main", 0))) .withIncrSpec(incrSpec)