@@ -1954,7 +1954,8 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
1954
1954
//
1955
1955
// TODO: for V1 representing x1 and V2 standing for x1.head, encode that
1956
1956
// V1 = Nil implies -(V2 = Ci) for all Ci in V2's domain (i.e., it is unassignable)
1957
- def removeVarEq (props : List [Prop ], modelNull : Boolean = false ): (Prop , List [Prop ]) = {
1957
+ // may throw an AnalysisBudget.Exception
1958
+ def removeVarEq (props : List [Prop ], modelNull : Boolean = false ): (Formula , List [Formula ]) = {
1958
1959
val start = if (Statistics .canEnable) Statistics .startTimer(patmatAnaVarEq) else null
1959
1960
1960
1961
val vars = new scala.collection.mutable.HashSet [Var ]
@@ -1978,10 +1979,10 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
1978
1979
props foreach gatherEqualities.apply
1979
1980
if (modelNull) vars foreach (_.registerNull)
1980
1981
1981
- val pure = props map rewriteEqualsToProp.apply
1982
+ val pure = props map (p => eqFreePropToSolvable( rewriteEqualsToProp(p)))
1982
1983
1983
- var eqAxioms : Prop = True
1984
- def addAxiom (p : Prop ) = eqAxioms = And (eqAxioms, p )
1984
+ val eqAxioms = formulaBuilder
1985
+ @ inline def addAxiom (p : Prop ) = addFormula (eqAxioms, eqFreePropToSolvable(p) )
1985
1986
1986
1987
patmatDebug(" removeVarEq vars: " + vars)
1987
1988
vars.foreach { v =>
@@ -2007,23 +2008,37 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
2007
2008
}
2008
2009
}
2009
2010
2010
- patmatDebug(" eqAxioms:\n " + cnfString(eqFreePropToSolvable (eqAxioms)))
2011
- patmatDebug(" pure:" + pure.map(p => cnfString(eqFreePropToSolvable(p) )).mkString(" \n " ))
2011
+ patmatDebug(" eqAxioms:\n " + cnfString(toFormula (eqAxioms)))
2012
+ patmatDebug(" pure:" + pure.map(p => cnfString(p )).mkString(" \n " ))
2012
2013
2013
2014
if (Statistics .canEnable) Statistics .stopTimer(patmatAnaVarEq, start)
2014
2015
2015
- (eqAxioms, pure)
2016
+ (toFormula( eqAxioms) , pure)
2016
2017
}
2017
2018
2018
2019
2020
+ // an interface that should be suitable for feeding a SAT solver when the time comes
2019
2021
type Formula
2022
+ type FormulaBuilder
2023
+
2024
+ // creates an empty formula builder to which more formulae can be added
2025
+ def formulaBuilder : FormulaBuilder
2026
+
2027
+ // val f = formulaBuilder; addFormula(f, f1); ... addFormula(f, fN)
2028
+ // toFormula(f) == andFormula(f1, andFormula(..., fN))
2029
+ def addFormula (buff : FormulaBuilder , f : Formula ): Unit
2030
+ def toFormula (buff : FormulaBuilder ): Formula
2031
+
2032
+ // the conjunction of formulae `a` and `b`
2020
2033
def andFormula (a : Formula , b : Formula ): Formula
2021
2034
2035
+ // equivalent formula to `a`, but simplified in a lightweight way (drop duplicate clauses)
2036
+ def simplifyFormula (a : Formula ): Formula
2022
2037
2023
2038
// may throw an AnalysisBudget.Exception
2024
2039
def propToSolvable (p : Prop ): Formula = {
2025
2040
val (eqAxioms, pure :: Nil ) = removeVarEq(List (p), modelNull = false )
2026
- eqFreePropToSolvable( And ( eqAxioms, pure) )
2041
+ andFormula( eqAxioms, pure)
2027
2042
}
2028
2043
2029
2044
// may throw an AnalysisBudget.Exception
@@ -2039,24 +2054,34 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
2039
2054
}
2040
2055
2041
2056
trait CNF extends Logic {
2042
- // CNF: a formula is a conjunction of clauses
2043
- type Formula = Array [Clause ]
2044
2057
/** Override Array creation for efficiency (to not go through reflection). */
2045
2058
private implicit val clauseTag : scala.reflect.ClassTag [Clause ] = new scala.reflect.ClassTag [Clause ] {
2046
2059
def runtimeClass : java.lang.Class [Clause ] = classOf [Clause ]
2047
2060
final override def newArray (len : Int ): Array [Clause ] = new Array [Clause ](len)
2048
2061
}
2062
+
2063
+ import scala .collection .mutable .ArrayBuffer
2064
+ type FormulaBuilder = ArrayBuffer [Clause ]
2065
+ def formulaBuilder = ArrayBuffer [Clause ]()
2066
+ def addFormula (buff : FormulaBuilder , f : Formula ): Unit = buff ++= f
2067
+ def toFormula (buff : FormulaBuilder ): Formula = buff.toArray
2068
+
2069
+ // CNF: a formula is a conjunction of clauses
2070
+ type Formula = Array [Clause ]
2049
2071
def formula (c : Clause * ): Formula = c.toArray
2050
- def andFormula (a : Formula , b : Formula ): Formula = a ++ b
2051
2072
2073
+ type Clause = Set [Lit ]
2052
2074
// a clause is a disjunction of distinct literals
2053
- type Clause = Set [Lit ]
2054
2075
def clause (l : Lit * ): Clause = l.toSet
2055
- private def merge (a : Clause , b : Clause ) = a ++ b
2056
2076
2057
2077
type Lit
2058
2078
def Lit (sym : Sym , pos : Boolean = true ): Lit
2059
2079
2080
+ def andFormula (a : Formula , b : Formula ): Formula = a ++ b
2081
+ def simplifyFormula (a : Formula ): Formula = a.distinct
2082
+
2083
+ private def merge (a : Clause , b : Clause ) = a ++ b
2084
+
2060
2085
// throws an AnalysisBudget.Exception when the prop results in a CNF that's too big
2061
2086
// TODO: be smarter/more efficient about this (http://lara.epfl.ch/w/sav09:tseitin_s_encoding)
2062
2087
def eqFreePropToSolvable (p : Prop ): Formula = {
@@ -2621,23 +2646,22 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
2621
2646
2622
2647
val propsCasesOk = testCasesOk map (t => symbolicCase(t, modelNull = true ))
2623
2648
val propsCasesFail = testCasesFail map (t => Not (symbolicCase(t, modelNull = true )))
2624
- val (eqAxiomsFail, symbolicCasesFail) = removeVarEq(propsCasesFail, modelNull = true )
2625
- val (eqAxiomsOk, symbolicCasesOk) = removeVarEq(propsCasesOk, modelNull = true )
2626
2649
2627
2650
try {
2628
- // most of the time eqAxiomsFail == eqAxiomsOk, but the different approximations might cause different variables to disapper in general
2629
- val eqAxiomsCNF =
2630
- if (eqAxiomsFail == eqAxiomsOk) eqFreePropToSolvable(eqAxiomsFail)
2631
- else eqFreePropToSolvable(And (eqAxiomsFail, eqAxiomsOk))
2651
+ val (eqAxiomsFail, symbolicCasesFail) = removeVarEq(propsCasesFail, modelNull = true )
2652
+ val (eqAxiomsOk, symbolicCasesOk) = removeVarEq(propsCasesOk, modelNull = true )
2653
+ val eqAxioms = simplifyFormula(andFormula(eqAxiomsOk, eqAxiomsFail)) // I'm pretty sure eqAxiomsOk == eqAxiomsFail, but not 100% sure.
2654
+
2655
+ val prefix = formulaBuilder
2656
+ addFormula(prefix, eqAxioms)
2632
2657
2633
- var prefix = eqAxiomsCNF
2634
2658
var prefixRest = symbolicCasesFail
2635
2659
var current = symbolicCasesOk
2636
2660
var reachable = true
2637
2661
var caseIndex = 0
2638
2662
2639
2663
patmatDebug(" reachability, vars:\n " + ((propsCasesFail flatMap gatherVariables).distinct map (_.describe) mkString (" \n " )))
2640
- patmatDebug(" equality axioms:\n " + cnfString(eqAxiomsCNF ))
2664
+ patmatDebug(" equality axioms:\n " + cnfString(eqAxiomsOk ))
2641
2665
2642
2666
// invariant (prefixRest.length == current.length) && (prefix.reverse ++ prefixRest == symbolicCasesFail)
2643
2667
// termination: prefixRest.length decreases by 1
@@ -2647,11 +2671,11 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
2647
2671
prefixRest = prefixRest.tail
2648
2672
if (prefixRest.isEmpty) reachable = true
2649
2673
else {
2650
- prefix = andFormula(eqFreePropToSolvable(prefHead), prefix )
2674
+ addFormula(prefix, prefHead )
2651
2675
current = current.tail
2652
- val model = findModelFor(andFormula(eqFreePropToSolvable( current.head), prefix))
2676
+ val model = findModelFor(andFormula(current.head, toFormula( prefix) ))
2653
2677
2654
- // patmatDebug("trying to reach:\n"+ cnfString(eqFreePropToSolvable( current.head) ) +"\nunder prefix:\n"+ cnfString(prefix))
2678
+ // patmatDebug("trying to reach:\n"+ cnfString(current.head) +"\nunder prefix:\n"+ cnfString(prefix))
2655
2679
// if (NoModel ne model) patmatDebug("reached: "+ modelString(model))
2656
2680
2657
2681
reachable = NoModel ne model
0 commit comments