Minor optimisations and code cleanup

This commit is contained in:
Fedor Isakov 2018-09-04 18:44:42 +02:00
parent bd876c3e25
commit 59a0d958d7
4 changed files with 81 additions and 60 deletions

View File

@ -38,7 +38,7 @@ class RuleIndex(handlers: Iterable<Handler>) : Iterable<Rule> {
val rules = ArrayList<Rule>()
private val slotIndices = ArrayList<SlotIndex>()
private val slotIndices = ArrayList<SlotMask>()
init {
buildIndex(handlers)
@ -55,14 +55,16 @@ class RuleIndex(handlers: Iterable<Handler>) : Iterable<Rule> {
* Returns a pair of rule and bit mask with 1's marking matching slots.
*/
fun forOccurrenceWithMask(occ: ConstraintOccurrence): Iterable<Pair<Rule, BitSet>> {
val ruleIndices = symbol2index[occ.constraint().symbol()]?.select(occ) ?: return emptyList()
return ruleIndices.allSetBits().mapNotNull { idx -> slotIndices[idx][occ]?.let { mask -> rules[idx] to mask }}
val ruleBits = symbol2index[occ.constraint().symbol()]?.select(occ) ?: return emptyList()
return ruleBits.allSetBits().mapNotNull { ruleBit ->
slotIndices[ruleBit][occ]?.let { mask -> rules[ruleBit] to mask }
}
}
override fun iterator(): Iterator<Rule> = tag2rule.values.iterator()
private fun buildIndex(handlers: Iterable<Handler>) {
var pos = 0
var ruleBit = 0
for (h in handlers) {
for (rule in h.rules()) {
if (tag2rule.containsKey(rule.tag())) throw IllegalStateException("duplicate rule tag ${rule.tag()}")
@ -71,30 +73,38 @@ class RuleIndex(handlers: Iterable<Handler>) : Iterable<Rule> {
rules.add(rule)
val head = rule.headKept() + rule.headReplaced()
val symbol2mask = SlotIndex()
for ((bit, cst) in head.withIndex()) {
symbol2index.getOrPut(cst.symbol()) { ArgumentRuleIndex(cst.symbol()) }.update(pos, cst)
symbol2mask.update(cst, bit)
val cst2mask = SlotMask()
for ((pos, cst) in head.withIndex()) {
symbol2index.getOrPut(cst.symbol()) { ArgumentRuleIndex(cst.symbol()) }.update(cst, ruleBit)
cst2mask.update(cst, pos)
}
slotIndices.add(symbol2mask)
slotIndices.add(cst2mask)
pos += 1
ruleBit += 1
}
}
}
class SlotIndex() {
/**
* Represents a mask associated with a single rule.
* The mask tells whether or not a particular constraint occurrence can match
* any of the rule's constraints.
*/
class SlotMask {
val symbol2mask = HashMap<Symbol, BitSet>()
fun update(cst: Constraint, pos: Int) {
symbol2mask.getOrPut(cst.symbol()) { BitSet() }.set(pos)
fun update(cst: Constraint, posInHead: Int) {
symbol2mask.getOrPut(cst.symbol()) { BitSet() }.set(posInHead)
}
/**
* When null is returned, there can be no match for this constraint
*/
operator fun get(occ: ConstraintOccurrence): BitSet? =
symbol2mask[occ.constraint().symbol()] // guaranteed to != null
symbol2mask[occ.constraint().symbol()]
}
/**
@ -118,17 +128,17 @@ class RuleIndex(handlers: Iterable<Handler>) : Iterable<Rule> {
}
}
fun update(bit: Int, cst: Constraint) {
fun update(cst: Constraint, ruleBit: Int) {
for ((idx, arg) in cst.arguments().withIndex()) {
val value2indices = anySelectors[idx]
when (arg) {
is MetaLogical<*> ->
// all values should be accepted by a meta logical
wildcardSelectors[idx].set(bit)
wildcardSelectors[idx].set(ruleBit)
is Term ->
termSelectors.set(idx, termSelectors[idx].put(arg, bit))
termSelectors.set(idx, termSelectors[idx].put(arg, ruleBit))
is Any ->
value2indices.getOrPut(arg) { BitSet() }.also { it.set(bit) }
value2indices.getOrPut(arg) { BitSet() }.apply { set(ruleBit) }
else ->
throw NullPointerException() // never happens

View File

@ -18,7 +18,6 @@ package jetbrains.mps.logic.reactor.core
import com.github.andrewoma.dexx.collection.Map as PersMap
import com.github.andrewoma.dexx.collection.List as PersList
import com.github.andrewoma.dexx.collection.ConsList
import com.github.andrewoma.dexx.collection.Maps
import com.github.andrewoma.dexx.collection.Vector as PersVector
import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence
@ -32,6 +31,7 @@ import jetbrains.mps.logic.reactor.program.Rule
import jetbrains.mps.logic.reactor.util.*
import jetbrains.mps.unification.Term
import java.util.*
import kotlin.collections.ArrayList
/**
* @author Fedor Isakov
@ -48,9 +48,9 @@ class RuleMatcher(val rule: Rule) {
val propagation = rule.headReplaced().count() == 0
fun fringe() = MatchFringe(cons(FringeNode(emptySubst())), emptySet(), 0)
fun fringe() = MatchFringe(listOf(FringeNode(emptySubst())), emptySet(), 0)
inner class MatchFringe(val nodes: ConsList<FringeNode>,
inner class MatchFringe(val nodes: List<FringeNode>,
val seen: IdHashSet<ConstraintOccurrence>,
val genId: Int) {
@ -72,27 +72,33 @@ class RuleMatcher(val rule: Rule) {
// select complete (leaf) nodes and make them appear as if newly expanded
// *unless* propagation (implement propagation history feature)
if (propagation) return MatchFringe(nodes, seen, genId + 1)
val newNodes = nodes.asSequence().mapNotNull { fn ->
if (fn is ActiveFringeNode && fn.complete)
val newNodes = ArrayList<FringeNode>()
for (fn in nodes) {
if (fn is ActiveFringeNode && fn.complete) {
fn.unrelatedOrCopy(occ, genId + 1)
else
fn
}.toConsList()
} else { fn }?.let { newNodes.add(it) }
}
return MatchFringe(newNodes, seen, genId + 1)
} else {
val newNodes = nodes.asSequence().filter { fn ->
mask == null || fn.matchesVacant(mask)
}.flatMap { fn ->
fn.expand(occ, genId + 1) }
return MatchFringe(newNodes.prependTo(nodes), seen.add(occ), genId + 1)
val newNodes = ArrayList<FringeNode>(nodes)
for (fn in nodes) {
// TODO: mask can't be null in normal circumstances
if (mask == null || fn.matchesVacant(mask)) {
newNodes.addAll(fn.expand(occ, genId + 1))
}
}
return MatchFringe(newNodes, seen.add(occ), genId + 1)
}
}
fun cleanup(occ: ConstraintOccurrence): MatchFringe {
val newNodes = nodes.asSequence().mapNotNull { it.unrelatedOrNull(occ) }
return MatchFringe(newNodes.toConsList(), seen.remove(occ), genId + 1)
val newNodes = nodes.mapNotNull { it.unrelatedOrNull(occ) }
return MatchFringe(newNodes, seen.remove(occ), genId + 1)
}
}
@ -104,13 +110,16 @@ class RuleMatcher(val rule: Rule) {
* Returns the additional nodes built from this node on adding the occurrence.
* If the occurrence is already in the path, return empty sequence.
*/
fun expand(occ: ConstraintOccurrence, genId: Int): Sequence<ActiveFringeNode> {
val unrelated = unrelatedOrNull(occ) ?: return emptySequence()
return unrelated.vacant.allSetBits().map { idx ->
idx to match(head[idx] !!, occ, subst) }.asSequence().mapNotNull { (idx, newSubst) ->
newSubst?.let { ActiveFringeNode(this, occ, idx, genId, it) }
}
}
fun expand(occ: ConstraintOccurrence, genId: Int): List<ActiveFringeNode> =
unrelatedOrNull(occ)?.run {
ArrayList<ActiveFringeNode>().also { expanded ->
for (idx in vacant.allSetBits()) {
match(head[idx]!!, occ, subst)?.let { newSubst ->
expanded.add(ActiveFringeNode(this, occ, idx, genId, newSubst))
}
}
}
} ?: emptyList()
/**
* Returns this node if it doesn't have the occurrence in its path, null otherwise.

View File

@ -26,13 +26,13 @@ inline fun BitSet.copyApply (f: BitSet.() -> Unit): BitSet =
(clone() as BitSet).apply(f)
fun bitSetOfOnes(size: Int): BitSet =
BitSet(size).also { it.set(0, size) }
BitSet(size).apply { set(0, size) }
fun BitSet.setBit(bit: Int): BitSet =
BitSet.valueOf(this.toLongArray()).also { it.set(bit) }
BitSet.valueOf(this.toLongArray()).apply { set(bit) }
fun BitSet.clearBit(bit: Int): BitSet =
BitSet.valueOf(this.toLongArray()).also { it.clear(bit) }
BitSet.valueOf(this.toLongArray()).apply { clear(bit) }
fun BitSet.allSetBits(): Iterable<Int> = object : Iterable<Int> {
override fun iterator(): Iterator<Int> = object : Iterator<Int> {

View File

@ -16,7 +16,6 @@
package jetbrains.mps.unification
import gnu.trove.TIntHashSet
import gnu.trove.list.array.TIntArrayList
import gnu.trove.map.hash.TIntObjectHashMap
import jetbrains.mps.unification.Substitution.FailureCause.CYCLE_DETECTED
@ -52,6 +51,9 @@ import java.util.*
* @author Fedor Isakov
*/
typealias IntList = TIntArrayList
typealias IntAnyHashMap<V> = TIntObjectHashMap<V>
class TermGraphUnifier {
companion object {
@ -65,12 +67,12 @@ class TermGraphUnifier {
private val backref = IdentityHashMap<Any?, Int>()
private val origin = ArrayList<Term>()
private val innerClass = TIntArrayList()
private val innerSchema = TIntArrayList()
private val innerSize = TIntArrayList()
private val innerVars = TIntObjectHashMap<TIntArrayList?>()
private val innerAcyclic = TIntHashSet()
private val innerVisited = TIntHashSet()
private val innerClass = IntList()
private val innerSchema = IntList()
private val innerSize = IntList()
private val innerVars = IntAnyHashMap<IntList?>()
private val innerAcyclic = BitSet()
private val innerVisited = BitSet()
constructor() {
this.wrapper = TermWrapper.ID
@ -96,23 +98,23 @@ class TermGraphUnifier {
private fun findSolution(s: Int, defSubs: Substitution) : Substitution {
val z = innerSchema[find(s)]
if (innerAcyclic.contains(z)) { return defSubs } // not part of a cycle
if (innerVisited.contains(z)) { return failedSubstitution(CYCLE_DETECTED) } // there exists a cycle
if (innerAcyclic[z]) { return defSubs } // not part of a cycle
if (innerVisited[z]) { return failedSubstitution(CYCLE_DETECTED) } // there exists a cycle
var subs = defSubs
if (origin[z].`is`(FUN)) {
innerVisited.add(z)
innerVisited.set(z)
for (c in origin[z].arguments()) {
subs = findSolution(toInner(c), subs)
if (!subs.isSuccessful) break
}
innerVisited.add(z)
innerVisited.set(z)
}
if (subs.isSuccessful) {
innerAcyclic.add(z)
innerAcyclic.set(z)
// avoid unnecessary instatiation
val success = if (subs is SuccessfulSubstitution) subs else SuccessfulSubstitution(subs)
@ -240,7 +242,7 @@ class TermGraphUnifier {
if (repr != innerClass[repr]) {
// find representative and compress paths
val path = TIntArrayList()
val path = IntList()
path.add(t)
while (repr != innerClass[repr]) {
path.add(repr)
@ -258,7 +260,7 @@ class TermGraphUnifier {
private fun prependVars(t: Int, vars: TIntArrayList?) {
if (vars?.isEmpty ?: true) { return }
val newVars = TIntArrayList(innerVars[t] ?: EMPTY)
val newVars = IntList(innerVars[t] ?: EMPTY)
newVars.addAll(vars)
innerVars.put (t, newVars)
}
@ -281,7 +283,7 @@ class TermGraphUnifier {
innerClass.add(next)
innerSize.add(1)
if (wrapped.`is`(VAR)) {
innerVars.put(next, TIntArrayList(intArrayOf(next)))
innerVars.put(next, IntList(intArrayOf(next)))
}
backref.put(key, next)
next