structure minisatProve :> minisatProve = struct local open Lib boolLib Globals Parse Term Type Thm Drule Conv Feedback FileSys open dimacsTools satTools SatSolvers satCommonTools minisatParse satConfig dpll def_cnf in exception SAT_cex of thm val mk_sat_oracle_thm = mk_oracle_thm "HolSatLib"; val sat_warn = ref true (* control interactive warnings *) val sat_limit = ref 100 (* if > sat_limit clauses then interactive warning if using SML prover *) val _ = register_btrace ("HolSatLib_warn",sat_warn); fun warn ss = if !Globals.interactive andalso !sat_warn then print ("\nHolSat WARNING: "^ss^ "\nTo turn off this warning, type: \ \set_trace \"HolSatLib_warn\" 0; RET\n\n") else () fun replay_proof is_proved sva nr in_name solver vc clauseth lfn ntm proof = if is_proved then ((case getSolverPostExe solver of (* post-process proof, if required *) SOME post_exe => let val (fin,fout) = (in_name,in_name^"."^(getSolverName solver)) in ignore(Systeml.system_ps (getSolverPostRun solver post_exe (fin,fout))) end | NONE => ()); (case replayProof sva nr in_name solver vc clauseth lfn proof of SOME th => th | NONE => (warn "Proof replay failed. Using internal prover."; DPLL_TAUT (dest_neg ntm)))) (*triv prob/unknown err*) else mk_sat_oracle_thm([ntm],F) local fun transform_model cnfv lfn model = let val model2 = if isSome cnfv then mapfilter (fn l => let val x = hd(free_vars l) val y = rbapply lfn x in if is_var y then subst [x|->y] l else failwith"" end) model else model in model2 end in fun invoke_solver solver lfn ntm clauseth cnfv vc is_proved svm sva in_name = let val nr = Array.length clauseth in if access(getSolverExe solver,[A_EXEC]) then let val answer = invokeSat solver T (SOME vc) (SOME nr) svm sva in_name in case answer of SOME model => (* returns |- model ==> ~t *) let val model' = transform_model cnfv lfn model in if is_proved then satCheck model' ntm else mk_sat_oracle_thm([],mk_imp(list_mk_conj model',ntm)) end | NONE => (* returns ~t |- F *) replay_proof is_proved sva nr in_name solver vc clauseth lfn ntm NONE end else (* do not have execute access to solver binary, or it doesn't exist*) (if nr > !sat_limit then warn "SAT solver not found. Using slow internal prover." else (); DPLL_TAUT (dest_neg ntm)) end end (* cleanup temp files. this won't work fully if we cannot delete files generated by the SAT solver *) fun clean_delete s = OS.FileSys.remove s handle Interrupt => raise Interrupt | e => () (* then raise exception if countermodel was found, else deduce input term from solver's result *) fun finalise solver infile is_cnf th tmpname in_name = let val solver_name = getSolverName solver val _ = if isSome infile then () else app clean_delete [ "resolve_trace", (* in case using ZChaff *) in_name, (* CNF *) tmpname,(* tmpfile(created by Poly/ML, but not MoscowML) *) in_name^"."^solver_name, (* result/status *) in_name^"."^solver_name^".proof", (* proof *) in_name^"."^solver_name^".stats" ] val res = if is_imp(concl th) then raise (SAT_cex th) else if is_cnf then (NOT_INTRO(DISCH_ALL th)) else CONV_RULE NOT_NOT_CONV (NOT_INTRO(DISCH_ALL th)) in res end (* convert input to term to CNF and write out DIMACS file *) (* if infile is SOME name, then assume name.cnf exists and is_cnf=true *) (* if is_cnf, then assume tm \equiv ~s where s is in CNF *) (* if CNF conversion found true or false, return theorem directly *) exception initexp of thm; fun initialise infile is_cnf tm = let val (cnfv,vc,svm,sva,tmpname,in_name,ntm,lfn,clauseth) = if isSome infile then let val fname = valOf infile val (tm,svm,sva) = genReadDimacs fname val (cnfv,vc,lfn,clauseth) = to_cnf is_cnf (mk_neg tm) in (cnfv,vc,svm,sva,"",fname,mk_neg tm,lfn,clauseth) end else let val (cnfv,vc,lfn,clauseth) = to_cnf is_cnf (mk_neg tm) val (tmpname,cnfname,sva,svm) = generateDimacs (SOME vc) tm (SOME clauseth) (SOME (Array.length clauseth)) in (cnfv,vc,svm,sva,tmpname,cnfname,mk_neg tm,lfn,clauseth) end in if vc=0 then (* no vars: all 'presimp'-ified conjuncts were either T or F *) let val th0 = presimp_conv (dest_neg ntm) in if is_F (rhs (concl th0)) then raise SAT_cex (EQF_ELIM th0) else raise initexp (EQT_ELIM th0) end else (cnfv,vc,svm,sva,tmpname,in_name,ntm,lfn,clauseth) end val dbg_show_input = ref false; fun GEN_SAT conf = (* single entry point into HolSatLib *) let val (tm,solver,infile,proof,is_cnf,is_proved) = dest_config conf val _ = if !dbg_show_input then (print "\n"; print_term tm; print "\n") else () val (cnfv,vc,svm,sva,tmpname,in_name,ntm,lfn,clauseth) = initialise infile is_cnf tm val th = if isSome proof then replay_proof is_proved sva (Array.length clauseth) in_name solver vc clauseth lfn ntm proof else invoke_solver solver lfn ntm clauseth cnfv vc is_proved svm sva in_name val res = finalise solver infile is_cnf th tmpname in_name in res end handle initexp th => th (* default config invokes pre-installed MiniSat 1.14p *) fun SAT_PROVE tm = GEN_SAT (set_term tm base_config) fun SAT_ORACLE tm = GEN_SAT ((set_term tm o set_flag_is_proved false) base_config) (* same functionality for ZChaff, if installed by user *) val zchaff_config = set_solver zchaff base_config fun ZSAT_PROVE tm = GEN_SAT (set_term tm zchaff_config) fun ZSAT_ORACLE tm = GEN_SAT ((set_term tm o set_flag_is_proved false) zchaff_config) end end