fix(sat): allow proofs with unary resolution history

can happen if the conflict clause is a theory lemma
This commit is contained in:
Simon Cruanes 2018-08-18 19:54:46 -05:00
parent 400f2e02f1
commit 324c9d2e36

View file

@ -378,6 +378,8 @@ module Make (Th : Theory_intf.S) = struct
let debug_a out vec = let debug_a out vec =
Array.iteri (fun i a -> if i>0 then Format.fprintf out "@ "; debug out a) vec Array.iteri (fun i a -> if i>0 then Format.fprintf out "@ "; debug out a) vec
module Set = CCSet.Make(struct type t = atom let compare = compare end)
end end
module Clause = struct module Clause = struct
@ -485,14 +487,14 @@ module Make (Th : Theory_intf.S) = struct
let fresh_pcl_name () = incr _c; "R" ^ (string_of_int !_c) let fresh_pcl_name () = incr _c; "R" ^ (string_of_int !_c)
(* Compute resolution of 2 clauses *) (* Compute resolution of 2 clauses *)
let resolve l = let resolve (l:atom list) : atom list * atom list =
let rec aux resolved acc = function let rec aux resolved acc = function
| [] -> resolved, acc | [] -> resolved, acc
| [a] -> resolved, a :: acc | [a] -> resolved, a :: acc
| a :: b :: r -> | a :: b :: r ->
if equal_atoms a b then if equal_atoms a b then
aux resolved (a :: acc) r aux resolved (a :: acc) r
else if equal_atoms (a.neg) b then else if equal_atoms a.neg b then
aux ((a.var.v_pa) :: resolved) acc r aux ((a.var.v_pa) :: resolved) acc r
else else
aux resolved (a :: acc) (b :: r) aux resolved (a :: acc) (b :: r)
@ -500,20 +502,20 @@ module Make (Th : Theory_intf.S) = struct
let resolved, new_clause = aux [] [] l in let resolved, new_clause = aux [] [] l in
resolved, List.rev new_clause resolved, List.rev new_clause
(* Compute the set of doublons of a clause *) let to_list c = List.sort Atom.compare @@ Array.to_list c.atoms
let list c = List.sort Atom.compare (Array.to_list (c.atoms))
let analyze cl = (* TODO: use sets instead of lists, simplify this *)
let analyze (cl:atom list) =
let rec aux duplicates free = function let rec aux duplicates free = function
| [] -> duplicates, free | [] -> duplicates, free
| [ x ] -> duplicates, x :: free | [ x ] -> duplicates, x :: free
| x :: ((y :: r) as l) -> | x :: ((y :: r) as l) ->
if x == y then if Atom.equal x y then
count duplicates (x :: free) x [y] r count duplicates (x :: free) x [y] r
else else
aux duplicates (x :: free) l aux duplicates (x :: free) l
and count duplicates free x acc = function and count duplicates free x acc = function
| (y :: r) when x == y -> | (y :: r) when Atom.equal x y ->
count duplicates free x (y :: acc) r count duplicates free x (y :: acc) r
| l -> | l ->
aux (acc :: duplicates) free l aux (acc :: duplicates) free l
@ -521,29 +523,10 @@ module Make (Th : Theory_intf.S) = struct
let doublons, acc = aux [] [] cl in let doublons, acc = aux [] [] cl in
doublons, List.rev acc doublons, List.rev acc
let to_list c =
let cl = list c in
let doublons, l = analyze cl in
let conflicts, _ = resolve l in
if doublons <> [] then
Log.debug 5 "Input clause has redundancies";
if conflicts <> [] then
Log.debug 5 "Input clause is a tautology";
cl
(* Comparison of clauses *) (* Comparison of clauses *)
let cmp_cl c d = let cmp_cl c d =
let rec aux = function Atom.Set.compare (Atom.Set.of_list c) (Atom.Set.of_list d)
| [], [] -> 0
| a :: r, a' :: r' ->
begin match compare_atoms a a' with
| 0 -> aux (r, r')
| x -> x
end
| _ :: _ , [] -> -1
| [], _ :: _ -> 1
in
aux (c, d)
let[@inline] prove conclusion = let[@inline] prove conclusion =
assert (conclusion.c_premise <> History []); assert (conclusion.c_premise <> History []);
@ -595,23 +578,25 @@ module Make (Th : Theory_intf.S) = struct
let rec chain_res (c, cl) = function let rec chain_res (c, cl) = function
| d :: r -> | d :: r ->
Log.debugf 5 Log.debugf 5
(fun k -> k " Resolving clauses : @[%a@\n%a@]" Clause.debug c Clause.debug d); (fun k -> k "(@[sat.proof.resolving@ :c1 %a@ :c2 %a@])" Clause.debug c Clause.debug d);
let dl = to_list d in let dl = to_list d in
begin match resolve (merge cl dl) with begin match resolve (merge cl dl) with
| [ a ], l -> | [a], res ->
begin match r with begin match r with
| [] -> (l, c, d, a) | [] -> res, c, d, a
| _ -> | _ ->
let new_clause = Clause.make_l l (History [c; d]) in let new_clause = Clause.make_l res (History [c; d]) in
chain_res (new_clause, l) r chain_res (new_clause, res) r
end end
| _ -> | pivots, _ ->
Log.debugf 1 Log.debugf 1
(fun k -> k "While resolving clauses:@[<hov>%a@\n%a@]" (fun k -> k "(@[err While resolving clauses:@ %a@ %a@ :pivots %a@])"
Clause.debug c Clause.debug d); Clause.debug c Clause.debug d (CCFormat.Dump.list Atom.debug) pivots);
raise (Resolution_error "Clause mismatch") raise (Resolution_error "Clause mismatch")
end end
| _ -> | [] ->
Log.debugf 1
(fun k -> k "*@[err While resolving clause:@ %a@])" Clause.debug c);
raise (Resolution_error "Bad history") raise (Resolution_error "Bad history")
let[@inline] conclusion (p:proof) : clause = p let[@inline] conclusion (p:proof) : clause = p
@ -626,9 +611,10 @@ module Make (Th : Theory_intf.S) = struct
| History [] -> | History [] ->
Log.debugf 1 (fun k -> k "(@[proof.empty_history@ %a@]" Clause.debug conclusion); Log.debugf 1 (fun k -> k "(@[proof.empty_history@ %a@]" Clause.debug conclusion);
raise (Resolution_error "Empty history") raise (Resolution_error "Empty history")
| Simplified c -> | Simplified c
let duplicates, res = analyze (list c) in | History ([c]) ->
assert (cmp_cl res (list conclusion) = 0); let duplicates, res = analyze (to_list c) in
assert (cmp_cl res (to_list conclusion) = 0);
{ conclusion; step = Duplicate (c, List.concat duplicates) } { conclusion; step = Duplicate (c, List.concat duplicates) }
| History ( c :: ([_] as r)) -> | History ( c :: ([_] as r)) ->
let (l, c', d', a) = chain_res (c, to_list c) r in let (l, c', d', a) = chain_res (c, to_list c) r in