sidekick/solver/res.ml

298 lines
8.2 KiB
OCaml
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(*
MSAT is free software, using the Apache license, see file LICENSE
Copyright 2014 Guillaume Bury
Copyright 2014 Simon Cruanes
*)
module type S = Res_intf.S
module Make(L : Log_intf.S)(St : Solver_types.S) = struct
(* Type definitions *)
type lemma = St.proof
type clause = St.clause
type atom = St.atom
type int_cl = clause * St.atom list
type node =
| Assumption
| Lemma of lemma
| Resolution of atom * int_cl * int_cl
(* lits, c1, c2 with lits the literals used to resolve c1 and c2 *)
exception Insuficient_hyps
exception Resolution_error of string
(* Proof graph *)
let hash_cl cl =
Hashtbl.hash (List.map (fun a -> St.(a.aid)) cl)
let equal_cl cl_c cl_d =
try
List.for_all2 (==) cl_c cl_d
with Invalid_argument _ ->
false
module H = Hashtbl.Make(struct
type t = St.atom list
let hash = hash_cl
let equal = equal_cl
end)
let proof : node H.t = H.create 1007;;
(* Misc functions *)
let equal_atoms a b = St.(a.aid) = St.(b.aid)
let compare_atoms a b = Pervasives.compare St.(a.aid) St.(b.aid)
let merge = List.merge compare_atoms
let _c = ref 0
let fresh_pcl_name () = incr _c; "P" ^ (string_of_int !_c)
(* Printing functions *)
let rec print_cl fmt = function
| [] -> Format.fprintf fmt "[]"
| [a] -> St.print_atom fmt a
| a :: ((_ :: _) as r) -> Format.fprintf fmt "%a %a" St.print_atom a print_cl r
(* Compute resolution of 2 clauses *)
let resolve l =
let rec aux resolved acc = function
| [] -> resolved, acc
| [a] -> resolved, a :: acc
| a :: b :: r ->
if equal_atoms a b then
aux resolved (a :: acc) r
else if equal_atoms St.(a.neg) b then
aux (St.(a.var.pa) :: resolved) acc r
else
aux resolved (a :: acc) (b :: r)
in
let resolved, new_clause = aux [] [] l in
resolved, List.rev new_clause
(* List.sort_uniq is only since 4.02.0 *)
let sort_uniq compare l =
let rec aux = function
| x :: ((y :: _) as r) -> if compare x y = 0 then aux r else x :: aux r
| l -> l
in
aux (List.sort compare l)
let to_list c =
let v = St.(c.atoms) in
let l = ref [] in
for i = 0 to Vec.size v - 1 do
l := (Vec.get v i) :: !l
done;
let res = sort_uniq compare_atoms !l in
let l, _ = resolve res in
if l <> [] then
L.debug 3 "Input clause is a tautology";
res
let print_clause fmt c = print_cl fmt (to_list c)
(* Adding hyptoheses *)
let has_been_proved c = H.mem proof (to_list c)
let is_proved (c, cl) =
if H.mem proof cl then
true
else if not St.(c.learnt) then begin
H.add proof cl Assumption;
true
end else match St.(c.cpremise) with
| St.Lemma p -> H.add proof cl (Lemma p); true
| St.History _ -> false
let is_proven c = is_proved (c, to_list c)
let add_res (c, cl_c) (d, cl_d) =
L.debug 7 " Resolving clauses :";
L.debug 7 " %a" St.pp_clause c;
L.debug 7 " %a" St.pp_clause d;
assert (is_proved (c, cl_c));
assert (is_proved (d, cl_d));
let l = merge cl_c cl_d in
let resolved, new_clause = resolve l in
match resolved with
| [] -> raise (Resolution_error "No literal to resolve over")
| [a] ->
H.add proof new_clause (Resolution (a, (c, cl_c), (d, cl_d)));
let new_c = St.make_clause (fresh_pcl_name ()) new_clause (List.length new_clause) true St.(History [c; d]) in
L.debug 5 "New clause : %a" St.pp_clause new_c;
new_c, new_clause
| _ -> raise (Resolution_error "Resolved to a tautology")
let rec diff_learnt acc l l' =
match l, l' with
| [], _ -> l' @ acc
| a :: r, b :: r' ->
if equal_atoms a b then
diff_learnt acc r r'
else
diff_learnt (b :: acc) l r'
| _ -> raise (Resolution_error "Impossible to derive correct clause")
let clause_unit a = match St.(a.var.level, a.var.reason) with
| 0, St.Bcp Some c -> c, to_list c
| _ ->
raise (Resolution_error "Could not find a reason needed to resolve")
let need_clause (c, cl) =
if is_proved (c, cl) then
[]
else
match St.(c.cpremise) with
| St.History l -> l
| St.Lemma _ -> assert false
let rec add_clause c cl l = (* We assume that all clauses in l are already proved ! *)
match l with
| a :: r ->
L.debug 5 "Resolving (with history) %a" St.pp_clause c;
let temp_c, temp_cl = List.fold_left add_res a r in
L.debug 10 " Switching to unit resolutions";
let new_c, new_cl = (ref temp_c, ref temp_cl) in
while not (equal_cl cl !new_cl) do
let unit_to_use = diff_learnt [] cl !new_cl in
let unit_r = List.map (fun a -> clause_unit a) unit_to_use in
do_clause (List.map fst unit_r);
let temp_c, temp_cl = List.fold_left add_res (!new_c, !new_cl) unit_r in
new_c := temp_c;
new_cl := temp_cl;
done
| _ -> assert false
and do_clause = function
| [] -> ()
| c :: r ->
let cl = to_list c in
match need_clause (c, cl) with
| [] -> do_clause r
| history ->
let history_cl = List.rev_map (fun c -> c, to_list c) history in
let to_prove = List.filter (fun (c, cl) -> not (is_proved (c, cl))) history_cl in
let to_prove = (List.rev_map fst to_prove) in
begin match to_prove with
| [] ->
add_clause c cl history_cl;
do_clause r
| _ -> do_clause (to_prove @ (c :: r))
end
let prove c =
L.debug 3 "Proving : %a" St.pp_clause c;
do_clause [c];
L.debug 3 "Proved : %a" St.pp_clause c
let rec prove_unsat_cl (c, cl) = match cl with
| [] -> true
| a :: r ->
L.debug 2 "Eliminating %a in %a" St.pp_atom a St.pp_clause c;
let d = match St.(a.var.level, a.var.reason) with
| 0, St.Bcp Some d -> d
| _ -> raise Exit
in
prove d;
let cl_d = to_list d in
prove_unsat_cl (add_res (c, cl) (d, cl_d))
let prove_unsat_cl c =
try
prove_unsat_cl c
with Exit ->
false
let learn v =
Vec.iter (fun c -> L.debug 15 "history : %a" St.pp_clause c) v;
Vec.iter prove v
let assert_can_prove_unsat c =
L.debug 1 "=================== Proof =====================";
prove c;
if not (prove_unsat_cl (c, to_list c)) then
raise Insuficient_hyps
(* Interface exposed *)
type proof_node = {
conclusion : clause;
step : step;
}
and proof = clause * atom list
and step =
| Hypothesis
| Lemma of lemma
| Resolution of proof * proof * atom
let expand (c, cl) =
let st = match H.find proof cl with
| Assumption -> Hypothesis
| Lemma l -> Lemma l
| Resolution (a, cl_c, cl_d) ->
Resolution (cl_c, cl_d, a)
in
{ conclusion = c; step = st }
let prove_unsat c =
assert_can_prove_unsat c;
(St.empty_clause, [])
(* Compute unsat-core *)
let compare_cl c d =
let rec aux = function
| [], [] -> 0
| a :: r, a' :: r' -> begin match compare_atoms a a' with
| 0 -> aux (r, r')
| x -> x
end
| _ :: _ , [] -> -1
| [], _ :: _ -> 1
in
aux (to_list c, to_list d)
let unsat_core proof =
let rec aux acc proof =
let p = expand proof in
match p.step with
| Hypothesis | Lemma _ -> p.conclusion :: acc
| Resolution (proof1, proof2, _) ->
aux (aux acc proof1) proof2
in
sort_uniq compare_cl (aux [] proof)
(* Iter on proofs *)
type task =
| Enter of proof
| Leaving of proof
let pop s = try Some (Stack.pop s) with Stack.Empty -> None
let rec fold_aux s h f acc =
match pop s with
| None -> acc
| Some (Leaving ((_, cl) as p)) ->
H.add h cl true;
fold_aux s h f (f acc (expand p))
| Some (Enter ((_, cl) as p)) ->
if not (H.mem h cl) then begin
Stack.push (Leaving p) s;
let node = expand p in
begin match node.step with
| Resolution (p1, p2, _) ->
Stack.push (Enter p2) s;
Stack.push (Enter p1) s
| _ -> ()
end
end;
fold_aux s h f acc
let fold f acc p =
let h = H.create 42 in
let s = Stack.create () in
Stack.push (Enter p) s;
fold_aux s h f acc
end