From 3e50f47f738caff537981d517c0bad56810cbc8e Mon Sep 17 00:00:00 2001 From: Grigorii Kirgizov Date: Tue, 4 Jun 2019 19:44:56 +0300 Subject: [PATCH] Add MatchJournal.Index for additional queries and operations involving ordering of occurrences --- .../reactor/core/internal/MatchJournal.kt | 73 ++++++++++++++----- .../core/internal/ProcessingStateImpl.kt | 11 ++- 2 files changed, 62 insertions(+), 22 deletions(-) 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 f836b8f1..e36fd869 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 @@ -25,7 +25,9 @@ import jetbrains.mps.logic.reactor.logical.LogicalContext import jetbrains.mps.logic.reactor.logical.MetaLogical import jetbrains.mps.logic.reactor.program.* import jetbrains.mps.logic.reactor.util.Id +import org.jetbrains.kotlin.utils.mapToIndex import java.util.* +import kotlin.Comparator import kotlin.collections.ArrayList import kotlin.collections.HashMap @@ -35,8 +37,6 @@ interface MatchJournal : MutableIterable { fun logMatch(match: RuleMatch) fun logActivation(occ: Occurrence) - fun activatingChunkOf(occ: Occurrence): Chunk? - fun currentPos(): Pos fun resetPos() fun reset(pastPos: Pos) @@ -44,7 +44,11 @@ interface MatchJournal : MutableIterable { fun view(): View fun storeView(): StoreView + fun index(): Index + interface Index : Comparator { + fun activatingChunkOf(occ: Id): Chunk? + } data class View(val chunks: List, val nextChunkId: Int) @@ -115,8 +119,6 @@ internal open class MatchJournalImpl( private var pos: MutableListIterator = hist.listIterator() private var current: ChunkImpl = pos.next() // take the initial chunk, move pos - private val parentChunksIndex = HashMap() - override fun iterator(): MutableIterator = hist.iterator() @@ -147,19 +149,6 @@ internal open class MatchJournalImpl( } - override fun activatingChunkOf(occ: Occurrence): MatchJournal.Chunk? { - if (ispec.isPrincipal(occ.constraint)) { - //todo: maintain index and find from index - // maintain, build from view, anything else? - // how to handle removed chunks? when to invalidate index? - return parentChunksIndex[occ] - - } - // todo: for non-principal find manually... need it? - return null - } - - override fun currentPos(): MatchJournal.Pos = current // fixme: unclear, whether this makes sense here, in "pure" journal without store? along with reset and replay? @@ -198,6 +187,8 @@ internal open class MatchJournalImpl( override fun storeView(): StoreView = StoreViewImpl(allOccurrences()) + override fun index(): MatchJournal.Index = IndexImpl(ispec, hist) + private fun allOccurrences(): Sequence { // the following loop doesn't handle this case of starting pos, when 'current' isn't valid (e.g. just right after resetPos()) if (!pos.hasPrevious()) @@ -216,6 +207,7 @@ internal open class MatchJournalImpl( } + // todo: remove ispec from there private class ChunkImpl(val ispec: IncrementalProgramSpec, match: RuleMatch, id: Int, justifications: Justs) : MatchJournal.Chunk(match, id, justifications) { var occurrences: MutableList = mutableListOf() @@ -228,8 +220,8 @@ internal open class MatchJournalImpl( override fun principalConstraint(): Constraint? { try { val body = match.rule().bodyAlternation().first() - val princProds = body.filter { it is Constraint && it.isPrincipal() } - return princProds.first() as Constraint + val pProds = body.filter { it is Constraint && it.isPrincipal() } + return pProds.first() as Constraint } catch (e: NoSuchElementException) { return null } @@ -237,6 +229,49 @@ internal open class MatchJournalImpl( } + class IndexImpl(ispec: IncrementalProgramSpec, chunks: Iterable): MatchJournal.Index + { + private val chunkOrder: Map + // only for principal constraints + private val parentChunks: Map, MatchJournal.Chunk> + + init { + chunkOrder = chunks.map { it.id }.mapToIndex() + + val m = HashMap, MatchJournal.Chunk>() + chunks.forEach {chunk -> + chunk.activatedLog().filter { occ -> + ispec.isPrincipal(occ.constraint) + }.forEach {occ -> + m[Id(occ)] = chunk + } + } + parentChunks = m + } + + // todo: for non-principal find manually, walk through the whole journal... need it? + override fun activatingChunkOf(occ: Id): MatchJournal.Chunk? = parentChunks[occ] + + // todo: throw for invalid positions? + override fun compare(lhs: MatchJournal.Pos, rhs: MatchJournal.Pos): Int { + val co = compareBy{ chunkOrder[it.chunk().id] }.compare(lhs, rhs) + return if (co == 0) lhs.entriesInChunk().compareTo(rhs.entriesInChunk()) else co + } + + // Compare occurrences by their activating chunk + // todo: test case for two principal occurrences activated in the same chunk??? + fun compare(lhs: Occurrence, rhs: Occurrence): Int { + val cl = activatingChunkOf(Id(lhs)) + val cr = activatingChunkOf(Id(rhs)) + + if (cl == null && cr == null) return 0 + if (cl == null) return -1 + if (cr == null) return 1 + return this.compare(cl, cr) + } + } + + private class StoreViewImpl(occurrences: Sequence) : StoreView { val allOccurrences = occurrences.toSet() diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingStateImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingStateImpl.kt index e6b6608b..62434ec8 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingStateImpl.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingStateImpl.kt @@ -22,6 +22,7 @@ import jetbrains.mps.logic.reactor.evaluation.RuleMatch import jetbrains.mps.logic.reactor.logical.Logical import jetbrains.mps.logic.reactor.program.Constraint import jetbrains.mps.logic.reactor.program.Rule +import jetbrains.mps.logic.reactor.util.Id import jetbrains.mps.logic.reactor.util.Profiler import jetbrains.mps.logic.reactor.util.profile import org.jetbrains.kotlin.utils.mapToIndex @@ -110,11 +111,15 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp : StoreAwareJournalImpl(journal) { private val ruleOrder: Map = ruleIndex.map { it.uniqueTag() }.mapToIndex() + private val journalIndex: MatchJournal.Index = journal.index() private val execQueue: Queue = LinkedList() +// private val execQueue: Queue = PriorityQueue(this.count() / 3) { lhs, rhs -> journalIndex.compare(lhs.pos, rhs.pos) } private data class ExecPos(val pos: MatchJournal.Pos, val activeOcc: Occurrence) + // fixme: wtf, why idea's compiler complains??? + override fun index(): MatchJournal.Index = journalIndex // only for tests fun pushActivateFirstOccOf(ctr: Constraint): Boolean { @@ -154,7 +159,7 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp // todo: do need to reactivate only the main, matching~activating match? // (i.e. don't reactivate additional, inactive heads that only completed the match?) execQueue.addAll(validOccs.map { occ -> - val chunkPos = activatingChunkOf(occ) + val chunkPos = journalIndex.activatingChunkOf(Id(occ)) if (chunkPos != null) ExecPos(chunkPos, occ) else null }.filterNotNull()) @@ -205,7 +210,7 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp // Does this chunk have principal occurrence and can activate anything at all? if (pCtr != null) { for (rule in headedRules) { - // Can this rule be matched by 'pp'? + // Can this rule be matched by principal occurrence? // fixme: maybe use RuleIndex here? if (rule.headKept().contains(pCtr) || rule.headReplaced().contains(pCtr)) { @@ -381,4 +386,4 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp private fun RuleMatch.allHeads() = matchHeadKept() + matchHeadReplaced() private fun RuleMatch.allStored() = allHeads().all { co -> (co as Occurrence).stored } -} \ No newline at end of file +}