Boolean Expression Checkers

Boolean Expression Checkers
Tobias Nipkow
May 27, 2015
Abstract
This entry provides executable checkers for the following properties
of boolean expressions: satisfiability, tautology and equivalence. Internally, the checkers operate on binary decision trees and are reasonably
efficient (for purely functional algorithms).
Contents
1 Tautology (etc) Checking via Binary Decision Trees
1.1 Boolean Expressions . . . . . . . . . . . . . . . . . . . . .
1.2 Binary Decision Trees . . . . . . . . . . . . . . . . . . . .
1.3 A Simple Minded Translation . . . . . . . . . . . . . . . .
1.4 Translation to Reduced Binary Decision Trees . . . . . . .
1.4.1 Environment . . . . . . . . . . . . . . . . . . . . .
1.4.2 Translation and Normalisation . . . . . . . . . . .
1.4.3 Functional Correctness Proof . . . . . . . . . . . .
1.4.4 A Tautology Checker for Arbitrary If-Expressions .
1.4.5 Reduced If-Expressions . . . . . . . . . . . . . . .
1.5 Tautology Checking . . . . . . . . . . . . . . . . . . . . .
1.6 Satisfiability Checking . . . . . . . . . . . . . . . . . . . .
1.7 Equivalence Checking . . . . . . . . . . . . . . . . . . . .
2 Example
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2
2
2
2
3
3
3
4
5
5
7
7
8
8
theory Boolean-Expression-Checkers
imports Main
begin
1
1
Tautology (etc) Checking via Binary Decision
Trees
1.1
Boolean Expressions
This is the interface to the tautology checker. If you have your own type of
boolean expressions you need to translate into this type first.
datatype 0a bool-expr =
Const-bool-expr bool |
Atom-bool-expr 0a |
Neg-bool-expr 0a bool-expr |
And-bool-expr 0a bool-expr 0a bool-expr |
Or-bool-expr 0a bool-expr 0a bool-expr |
Imp-bool-expr 0a bool-expr 0a bool-expr |
Iff-bool-expr 0a bool-expr 0a bool-expr
primrec val-bool-expr :: 0a bool-expr ⇒ ( 0a ⇒ bool ) ⇒ bool where
val-bool-expr (Const-bool-expr b) s = b |
val-bool-expr (Atom-bool-expr x ) s = s x |
val-bool-expr (Neg-bool-expr b) s = (¬ val-bool-expr b s) |
val-bool-expr (And-bool-expr b1 b2 ) s = (val-bool-expr b1 s ∧ val-bool-expr b2 s) |
val-bool-expr (Or-bool-expr b1 b2 ) s = (val-bool-expr b1 s ∨ val-bool-expr b2 s) |
val-bool-expr (Imp-bool-expr b1 b2 ) s = (val-bool-expr b1 s −→ val-bool-expr b2 s)
|
val-bool-expr (Iff-bool-expr b1 b2 ) s = (val-bool-expr b1 s = val-bool-expr b2 s)
1.2
Binary Decision Trees
datatype 0a ifex = Trueif | Falseif | IF 0a 0a ifex 0a ifex
fun val-ifex :: 0a ifex ⇒ ( 0a ⇒ bool ) ⇒ bool where
val-ifex Trueif s = True |
val-ifex Falseif s = False |
val-ifex (IF n t1 t2 ) s = (if s n then val-ifex t1 s else val-ifex t2 s)
1.3
A Simple Minded Translation
Simple minded normalisation, can create branches with repeated vars:
primrec normif0 :: 0a ifex ⇒ 0a ifex ⇒ 0a ifex ⇒ 0a ifex where
normif0 Trueif t1 t2 = t1 |
normif0 Falseif t1 t2 = t2 |
normif0 (IF x t1 t2 ) t3 t4 = IF x (normif0 t1 t3 t4 ) (normif0 t2 t3 t4 )
The corresponding translation from boolean expressions to if-expressions:
primrec ifex0 :: 0a bool-expr ⇒ 0a ifex where
ifex0 (Const-bool-expr b) = (if b then Trueif else Falseif ) |
ifex0 (Atom-bool-expr x ) = IF x Trueif Falseif |
ifex0 (Neg-bool-expr b) = normif0 (ifex0 b) Falseif Trueif |
2
ifex0 (And-bool-expr b1 b2 ) = normif0 (ifex0 b1 ) (ifex0 b2 ) Falseif |
ifex0 (Or-bool-expr b1 b2 ) = normif0 (ifex0 b1 ) Trueif (ifex0 b2 ) |
ifex0 (Imp-bool-expr b1 b2 ) = normif0 (ifex0 b1 ) (ifex0 b2 ) Trueif |
ifex0 (Iff-bool-expr b1 b2 ) = (let t1 = ifex0 b1 ; t2 = ifex0 b2 in
normif0 t1 t2 (normif0 t2 Falseif Trueif ))
lemma val-normif0 :
val-ifex (normif0 t t1 t2 ) s = val-ifex (if val-ifex t s then t1 else t2 ) s
by(induct t arbitrary: t1 t2 ) auto
theorem val-ifex0 : val-ifex (ifex0 b) = val-bool-expr b
by(induct-tac b)(auto simp: val-normif0 Let-def )
1.4
Translation to Reduced Binary Decision Trees
An improved translation.
1.4.1
Environment
Environments are substitutions of values for variables:
type-synonym 0a env-bool = ( 0a ∗ bool ) list
definition agree :: ( 0a ⇒ bool ) ⇒ 0a env-bool ⇒ bool where
agree s env = (∀ x b. map-of env x = Some b −→ s x = b)
lemma agree-Nil : agree s []
by(simp add : agree-def )
lemma agree-Cons: distinct(map fst env ) =⇒ x ∈
/ set(map fst env )
=⇒ agree s ((x ,b) # env ) = ((if b then s x else ¬ s x ) ∧ agree s env )
by(auto simp: agree-def image-iff )
lemma agreeDT :
[[ agree s env ; distinct (map fst env ) ]] =⇒ (x ,True) ∈ set env =⇒ s x
by(simp add : agree-def )
lemma agreeDF :
[[ agree s env ; distinct (map fst env ) ]] =⇒ (x ,False) ∈ set env =⇒ ¬ s x
by(simp add : agree-def )
1.4.2
Translation and Normalisation
A normalisation avoiding duplicate variables and collapsing if x then t else
t to t.
definition mkIF :: 0a ⇒ 0a ifex ⇒ 0a ifex ⇒ 0a ifex where
mkIF x t1 t2 = (if t1 =t2 then t1 else IF x t1 t2 )
fun reduce :: 0a env-bool ⇒ 0a ifex ⇒ 0a ifex where
3
reduce env (IF x t1 t2 ) = (case map-of env x of
None ⇒ mkIF x (reduce ((x ,True)#env ) t1 ) (reduce ((x ,False)#env ) t2 ) |
Some b ⇒ reduce env (if b then t1 else t2 )) |
reduce - t = t
primrec normif :: 0a env-bool ⇒ 0a ifex ⇒ 0a ifex ⇒ 0a ifex ⇒ 0a ifex where
normif env Trueif t1 t2 = reduce env t1 |
normif env Falseif t1 t2 = reduce env t2 |
normif env (IF x t1 t2 ) t3 t4 =
(case map-of env x of
None ⇒ mkIF x (normif ((x ,True)#env ) t1 t3 t4 ) (normif ((x ,False)#env )
t2 t3 t4 ) |
Some b ⇒ if b then normif env t1 t3 t4 else normif env t2 t3 t4 )
primrec ifex-of :: 0a bool-expr ⇒ 0a ifex where
ifex-of (Const-bool-expr b) = (if b then Trueif else Falseif ) |
ifex-of (Atom-bool-expr x ) = IF x Trueif Falseif |
ifex-of (Neg-bool-expr b) = normif [] (ifex-of b) Falseif Trueif |
ifex-of (And-bool-expr b1 b2 ) = normif [] (ifex-of b1 ) (ifex-of b2 ) Falseif |
ifex-of (Or-bool-expr b1 b2 ) = normif [] (ifex-of b1 ) Trueif (ifex-of b2 ) |
ifex-of (Imp-bool-expr b1 b2 ) = normif [] (ifex-of b1 ) (ifex-of b2 ) Trueif |
ifex-of (Iff-bool-expr b1 b2 ) = (let t1 = ifex-of b1 ; t2 = ifex-of b2 in
normif [] t1 t2 (normif [] t2 Falseif Trueif ))
1.4.3
Functional Correctness Proof
lemma val-mkIF : val-ifex (mkIF x t1 t2 ) s = val-ifex (IF x t1 t2 ) s
by(auto simp: mkIF-def Let-def )
theorem val-reduce: agree s env =⇒ distinct(map fst env ) =⇒
val-ifex (reduce env t) s = val-ifex t s
apply(induct t arbitrary: s env )
apply(auto simp: map-of-eq-None-iff val-mkIF agree-Cons Let-def
dest: agreeDT agreeDF split: option.splits)
done
lemma val-normif : agree s env =⇒ distinct(map fst env ) =⇒
val-ifex (normif env t t1 t2 ) s =
val-ifex (if val-ifex t s then t1 else t2 ) s
apply(induct t arbitrary: t1 t2 s env )
apply(auto simp: val-reduce val-mkIF agree-Cons map-of-eq-None-iff
dest: agreeDT agreeDF split: option.splits)
done
theorem val-ifex : val-ifex (ifex-of b) s = val-bool-expr b s
by(induct-tac b)(auto simp: val-normif agree-Nil Let-def )
4
1.4.4
A Tautology Checker for Arbitrary If-Expressions
Not really needed because ifex-of produces reduced expressions which can
be checked very easily.
fun taut-test-rec :: 0a ifex ⇒ 0a env-bool ⇒ bool where
taut-test-rec Trueif env = True |
taut-test-rec Falseif env = False |
taut-test-rec (IF x t1 t2 ) env = (case map-of env x of
Some b ⇒ taut-test-rec (if b then t1 else t2 ) env |
None ⇒ taut-test-rec t1 ((x ,True)#env ) ∧ taut-test-rec t2 ((x ,False)#env ))
lemma taut-test-rec: distinct(map fst env )
=⇒ taut-test-rec t env = (∀ s. agree s env −→ val-ifex t s)
proof (induct t arbitrary: env )
case Trueif thus ?case by simp
next
case Falseif
have agree (λx . the(map-of env x )) env by(auto simp: agree-def )
thus ?case by(auto)
next
case (IF x t1 t2 ) show ?case
proof (cases map-of env x )
case None thus ?thesis using IF
by (simp) (auto simp: map-of-eq-None-iff image-iff agree-Cons)
next
case Some thus ?thesis using IF
by (simp add : agree-def )
qed
qed
definition taut-test-ifex :: 0a ifex ⇒ bool where
taut-test-ifex t = taut-test-rec t []
corollary taut-test-ifex : taut-test-ifex t = (∀ s. val-ifex t s)
using taut-test-rec[of [] t]
by (auto simp: val-ifex taut-test-ifex-def agree-Nil )
1.4.5
Reduced If-Expressions
Proof that the result of ifex-of is reduced. An expression reduced iff no
variable appears twice on any branch and there is no subexpression IF x t t.
fun reduced :: 0a ifex ⇒ 0a set ⇒ bool where
reduced (IF x t1 t2 ) X =
(x ∈
/ X ∧ t1 6= t2 ∧ reduced t1 (insert x X ) ∧ reduced t2 (insert x X )) |
reduced - - = True
lemma reduced-antimono: X ⊆ Y =⇒ reduced t Y =⇒ reduced t X
apply(induction t arbitrary: X Y )
by auto (metis insert-mono)+
5
lemma reduced-mkIF : x ∈
/ X =⇒
reduced t1 (insert x X ) =⇒ reduced t2 (insert x X ) =⇒ reduced (mkIF x t1 t2 )
X
by(auto simp: mkIF-def intro:reduced-antimono)
lemma reduced-reduce:
distinct(map fst env ) =⇒ reduced (reduce env t) (fst ‘ set env )
proof (induction t arbitrary: env )
case (IF x t1 t2 )
thus ?case using IF .IH (1 )[of (x , True) # env ] IF .IH (2 )[of (x , False) # env ]
by(auto simp: map-of-eq-None-iff image-iff reduced-mkIF split: option.split)
qed auto
lemma reduced-normif :
distinct(map fst env ) =⇒ reduced (normif env t t1 t2 ) (fst ‘ set env )
proof (induction t arbitrary: t1 t2 env )
case (IF x s1 s2 )
thus ?case using IF .IH (1 )[of (x , True) # env ] IF .IH (2 )[of (x , False) # env ]
by(auto simp: reduced-mkIF map-of-eq-None-iff split: option.split)
qed (auto simp: reduced-reduce)
theorem reduced-ifex : reduced (ifex-of b) {}
by(induct b)(auto simp: reduced-normif [of [], simplified ] Let-def )
Proof that reduced if-expressions are Trueif, Falseif or can evaluate to
both True and False.
lemma same-val-if-reduced :
reduced t X =⇒ ∀ x . x ∈
/ X −→ s1 x = s2 x =⇒ val-ifex t s1 = val-ifex t s2
by(induction t arbitrary: X ) auto
lemma reduced-IF-depends: [[ reduced t X ; t 6= Trueif ; t 6= Falseif ]]
=⇒ ∃ s1 s2 . val-ifex t s1 6= val-ifex t s2
proof (induction t arbitrary: X )
case (IF x t1 t2 )
let ?t = IF x t1 t2
have 1 : reduced t1 (insert x X ) using IF .prems(1 ) by simp
have 2 : reduced t2 (insert x X ) using IF .prems(1 ) by simp
show ?case
proof (cases t1 )
case Trueif [simp]
show ?thesis
proof (cases t2 )
case Trueif thus ?thesis using IF .prems(1 ) by simp
next
case Falseif
hence val-ifex ?t (λ-. True) 6= val-ifex ?t (λ-. False) by simp
thus ?thesis by blast
next
6
case IF
then obtain s1 s2 where val-ifex t2 s1 6= val-ifex t2 s2
using IF .IH (2 )[OF 2 ] IF .prems(1 ) by auto
hence val-ifex ?t (s1 (x :=False)) 6= val-ifex ?t (s2 (x :=False))
using same-val-if-reduced [OF 2 , of s1 (x :=False) s1 ]
same-val-if-reduced [OF 2 , of s2 (x :=False) s2 ] by simp
thus ?thesis by blast
qed
next
case Falseif [simp]
show ?thesis
proof (cases t2 )
case Falseif thus ?thesis using IF .prems(1 ) by simp
next
case Trueif
hence val-ifex ?t (λ-. True) 6= val-ifex ?t (λ-. False) by simp
thus ?thesis by blast
next
case IF
then obtain s1 s2 where val-ifex t2 s1 6= val-ifex t2 s2
using IF .IH (2 )[OF 2 ] IF .prems(1 ) by auto
hence val-ifex ?t (s1 (x :=False)) 6= val-ifex ?t (s2 (x :=False))
using same-val-if-reduced [OF 2 , of s1 (x :=False) s1 ]
same-val-if-reduced [OF 2 , of s2 (x :=False) s2 ] by simp
thus ?thesis by blast
qed
next
case IF
then obtain s1 s2 where val-ifex t1 s1 6= val-ifex t1 s2
using IF .IH (1 )[OF 1 ] IF .prems(1 ) by auto
hence val-ifex ?t (s1 (x :=True)) 6= val-ifex ?t (s2 (x :=True))
using same-val-if-reduced [OF 1 , of s1 (x :=True) s1 ]
same-val-if-reduced [OF 1 , of s2 (x :=True) s2 ] by simp
thus ?thesis by blast
qed
qed auto
1.5
Tautology Checking
definition taut-test :: 0a bool-expr ⇒ bool where
taut-test b = (ifex-of b = Trueif )
corollary taut-test: taut-test b = (∀ s. val-bool-expr b s)
unfolding taut-test-def using reduced-IF-depends[OF reduced-ifex , of b]
by (metis val-ifex val-ifex .simps)
1.6
Satisfiability Checking
definition sat-test :: 0a bool-expr ⇒ bool where
sat-test b = (ifex-of b 6= Falseif )
7
corollary sat-test: sat-test b = (∃ s. val-bool-expr b s)
unfolding sat-test-def using reduced-IF-depends[OF reduced-ifex , of b]
by (metis val-ifex val-ifex .simps(1 ) val-ifex .simps(2 ))
1.7
Equivalence Checking
definition equiv-test :: 0a bool-expr ⇒ 0a bool-expr ⇒ bool where
equiv-test b1 b2 = taut-test (Iff-bool-expr b1 b2 )
corollary equiv-test: equiv-test b1 b2 = (∀ s. val-bool-expr b1 s = val-bool-expr b2
s)
by(auto simp:equiv-test-def taut-test)
Hide everything except the boolean expressions and the checkers.
hide-type (open) ifex env-bool
hide-const (open) Trueif Falseif IF val-ifex normif0 ifex0 agree mkIF
reduce normif ifex-of reduced taut-test-rec taut-test-ifex
end
theory Boolean-Expression-Example
imports Boolean-Expression-Checkers
begin
2
Example
Example usage of checkers. We have our own type of boolean expressions
with its own evaluation function:
datatype 0a bexp =
Const bool |
Atom 0a |
Neg 0a bexp |
And 0a bexp 0a bexp
fun
bval
bval
bval
bval
bval where
(Const b) s = b |
(Atom a) s = s a |
(Neg b) s = (¬ bval b s) |
(And b1 b2 ) s = (bval b1 s ∧ bval b2 s)
Now we translate into the datatype provided by the checkers interface
and show that the semantics remains the same:
fun bool-expr-of-bexp :: 0a bexp ⇒ 0a bool-expr where
bool-expr-of-bexp (Const b) = Const-bool-expr b |
bool-expr-of-bexp (Atom a) = Atom-bool-expr a |
bool-expr-of-bexp (Neg b) = Neg-bool-expr (bool-expr-of-bexp b) |
bool-expr-of-bexp (And b1 b2 ) = And-bool-expr (bool-expr-of-bexp b1 ) (bool-expr-of-bexp
b2 )
8
lemma val-preservation: val-bool-expr (bool-expr-of-bexp b) s = bval b s
by(induction b) auto
Trivial tautology checker and its correctness:
definition my-taut-test = taut-test o bool-expr-of-bexp
corollary my-taut-test: my-taut-test b = (∀ s. bval b s)
by(simp add : my-taut-test-def val-preservation taut-test)
Test: pigeonhole formulas
definition Or b1 b2 == Neg (And (Neg b1 ) (Neg b2 ))
definition ors = foldl Or (Const False)
definition ands = foldl And (Const True)
definition pc n = ands[ors[Atom(i ,j ). j <− [1 ..<n+1 ]]. i <− [1 ..<n+2 ]]
definition nc n = ands[Or (Neg(Atom(i ,k ))) (Neg(Atom(j ,k ))).
k <− [1 ..<n+1 ], i <− [1 ..<n+1 ], j <− [i +1 ..<n+2 ]]
definition php n = Neg(And (pc n) (nc n))
Takes about 5 secs; with 7 instead of 6 it takes about 4 mins (2014).
lemma my-taut-test (php 6 )
by eval
end
9