Minor optimizations of rule indexing/processing
This commit is contained in:
parent
4a97d3e89f
commit
20a6b70de1
|
|
@ -17,11 +17,8 @@
|
|||
package jetbrains.mps.logic.reactor.core
|
||||
|
||||
import gnu.trove.map.hash.TIntObjectHashMap
|
||||
|
||||
|
||||
typealias DispatchingFrontState = Map<Any, RuleMatcher>
|
||||
|
||||
internal fun emptyFrontState(): DispatchingFrontState = emptyMap()
|
||||
import jetbrains.mps.logic.reactor.program.Rule
|
||||
import java.util.IdentityHashMap
|
||||
|
||||
/**
|
||||
* A front-end interface to [RuleMatcher].
|
||||
|
|
@ -30,38 +27,38 @@ internal fun emptyFrontState(): DispatchingFrontState = emptyMap()
|
|||
*/
|
||||
class Dispatcher (val ruleIndex: RuleIndex) {
|
||||
|
||||
private val ruletag2matcher = HashMap<Any, RuleMatcher>()
|
||||
private val matcherByRule = IdentityHashMap<Rule, RuleMatcher>()
|
||||
|
||||
/**
|
||||
* Create new empty [DispatchingFront] ready to accept constraints.
|
||||
*/
|
||||
fun front() = DispatchingFront()
|
||||
|
||||
fun getOrCreateMatcher(ruleTag: Any): RuleMatcher =
|
||||
ruletag2matcher.getOrPut(ruleTag) { createRuleMatcher(ruleIndex, ruleTag) }
|
||||
fun getOrCreateMatcher(rule: Rule): RuleMatcher =
|
||||
matcherByRule.getOrPut(rule) { createRuleMatcher(rule) }
|
||||
|
||||
inner class DispatchingFront {
|
||||
|
||||
private val ruletag2probe : HashMap<Any, RuleMatchingProbe>
|
||||
private val probeByRule : IdentityHashMap<Rule, RuleMatchingProbe>
|
||||
|
||||
private val occId2tags : TIntObjectHashMap<MutableList<Any>>
|
||||
private val occId2tags : IdentityHashMap<Occurrence, MutableList<Rule>>
|
||||
|
||||
private val matching: Iterable<RuleMatchingProbe>?
|
||||
|
||||
constructor() {
|
||||
this.ruletag2probe = hashMapOf()
|
||||
this.occId2tags = TIntObjectHashMap()
|
||||
this.probeByRule = IdentityHashMap()
|
||||
this.occId2tags = IdentityHashMap()
|
||||
this.matching = null
|
||||
}
|
||||
|
||||
private constructor(pred: DispatchingFront, matching: Iterable<RuleMatchingProbe>? = null) {
|
||||
this.ruletag2probe = pred.ruletag2probe
|
||||
this.probeByRule = pred.probeByRule
|
||||
this.occId2tags = pred.occId2tags
|
||||
this.matching = matching
|
||||
}
|
||||
|
||||
fun getOrCreateProbe(ruleTag: Any): RuleMatchingProbe =
|
||||
ruletag2probe.getOrPut(ruleTag) { getOrCreateMatcher(ruleTag).probe() }
|
||||
fun getOrCreateProbe(rule: Rule): RuleMatchingProbe =
|
||||
probeByRule.getOrPut(rule) { getOrCreateMatcher(rule).probe() }
|
||||
|
||||
/**
|
||||
* Returns all matches satisfied by the constraint occurrences received so far.
|
||||
|
|
@ -72,9 +69,7 @@ class Dispatcher (val ruleIndex: RuleIndex) {
|
|||
allMatches.addAll(probe.matches())
|
||||
}
|
||||
return allMatches
|
||||
}
|
||||
|
||||
fun state() : DispatchingFrontState = ruletag2matcher //.asMap()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [DispatchingFront] instance that is "expanded" with matches corresponding to the
|
||||
|
|
@ -83,19 +78,16 @@ class Dispatcher (val ruleIndex: RuleIndex) {
|
|||
fun expand(activated: Occurrence): DispatchingFront = DispatchingFront(this,
|
||||
ruleIndex.forOccurrenceWithMask(activated)
|
||||
.map { (rule, mask) ->
|
||||
if (!occId2tags.contains(activated.identity)) {
|
||||
occId2tags.put(activated.identity, arrayListOf())
|
||||
}
|
||||
occId2tags[activated.identity].add(rule.uniqueTag())
|
||||
getOrCreateProbe(rule.uniqueTag()).expand(activated, mask) })
|
||||
occId2tags.computeIfAbsent(activated) { _ -> arrayListOf() }.add(rule)
|
||||
getOrCreateProbe(rule).expand(activated, mask) })
|
||||
|
||||
/**
|
||||
* Returns a [DispatchingFront] instance that is "contracted": all matches corresponding to the
|
||||
* specified discarded constraint occurrence are eliminated.
|
||||
*/
|
||||
fun contract(discarded: Occurrence): DispatchingFront = DispatchingFront(this,
|
||||
occId2tags.remove(discarded.identity)
|
||||
?.mapNotNull { ruleTag -> ruletag2probe[ruleTag] }
|
||||
occId2tags.remove(discarded)
|
||||
?.mapNotNull { ruleTag -> probeByRule[ruleTag] }
|
||||
?.map { probe -> probe.contract(discarded) })
|
||||
|
||||
/**
|
||||
|
|
@ -103,7 +95,7 @@ class Dispatcher (val ruleIndex: RuleIndex) {
|
|||
* be excluded from any further "match" set returned by [matches].
|
||||
*/
|
||||
internal fun consume(consumedMatch: RuleMatchEx): DispatchingFront {
|
||||
ruletag2probe[consumedMatch.rule().uniqueTag()]?.consume(consumedMatch)
|
||||
probeByRule[consumedMatch.rule()]?.consume(consumedMatch)
|
||||
return DispatchingFront(this)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
package jetbrains.mps.logic.reactor.core
|
||||
|
||||
import gnu.trove.TIntObjectHashMap
|
||||
import gnu.trove.TObjectIntHashMap
|
||||
import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence
|
||||
import jetbrains.mps.logic.reactor.logical.Logical
|
||||
import jetbrains.mps.logic.reactor.logical.MetaLogical
|
||||
|
|
@ -29,16 +28,22 @@ import kotlin.collections.ArrayList
|
|||
import kotlin.collections.HashMap
|
||||
|
||||
typealias RuleBits = BitSet
|
||||
|
||||
typealias HeadBits = BitSet
|
||||
|
||||
fun emptyRuleBits() = BitSet()
|
||||
|
||||
fun emptyHeadBits() = BitSet()
|
||||
|
||||
|
||||
/**
|
||||
* A container for [Rule] instances with the ability to look up by [ConstraintOccurrence].
|
||||
*
|
||||
* @author Fedor Isakov
|
||||
*/
|
||||
class RuleIndex(rules: Iterable<Rule>, profiler: Profiler? = null) : Iterable<Rule>, RuleLookup
|
||||
class RuleIndex(rules: Iterable<Rule>, profiler: Profiler? = null) : RuleLookup
|
||||
{
|
||||
private class IndexedRule(var idx: Int, val rule: Rule)
|
||||
private class OrderedRule(var order: Int, val rule: Rule)
|
||||
|
||||
// Terminology:
|
||||
// ruleBit - rule's index in the rules list
|
||||
|
|
@ -49,13 +54,9 @@ class RuleIndex(rules: Iterable<Rule>, profiler: Profiler? = null) : Iterable<Ru
|
|||
|
||||
private val tag2rule = HashMap<Any, Rule>()
|
||||
|
||||
private val tag2bit = TObjectIntHashMap<Any>()
|
||||
|
||||
var nextBit: Int = 0
|
||||
|
||||
private val bit2ruleAndMask = TIntObjectHashMap<Pair<IndexedRule, SlotMask>>()
|
||||
|
||||
private val allRules = LinkedList<IndexedRule>()
|
||||
private val bit2ruleAndMask = TIntObjectHashMap<Pair<OrderedRule, SlotMask>>()
|
||||
|
||||
init {
|
||||
profiler.profile("build rule index") {
|
||||
|
|
@ -70,23 +71,23 @@ class RuleIndex(rules: Iterable<Rule>, profiler: Profiler? = null) : Iterable<Ru
|
|||
*/
|
||||
fun forOccurrence(occ: ConstraintOccurrence): Iterable<Rule> {
|
||||
val (ruleBits, slotMasks) = symbol2index[occ.constraint().symbol()]?.select(occ) ?: return emptyList()
|
||||
val result = ArrayList<IndexedRule>()
|
||||
val result = ArrayList<OrderedRule>()
|
||||
val itr = ruleBits.iterator()
|
||||
while (itr.hasNext()) {
|
||||
bit2ruleAndMask[itr.next()]?.let{
|
||||
result.add(it.first)
|
||||
}
|
||||
}
|
||||
result.sortBy { it.idx }
|
||||
result.sortBy { it.order }
|
||||
return result.map { it.rule }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pair of rule and bit mask with 1's marking matching slots in constraint's head.
|
||||
* Returns a pair of rule and bit mask with 1's marking matching slots in rule's head.
|
||||
*/
|
||||
fun forOccurrenceWithMask(occ: ConstraintOccurrence): Iterable<Pair<Rule, BitSet>> {
|
||||
val (ruleBits, argSlotMasks) = symbol2index[occ.constraint().symbol()]?.select(occ) ?: return emptyList()
|
||||
val result = ArrayList<Pair<IndexedRule, BitSet>>()
|
||||
val result = ArrayList<Pair<OrderedRule, BitSet>>()
|
||||
val itr = ruleBits.iterator()
|
||||
while (itr.hasNext()) {
|
||||
val ruleBit = itr.next()
|
||||
|
|
@ -98,21 +99,17 @@ class RuleIndex(rules: Iterable<Rule>, profiler: Profiler? = null) : Iterable<Ru
|
|||
}
|
||||
}
|
||||
// }
|
||||
result.sortBy { it.first.idx }
|
||||
result.sortBy { it.first.order }
|
||||
return result.map { it.first.rule to it.second }
|
||||
}
|
||||
|
||||
override fun iterator(): Iterator<Rule> = allRules.map { it.rule }.iterator()
|
||||
|
||||
fun buildIndexFromRules(rules: Iterable<Rule>) {
|
||||
rules.forEachIndexed { idx, rule ->
|
||||
val irule = IndexedRule(idx, rule)
|
||||
addRuleToIndex(irule)
|
||||
allRules.add(irule)
|
||||
addRuleToIndex(OrderedRule(idx, rule))
|
||||
}
|
||||
}
|
||||
|
||||
private fun addRuleToIndex(irule: IndexedRule) {
|
||||
private fun addRuleToIndex(irule: OrderedRule) {
|
||||
val uniqueTag = irule.rule.uniqueTag()
|
||||
if (tag2rule.containsKey(uniqueTag)) throw IllegalStateException("duplicate rule tag $uniqueTag")
|
||||
val head = irule.rule.headKept() + irule.rule.headReplaced()
|
||||
|
|
@ -123,7 +120,6 @@ class RuleIndex(rules: Iterable<Rule>, profiler: Profiler? = null) : Iterable<Ru
|
|||
}
|
||||
|
||||
tag2rule[uniqueTag] = irule.rule
|
||||
tag2bit.put(uniqueTag, nextBit)
|
||||
bit2ruleAndMask.put(nextBit, irule to slotMask)
|
||||
|
||||
nextBit += 1
|
||||
|
|
@ -131,7 +127,7 @@ class RuleIndex(rules: Iterable<Rule>, profiler: Profiler? = null) : Iterable<Ru
|
|||
|
||||
/**
|
||||
* Represents a mask associated with a single rule.
|
||||
* The mask tells whether or not a particular constraint occurrence can match
|
||||
* The mask tells whether a particular constraint occurrence can match
|
||||
* any of the rule's constraints.
|
||||
*/
|
||||
private class SlotMask {
|
||||
|
|
@ -208,9 +204,11 @@ class RuleIndex(rules: Iterable<Rule>, profiler: Profiler? = null) : Iterable<Ru
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns bit set where 1's indicate the indices of matching rules.
|
||||
* Returns bit set where 1's indicate the indices of matching rules in the first component.
|
||||
* The second component contains a map of rule bit to a mask with 1's corresponding
|
||||
* to the occurrence position in the rule's head.
|
||||
*/
|
||||
fun select(occ: ConstraintOccurrence): Pair<RuleBits, Map<Int, BitSet>> {
|
||||
fun select(occ: ConstraintOccurrence): Pair<RuleBits, Map<Int, HeadBits>> {
|
||||
if (occ.constraint().symbol() != symbol) throw IllegalArgumentException()
|
||||
|
||||
// by default select all rules where this constraint is in the head
|
||||
|
|
@ -264,11 +262,11 @@ class RuleIndex(rules: Iterable<Rule>, profiler: Profiler? = null) : Iterable<Ru
|
|||
|
||||
selectedRuleBits.retainAll(symbolSelector)
|
||||
|
||||
val slotMasks = HashMap<Int, BitSet>()
|
||||
val slotMasks = HashMap<Int, HeadBits>()
|
||||
for ((p, votes) in slotVotes.entries) {
|
||||
val (ruleBit, headPos) = p
|
||||
if (!votes.isEmpty || !wildcardSlots.isEmpty) {
|
||||
slotMasks.getOrPut(ruleBit) { BitSet() }.set(headPos)
|
||||
slotMasks.getOrPut(ruleBit) { emptyHeadBits() }.set(headPos)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,13 +38,9 @@ interface RuleMatcher {
|
|||
|
||||
fun probe(): RuleMatchingProbe
|
||||
|
||||
fun setRuleLookup(ruleLookup: RuleLookup)
|
||||
|
||||
fun resetRuleLookup()
|
||||
|
||||
}
|
||||
|
||||
fun createRuleMatcher(lookup: RuleLookup, tag: Any): RuleMatcher = ReteRuleMatcherImpl(lookup, tag)
|
||||
fun createRuleMatcher(rule: Rule): RuleMatcher = ReteRuleMatcherImpl(rule)
|
||||
|
||||
// Trove stuff
|
||||
typealias Signature = TIntList
|
||||
|
|
|
|||
|
|
@ -44,21 +44,20 @@ fun Signature.toTrail() = TIntHashSet(this)
|
|||
|
||||
fun signatureIndexOf() = TIntObjectHashMap<MutableList<Signature>>()
|
||||
|
||||
internal class ReteRuleMatcherImpl(private var ruleLookup: RuleLookup?,
|
||||
private val tag: Any) : RuleMatcher
|
||||
internal class ReteRuleMatcherImpl(private val rule: Rule) : RuleMatcher
|
||||
{
|
||||
|
||||
val head = (lookupRule().headKept() + lookupRule().headReplaced()).toList()
|
||||
|
||||
val propagation = lookupRule().headReplaced().count() == 0
|
||||
|
||||
fun lookupRule(): Rule = ruleLookup?.lookupRuleByTag(tag) ?: throw IllegalStateException("can't lookup rule by tag: '${tag}'")
|
||||
fun lookupRule(): Rule = rule
|
||||
|
||||
override fun rule() = lookupRule()
|
||||
|
||||
override fun setRuleLookup(ruleLookup: RuleLookup) { this.ruleLookup = ruleLookup }
|
||||
|
||||
override fun resetRuleLookup() { this.ruleLookup = null }
|
||||
// override fun setRuleLookup(ruleLookup: RuleLookup) { this.ruleLookup = ruleLookup }
|
||||
//
|
||||
// override fun resetRuleLookup() { this.ruleLookup = null }
|
||||
|
||||
override fun newProbe(): RuleMatchingProbe = ReteNetwork(head.size).also { probe = it }
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class Builder(var rulesList: List<Rule>) : RuleLookup {
|
|||
|
||||
override fun lookupRuleByTag(tag: Any): Rule? = tag2rule[tag]
|
||||
|
||||
fun ruleMatcher(): RuleMatcher = createRuleMatcher(this, rules.first().uniqueTag())
|
||||
fun ruleMatcher(): RuleMatcher = createRuleMatcher(rules.first())
|
||||
|
||||
fun program(name: String): Program = programBuilder.program(name, rulesList)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue