Commit 6e31cd1c authored by Christian Müller's avatar Christian Müller

build approxElim

parent 4efe503d
Workflow Workflow
forallmay x:A,p:P forallmay x:A,p:P
True -> Conf += (x,p) True Conf += (x,p)
forallmay x:A,p:P forallmay x:A,p:P
!Conf(x,p) -> Assign += (x,p) !Conf(x,p) Assign += (x,p)
forall x:A,p:P forall x:A,p:P
(Assign(x,p) ∧ O(x,p)) -> Acc += (x,p) (Assign(x,p) ∧ O(x,p)) Acc += (x,p)
loop { loop {
forall xa:A,xb:A,p:P (Assign(xa,p) ∧ Acc(xb,p)) -> Read += (xa,xb,p) forall xa:A,xb:A,p:P (Assign(xa,p) ∧ Acc(xb,p)) Read += (xa,xb,p)
forallmay x:A,p:P (Assign(x,p) ∧ O(x,p)) -> Acc += (x,p) forallmay x:A,p:P (Assign(x,p)) → Acc += (x,p)
} }
Declassify Declassify
......
...@@ -43,18 +43,23 @@ object Utils extends LazyLogging { ...@@ -43,18 +43,23 @@ object Utils extends LazyLogging {
((t1 - t0) / 1000000, result) ((t1 - t0) / 1000000, result)
} }
def clear() { private def recclear(folder: File) {
def recclear(folder: File) { for (fol <- folder.listFiles() if fol.isDirectory()) {
for (fol <- folder.listFiles() if fol.isDirectory()) { recclear(fol)
recclear(fol)
}
folder.listFiles().foreach(_.delete())
} }
folder.listFiles().foreach(_.delete())
}
def clear() {
val fol = new File(RESULTSFOLDER) val fol = new File(RESULTSFOLDER)
fol.mkdirs() fol.mkdirs()
recclear(fol) recclear(fol)
} }
def clear(name:String) {
val fol = new File(RESULTSFOLDER, name)
fol.mkdirs()
recclear(fol)
}
def write(dir: String, filename:String, prop: String) { def write(dir: String, filename:String, prop: String) {
val file = new File(s"$RESULTSFOLDER/$dir/$filename") val file = new File(s"$RESULTSFOLDER/$dir/$filename")
...@@ -68,8 +73,21 @@ object Utils extends LazyLogging { ...@@ -68,8 +73,21 @@ object Utils extends LazyLogging {
logger.info(s"Written $file") logger.info(s"Written $file")
} }
def check(name:String, desc:String, spec:InvariantSpec, properties:InvProperties):Boolean = {
clear(name)
infer(name:String, desc:String, spec:InvariantSpec, properties:InvProperties)
}
def check(name:String, desc:String, inv:NISpec => Formula, properties:InvProperties):Boolean = {
val spec = Examples.parseExampleWF(name).get
check(name, desc, inv, spec, properties)
}
def check(name: String, desc:String, inv:NISpec => Formula, spec:NISpec, properties: InvProperties):Boolean = { def check(name: String, desc:String, inv:NISpec => Formula, spec:NISpec, properties: InvProperties):Boolean = {
clear(name)
val basename = name.split("/").last val basename = name.split("/").last
val filenames = s"$basename${if (desc.isEmpty) "" else s"_$desc"}" val filenames = s"$basename${if (desc.isEmpty) "" else s"_$desc"}"
...@@ -82,7 +100,7 @@ object Utils extends LazyLogging { ...@@ -82,7 +100,7 @@ object Utils extends LazyLogging {
check(name, desc, invspec, properties) check(name, desc, invspec, properties)
} }
def check(name:String, desc:String, spec:InvariantSpec, properties:InvProperties):Boolean = { private def infer(name:String, desc:String, spec:InvariantSpec, properties:InvProperties):Boolean = {
val model = if (properties.stubborn) "stubborn" else "causal" val model = if (properties.stubborn) "stubborn" else "causal"
val basename = name.split("/").last val basename = name.split("/").last
...@@ -111,9 +129,9 @@ object Utils extends LazyLogging { ...@@ -111,9 +129,9 @@ object Utils extends LazyLogging {
val labels = (for ((node, inv) <- labelling.last) yield { val labels = (for ((node, inv) <- labelling.last) yield {
s"Node ${node}:\n${inv.pretty}\n" s"Node ${node}:\n${inv.pretty}\n"
}).mkString("\n") }).mkString("\n")
Utils.write(name, s"$filenames.invariants", labels) Utils.write(name, s"$filenames.invariants", labels)
val wfsize = graph.edges.size - 1 val wfsize = graph.edges.size - 1
val invsizes = labelling.last.map(_._2.opsize) val invsizes = labelling.last.map(_._2.opsize)
val maxsize = invsizes.max val maxsize = invsizes.max
...@@ -134,9 +152,4 @@ object Utils extends LazyLogging { ...@@ -134,9 +152,4 @@ object Utils extends LazyLogging {
) )
res res
} }
def check(name:String, desc:String, inv:NISpec => Formula, properties:InvProperties):Boolean = {
val spec = Examples.parseExampleWF(name).get
check(name, desc, inv, spec, properties)
}
} }
\ No newline at end of file
package de.tum.niwo.blocks
import de.tum.niwo.foltl.FOLTL.{And, Formula, Or}
import de.tum.niwo.foltl.FormulaFunctions
object Saturator {
def saturate(ts:TransitionSystem): TransitionSystem = {
val allpredicates = ts.sig.allpredicates
def saturateGuard(f:Formula) = {
val (quantifiers, clauses) = FormulaFunctions.toCNFClauses(f)
// part 1: Add equalities in CNF
// FIXME: this may add too many eqs, since annotations T1, T2 are ignored
val witheqs = for (c <- clauses) yield {
val ineqs = for (pred <- allpredicates;
pos <- FormulaFunctions.getPositiveArguments(c, pred.name);
neg <- FormulaFunctions.getNegativeArguments(c, pred.name)
) yield {
FormulaFunctions.eq(pos, neg)
}
c ++ ineqs
}
// do not rewrap quantifiers here
val cnfwitheqs = And.make(witheqs.map(Or.make))
// part 2: Add inequalities in DNF
val (_, dnfclauses) = FormulaFunctions.toDNFClauses(cnfwitheqs)
val withineqs = for (c <- dnfclauses) yield {
val ineqs = for (pred <- allpredicates;
pos <- FormulaFunctions.getPositiveArguments(c, pred.name);
neg <- FormulaFunctions.getNegativeArguments(c, pred.name)
) yield {
FormulaFunctions.ineq(pos, neg)
}
c ++ ineqs
}
val dnfwithineqs = Or.make(withineqs.map(And.make))
// val theta = dnfwithineqs.toCNF
// FIXME this should use theta if equalities would actually be introduced
val newform = FormulaFunctions.rewrapQuantifiers(quantifiers, cnfwitheqs).simplify
newform
}
val newsteps = ts.steps.map(_.mapStatements{
case SetStmt(guard, fun, tuple) => SetStmt(saturateGuard(guard), fun, tuple)
})
ts.copy(steps = newsteps)
}
}
...@@ -19,21 +19,21 @@ object TSConverter extends LazyLogging { ...@@ -19,21 +19,21 @@ object TSConverter extends LazyLogging {
val elaboratefun = (b:SimpleWFBlock[_]) => elaborate(b, spec, properties) val elaboratefun = (b:SimpleWFBlock[_]) => elaborate(b, spec, properties)
val elaborated = everywhere(w.steps, elaboratefun) val elaborated = everywhere(w.steps, elaboratefun)
// Map WFLoop/Nondet to TSLoop/TSNondet
// Map Add/Remove statements to SetStmt
val steps = elaborated.map(s => toTSBlock(s)(w.sig, properties))
// Add choice predicates to As
// FIXME: Add causals to constants, add informedness for causal agents // FIXME: Add causals to constants, add informedness for causal agents
val newsig = if (!properties.stubborn) { val newsig = if (!properties.stubborn) {
w.sig.copy( w.sig.copy(
preds = w.sig.preds + Fun(INFNAME, List(spec.target.params.head)), preds = w.sig.preds + Fun(INFNAME, List(spec.target.params.head)),
constants = spec.causals.toSet ++ w.sig.constants constants = spec.causals.toSet ++ spec.constants ++ w.sig.constants
) )
} else { } else {
w.sig w.sig
} }
// Map WFLoop/Nondet to TSLoop/TSNondet
// Map Add/Remove statements to SetStmt
val steps = elaborated.map(s => toTSBlock(s)(newsig, properties))
val ts = TransitionSystem(newsig, steps) val ts = TransitionSystem(newsig, steps)
InvariantSpec(ts, spec.axioms, invgen(spec)) InvariantSpec(ts, spec.axioms, invgen(spec))
...@@ -65,7 +65,7 @@ object TSConverter extends LazyLogging { ...@@ -65,7 +65,7 @@ object TSConverter extends LazyLogging {
def toSetStmt(s:Statement[_], sig:Signature, properties:InvProperties): SetStmt = { def toSetStmt(s:Statement[_], sig:Signature, properties:InvProperties): SetStmt = {
val frees = s.guard.freeVars -- s.tuple.toSet -- sig.constants val frees = (s.guard.freeVars -- s.tuple.toSet) -- sig.constants
// Quantify free, non-const variables // Quantify free, non-const variables
val guard = s match { val guard = s match {
...@@ -89,24 +89,25 @@ object TSConverter extends LazyLogging { ...@@ -89,24 +89,25 @@ object TSConverter extends LazyLogging {
val guardfix = if (!b.isoracly && b.pred.isDefined) { val guardfix = if (!b.isoracly && b.pred.isDefined) {
// is "normal" may block // is "normal" may block
val choice = Fun(b.pred.get, b.agents) val choice = Fun(b.pred.get, b.agents)
val first = b.agents.head
val fixed = for (s <- b.steps) yield { if (first.typ == spec.target.params.head.typ) {
val first = s.tuple.head for (s <- b.steps) yield {
val inner = if (first.typ == spec.target.params.head.typ) { val inner = if (properties.stubborn) {
if (properties.stubborn) {
choice.in(T1) choice.in(T1)
} else { } else {
val inf = Fun(INFNAME, List(b.agents.head)) val inf = Fun(INFNAME, List(b.agents.head))
Or(And(Neg(inf.in(T1)), choice.in(T1)), And(inf.in(T1), choice)) Or(And(Neg(inf.in(T1)), choice.in(T1)), And(inf.in(T1), choice))
} }
} else { val newguard = And(s.guard, inner)
choice s.update(guard = newguard)
} }
val newguard = And(s.guard, inner) } else {
s.update(guard = newguard) logger.warn(s"First agent of tuple in block: $b can not be informed - skipping introduction of choice predicate")
b.steps
} }
fixed
} else { } else {
b.steps b.steps
} }
......
...@@ -6,6 +6,9 @@ import de.tum.niwo.foltl.Properties ...@@ -6,6 +6,9 @@ import de.tum.niwo.foltl.Properties
import de.tum.niwo.parser.ParserUtils import de.tum.niwo.parser.ParserUtils
case class Signature(as: Set[Fun], constas: Set[Fun], bs: Set[Fun], preds: Set[Fun], constants: Set[Var]) { case class Signature(as: Set[Fun], constas: Set[Fun], bs: Set[Fun], preds: Set[Fun], constants: Set[Var]) {
lazy val allpredicates: Set[Fun] = as ++ bs ++ constas ++ preds
override def toString: String = override def toString: String =
{ {
def mkString(set:Set[Fun]) = set.map(_.toTypedString).mkString(",") def mkString(set:Set[Fun]) = set.map(_.toTypedString).mkString(",")
...@@ -124,19 +127,28 @@ trait NondetChoice[T <: NondetChoice[T, B], B <: Block] extends Block { ...@@ -124,19 +127,28 @@ trait NondetChoice[T <: NondetChoice[T, B], B <: Block] extends Block {
} }
} }
sealed trait TSBlock extends Block sealed trait TSBlock extends Block {
// TODO: maybe move this to general blocks?
def mapStatements(f:SetStmt => SetStmt):TSBlock
}
case class SimpleTSBlock(steps: List[SetStmt]) extends SimpleBlock[SimpleTSBlock, SetStmt] with TSBlock { case class SimpleTSBlock(steps: List[SetStmt]) extends SimpleBlock[SimpleTSBlock, SetStmt] with TSBlock {
override def toString: String = steps.mkString("{", "\n ", "}") override def toString: String = steps.mkString("{", "\n ", "}")
def toTypedString: String = steps.map(_.toTypedString).mkString("{", "\n ", "}") def toTypedString: String = steps.map(_.toTypedString).mkString("{", "\n ", "}")
override def update(newsteps: List[SetStmt]): SimpleTSBlock = copy(newsteps) override def update(newsteps: List[SetStmt]): SimpleTSBlock = copy(newsteps)
override def mapStatements(f: SetStmt => SetStmt): TSBlock = update(steps.map(f))
} }
case class TSLoop(steps:List[TSBlock]) extends Loop[TSLoop, TSBlock] with TSBlock { case class TSLoop(steps:List[TSBlock]) extends Loop[TSLoop, TSBlock] with TSBlock {
type B = TSBlock type B = TSBlock
override def update(steps:List[TSBlock] = steps):TSLoop = copy(steps) override def update(steps:List[TSBlock] = steps):TSLoop = copy(steps)
override def mapStatements(f: SetStmt => SetStmt): TSBlock = update(steps.map(_.mapStatements(f)))
} }
case class TSNondetChoice(left:List[TSBlock], right:List[TSBlock]) extends NondetChoice[TSNondetChoice, TSBlock] with TSBlock { case class TSNondetChoice(left:List[TSBlock], right:List[TSBlock]) extends NondetChoice[TSNondetChoice, TSBlock] with TSBlock {
type B = TSBlock type B = TSBlock
override def update(left:List[TSBlock] = left, right:List[TSBlock] = right):TSNondetChoice = copy(left, right) override def update(left:List[TSBlock] = left, right:List[TSBlock] = right):TSNondetChoice = copy(left, right)
override def mapStatements(f: SetStmt => SetStmt): B = update(left.map(_.mapStatements(f)), right.map(_.mapStatements(f)))
} }
...@@ -27,12 +27,12 @@ case class NISpec(w: Workflow, declass: Map[String, (List[Var], Formula)], axiom ...@@ -27,12 +27,12 @@ case class NISpec(w: Workflow, declass: Map[String, (List[Var], Formula)], axiom
s"NISpec\nWorkflow:\n$w\nDeclass:$declass\nTarget:$target" s"NISpec\nWorkflow:\n$w\nDeclass:$declass\nTarget:$target"
} }
// FIXME: why is this not part of the sig?
lazy val constants:Set[Var] = { lazy val constants:Set[Var] = {
// Treat free variables in the declassification as constants // Treat free variables in the declassification as constants
declass.values.flatMap { declass.values.flatMap {
case (params, f) => f.freeVars -- params case (params, f) => f.freeVars -- params
}.toSet }.toSet
// TODO: How to check for more constants?
} }
def isSane(): Boolean = { def isSane(): Boolean = {
......
...@@ -25,6 +25,7 @@ object FOLTL { ...@@ -25,6 +25,7 @@ object FOLTL {
def toPrenex: Formula = FormulaFunctions.toPrenex(this) def toPrenex: Formula = FormulaFunctions.toPrenex(this)
def toNegNf: Formula = FormulaFunctions.toNegNf(this) def toNegNf: Formula = FormulaFunctions.toNegNf(this)
def toCNF: Formula = FormulaFunctions.toCNF(this) def toCNF: Formula = FormulaFunctions.toCNF(this)
def toDNF: Formula = FormulaFunctions.toDNF(this)
lazy val isBS: Boolean = FormulaFunctions.isBS(this) lazy val isBS: Boolean = FormulaFunctions.isBS(this)
lazy val isUniversal: Boolean = FormulaFunctions.isU(this) lazy val isUniversal: Boolean = FormulaFunctions.isU(this)
......
...@@ -46,7 +46,7 @@ object FOTransformers extends LazyLogging { ...@@ -46,7 +46,7 @@ object FOTransformers extends LazyLogging {
} }
} }
def eliminateDoubleQuantifiers(f:Formula) = { def eliminateDoubleQuantifiers(f:Formula): Formula = {
def elimSub(f:Formula, bound:Set[Var]):Formula = { def elimSub(f:Formula, bound:Set[Var]):Formula = {
f everywhere { f everywhere {
// filter out all that are contained in bound // filter out all that are contained in bound
...@@ -56,7 +56,7 @@ object FOTransformers extends LazyLogging { ...@@ -56,7 +56,7 @@ object FOTransformers extends LazyLogging {
elimSub(f.toNegNf, Set()) elimSub(f.toNegNf, Set())
} }
def eliminateAuxiliaryPredicate(f:Formula, AUX:String, props:InvProperties) = { def eliminateAuxiliaryPredicate(f:Formula, AUX:String, props:InvProperties): Formula = {
logger.debug(s"Eliminating universal second order predicate $AUX") logger.debug(s"Eliminating universal second order predicate $AUX")
val cnf = if (!props.approxElim) f.toCNF else f.toNegNf val cnf = if (!props.approxElim) f.toCNF else f.toNegNf
...@@ -68,7 +68,7 @@ object FOTransformers extends LazyLogging { ...@@ -68,7 +68,7 @@ object FOTransformers extends LazyLogging {
repld.simplify repld.simplify
} }
def eliminateBPredicate(f:Formula, B:Fun) = { def eliminateBPredicate(f:Formula, B:Fun): (Formula, Formula) = {
val NAME = B.name val NAME = B.name
// SOQE: \exists B. f <-> f' // SOQE: \exists B. f <-> f'
...@@ -84,6 +84,9 @@ object FOTransformers extends LazyLogging { ...@@ -84,6 +84,9 @@ object FOTransformers extends LazyLogging {
val (quantifiers, clauses) = FormulaFunctions.toCNFClauses(noannotation) val (quantifiers, clauses) = FormulaFunctions.toCNFClauses(noannotation)
// Group clauses by positive/negative occurences // Group clauses by positive/negative occurences
val getPositiveBArguments = FormulaFunctions.getPositiveArguments(_, NAME)
val getNegativeBArguments = FormulaFunctions.getNegativeArguments(_, NAME)
def ispositive(clause: List[Formula]) = def ispositive(clause: List[Formula]) =
getPositiveBArguments(clause).nonEmpty getPositiveBArguments(clause).nonEmpty
...@@ -97,29 +100,6 @@ object FOTransformers extends LazyLogging { ...@@ -97,29 +100,6 @@ object FOTransformers extends LazyLogging {
case _ => false case _ => false
} }
} }
def getPositiveBArguments(clause: List[Formula]):Set[List[Var]] = {
clause flatMap {
case Fun(NAME, _, vars) => List(vars)
case _ => List()
} toSet
}
def getNegativeBArguments(clause: List[Formula]):Set[List[Var]] = {
clause flatMap {
case Neg(Fun(NAME, _, vars))=> List(vars)
case _ => List()
} toSet
}
def ineq(l1:List[Var], l2:List[Var]) = {
if (l1.size != l2.size) {
logger.warn("Trying to generate inequalities for different sized vectors")
}
val ineqs = l1.zip(l2).filterNot(x => x._1 == x._2).map {
case (x, y) if x.name <= y.name => Neg(Equal(x,y))
case (x, y) => Neg(Equal(y,x))
}.toSet
Or.make(ineqs.toList)
}
val E = clauses.filter(c => !ispositive(c) && !isnegative(c)) val E = clauses.filter(c => !ispositive(c) && !isnegative(c))
val F = clauses.filter(c => ispositive(c) && !isnegative(c)) val F = clauses.filter(c => ispositive(c) && !isnegative(c))
...@@ -146,7 +126,7 @@ object FOTransformers extends LazyLogging { ...@@ -146,7 +126,7 @@ object FOTransformers extends LazyLogging {
val hls = H.map(c => Or.make(pruneBs(c))) val hls = H.map(c => Or.make(pruneBs(c)))
val FGineq = for ((fi, fargs) <- fis.zip(Fargs); (gk, gargs) <- gks.zip(Gargs); farg <- gargs; garg <- gargs) yield { val FGineq = for ((fi, fargs) <- fis.zip(Fargs); (gk, gargs) <- gks.zip(Gargs); farg <- gargs; garg <- gargs) yield {
Or.make(fi, gk, ineq(farg, garg)) Or.make(fi, gk, FormulaFunctions.ineq(farg, garg))
} }
val fFGineq = And.make(FGineq) val fFGineq = And.make(FGineq)
val simpfFGineq = Z3BSFO.simplifyBS(fFGineq) // FIXME: is this correct? there are no quantifiers binding stuff val simpfFGineq = Z3BSFO.simplifyBS(fFGineq) // FIXME: is this correct? there are no quantifiers binding stuff
...@@ -186,7 +166,7 @@ object FOTransformers extends LazyLogging { ...@@ -186,7 +166,7 @@ object FOTransformers extends LazyLogging {
* Gobble up all outermost existential quantifiers * Gobble up all outermost existential quantifiers
* Needs to have non-conflicting bindings * Needs to have non-conflicting bindings
*/ */
def eliminateExistentials(t:Formula) = { def eliminateExistentials(t:Formula): (List[Var], Formula) = {
def coll:PartialFunction[Formula, List[Var]] = { def coll:PartialFunction[Formula, List[Var]] = {
case Exists(a, term) => a ++ term.collect(coll) case Exists(a, term) => a ++ term.collect(coll)
...@@ -260,7 +240,7 @@ object FOTransformers extends LazyLogging { ...@@ -260,7 +240,7 @@ object FOTransformers extends LazyLogging {
/** /**
* Replace function calls Fun("f", Some("t1"), List("x")) by Var("f#t1_x") * Replace function calls Fun("f", Some("t1"), List("x")) by Var("f#t1_x")
*/ */
def eliminatePredicates(t: Formula) = { def eliminatePredicates(t: Formula): Formula = {
t.everywhere({ t.everywhere({
case f:Fun => f.encodeToVar() case f:Fun => f.encodeToVar()
}) })
...@@ -270,7 +250,7 @@ object FOTransformers extends LazyLogging { ...@@ -270,7 +250,7 @@ object FOTransformers extends LazyLogging {
* Instantiates universals in EA formulae. * Instantiates universals in EA formulae.
* Resulting formula is in E* with all existentials outermost and in NegNF * Resulting formula is in E* with all existentials outermost and in NegNF
*/ */
def instantiateUniversals(f:Formula) = { def instantiateUniversals(f:Formula): Formula = {
// Check fragment // Check fragment
if (!f.isBS) { if (!f.isBS) {
...@@ -290,7 +270,7 @@ object FOTransformers extends LazyLogging { ...@@ -290,7 +270,7 @@ object FOTransformers extends LazyLogging {
* Instantiates existentials in AE formulae. * Instantiates existentials in AE formulae.
* Resulting formula is in NegNF * Resulting formula is in NegNF
*/ */
def abstractExistentials(f:Formula) = { def abstractExistentials(f:Formula): Formula = {
val neg = Neg(f).toNegNf.simplify val neg = Neg(f).toNegNf.simplify
// Check fragment // Check fragment
......
...@@ -2,6 +2,7 @@ package de.tum.niwo.foltl ...@@ -2,6 +2,7 @@ package de.tum.niwo.foltl
import FOLTL._ import FOLTL._
import com.typesafe.scalalogging.LazyLogging import com.typesafe.scalalogging.LazyLogging
import de.tum.niwo.foltl.FOTransformers.logger
import scala.annotation.tailrec import scala.annotation.tailrec
...@@ -545,7 +546,6 @@ object FormulaFunctions extends LazyLogging { ...@@ -545,7 +546,6 @@ object FormulaFunctions extends LazyLogging {
def toPrenex(f: Formula): Formula = { def toPrenex(f: Formula): Formula = {
val negnf = f.toNegNf.simplify val negnf = f.toNegNf.simplify
// TODO: test fix bindings
val simp = fixBinding(negnf) val simp = fixBinding(negnf)
// val simp = negnf // val simp = negnf
...@@ -588,37 +588,37 @@ object FormulaFunctions extends LazyLogging { ...@@ -588,37 +588,37 @@ object FormulaFunctions extends LazyLogging {
// } else { // } else {
// logger.warn("Using internal CNF conversion - may blow up") // logger.warn("Using internal CNF conversion - may blow up")
// FIXME: can we do it without prenex? may be expensive later on // FIXME: can we do it without prenex? may be expensive later on
val normalized = f.toPrenex.simplify val normalized = f.toPrenex.simplify
logger.debug(s"Computing CNF of prenex formula of opsize ${normalized.opsize}") logger.debug(s"Computing CNF of prenex formula of opsize ${normalized.opsize}")
val quantifiers = FormulaFunctions.collectQuantifiers(normalized) val quantifiers = FormulaFunctions.collectQuantifiers(normalized)
// normalized is now Quantifiers, then only And/Or with Negation inside // normalized is now Quantifiers, then only And/Or with Negation inside
// no simplification here // no simplification here
val cnf1 = toCNFSub(normalized) val cnf1 = toCNFSub(normalized)
// Inspect clauses // Inspect clauses
val clauses = FOTransformers.collectClauses(cnf1) val clauses = FOTransformers.collectClauses(cnf1)
// Simplify clauses // Simplify clauses
// TODO: improve, don't sort by tostring // TODO: improve, don't sort by tostring
val sorted = clauses.map(c => c.sortBy(_.toString())).sortBy(f => f.toString()) val sorted = clauses.map(c => c.sortBy(_.toString())).sortBy(f => f.toString())
// Remove trivial clauses // Remove trivial clauses
val notrivials = sorted.filterNot(c => { val notrivials = sorted.filterNot(c => {
c.exists(f => c.contains(Neg(f))) c.exists(f => c.contains(Neg(f)))
}) })
// TODO improve runtime // TODO improve runtime
val simped = notrivials.filterNot(c => notrivials.exists(c2 => { val simped = notrivials.filterNot(c => notrivials.exists(c2 => {
// check if c is superset of c2 // check if c is superset of c2
// c != c2 && c.startsWith(c2) // c != c2 && c.startsWith(c2)
c != c2 && c.forall(f => c2.contains(f)) c != c2 && c.forall(f => c2.contains(f))
})) }))
// logger.debug(s"After simplification ${simped.size} unsubsumed clauses remaining.") // logger.debug(s"After simplification ${simped.size} unsubsumed clauses remaining.")
logger.debug(s"Resulting CNF has ${sorted.size} clauses") logger.debug(s"Resulting CNF has ${sorted.size} clauses")
(quantifiers, simped) (quantifiers, simped)
// } // }
} }
...@@ -629,6 +629,26 @@ object FormulaFunctions extends LazyLogging { ...@@ -629,6 +629,26 @@ object FormulaFunctions extends LazyLogging {
newform newform
} }
def toDNFClauses(f:Formula):(List[(Boolean, List[Var])], List[List[Formula]]) = {
val simp = f.toPrenex.simplify
val quantifiers = collectQuantifiers(simp)
val stripped = stripQuantifiers(simp)
val (_, cnfclauses) = toCNFClauses(Neg(stripped))
// Negate all single valued
val dnfclauses = cnfclauses.map(_.map(Neg(_).simplify))
(quantifiers, dnfclauses)
}
def toDNF(f: Formula): Formula = {
val (quantifiers, clauses) = toDNFClauses(f)
val form = Or.make(clauses.map(And.make))
val newform = rewrapQuantifiers(quantifiers, form)
newform
}
/** /**
* Resolves a universally quantified predicate * Resolves a universally quantified predicate
* Blows up the formula exponentially due to CNF * Blows up the formula exponentially due to CNF
...@@ -670,4 +690,37 @@ object FormulaFunctions extends LazyLogging { ...@@ -670,4 +690,37 @@ object FormulaFunctions extends LazyLogging {
removed.simplify removed.simplify
} }
def getPositiveArguments(clause: List[Formula], NAME:String):Set[List[Var]] = {
clause flatMap {
case Fun(NAME, _, vars) => List(vars)
case _ => List()
} toSet