SML version of Proof Procedures for HOL Terms

One of the keys to my research project is writing some simple, but useful, proof procedures for HOL Terms as SML code. I've got three done so far. I'm still working on getting the proof engine set up, but in the mean time, I'd appreciate it if folks could take a look at this stuff and make sure that my implementation is more or less sensible. Just send me your comments (woodcock@cs.umbc.edu), thx.

  1. Boyer-Moore Tautology Checker
  2. Arithmetic Zero Rules
  3. HOL Taut Library

SML version of the Boyer-Moore Tautology Checker for HOL Terms

The idea for this code is stolen directly from "A Computational Logic", by Robert S. Boyer and J Strother Moore (ACM Monograph Series, Academic Press, 1979; pages 57-66; thx Bob & J). Its, of course, translated in at least two ways: the implementation is now SML instead of LISP, and the terms is works on now are HOL terms instead of B-M logic terms.

The issues that particularly concern me are (in descending order of importance):

N.B. Since this a Boyer-Moore procedure, it doesn't recognize quantifiers; anything that looks like a quantifier will be treated as an internally complicated proposition--which is to say just like any proposition, since it really doesn't care about the structure of propositions, except to note that they might be either true or false..

Download for testing

(* some types I'll need in a minute for matching *)
val boolfunt = Tyapp("fun",[Tyapp("bool",[]),
                           Tyapp("bool",[])]);

val boolopt = Tyapp("fun",[Tyapp("bool",[]),
                           boolfunt]);

val condt = Tyapp("fun",[Tyapp("bool",[]),
			 boolopt]);

val COND = Const("COND",condt);

(* convert a bool term into an if-expression,
   mainly looking for the typical boolean connectives
   anything else (a quantifier, a predicate application,
   the result of applying a non-typical boolean connective)
   will be treated like an ordinary proposition  *)
fun iffify (tm as Comb (Comb(Const(f,boolopt),lrand),rrand)) =
    (case f of 
	  "/\\" => (mk_cond (convert lrand,convert rrand, X "F")) |
	  "\\/" => (mk_cond (convert lrand, X "T", convert rrand)) |
 	  "==>" => (mk_cond (convert lrand, convert rrand, X "T")) |
          "="   => (mk_cond (convert lrand, convert rrand,
			     mk_cond (convert rrand, X "F", X "T"))) |
	  _ => tm ) |
    iffify (Comb (Const("~",boolfunt),rand)) = 
		   (mk_cond(convert rand, X "F", X "T")) |
    iffify (Comb (Comb (Comb (COND,
                              onerand),tworand),threerand)) = 
             (mk_cond (convert onerand, convert tworand,convert threerand)) |
    iffify x = x

(* just screen out the non-bool terms *)
and convert x = if ((type_of x) = Y "bool") then iffify x 
		else raise Failure ("convert:  non-bool term");

(* apply an assignment in a assoc-list to a term *)
fun assignment x alist = assoc x alist handle Failure str => 
				raise (Failure ("assignment: "^str));

(* normalize the if-expressions,
   i.e. get rid of "if"'s in the branch part of "if"'s *)
fun norm (Comb (Comb (Comb (COND,a),b),c)) = 
   (case a of (Comb (Comb (Comb (COND,a'),b'),c')) => 
              (norm  (mk_cond (a',
		              mk_cond(b',b,c),
			      mk_cond(c',b,c)))) |
	     tm => mk_cond (a,(norm b),(norm c))) |
    norm tm = tm;

(* check if a normalized if-expression is a tautology *)
fun tautp (Comb (Comb (Comb (COND,a),b),c))  alist = 
     if (mem a (fst (unzip alist)))
        then if (assignment a alist)
   	     then (tautp b alist)
  	     else (tautp c alist)
        else ((tautp b ((a,true)::alist))
	      andalso
	      (tautp c ((a,false)::alist))) |
    tautp x alist = assignment x alist handle Failure _ => false;


(* put the pieces together *)
fun taut x = (tautp (norm x) [(X "T",true),(X "F",false)]);

fun check_taut x = taut (convert x);

fun bmTAUT x = if (check_taut x) then X "T" else x;

(* should work (and do) *)
val dem1a = X "(a ==> b) = (~a \\/ b)";

val dem2 = X "(a /\\ b) = ~(~a \\/ ~b)";

val dem3 = X "(a \\/ b) = ~(~a /\\ ~b)";

val taut1 = X "(a \\/ ~a)";

val taut1a = X "(a \\/ ~a) = T";

(* shouldn't work -- and don't *)

val unsat1 = X "(a /\\ ~a)";

val unsat2 = X "T ==> F";

val sat1 = X "a /\\ b";

val sat2 = X "(a:bool) = b";

Reduce Arithmetic Expressions (in HOL Terms) to Zero where Possible

This one I came up with completely on my own and mainly at random. It seemed that the implementation would be pretty simple and the utility pretty obvious. My issues with this code are pretty much the same as above, except that there is obviously no concern about matching a reference, and I'm more (comparatively) interested in hearing about straightforward improvements.

Download for testing

(* check the known rules to determine if we can reduce a term to
   zero *)
fun opred lrand rator rrand =
	let val l' = zred lrand
	    val r' = zred rrand
            val zero = X "0"
	    val binopt = Tyapp("fun",[Tyapp("num",[]),
				      Tyapp("fun",[Tyapp("num",[]),
					           Tyapp("num",[])])])
	in
	  if (rator = (Const("*",binopt)))
	  then (if ((l' = zero ) orelse (r' = zero))
		then zero
	        else Comb (Comb (rator,l'), r'))
	  else if (rator = (Const("+",binopt)))
		then if (l' = zero)
		     then r'
		     else if (r' = zero)
			  then l'
			  else Comb (Comb(rator,l'), r')
		else if (rator = (Const ("-",binopt)))
		     then if (r' = zero)
			  then l'
			  else Comb (Comb(rator,l'), r')
		     else Comb (Comb(rator,l'),r') end


(* find the terms which actually have feature an operator on a 
   numeric term *)
and numred (Comb (x,y)) b = if (type_of(y) = (Y "num"))
			     then (opred y x b)
			     else Comb (zred (Comb(x,y)), zred b) |
    numred (Const ("-",t1)) b = let val b' = zred b
			        in if (b' = (X "0"))
				   then b'
				   else Comb(Const("-",t1),b') end |
    numred a b = Comb (zred a, zred b)


(* just find the terms of type num which could be reduced *)
and zred (Var(x,y)) = Var(x,y) |
    zred (Const(x,y)) = Const(x,y) |
    zred (Comb(x,y)) = if (type_of (Comb(x,y)) = (Y "num"))
		       then numred  x y
		       else Comb(zred x, zred y) |
    zred (Abs(x,y)) = Abs(x, zred y);

HOL (Richard Boulton's) Taut Library (just SML, no theorems)

As the title suggests, this is ripped right out of the HOL 90.7 Library. Anything vaguely like a theorem went straight to the trash heap (later, my extension rule will be used to make the equivalence of the original term and what this procedure produces a new theorem; assuming I can prove the code is ok), and what I ended up with is "TAUT_CHECK" a function which returns "true" if the term is a tautology, and "TAUT" which returns the term representing true if its a tautology and the original term otherwise.

The issues that particularly concern me are (in descending order of importance):

Download for testing

(*===========================================================================*)
(* Discriminator functions for T (true) and F (false)                        *)
(*===========================================================================*)

val T = X "T"
and F = X "F";

fun is_T tm = (tm = T)
and is_F tm = (tm = F);

val boolty = Y "bool";
val boolopty = Y "bool->bool";
val boolfunty = Y "bool->bool->bool";
val boolthreety = Y "bool->bool->bool->bool";



(*===========================================================================*)
(* Conversions used for simplifying Boolean terms                            *)
(*===========================================================================*)

(*---------------------------------------------------------------------------*)
(* NOT_CONV : conv                                                           *)
(*                                                                           *)
(*    |- !t. ~~t = t                                                         *)
(*    |- ~T = F                                                              *)
(*    |- ~F = T                                                              *)
(*---------------------------------------------------------------------------*)


fun NOT_FUN tm =
	let val arg = dest_neg tm
	in if (is_T arg)
	   then F
	   else if (is_F arg)
		then T
		else if (is_neg arg)
		     then dest_neg arg
		     else tm
	end handle Failure x => raise (Failure ("NOT_FUN: "^x));

(*---------------------------------------------------------------------------*)
(* EQ_CONV : conv                                                            *)
(*                                                                           *)
(*    |- (t = t) = T                                                         *)
(*    |- (T = t) = t                                                         *)
(*    |- (t = T) = t                                                         *)
(*    |- (F = t) = ~t                                                        *)
(*    |- (t = F) = ~t                                                        *)
(*---------------------------------------------------------------------------*)

fun EQ_FUN tm =
	let val (lhs,rhs) = dest_eq tm
	    val neg = Const("~",boolopty)
	in
	if (is_T rhs)
	then lhs
	else if (is_T lhs)
	     then rhs
	     else if (is_F rhs)
		  then Comb(neg,lhs)
		  else if (is_F lhs)
		       then Comb(neg,rhs)
		       else if (lhs=rhs)
			    then T
			    else tm
	end handle Failure x => raise (Failure ("EQ_FUN: "^x));


(*---------------------------------------------------------------------------*)
(* AND_CONV : conv                                                           *)
(*                                                                           *)
(*    |- T /\ t = t                                                          *)
(*    |- t /\ T = t                                                          *)
(*    |- F /\ t = F                                                          *)
(*    |- t /\ F = F                                                          *)
(*    |- t /\ t = t                                                          *)
(*---------------------------------------------------------------------------*)


fun AND_FUN tm =
	let val (c1,c2) = dest_conj tm
	in
        if (is_T c1)
	then c2
	else if (is_T c2)
	     then c1
	     else if (is_F c1)
		  then F
		  else if (is_F c2)
		       then F
	               else if (c1 = c2)
			    then T
			    else c1
	end handle Failure x => raise Failure ("AND_FUN: "^x); 
	

(*---------------------------------------------------------------------------*)
(* OR_CONV : conv                                                            *)
(*                                                                           *)
(*    |- T \/ t = T                                                          *)
(*    |- t \/ T = T                                                          *)
(*    |- F \/ t = t                                                          *)
(*    |- t \/ F = t                                                          *)
(*    |- t \/ t = t                                                          *)
(*---------------------------------------------------------------------------*)

fun OR_FUN tm =
	let val (d1,d2) = dest_disj tm
	in
	if (is_T d1) 
	then T
	else if (is_T d2)
	     then T
	     else if (is_F d1)
		  then d2
		  else if (is_F d2)
		       then d1
		       else if (d1 = d2)
			    then d1
			    else tm
	end handle Failure x => raise Failure ("OR_FUN: "^x);

(*---------------------------------------------------------------------------*)
(* IMP_CONV : conv                                                           *)
(*                                                                           *)
(*    |- T ==> t = t                                                         *)
(*    |- t ==> T = T                                                         *)
(*    |- F ==> t = T                                                         *)
(*    |- t ==> t = T                                                         *)
(*    |- t ==> F = ~t                                                        *)
(*---------------------------------------------------------------------------*)

fun IMP_FUN tm =
    let val (ant,cons) = dest_imp tm
	val neg = Const("~",Tyapp("fun",[Tyapp("bool",[]),Tyapp("bool",[])]))	
	in
	if (is_T ant) 
	then cons
	else if (is_T cons)
	     then T
	     else if (is_F ant)
		  then T
		  else if (is_F cons)
		       then Comb(neg,ant)
		       else if (ant = cons)
			    then T
			    else tm
	end handle Failure x => raise Failure ("IMP_FUN: "^x);

(*---------------------------------------------------------------------------*)
(* IF_CONV : conv                                                            *)
(*                                                                           *)
(*    |- (T => t1 | t2) = t1                                                 *)
(*    |- (F => t1 | t2) = t2                                                 *)
(*---------------------------------------------------------------------------*)

fun IF_FUN tm =
    let val (br,(lef,rig)) = dest_cond tm
	in
	if (is_T br)
	then lef
	else if (is_F br)
	     then rig
	     else tm
	end handle Failure x => raise Failure ("IF_FUN: "^x);

(*---------------------------------------------------------------------------*)
(* SIMP_PROP_QCONV : conv                                                    *)
(*                                                                           *)
(* Conversion for simplifying propositional terms containing constants,      *)
(* variables, equality, implication, AND, OR, NOT and conditionals.          *)
(* Uses failure to avoid rebuilding unchanged subterms.                      *)
(*---------------------------------------------------------------------------*)

fun SIMP_PROP_FUN (tm as Const(_,_)) = tm |
    SIMP_PROP_FUN (tm as Var (_,_)) = tm |
    SIMP_PROP_FUN (tm as Comb(x,y)) = 
    let val NEG = Const("~",boolopty)
    in
     ((if (is_neg tm) 
     	then NOT_FUN (Comb(NEG,SIMP_PROP_FUN y))
	else
	(case x of (Comb(v,w)) => 
			if (is_conj tm) 
			then let val x1' = SIMP_PROP_FUN w
				 val y' = SIMP_PROP_FUN y
				 in AND_FUN (Comb(Comb(v,x1'),y')) end 
		        else if (is_disj tm)
			     then let val x1' = SIMP_PROP_FUN w
				    val y' = SIMP_PROP_FUN y
				    in OR_FUN (Comb(Comb(v,x1'),y')) end 
			else if (is_imp tm)
			     then let val x1' = SIMP_PROP_FUN w
				     val y' = SIMP_PROP_FUN y
				     in IMP_FUN (Comb(Comb(v,x1'),y')) end  
			else if (is_eq tm)
			     then let val x1' = SIMP_PROP_FUN w
				    val y' = SIMP_PROP_FUN y
				    in SIMP_PROP_FUN 
					   (EQ_FUN (Comb(Comb(v,x1'),y'))) 
				  end  
			else (case v of (Comb(r,s)) =>
				(if (is_cond tm) then 
					  let val x1' = SIMP_PROP_FUN w
					      val y' = SIMP_PROP_FUN y
					      val z' = SIMP_PROP_FUN s
					   in IF_FUN (Comb(Comb(Comb(r,
								     z'),
							         x1'),
                                                           y')) end 
					         else tm) |
				           _ => tm) |

                  _ => tm ))
	handle UNCHANGED => tm) end |
   SIMP_PROP_FUN (Abs (v,b)) = Abs(v,SIMP_PROP_FUN b);




(*---------------------------------------------------------------------------*)
(* TAUT_CHECK_CONV : conv                                                    *)
(*                                                                           *)
(* Given a propositional term with all variables universally quantified,     *)
(* e.g. `!x1 ... xn. f[x1,...,xn]`, this conversion proves the term to be    *)
(* either true or false, i.e. it returns one of:                             *)
(*                                                                           *)
(*    |- (!x1 ... xn. f[x1,...,xn]) = T                                      *)
(*    |- (!x1 ... xn. f[x1,...,xn]) = F                                      *)
(*---------------------------------------------------------------------------*)

fun TAUT_CHECK tm =
    let val (vars,tm') = strip_forall tm
    in
    if (is_T tm')
    then true
    else if (is_F tm')
	 then false
	 else let val (var,bod) = dest_forall tm
		  val tmT = subst [(T,var)] bod
		  val tmT1 = SIMP_PROP_FUN tmT
	       in
		(TAUT_CHECK tmT1) andalso 
                (let val tmF = subst [(F,var)] bod
		     val tmF1 = SIMP_PROP_FUN tmF
		  in
		    TAUT_CHECK tmF1
		 end) 
               end
    end handle Failure x => raise Failure ("TAUT_CHECK: "^x);


fun TAUT tm =
	if TAUT_CHECK tm then T else tm;

val rtaut1 = X "!a. a \\/ ~a";

val rtaut2 = X "!a. a ==> a";

val rsat1 = X "!a. a /\\ a";

val rsat2 = X "!a. a \\/ a";

val rnonsat1 = X "!a. a /\\ ~a";

val rtaut3 = X "!a b c. (a ==> b) \\/ (b ==> c) \\/ (c ==> a)";