Commit 71ff4192 authored by Christian Müller's avatar Christian Müller

fix, better simplifier

parent 64d2a9a9
......@@ -10,7 +10,7 @@ libraryDependencies ++= Seq(
"com.typesafe.scala-logging" %% "scala-logging" % "3.5.0",
"ch.qos.logback" % "logback-classic" % "1.1.7",
"org.scalactic" %% "scalactic" % "3.0.4",
"org.scalatest" %% "scalatest" % "3.0.4" % "test",
"org.scalatest" %% "scalatest" % "3.0.4",
"org.scala-graph" %% "graph-core" % "1.12.1",
"org.scala-graph" %% "graph-dot" % "1.12.1",
"org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.5"
......
......@@ -57,7 +57,7 @@ object MainInvariants extends App with LazyLogging {
}
clear()
generateExample("nonomitting/conference")
// generateExample("tests/declasstest")
// generateExample("nonomitting/conference")
generateExample("tests/loopexampleNoOracle")
// generateAllExamples()
}
\ No newline at end of file
......@@ -50,8 +50,22 @@ object FormulaFunctions extends LazyLogging {
case And(t, False) => False
case And(True, t) => t
case And(t, True) => t
case Implies(False, t) => True
case Implies(True, t) => t
// left-trees
case And(And(t1, t2), t3) => And(t1, And(t2, t3))
case Or(Or(t1, t2), t3) => Or(t1, Or(t2, t3))
// case Or(t1, Neg(t2)) if t1 == t2 => True
// equal things in tree
case And(t1, t2) if t1 == t2 => t1
case Or(t1, t2) if t1 == t2 => t1
case And(t1, And(t2, t3)) if (t1 == t2) => And(t1, t3)
case And(t1, And(t2, t3)) if (t1 == t3) => And(t1, t2)
case Or(t1, Or(t2, t3)) if (t1 == t2) => Or(t1, t3)
case Or(t1, Or(t2, t3)) if (t1 == t3) => Or(t1, t2)
// case Or(t1, Neg(t2)) if t1 == t2 => True
// Equivalence
case Eq(t1, t2) if t1 == t2 => True
......@@ -64,6 +78,12 @@ object FormulaFunctions extends LazyLogging {
case Quantifier(_, xs, t) if xs.isEmpty => t
case Quantifier(_, _, True) => True
case Quantifier(_, _, False) => False
// Quantifiers binding same things
case And(Forall(vars1, t1), Forall(vars2, t2)) if (vars1 == vars2) => Forall(vars1, And(t1, t2))
case Or(Forall(vars1, t1), Forall(vars2, t2)) if (vars1 == vars2) => Forall(vars1, Or(t1, t2))
case And(Exists(vars1, t1), Exists(vars2, t2)) if (vars1 == vars2) => Exists(vars1, And(t1, t2))
case Or(Exists(vars1, t1), Exists(vars2, t2)) if (vars1 == vars2) => Exists(vars1, Or(t1, t2))
// Gobble up equal quantifiers
case Forall(vars1, Forall(vars2, f)) => Forall(vars1 ++ vars2, f)
......@@ -84,13 +104,13 @@ object FormulaFunctions extends LazyLogging {
if (t == t1) t1 else simplify(t1)
}
def eliminateImplication(f: Formula):Formula = {
def eliminateImplication(f: Formula): Formula = {
f everywhere {
case Implies(f1, f2) => Or(Neg(eliminateImplication(f1)), eliminateImplication(f2))
}
}
def eliminateEq(f:Formula):Formula = {
def eliminateEq(f: Formula): Formula = {
f everywhere {
case Eq(f1, f2) => {
val e1 = eliminateEq(f1)
......@@ -148,25 +168,25 @@ object FormulaFunctions extends LazyLogging {
case x => List.empty
}
}
def hasSubFormula(coll: PartialFunction[Formula, Boolean], t: Formula) = {
def or(bs:Boolean*) = { bs.exists(identity) }
def or(bs: Boolean*) = { bs.exists(identity) }
collect(or)(coll, t)
}
// def hasSubFormula(check: (Formula => Boolean), t: Formula): Boolean = {
// if (check(t)) {
// true
// } else {
// t match {
// // Extractors
// case Quantifier(make, ps, t) => hasSubFormula(check, t)
// case UnOp(make, t) => hasSubFormula(check, t)
// case BinOp(make, t1, t2) => hasSubFormula(check, t1) || hasSubFormula(check, t2)
// case x => false
// }
// }
// }
// def hasSubFormula(check: (Formula => Boolean), t: Formula): Boolean = {
// if (check(t)) {
// true
// } else {
// t match {
// // Extractors
// case Quantifier(make, ps, t) => hasSubFormula(check, t)
// case UnOp(make, t) => hasSubFormula(check, t)
// case BinOp(make, t1, t2) => hasSubFormula(check, t1) || hasSubFormula(check, t2)
// case x => false
// }
// }
// }
def assumeEmpty(t: Formula, name: String) = {
t.everywhere({
......@@ -186,8 +206,8 @@ object FormulaFunctions extends LazyLogging {
case Fun(f, None, xs) => Fun(f, Some(name), xs)
})
}
def annotateOverwrite(t: Formula, Name1:String, name: String) = {
def annotateOverwrite(t: Formula, Name1: String, name: String) = {
t.everywhere({
case Fun(f, Some(Name1), xs) => Fun(f, Some(name), xs)
})
......@@ -265,9 +285,9 @@ object FormulaFunctions extends LazyLogging {
val q1 = collectQuantifiersSub(f1)
val q2 = collectQuantifiersSub(f2)
// Merge while trying to stay in BS, if possible
// Merge while trying to stay in BS, if possible
// TODO: could be improved to minimize quantifier alternations)
// TODO: merge universals with same type by renaming
// TODO: merge universals with same type by renaming
val ex1 = q1.takeWhile(_._1)
val rest1 = q1.drop(ex1.size)
......@@ -294,7 +314,7 @@ object FormulaFunctions extends LazyLogging {
// logger.info(s"Formula binds variables more than once - renaming bindings: ${f.pretty}")
FormulaFunctions.fixBinding(f, Set())._1
} else f
val noeq = eliminateEq(bound)
eliminateImplication(noeq).simplify()
}
......@@ -365,47 +385,47 @@ object FormulaFunctions extends LazyLogging {
// normalized is now Quantifiers, then only And/Or with Negation inside
toCNFSub(normalized)
}
/**
* Resolves a universally quantified predicate
* Blows up the formula linearly due to CNF
*/
def eliminatePredicate(f:Formula, name: String) = {
def listFunsWithName(f:Formula, name:String) = {
def eliminatePredicate(f: Formula, name: String) = {
def listFunsWithName(f: Formula, name: String) = {
f.collect({
case Fun(n, ind, vars) if n == name => List((Some(Fun(n, ind, vars)), None))
case Fun(n, ind, vars) if n == name => List((Some(Fun(n, ind, vars)), None))
case Neg(Fun(n, ind, vars)) if n == name => List((None, Some(Neg(Fun(n, ind, vars)))))
})
}
val norm = f.toCNF()
val removed = norm.everywhere({
// find top-level ORs in CNF
case inner:Or => {
case inner: Or => {
val list = listFunsWithName(inner, name)
val positives = list.map(_._1).flatten
val negatives = list.map(_._2).flatten
val isSuperFluous = positives.exists { f1 =>
val isSuperFluous = positives.exists { f1 =>
negatives.exists { f2 => Neg(f1) == f2 }
}
if (isSuperFluous) {
// Remove clause
True
} else {
// Remove predicate both in positive and negative
inner.everywhere ({
inner.everywhere({
case Neg(Fun(n, ind, vars)) if n == name => False
case Fun(n, ind, vars) if n == name => False
case Fun(n, ind, vars) if n == name => False
})
}
}
})
removed.simplify()
}
}
\ No newline at end of file
......@@ -83,6 +83,9 @@ object LTL extends LazyLogging {
}
t.everywhere({
case Exists(tup, term) => {
Exists(tup, eliminateUniversals(term, agents ++ tup))
}
case Forall(tup, term) => {
instantiate(tup, term, universe)
}
......@@ -122,14 +125,21 @@ object LTL extends LazyLogging {
}
/**
* Instantiates inner Existentials in AE formulae.
* Does not change quantifier structure, but returns formula in NegNF
* Instantiates existentials in AE formulae.
* Resulting formula is in NegNF
*/
def instantiateInnerExistentials(f:Formula) = {
val neg = Neg(f).toNegNf()
def instantiateExistentials(f:Formula) = {
val neg = Neg(f).toNegNf().simplify()
// Check fragment
if (!neg.isBS()) {
logger.error("Trying to instantiate Existentials for wrong fragment")
logger.error(s"Formula was $f")
}
val res = instantiateUniversals(neg)
Neg(res).toNegNf()
// val (agents, inner) = LTL.eliminateExistentials(nf)
// val existbound = Exists(agents, inner)
val res = LTL.eliminateUniversals(neg, List())
Neg(res).simplify()
}
}
\ No newline at end of file
......@@ -33,16 +33,12 @@ object InvariantChecker extends LazyLogging {
// val s = ctx.mkSolver(t)
val s = ctx.mkSolver()
// Make QFree - will work for any A* or A*E* formula
val tocheck = Neg(f).simplify()
if (!tocheck.isBS()) {
logger.error(s"Not in BS: ${tocheck.pretty}")
}
val univfree = LTL.instantiateUniversals(tocheck)
// Can only check universal things
val neg = Neg(f).simplify()
// univfree is now in E*, so can be solved as SAT Problem
val stripped = FormulaFunctions.stripQuantifiers(univfree)
val stripped = FormulaFunctions.stripQuantifiers(neg)
// val prefix = FormulaFunctions.collectQuantifiersSub(univfree)
val satform = LTL.eliminatePredicates(stripped)
......@@ -122,9 +118,16 @@ object InvariantChecker extends LazyLogging {
// check if relabelled invariant still satisfiable
// never relabel initial node
if (!isfirst) {
val newinv = And(labels(next._1), Preconditions.weakestPrecondition(post, next))
val (status2, solver2) = checkOnZ3(Implies(newinv, False))
val newinv = And(labels(next._1), Preconditions.weakestPrecondition(post, next)).simplify()
// Abstract away inner existentials
val universalinv = LTL.instantiateExistentials(newinv).simplify()
// TODO: Abstract away auxilliaries
val (status2, solver2) = checkOnZ3(Implies(universalinv, False))
// FIXME breaks if universalinv is used
val newlabels = labels.updated(next._1, newinv)
val invalidated = proven.filter(_._2 == next._1)
......@@ -146,8 +149,8 @@ object InvariantChecker extends LazyLogging {
}
}
// create labelling
val labels = (for (n <- graph.nodes) yield { n -> inv }) toMap
// create labelling - FIXME: maybe use inv only for last
val labels = (for (n <- graph.nodes) yield { n -> inv }).toMap
val (result, afterlabels, proven) = checkInvariantRec(labels, Set())
val dot = Encoding.toDot(graph)(afterlabels.map(t => (t._1, t._2.pretty())), proven)
......
......@@ -27,6 +27,11 @@ class FOLTLTest extends FlatSpec {
)
}
"Simplification" should "simplify equal things" in {
val t = Forall("x", Fun("S", "x"))
And(t, t).simplify() should be (t)
}
"Parallel rename" should "work on simple functions" in {
val f = And("a", Fun("f", List("b","a")))
......
......@@ -22,7 +22,7 @@ class LTLSpec extends FlatSpec {
LTL.eliminateUniversals(f, List("a")) should be(Globally(Fun("f", List("a", "a"))))
val f2 = Exists("x", Forall(List("y"), Fun("f", List("x", "y"))))
LTL.eliminateUniversals(f2, List("a")) should be(Exists("x", Fun("f", List("x", "a"))))
LTL.eliminateUniversals(f2, List()) should be(Exists("x", Fun("f", List("x", "x"))))
val f3 = Forall(List("x","y"), Fun("f", List("x", "y")))
val r = """
......@@ -69,7 +69,7 @@ class LTLSpec extends FlatSpec {
"Existential Elimination" should "work in AE formulae" in {
val f = Forall(List("x","y"), Exists("z", Fun("f", List("x","z"))))
LTL.instantiateInnerExistentials(f) should be (
LTL.instantiateExistentials(f) should be (
Forall(List("x","y"), Or(Fun("f", List("x","x")), Fun("f", List("x","y"))))
)
}
......
......@@ -202,7 +202,7 @@ class Z3Test extends FlatSpec with LazyLogging with BeforeAndAfterEach {
check(s, Status.SATISFIABLE)
}
it should "check tests/fixedarity5 (nonomitting, stubborn) as LTL prop" in {
it should "check tests/fixedarity5 (nonomitting, stubborn) as LTL prop" ignore {
val s = checkNonOmittingWF(ExampleWorkflows.parseExample("tests/fixedarity5").get)
check(s, Status.SATISFIABLE)
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment