1 (* open satTools dimacsTools SatSolvers minisatResolve
2 satCommonTools minisatParse satScript def_cnf *)
9 #use "satCommonTools.ml";;
10 #use "dimacsTools.ml";;
11 #use "SatSolvers.ml";;
14 #use "minisatParse.ml";;
15 #use "minisatResolve.ml";;
16 #use "minisatProve.ml";;
19 (* ------------------------------------------------------------------------- *)
20 (* Flag to (de-)activate debugging facilities. *)
21 (* ------------------------------------------------------------------------- *)
23 let sat_debugging = ref false;;
25 (* ------------------------------------------------------------------------- *)
26 (* Split up a theorem according to conjuncts, in a general sense. *)
27 (* ------------------------------------------------------------------------- *)
30 let [pth_ni1; pth_ni2; pth_no1; pth_no2; pth_an1; pth_an2; pth_nn] =
31 (map UNDISCH_ALL o CONJUNCTS o TAUT)
32 `(~(p ==> q) ==> p) /\
33 (~(p ==> q) ==> ~q) /\
39 let p_tm = concl pth_an1 and q_tm = concl pth_an2 in
40 let rec GCONJUNCTS th acc =
42 Comb(Const("~",_),Comb(Comb(Const("==>",_),p),q)) ->
43 GCONJUNCTS (PROVE_HYP th (INST [p,p_tm; q,q_tm] pth_ni1))
44 (GCONJUNCTS (PROVE_HYP th (INST [p,p_tm; q,q_tm] pth_ni2))
46 | Comb(Const("~",_),Comb(Comb(Const("\\/",_),p),q)) ->
47 GCONJUNCTS (PROVE_HYP th (INST [p,p_tm; q,q_tm] pth_no1))
48 (GCONJUNCTS (PROVE_HYP th (INST [p,p_tm; q,q_tm] pth_no2))
50 | Comb(Comb(Const("/\\",_),p),q) ->
51 GCONJUNCTS (PROVE_HYP th (INST [p,p_tm; q,q_tm] pth_an1))
52 (GCONJUNCTS (PROVE_HYP th (INST [p,p_tm; q,q_tm] pth_an2))
54 | Comb(Const("~",_),Comb(Const("~",_),p)) ->
55 GCONJUNCTS (PROVE_HYP th (INST [p,p_tm] pth_nn)) acc
57 fun th -> GCONJUNCTS th [];;
59 (* ------------------------------------------------------------------------- *)
60 (* Generate fresh variable names (could just use genvars). *)
61 (* ------------------------------------------------------------------------- *)
63 let propvar i = mk_var("x"^string_of_int i,bool_ty);;
65 (* ------------------------------------------------------------------------- *)
66 (* Set up the basic definitional arrangement. *)
67 (* ------------------------------------------------------------------------- *)
69 let rec localdefs tm (n,defs,lfn) =
71 let n1,v1,defs1,lfn1 = localdefs (rand tm) (n,defs,lfn) in
72 let tm' = mk_neg v1 in
73 try (n1,apply defs1 tm',defs1,lfn1) with Failure _ ->
75 let v2 = propvar n2 in
76 n2,v2,(tm' |-> v2) defs1,(v2 |-> tm) lfn1
77 else if is_conj tm or is_disj tm or is_imp tm or is_iff tm then
78 let n1,v1,defs1,lfn1 = localdefs (lhand tm) (n,defs,lfn) in
79 let n2,v2,defs2,lfn2 = localdefs (rand tm) (n1,defs1,lfn1) in
80 let tm' = mk_comb(mk_comb(rator(rator tm),v1),v2) in
81 try (n2,apply defs2 tm',defs2,lfn2) with Failure _ ->
83 let v3 = propvar n3 in
84 n3,v3,(tm' |-> v3) defs2,(v3 |-> tm) lfn2
85 else try (n,apply defs tm,defs,lfn) with Failure _ ->
87 let v1 = propvar n1 in
88 n1,v1,(tm |-> v1) defs,(v1 |-> tm) lfn;;
90 (* ------------------------------------------------------------------------- *)
91 (* Just translate to fresh variables, but otherwise leave unchanged. *)
92 (* ------------------------------------------------------------------------- *)
94 let rec transvar (n,tm,vdefs,lfn) =
96 let n1,tm1,vdefs1,lfn1 = transvar (n,rand tm,vdefs,lfn) in
97 n1,mk_comb(rator tm,tm1),vdefs1,lfn1
98 else if is_conj tm or is_disj tm or is_imp tm or is_iff tm then
99 let n1,tm1,vdefs1,lfn1 = transvar (n,lhand tm,vdefs,lfn) in
100 let n2,tm2,vdefs2,lfn2 = transvar (n1,rand tm,vdefs1,lfn1) in
101 n2,mk_comb(mk_comb(rator(rator tm),tm1),tm2),vdefs2,lfn2
102 else try n,apply vdefs tm,vdefs,lfn with Failure _ ->
104 let v1 = propvar n1 in
105 n1,v1,(tm |-> v1) vdefs,(v1 |-> tm) lfn;;
107 (* ------------------------------------------------------------------------- *)
108 (* Flag to choose whether to exploit existing conjunctive structure. *)
109 (* ------------------------------------------------------------------------- *)
111 let exploit_conjunctive_structure = ref true;;
113 (* ------------------------------------------------------------------------- *)
114 (* Check if something is clausal (slightly stupid). *)
115 (* ------------------------------------------------------------------------- *)
117 let is_literal tm = is_var tm or is_neg tm & is_var(rand tm);;
120 let djs = disjuncts tm in
121 forall is_literal djs & list_mk_disj djs = tm;;
123 (* ------------------------------------------------------------------------- *)
124 (* Now do the definitional arrangement but not wastefully at the top. *)
125 (* ------------------------------------------------------------------------- *)
127 let definitionalize =
129 let pth = TAUT `(p ==> q) <=> ~p \/ q` in
130 let ptm = rand(concl pth) in
131 let p_tm = rand(lhand ptm) and q_tm = rand ptm in
132 fun th -> let ip,q = dest_comb(concl th) in
134 EQ_MP (INST [p,p_tm; q,q_tm] pth) th
135 and transform_iff_1 =
136 let pth = UNDISCH(TAUT `(p <=> q) ==> (p \/ ~q)`) in
137 let ptm = concl pth in
138 let p_tm = lhand ptm and q_tm = rand(rand ptm) in
139 fun th -> let ip,q = dest_comb(concl th) in
141 PROVE_HYP th (INST [p,p_tm; q,q_tm] pth)
142 and transform_iff_2 =
143 let pth = UNDISCH(TAUT `(p <=> q) ==> (~p \/ q)`) in
144 let ptm = concl pth in
145 let p_tm = rand(lhand ptm) and q_tm = rand ptm in
146 fun th -> let ip,q = dest_comb(concl th) in
148 PROVE_HYP th (INST [p,p_tm; q,q_tm] pth) in
149 let definitionalize th (n,tops,defs,lfn) =
152 let n',v,defs',lfn' = transvar (n,t,defs,lfn) in
153 (n',(v,th)::tops,defs',lfn')
154 else if is_neg t then
155 let n1,v1,defs1,lfn1 = localdefs (rand t) (n,defs,lfn) in
156 (n1,(mk_neg v1,th)::tops,defs1,lfn1)
157 else if is_disj t then
158 let n1,v1,defs1,lfn1 = localdefs (lhand t) (n,defs,lfn) in
159 let n2,v2,defs2,lfn2 = localdefs (rand t) (n1,defs1,lfn1) in
160 (n2,(mk_disj(v1,v2),th)::tops,defs2,lfn2)
161 else if is_imp t then
162 let n1,v1,defs1,lfn1 = localdefs (lhand t) (n,defs,lfn) in
163 let n2,v2,defs2,lfn2 = localdefs (rand t) (n1,defs1,lfn1) in
164 (n2,(mk_disj(v1,v2),transform_imp th)::tops,defs2,lfn2)
165 else if is_iff t then
166 let n1,v1,defs1,lfn1 = localdefs (lhand t) (n,defs,lfn) in
167 let n2,v2,defs2,lfn2 = localdefs (rand t) (n1,defs1,lfn1) in
168 (n2,(mk_disj(v1,mk_neg v2),transform_iff_1 th)::
169 (mk_disj(mk_neg v1,v2),transform_iff_2 th)::tops,defs2,lfn2)
171 let n',v,defs',lfn' = localdefs t (n,defs,lfn) in
172 (n',(v,th)::tops,defs',lfn') in
175 (* SAT_PROVE is the main interface function.
176 Takes in a term t and returns thm or exception if not a taut *)
177 (* invokes minisatp, returns |- t or |- model ==> ~t *)
179 (* if minisatp proof log does not exist after minisatp call returns,
180 we will assume that minisatp discovered UNSAT during the read-in
181 phase and did not bother with a proof log.
182 In that case the problem is simple and can be delegated to TAUT *)
184 (* FIXME: I do not like the TAUT solution;
185 what is trivial for Minisat may not be so for TAUT *)
187 exception Sat_counterexample of thm;;
189 (* delete temporary files *)
190 (* if zChaff was used, also delete hard-wired trace filenames*)
191 let CLEANUP fname solvername =
192 let delete fname = try Sys.remove fname with Sys_error _ -> () in
194 delete (fname^".cnf");
195 delete (fname^"."^solvername);
196 delete (fname^"."^solvername^".proof");
197 delete (fname^"."^solvername^".stats");
198 if solvername="zchaff" then
199 (delete(Filename.concat (!temp_path) "resolve_trace");
200 delete(Filename.concat (!temp_path) "zc2mso_trace"))
203 let GEN_SAT_PROVE solver solvername =
205 and presimp_conv = GEN_REWRITE_CONV DEPTH_CONV
206 [NOT_CLAUSES; AND_CLAUSES; OR_CLAUSES; IMP_CLAUSES; EQ_CLAUSES]
207 and p_tm = `p:bool` and q_tm = `q:bool`
208 and pth_triv = TAUT `(~p <=> F) <=> p`
209 and pth_main = UNDISCH_ALL(TAUT `(~p <=> q) ==> (q ==> F) ==> p`) in
210 let triv_rule p th = EQ_MP(INST [p,p_tm] pth_triv) th
211 and main_rule p q sth th =
212 itlist PROVE_HYP [sth; DISCH_ALL th] (INST [p,p_tm; q,q_tm] pth_main) in
213 let invoke_minisat lfn mcth stm t rcv vc =
214 let nr = Array.length rcv in
215 let res = match invokeSat solver None t (Some vc) with
218 mapfilter (fun l -> let x = hd(frees l) in
219 let y = apply lfn x in
220 if is_var y then vsubst [y,x] l else fail())
224 (match parseMinisatProof nr ((!tmp_name)^"."^solvername^".proof") vc rcv with
225 Some (cl,sk,scl,srl,cc) ->
226 unsatProveResolve lfn mcth (cl,sk,srl) (* returns p |- F *)
227 | None -> UNDISCH(TAUT(mk_imp(stm,false_tm)))) in
230 let sth = presimp_conv (mk_neg tm) in
231 let stm = rand(concl sth) in
232 if stm = false_tm then triv_rule tm sth else
233 let th = ASSUME stm in
234 let ths = if !exploit_conjunctive_structure then GCONJUNCTS th
236 let n,tops,defs,lfn =
237 itlist definitionalize ths (-1,[],undefined,undefined) in
238 let defg = foldl (fun a t nv -> (t,nv)::a) [] defs in
239 let mdefs = filter (fun (r,_) -> not (is_var r)) defg in
240 let eqs = map (fun (r,l) -> mk_iff(l,r)) mdefs in
241 let clausify eq cls =
242 let fvs = frees eq and eth = (NNFC_CONV THENC CNF_CONV) eq in
243 let tth = INST (map (fun v -> apply lfn v,v) fvs) eth in
244 let xth = ADD_ASSUM stm (EQ_MP tth (REFL(apply lfn (lhand eq)))) in
245 zip (conjuncts(rand(concl eth))) (CONJUNCTS xth) @ cls in
246 let all_clauses = itlist clausify eqs tops in
247 let mcth = itlist (fun (c,th) m -> Termmap.add c th m) all_clauses
250 let rcv = Array.of_list (map fst all_clauses) in
251 let ntdcnf = list_mk_conj (map fst all_clauses) in
252 let th = invoke_minisat lfn mcth stm ntdcnf rcv vc in
253 (if not (!sat_debugging) then CLEANUP (!tmp_name) solvername else ();
255 then raise (Sat_counterexample
256 (EQ_MP (AP_TERM (rator(concl th)) (SYM sth)) th))
257 else main_rule tm stm sth th);;
259 let SAT_PROVE = GEN_SAT_PROVE minisatp "minisatp";;
261 let ZSAT_PROVE = GEN_SAT_PROVE zchaff "zchaff";;