(* ========================================================================= *)
(* Dense linear order decision procedure for reals, by Sean McLaughlin. *)
(* ========================================================================= *)
prioritize_real();;
(* ---------------------------------------------------------------------- *)
(* Util *)
(* ---------------------------------------------------------------------- *)
let list_conj =
let t_tm = `T` in
fun l -> if l = [] then t_tm else end_itlist (curry mk_conj) l;;
let mk_lt = mk_binop `(<)`;;
(* ---------------------------------------------------------------------- *)
(* cnnf *)
(* ---------------------------------------------------------------------- *)
let DOUBLE_NEG_CONV =
let dn_thm = TAUT `!x. ~(~ x) <=> x` in
let dn_conv =
fun tm ->
let tm' = dest_neg (dest_neg tm) in
ISPEC tm' dn_thm in
dn_conv;;
let IMP_CONV =
let i_thm = TAUT `!a b. (a ==> b) <=> (~a \/ b)` in
let i_conv =
fun tm ->
let (a,b) = dest_imp tm in
ISPECL [a;b] i_thm in
i_conv;;
let BEQ_CONV =
let beq_thm = TAUT `!a b. (a = b) <=> (a /\ b \/ ~a /\ ~b)` in
let beq_conv =
fun tm ->
let (a,b) = dest_eq tm in
ISPECL [a;b] beq_thm in
beq_conv;;
let NEG_AND_CONV =
let na_thm = TAUT `!a b. ~(a /\ b) <=> (~a \/ ~b)` in
let na_conv =
fun tm ->
let (a,b) = dest_conj (dest_neg tm) in
ISPECL [a;b] na_thm in
na_conv;;
let NEG_OR_CONV =
let no_thm = TAUT `!a b. ~(a \/ b) <=> (~a /\ ~b)` in
let no_conv =
fun tm ->
let (a,b) = dest_disj (dest_neg tm) in
ISPECL [a;b] no_thm in
no_conv;;
let NEG_IMP_CONV =
let ni_thm = TAUT `!a b. ~(a ==> b) <=> (a /\ ~b)` in
let ni_conv =
fun tm ->
let (a,b) = dest_imp (dest_neg tm) in
ISPECL [a;b] ni_thm in
ni_conv;;
let NEG_BEQ_CONV =
let nbeq_thm = TAUT `!a b. ~(a = b) <=> (a /\ ~b \/ ~a /\ b)` in
let nbeq_conv =
fun tm ->
let (a,b) = dest_eq (dest_neg tm) in
ISPECL [a;b] nbeq_thm in
nbeq_conv;;
(* tm = (p /\ q0) \/ (~p /\ q1) *)
let dest_cases tm =
try
let (l,r) = dest_disj tm in
let (p,q0) = dest_conj l in
let (np,q1) = dest_conj r in
if mk_neg p = np then (p,q0,q1) else failwith "not a cases term"
with Failure _ -> failwith "not a cases term";;
let is_cases = can dest_cases;;
let CASES_CONV =
let c_thm =
TAUT `!p q0 q1. ~(p /\ q0 \/ ~p /\ q1) <=> (p /\ ~q0 \/ ~p /\ ~q1)` in
let cc =
fun tm ->
let (p,q0,q1) = dest_cases tm in
ISPECL [p;q0;q1] c_thm in
cc;;
let QE_SIMPLIFY_CONV =
let CNNF_CONV =
let refl_conj = REFL `(/\)`
and refl_disj = REFL `(\/)` in
fun lfn_conv ->
let rec cnnf_conv tm =
if is_conj tm then
let (p,q) = dest_conj tm in
let thm1 = cnnf_conv p in
let thm2 = cnnf_conv q in
MK_COMB (MK_COMB (refl_conj,thm1),thm2)
else if is_disj tm then
let (p,q) = dest_disj tm in
let thm1 = cnnf_conv p in
let thm2 = cnnf_conv q in
MK_COMB (MK_COMB (refl_disj,thm1),thm2)
else if is_imp tm then
let (p,q) = dest_imp tm in
let thm1 = cnnf_conv (mk_neg p) in
let thm2 = cnnf_conv q in
TRANS (IMP_CONV tm) (MK_COMB(MK_COMB(refl_disj,thm1),thm2))
else if is_iff tm then
let (p,q) = dest_eq tm in
let pthm = cnnf_conv p in
let qthm = cnnf_conv q in
let npthm = cnnf_conv (mk_neg p) in
let nqthm = cnnf_conv (mk_neg q) in
let thm1 = MK_COMB(MK_COMB(refl_conj,pthm),qthm) in
let thm2 = MK_COMB(MK_COMB(refl_conj,npthm),nqthm) in
TRANS (BEQ_CONV tm) (MK_COMB(MK_COMB(refl_disj,thm1),thm2))
else if is_neg tm then
let tm' = dest_neg tm in
if is_neg tm' then
let tm'' = dest_neg tm' in
let thm = cnnf_conv tm in
TRANS (DOUBLE_NEG_CONV tm'') thm
else if is_conj tm' then
let (p,q) = dest_conj tm' in
let thm1 = cnnf_conv (mk_neg p) in
let thm2 = cnnf_conv (mk_neg q) in
TRANS (NEG_AND_CONV tm) (MK_COMB(MK_COMB(refl_disj,thm1),thm2))
else if is_cases tm' then
let (p,q0,q1) = dest_cases tm in
let thm1 = cnnf_conv (mk_conj(p,mk_neg q0)) in
let thm2 = cnnf_conv (mk_conj(mk_neg p,mk_neg q1)) in
TRANS (CASES_CONV tm) (MK_COMB(MK_COMB(refl_disj,thm1),thm2))
else if is_disj tm' then
let (p,q) = dest_disj tm' in
let thm1 = cnnf_conv (mk_neg p) in
let thm2 = cnnf_conv (mk_neg q) in
TRANS (NEG_OR_CONV tm) (MK_COMB(MK_COMB(refl_conj,thm1),thm2))
else if is_imp tm' then
let (p,q) = dest_imp tm' in
let thm1 = cnnf_conv p in
let thm2 = cnnf_conv (mk_neg q) in
TRANS (NEG_IMP_CONV tm) (MK_COMB(MK_COMB(refl_conj,thm1),thm2))
else if is_iff tm' then
let (p,q) = dest_eq tm' in
let pthm = cnnf_conv p in
let qthm = cnnf_conv q in
let npthm = cnnf_conv (mk_neg p) in
let nqthm = cnnf_conv (mk_neg q) in
let thm1 = MK_COMB (MK_COMB(refl_conj,pthm),nqthm) in
let thm2 = MK_COMB(MK_COMB(refl_conj,npthm),qthm) in
TRANS (NEG_BEQ_CONV tm) (MK_COMB(MK_COMB(refl_disj,thm1),thm2))
else lfn_conv tm
else lfn_conv tm in
QE_SIMPLIFY_CONV THENC cnnf_conv THENC QE_SIMPLIFY_CONV;;
(*
let tests = [
`~(a /\ b)`;
`~(a \/ b)`;
`~(a ==> b)`;
`~(a:bool <=> b)`;
`~ ~ a`;
];;
map (CNNF_CONV (fun x -> REFL x)) tests;;
*)
(* ---------------------------------------------------------------------- *)
(* Real Lists *)
(* ---------------------------------------------------------------------- *)
let MINL = new_recursive_definition list_RECURSION
`(MINL [] default = default) /\
(MINL (CONS h t) default = min h (MINL t default))`;;
let MAXL = new_recursive_definition list_RECURSION
`(MAXL [] default = default) /\
(MAXL (CONS h t) default = max h (MAXL t default))`;;
let ALL_LT_LEMMA = prove
(`!left x lefts.
ALL (\l. l < x) (CONS left lefts) <=>
MAXL lefts left < x`,
GEN_TAC THEN GEN_TAC THEN LIST_INDUCT_TAC THEN REWRITE_TAC[
MAXL;
ALL] THEN
SPEC_TAC(`t:real list`,`t:real list`) THEN LIST_INDUCT_TAC THEN
REWRITE_TAC[
ALL;
MAXL;
MAX_LT] THEN ASM_MESON_TAC[
MAX_LT]);;
let ALL_GT_LEMMA = prove
(`!right x rights.
ALL (\r. x < r) (CONS right rights) <=> x <
MINL rights right`,
GEN_TAC THEN GEN_TAC THEN LIST_INDUCT_TAC THEN REWRITE_TAC[
MINL;
ALL] THEN
SPEC_TAC(`t:real list`,`t:real list`) THEN LIST_INDUCT_TAC THEN
REWRITE_TAC[
ALL;
MINL;
MIN_GT] THEN ASM_MESON_TAC[
MIN_GT]);;
(* ---------------------------------------------------------------------- *)
(* Axioms *)
(* ---------------------------------------------------------------------- *)
(* ---------------------------------------------------------------------- *)
(* lfn_dlo *)
(* ---------------------------------------------------------------------- *)
let LFN_DLO_CONV =
PURE_REWRITE_CONV[
REAL_ARITH `~(s < t) <=> ((s = t) \/ (t < s))`;
REAL_ARITH `~(s = t) <=> (s < t \/ t < s)`;
];;
(* ------------------------------------------------------------------------- *)
(* Proforma theorems to support the main inference step. *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Deal with ?x. <conjunction of strict inequalities all involving x> *)
(* ------------------------------------------------------------------------- *)
let mk_rlist = let ty = `:real` in fun x -> mk_list(x,ty);;
let expand_all = PURE_REWRITE_RULE
[ALL; BETA_THM; GSYM CONJ_ASSOC; TAUT `a /\ T <=> a`];;
let DLO_EQ_CONV fm =
let x,p = dest_exists fm in
let xl,xr = partition (fun t -> rand t = x) (conjuncts p) in
let lefts = map lhand xl and rights = map rand xr in
let th1 =
if lefts = [] then SPECL [hd rights; mk_rlist(tl rights)] PROFORMA_RIGHT
else if rights = [] then SPECL [hd lefts; mk_rlist(tl lefts)] PROFORMA_LEFT
else SPECL [hd lefts; mk_rlist(tl lefts); hd rights; mk_rlist(tl rights)]
PROFORMA_BOTH in
let th2 = CONV_RULE (LAND_CONV(GEN_ALPHA_CONV x)) (expand_all th1) in
let p' = snd(dest_exists(lhand(concl th2))) in
let th3 = MK_EXISTS x (CONJ_ACI_RULE(mk_eq(p,p'))) in
TRANS th3 th2;;
(* ------------------------------------------------------------------------- *)
(* Deal with general ?x. <conjunction of atoms> *)
(* ------------------------------------------------------------------------- *)
let eq_triv_conv =
let pth_triv = prove
(`((?x. x = x) <=> T) /\
((?x. x = t) <=> T) /\
((?x. t = x) <=> T) /\
((?x. (x = t) /\ P x) <=> P t) /\
((?x. (t = x) /\ P x) <=> P t)`,
MESON_TAC[]) in
GEN_REWRITE_CONV I [
pth_triv]
and eq_refl_conv =
let rec DLOBASIC_CONV fm =
try let x,p = dest_exists fm in
let cjs = conjuncts p in
try let eq = find (fun e -> is_eq e & (lhs e = x or rhs e = x)) cjs in
let cjs' = eq::setify(subtract cjs [eq]) in
let p' = list_mk_conj cjs' in
let th1 = MK_EXISTS x (CONJ_ACI_RULE(mk_eq(p,p'))) in
let fm' = rand(concl th1) in
try TRANS th1 (eq_triv_conv fm') with Failure _ ->
TRANS th1 ((eq_refl_conv THENC DLOBASIC_CONV) fm')
with Failure _ ->
if mem (mk_lt x x) cjs then lt_refl_conv fm
else DLO_EQ_CONV fm
with Failure _ -> (print_qterm fm; failwith "dlobasic");;
(* ------------------------------------------------------------------------- *)
(* Overall quantifier elimination. *)
(* ------------------------------------------------------------------------- *)
let AFN_DLO_CONV vars =
PURE_REWRITE_CONV[
REAL_ARITH `s <= t <=> ~(t < s)`;
REAL_ARITH `s >= t <=> ~(s < t)`;
REAL_ARITH `s > t <=> t < s`
];;
let dest_binop_op tm =
try
let f,r = dest_comb tm in
let op,l = dest_comb f in
(l,r,op)
with Failure _ -> failwith "dest_binop_op";;
let LIFT_QELIM_CONV afn_conv nfn_conv qfn_conv =
let rec qelift_conv vars fm =
if fm = t_tm or fm = f_tm then REFL fm
else if is_neg fm then
let thm1 = qelift_conv vars (dest_neg fm) in
MK_COMB(REFL not_tm,thm1)
else if is_conj fm or is_disj fm or is_imp fm or is_iff fm then
let (p,q,op) = dest_binop_op fm in
let thm1 = qelift_conv vars p in
let thm2 = qelift_conv vars q in
MK_COMB(MK_COMB((REFL op),thm1),thm2)
else if is_forall fm then
let (x,p) = dest_forall fm in
let nex_thm = BETA_RULE (ISPEC (mk_abs(x,p)) forall_thm) in
let elim_thm = qelift_conv vars (mk_exists(x,mk_neg p)) in
TRANS nex_thm (MK_COMB (REFL not_tm,elim_thm))
else if is_exists fm then
let (x,p) = dest_exists fm in
let thm1 = qelift_conv (x::vars) p in
let thm1a = MK_EXISTS x thm1 in
let thm2 = nfn_conv (rhs(concl thm1)) in
let thm2a = MK_EXISTS x thm2 in
let djs = disjuncts (rhs (concl thm2)) in
let djthms = map (qelim x vars) djs in
let thm3 = end_itlist
(fun thm1 thm2 -> MK_COMB(MK_COMB (REFL or_tm,thm1),thm2)) djthms in
let split_ex_thm = GSYM (or_exists_conv (lhs (concl thm3))) in
let thm3a = TRANS split_ex_thm thm3 in
TRANS (TRANS thm1a thm2a) thm3a
else
afn_conv vars fm
and qelim x vars p =
let cjs = conjuncts p in
let ycjs,ncjs = partition (mem x o frees) cjs in
if ycjs = [] then triv_exists_conv(mk_exists(x,p))
else if ncjs = [] then qfn_conv vars (mk_exists(x,p)) else
let th1 = CONJ_ACI_RULE
(mk_eq(p,mk_conj(list_mk_conj ncjs,list_mk_conj ycjs))) in
let th2 = CONV_RULE (RAND_CONV push_exists_conv) (MK_EXISTS x th1) in
let t1,t2 = dest_comb (rand(concl th2)) in
TRANS th2 (AP_TERM t1 (qfn_conv vars t2)) in
fun fm -> ((qelift_conv (frees fm)) THENC QE_SIMPLIFY_CONV) fm;;
let QELIM_DLO_CONV =
(LIFT_QELIM_CONV AFN_DLO_CONV ((CNNF_CONV LFN_DLO_CONV) THENC DNF_CONV)
(fun v -> DLOBASIC_CONV)) THENC (REWRITE_CONV[]);;
(* ---------------------------------------------------------------------- *)
(* Test *)
(* ---------------------------------------------------------------------- *)
let tests = [
`!x y. ?z. z < x /\ z < y`;
`?z. x < x /\ z < y`;
`?z. x < z /\ z < y`;
`!x. x < a ==> x < b`;
`!a b. (!x. (x < a) <=> (x < b)) <=> (a = b)`; (* long time *)
`!x. ?y. x < y`;
`!x y z. x < y /\ y < z ==> x < z`;
`!x y. x < y \/ (x = y) \/ y < x`;
`!x y. x < y \/ (x = y) \/ y < x`;
`?x y. x < y /\ y < x`;
`!x y. ?z. z < x /\ x < y`;
`!x y. ?z. z < x /\ z < y`;
`!x y. x < y ==> ?z. x < z /\ z < y`;
`!x y. ~(x = y) ==> ?u. u < x /\ (y < u \/ x < y)`;
`?x. x = x:real`;
`?x.(x = x) /\ (x = y)`;
`?z. x < z /\ z < y`;
`?z. x <= z /\ z <= y`;
`?z. x < z /\ z <= y`;
`!x y z. ?u. u < x /\ u < y /\ u < z`;
`!y. x < y /\ y < z ==> w < z`;
`!x y . x < y`;
`?z. z < x /\ x < y`;
`!a b. (!x. x < a ==> x < b) <=> (a <= b)`;
`!x. x < a ==> x < b`;
`!x. x < a ==> x <= b`;
`!a b. ?x. ~(x = a) \/ ~(x = b) \/ (a = b:real)`;
`!x y. x <= y \/ x > y`;
`!x y. x <= y \/ x < y`
];;
map (time QELIM_DLO_CONV) tests;;