mirror of
https://github.com/c-cube/sidekick.git
synced 2025-12-06 11:15:43 -05:00
wip: refactor SAT solver
This commit is contained in:
parent
3968688a35
commit
5860612cd9
3 changed files with 97 additions and 221 deletions
|
|
@ -14,6 +14,7 @@ let () = Var_fields.freeze()
|
|||
module C_fields = Solver_intf.C_fields
|
||||
|
||||
let c_field_attached = C_fields.mk_field () (* watching literals? *)
|
||||
let c_field_dead = C_fields.mk_field () (* been removed once? *)
|
||||
let c_field_visited = C_fields.mk_field () (* used during propagation and proof generation. *)
|
||||
|
||||
module Make (Th : Theory_intf.S) = struct
|
||||
|
|
@ -44,7 +45,7 @@ module Make (Th : Theory_intf.S) = struct
|
|||
name : int;
|
||||
tag : int option;
|
||||
atoms : atom array;
|
||||
mutable cpremise : premise;
|
||||
mutable c_premise : premise;
|
||||
mutable activity : float;
|
||||
mutable c_flags : C_fields.t
|
||||
}
|
||||
|
|
@ -87,7 +88,7 @@ module Make (Th : Theory_intf.S) = struct
|
|||
atoms = [| |];
|
||||
activity = -1.;
|
||||
c_flags = C_fields.empty;
|
||||
cpremise = History [];
|
||||
c_premise = History [];
|
||||
}
|
||||
|
||||
let () = dummy_atom.watched <- Vec.make_empty dummy_clause
|
||||
|
|
@ -95,7 +96,7 @@ module Make (Th : Theory_intf.S) = struct
|
|||
(* Constructors *)
|
||||
module MF = Hashtbl.Make(Th.Form)
|
||||
|
||||
let name_of_clause c = match c.cpremise with
|
||||
let name_of_clause c = match c.c_premise with
|
||||
| Hyp -> "H" ^ string_of_int c.name
|
||||
| Local -> "L" ^ string_of_int c.name
|
||||
| Lemma _ -> "T" ^ string_of_int c.name
|
||||
|
|
@ -386,7 +387,7 @@ module Make (Th : Theory_intf.S) = struct
|
|||
Format.fprintf fmt ""
|
||||
|
||||
let debug out a =
|
||||
Format.fprintf out "%s%d[%a][@[%a@]]"
|
||||
Format.fprintf out "@[%s%d[%a][@[%a@]]@]"
|
||||
(sign a) (a.var.vid+1) debug_value a Th.Form.print a.lit
|
||||
|
||||
let debug_a out vec =
|
||||
|
|
@ -407,11 +408,13 @@ module Make (Th : Theory_intf.S) = struct
|
|||
atoms = atoms;
|
||||
c_flags = C_fields.empty;
|
||||
activity = 0.;
|
||||
cpremise = premise;
|
||||
c_premise = premise;
|
||||
}
|
||||
|
||||
let make_l ?tag l pr = make ?tag (Array.of_list l) pr
|
||||
|
||||
let[@inline] copy (c:t) : t = make ?tag:c.tag c.atoms c.c_premise
|
||||
|
||||
let empty = make [| |] (History [])
|
||||
let name = name_of_clause
|
||||
let[@inline] equal c1 c2 = c1==c2
|
||||
|
|
@ -420,8 +423,13 @@ module Make (Th : Theory_intf.S) = struct
|
|||
let[@inline] tag c = c.tag
|
||||
let hash cl = Array.fold_left (fun i a -> Hashtbl.hash (a.aid, i)) 0 cl.atoms
|
||||
|
||||
let[@inline] premise c = c.cpremise
|
||||
let[@inline] set_premise c p = c.cpremise <- p
|
||||
let[@inline] premise c = c.c_premise
|
||||
let[@inline] set_premise c p = c.c_premise <- p
|
||||
|
||||
(* a dead clause is a clause that has been removed. It should never
|
||||
be added again *)
|
||||
let[@inline] dead c = C_fields.get c_field_dead c.c_flags
|
||||
let[@inline] mark_dead c = c.c_flags <- C_fields.set c_field_dead true c.c_flags
|
||||
|
||||
let[@inline] visited c = C_fields.get c_field_visited c.c_flags
|
||||
let[@inline] set_visited c b = c.c_flags <- C_fields.set c_field_visited b c.c_flags
|
||||
|
|
@ -438,17 +446,18 @@ module Make (Th : Theory_intf.S) = struct
|
|||
let equal = equal
|
||||
end)
|
||||
|
||||
let pp fmt c =
|
||||
Format.fprintf fmt "%s : %a" (name c) Atom.pp_a c.atoms
|
||||
let pp fmt c = Format.fprintf fmt "%s : %a" (name c) Atom.pp_a c.atoms
|
||||
|
||||
let debug_premise out = function
|
||||
| Hyp -> Format.fprintf out "hyp"
|
||||
| Local -> Format.fprintf out "local"
|
||||
| Lemma _ -> Format.fprintf out "th_lemma"
|
||||
| History v -> Util.pp_list (CCFormat.of_to_string name_of_clause) out v
|
||||
| History v ->
|
||||
Format.fprintf out "[@[%a@]]"
|
||||
(Util.pp_list @@ CCFormat.of_to_string name_of_clause) v
|
||||
|
||||
let debug out ({atoms=arr; cpremise=cp;_}as c) =
|
||||
Format.fprintf out "%s@[<hov>{@[<hov>%a@]}@ cpremise={@[<hov>%a@]}@]"
|
||||
let debug out ({atoms=arr; c_premise=cp;_}as c) =
|
||||
Format.fprintf out "%s@[{@[%a@]}@ c_premise={@[%a@]}@]"
|
||||
(name c) Atom.debug_a arr debug_premise cp
|
||||
|
||||
let pp_dimacs fmt {atoms;_} =
|
||||
|
|
@ -544,7 +553,7 @@ module Make (Th : Theory_intf.S) = struct
|
|||
aux (c, d)
|
||||
|
||||
let[@inline] prove conclusion =
|
||||
assert (conclusion.cpremise <> History []);
|
||||
assert (conclusion.c_premise <> History []);
|
||||
conclusion
|
||||
|
||||
let rec set_atom_proof a =
|
||||
|
|
@ -626,7 +635,7 @@ module Make (Th : Theory_intf.S) = struct
|
|||
|
||||
let expand conclusion =
|
||||
Log.debugf 5 (fun k -> k "Expanding : @[%a@]" Clause.debug conclusion);
|
||||
match conclusion.cpremise with
|
||||
match conclusion.c_premise with
|
||||
| Lemma l ->
|
||||
{conclusion; step = Lemma l; }
|
||||
| Hyp ->
|
||||
|
|
@ -646,7 +655,7 @@ module Make (Th : Theory_intf.S) = struct
|
|||
{ conclusion; step = Resolution (c', d', a); }
|
||||
| History ( c :: r ) ->
|
||||
let (l, c', d', a) = chain_res (c, to_list c) r in
|
||||
conclusion.cpremise <- History [c'; d'];
|
||||
conclusion.c_premise <- History [c'; d'];
|
||||
assert (cmp_cl l (to_list conclusion) = 0);
|
||||
{ conclusion; step = Resolution (c', d', a); }
|
||||
|
||||
|
|
@ -683,7 +692,7 @@ module Make (Th : Theory_intf.S) = struct
|
|||
| c :: r ->
|
||||
if not (Clause.visited c) then (
|
||||
Clause.set_visited c true;
|
||||
match c.cpremise with
|
||||
match c.c_premise with
|
||||
| Hyp | Local | Lemma _ -> aux (c :: res) acc r
|
||||
| History h ->
|
||||
let l = List.fold_left (fun acc c ->
|
||||
|
|
@ -764,10 +773,6 @@ module Make (Th : Theory_intf.S) = struct
|
|||
);
|
||||
f()
|
||||
|
||||
(* Misc functions *)
|
||||
let to_float = float_of_int
|
||||
let to_int = int_of_float
|
||||
|
||||
(* Are the assumptions currently unsat ? *)
|
||||
let[@inline] is_unsat st =
|
||||
match st.unsat_conflict with
|
||||
|
|
@ -786,15 +791,8 @@ module Make (Th : Theory_intf.S) = struct
|
|||
H.insert st.order v;
|
||||
)
|
||||
|
||||
let new_atom ~permanent st (p:formula) : atom =
|
||||
let a = Atom.make st p in
|
||||
if permanent then (
|
||||
redo_down_to_level_0 st
|
||||
(fun () -> insert_var_order st a.var)
|
||||
) else (
|
||||
insert_var_order st a.var
|
||||
);
|
||||
a
|
||||
(* attach an atom by deciding on its variable, if needed *)
|
||||
let[@inline] attach_atom (st:t) (a:atom) : unit = insert_var_order st a.var
|
||||
|
||||
(* Rather than iterate over all the heap when we want to decrease all the
|
||||
variables/literals activity, we instead increase the value by which
|
||||
|
|
@ -873,17 +871,15 @@ module Make (Th : Theory_intf.S) = struct
|
|||
Clause.make_l !res (History [clause])
|
||||
)
|
||||
|
||||
(* Partition literals for new clauses, into:
|
||||
(* Sort literals for new clause, into:
|
||||
- true literals (maybe makes the clause trivial if the lit is proved true at level 0)
|
||||
- unassigned literals, yet to be decided
|
||||
- false literals (not suitable to watch, those at level 0 can be removed from the clause)
|
||||
|
||||
Clauses that propagated false lits are remembered to reconstruct resolution proofs.
|
||||
- false literals (not suitable to watch)
|
||||
*)
|
||||
let partition atoms : atom list * clause list =
|
||||
let rec partition_aux trues unassigned falses history i =
|
||||
let sort_lits_by_level atoms : atom list =
|
||||
let rec partition_aux trues unassigned falses i =
|
||||
if i >= Array.length atoms then (
|
||||
trues @ unassigned @ falses, history
|
||||
trues @ unassigned @ falses
|
||||
) else (
|
||||
let a = atoms.(i) in
|
||||
if a.is_true then (
|
||||
|
|
@ -891,30 +887,16 @@ module Make (Th : Theory_intf.S) = struct
|
|||
if l = 0 then
|
||||
raise Trivial (* A var true at level 0 gives a trivially true clause *)
|
||||
else
|
||||
(a :: trues) @ unassigned @ falses @
|
||||
(arr_to_list atoms (i + 1)), history
|
||||
(a :: trues) @ unassigned @ falses @ (arr_to_list atoms (i + 1))
|
||||
) else if a.neg.is_true then (
|
||||
let l = a.var.v_level in
|
||||
if l = 0 then (
|
||||
match a.var.reason with
|
||||
| Some (Bcp cl) ->
|
||||
partition_aux trues unassigned falses (cl :: history) (i + 1)
|
||||
(* A var false at level 0 can be eliminated from the clause,
|
||||
but we need to kepp in mind that we used another clause to simplify it. *)
|
||||
| None | Some Decision -> assert false
|
||||
(* The var must have a reason, and it cannot be a decision/assumption,
|
||||
since its level is 0. *)
|
||||
) else (
|
||||
partition_aux trues unassigned (a::falses) history (i + 1)
|
||||
)
|
||||
partition_aux trues unassigned (a::falses) (i + 1)
|
||||
) else (
|
||||
partition_aux trues (a::unassigned) falses history (i + 1)
|
||||
partition_aux trues (a::unassigned) falses (i + 1)
|
||||
)
|
||||
)
|
||||
in
|
||||
try partition_aux [] [] [] [] 0
|
||||
with Trivial -> Array.to_list atoms, []
|
||||
|
||||
try partition_aux [] [] [] 0
|
||||
with Trivial -> Array.to_list atoms
|
||||
|
||||
(* Making a decision.
|
||||
Before actually creatig a new decision level, we check that
|
||||
|
|
@ -935,6 +917,7 @@ module Make (Th : Theory_intf.S) = struct
|
|||
either because it is assumed or learnt.
|
||||
*)
|
||||
let attach_clause (self:t) (c:clause) : unit =
|
||||
assert (not @@ Clause.dead c);
|
||||
if not (Clause.attached c) then (
|
||||
Log.debugf 5 (fun k -> k "(@[sat.attach_clause@ %a@])" Clause.debug c);
|
||||
on_backtrack self
|
||||
|
|
@ -944,6 +927,8 @@ module Make (Th : Theory_intf.S) = struct
|
|||
Vec.push c.atoms.(0).neg.watched c;
|
||||
Vec.push c.atoms.(1).neg.watched c;
|
||||
Clause.set_attached c true;
|
||||
(* internalize atoms *)
|
||||
Array.iter (attach_atom self) c.atoms;
|
||||
)
|
||||
|
||||
(* perform all backtracking actions down to level [l].
|
||||
|
|
@ -1017,43 +1002,6 @@ module Make (Th : Theory_intf.S) = struct
|
|||
st.unsat_conflict <- Some confl;
|
||||
raise Unsat
|
||||
|
||||
(* Simplification of boolean propagation reasons.
|
||||
When doing boolean propagation *at level 0*, it can happen
|
||||
that the clause cl, which propagates a formula, also contains
|
||||
other formulas, but has been simplified. in which case, we
|
||||
need to rebuild a clause with correct history, in order to
|
||||
be able to build a correct proof at the end of proof search. *)
|
||||
let simpl_reason : reason -> reason = function
|
||||
| (Bcp cl) as r ->
|
||||
begin match partition cl.atoms with
|
||||
| [_] as l, history ->
|
||||
if history = [] then (
|
||||
(* no simplification has been done, so [cl] is actually a clause with only
|
||||
[a], so it is a valid reason for propagating [a]. *)
|
||||
r
|
||||
) else (
|
||||
(* Clauses in [history] have been used to simplify [cl] into a clause [tmp_cl]
|
||||
with only one formula (which is [a]). So we explicitly create that clause
|
||||
and set it as the cause for the propagation of [a], that way we can
|
||||
rebuild the whole resolution tree when we want to prove [a]. *)
|
||||
let c' = Clause.make_l l (History (cl :: history)) in
|
||||
Log.debugf 5
|
||||
(fun k -> k "(@[sat.simplified-reason@ :old %a@ :new %a@])"
|
||||
Clause.debug cl Clause.debug c');
|
||||
Bcp c'
|
||||
)
|
||||
| l, _history ->
|
||||
Log.debugf 1
|
||||
(fun k ->
|
||||
k "@[<v 2>Failed at reason simplification:@,%a@,%a@]"
|
||||
(Vec.print ~sep:"" Atom.debug)
|
||||
(Vec.from_list l Atom.dummy)
|
||||
Clause.debug cl);
|
||||
assert false
|
||||
| exception Trivial -> r
|
||||
end
|
||||
| r -> r
|
||||
|
||||
(* Boolean propagation.
|
||||
Wrapper function for adding a new propagated formula. *)
|
||||
let enqueue_bool st a reason : unit =
|
||||
|
|
@ -1063,7 +1011,6 @@ module Make (Th : Theory_intf.S) = struct
|
|||
let level = decision_level st in
|
||||
Log.debugf 5
|
||||
(fun k->k "(@[sat.enqueue_bool@ :lvl %d@ %a@])" level Atom.debug a);
|
||||
let reason = if at_level_0 st then simpl_reason reason else reason in
|
||||
(* backtrack assignment if needed. Trail is backtracked automatically. *)
|
||||
assert (not a.is_true && a.var.v_level < 0 && a.var.reason = None);
|
||||
on_backtrack st
|
||||
|
|
@ -1159,7 +1106,7 @@ module Make (Th : Theory_intf.S) = struct
|
|||
while !cond do
|
||||
let clause = !c in
|
||||
Log.debugf 5 (fun k->k"(@[sat.resolving-clause@ %a@])" Clause.debug clause);
|
||||
begin match clause.cpremise with
|
||||
begin match clause.c_premise with
|
||||
| History _ -> clause_bump_activity st clause
|
||||
| Hyp | Local | Lemma _ -> ()
|
||||
end;
|
||||
|
|
@ -1223,6 +1170,8 @@ module Make (Th : Theory_intf.S) = struct
|
|||
let[@inline] analyze st c_clause : conflict_res =
|
||||
analyze_sat st c_clause
|
||||
|
||||
(* FIXME: this shouldn't go through the backtracking stack, but directly
|
||||
into one vector where it can be GC'd later *)
|
||||
(* add the learnt clause to the clause database, propagate, etc. *)
|
||||
let record_learnt_clause st (confl:clause) (cr:conflict_res): unit =
|
||||
begin match cr.cr_learnt with
|
||||
|
|
@ -1240,6 +1189,7 @@ module Make (Th : Theory_intf.S) = struct
|
|||
| fuip :: _ ->
|
||||
let lclause = Clause.make_l cr.cr_learnt (History cr.cr_history) in
|
||||
Vec.push st.clauses_learnt lclause;
|
||||
(* clause will stay attached *)
|
||||
redo_down_to_level_0 st (fun () -> attach_clause st lclause);
|
||||
clause_bump_activity st lclause;
|
||||
if cr.cr_is_uip then (
|
||||
|
|
@ -1271,144 +1221,83 @@ module Make (Th : Theory_intf.S) = struct
|
|||
|
||||
(* Get the correct vector to insert a clause in. *)
|
||||
let rec clause_vector st c =
|
||||
match c.cpremise with
|
||||
match c.c_premise with
|
||||
| Hyp -> st.clauses_hyps
|
||||
| Local -> st.clauses_temp
|
||||
| History [c'] -> clause_vector st c' (* simplified version of [d] *)
|
||||
| Lemma _ | History _ -> st.clauses_learnt
|
||||
|
||||
(* TODO: rewrite this, accounting for backtracking semantics.
|
||||
(* Add clause, accounting for backtracking semantics.
|
||||
- always add literals to queue if not decided yet
|
||||
- if clause is already true, probably just do nothing
|
||||
- if clause is unit, propagate lit immediately (with clause as justification)
|
||||
but do not add clause
|
||||
|
||||
- permanent stuff: just re-add it on backtrack
|
||||
*)
|
||||
|
||||
(* add permanent clause, to be kept down to level 0.
|
||||
precond: non empty clause
|
||||
@param atoms list of atoms of [c]
|
||||
@param c the clause itself *)
|
||||
let add_clause_permanent st (atoms:atom list) (clause:clause) : unit =
|
||||
Log.debugf 5 (fun k->k "(@[sat.add_clause_permanent@ %a@])" Clause.debug clause);
|
||||
let vec_for_clause = clause_vector st clause in
|
||||
match atoms with
|
||||
| [] -> assert false
|
||||
| [a] ->
|
||||
if a.neg.is_true then (
|
||||
(* Since we cannot propagate the atom [a], in order to not lose
|
||||
the information that [a] must be true, we add clause to the list
|
||||
of clauses to add, so that it will be e-examined later. *)
|
||||
Log.debug 5 "(sat.false-unit-clause@ report failure)";
|
||||
report_unsat st clause
|
||||
) else if a.is_true then (
|
||||
(* If the atom is already true, then it should be because of a local hyp.
|
||||
However it means we can't propagate it at level 0. In order to not lose
|
||||
that information, we store the clause in a stack of clauses that we will
|
||||
add to the solver at the next pop. *)
|
||||
Log.debug 5 "(sat.unit-clause@ adding to root clauses)";
|
||||
assert (0 < a.var.v_level && a.var.v_level <= base_level st);
|
||||
on_backtrack st
|
||||
(fun () ->
|
||||
Vec.pop vec_for_clause);
|
||||
Vec.push vec_for_clause clause;
|
||||
) else (
|
||||
Log.debugf 5
|
||||
(fun k->k "(@[sat.add_clause.unit-clause@ :propagating %a@])" Atom.debug a);
|
||||
on_backtrack st (fun () -> Vec.pop vec_for_clause);
|
||||
Vec.push vec_for_clause clause;
|
||||
enqueue_bool st a (Bcp clause)
|
||||
)
|
||||
| a::b::_ ->
|
||||
Vec.push vec_for_clause clause;
|
||||
if a.neg.is_true then (
|
||||
(* Atoms need to be sorted in decreasing order of decision level,
|
||||
or we might watch the wrong literals. *)
|
||||
put_high_level_atoms_first clause.atoms;
|
||||
attach_clause st clause;
|
||||
add_boolean_conflict st clause
|
||||
) else (
|
||||
attach_clause st clause;
|
||||
if b.neg.is_true && not a.is_true && not a.neg.is_true then (
|
||||
let lvl = List.fold_left (fun m a -> max m a.var.v_level) 0 atoms in
|
||||
cancel_until st (max lvl (base_level st));
|
||||
enqueue_bool st a (Bcp clause)
|
||||
)
|
||||
)
|
||||
|
||||
(* Add a new clause, simplifying, propagating, and backtracking if
|
||||
the clause is false in the current trail *)
|
||||
let add_clause ~permanent st (init:clause) : unit =
|
||||
let add_clause_ st (c:clause) : unit =
|
||||
Log.debugf 5
|
||||
(fun k -> k "(@[@{<Yellow>sat.add_clause@}@ :permanent %B@ %a@])"
|
||||
permanent Clause.debug init);
|
||||
let vec_for_clause = clause_vector st init in
|
||||
match eliminate_duplicates init with
|
||||
(fun k -> k "(@[@{<Yellow>sat.add_clause@}@ %a@])" Clause.debug c);
|
||||
assert (not @@ Clause.dead c);
|
||||
let vec_for_clause = clause_vector st c in
|
||||
match eliminate_duplicates c with
|
||||
| exception Trivial ->
|
||||
Vec.push vec_for_clause init;
|
||||
Log.debugf 3 (fun k->k "(@[sat.add_clause.ignore-trivial@ %a@])" Clause.debug init)
|
||||
Log.debugf 3 (fun k->k "(@[sat.add_clause.ignore-trivial@ %a@])" Clause.debug c)
|
||||
| c ->
|
||||
Log.debugf 5 (fun k -> k "(@[sat.add_clause.after_eliminate_dups@ %a@])" Clause.debug c);
|
||||
let atoms, history = partition c.atoms in
|
||||
let clause =
|
||||
if history = []
|
||||
then (
|
||||
(* update order of atoms *)
|
||||
List.iteri (fun i a -> c.atoms.(i) <- a) atoms;
|
||||
c
|
||||
) else (
|
||||
Clause.make_l atoms (History (c :: history))
|
||||
)
|
||||
in
|
||||
Log.debugf 3 (fun k->k "(@[sat.add_clause.new_clause@ %a@])" Clause.debug clause);
|
||||
(* sort atoms by decreasing level of decision (undecided first) *)
|
||||
let atoms = sort_lits_by_level c.atoms in
|
||||
(* update order of atoms *)
|
||||
assert (List.length atoms = Array.length c.atoms);
|
||||
List.iteri (fun i a -> c.atoms.(i) <- a) atoms;
|
||||
Log.debugf 3 (fun k->k "(@[sat.add_clause.new_clause@ %a@])" Clause.debug c);
|
||||
(* kill clause once we backtrack *)
|
||||
on_backtrack st (fun () -> Clause.mark_dead c);
|
||||
match atoms with
|
||||
| [] ->
|
||||
(* report Unsat immediately *)
|
||||
report_unsat st clause
|
||||
| _::_ when permanent ->
|
||||
(* add clause, down to level 0 *)
|
||||
redo_down_to_level_0 st
|
||||
(fun () -> add_clause_permanent st atoms clause)
|
||||
report_unsat st c
|
||||
| [a] ->
|
||||
if a.neg.is_true then (
|
||||
(* Since we cannot propagate the atom [a], in order to not lose
|
||||
the information that [a] must be true, we add clause to the list
|
||||
of clauses to add, so that it will be e-examined later. *)
|
||||
Log.debug 5 "(sat.add_clause: false unit clause, report unsat)";
|
||||
report_unsat st clause
|
||||
report_unsat st c
|
||||
) else if a.is_true then (
|
||||
(* If the atom is already true, then it should be because of a local hyp.
|
||||
However it means we can't propagate it at level 0. In order to not lose
|
||||
that information, we store the clause in a stack of clauses that we will
|
||||
add to the solver at the next pop. *)
|
||||
(* ignore clause, will never be useful *)
|
||||
Log.debug 5 "(sat.add_clause: true unit clause, ignore)";
|
||||
assert (0 < a.var.v_level && a.var.v_level <= base_level st);
|
||||
) else (
|
||||
Log.debugf 5
|
||||
(fun k->k "(@[sat.add_clause.unit_clause@ :propagating %a@])" Atom.debug a);
|
||||
(* propagate but without adding the clause. May need to
|
||||
re-propagate after backtracking *)
|
||||
redo_down_to_level_0 st
|
||||
(fun () -> enqueue_bool st a (Bcp clause))
|
||||
enqueue_bool st a (Bcp c)
|
||||
)
|
||||
| a::b::_ ->
|
||||
| _::_::_ ->
|
||||
on_backtrack st (fun () -> Vec.pop vec_for_clause);
|
||||
Vec.push vec_for_clause clause;
|
||||
Vec.push vec_for_clause c;
|
||||
(* Atoms need to be sorted in decreasing order of decision level,
|
||||
or we might watch the wrong literals. *)
|
||||
put_high_level_atoms_first clause.atoms;
|
||||
put_high_level_atoms_first c.atoms;
|
||||
attach_clause st c;
|
||||
let a = c.atoms.(0) in
|
||||
let b = c.atoms.(1) in
|
||||
if a.neg.is_true then (
|
||||
(* conflict, even the last literal is false *)
|
||||
attach_clause st clause;
|
||||
add_boolean_conflict st clause
|
||||
add_boolean_conflict st c
|
||||
) else (
|
||||
attach_clause st clause;
|
||||
if b.neg.is_true && not a.is_true && not a.neg.is_true then (
|
||||
let lvl = List.fold_left (fun m a -> max m a.var.v_level) 0 atoms in
|
||||
cancel_until st (max lvl (base_level st));
|
||||
enqueue_bool st a (Bcp clause)
|
||||
enqueue_bool st a (Bcp c)
|
||||
)
|
||||
)
|
||||
|
||||
(* Add a new clause, simplifying, propagating, and backtracking if
|
||||
the clause is false in the current trail *)
|
||||
let add_clause ~permanent st (c:clause) : unit =
|
||||
if permanent then (
|
||||
redo_down_to_level_0 st (fun () -> add_clause_ st (Clause.copy c))
|
||||
) else (
|
||||
add_clause_ st c
|
||||
)
|
||||
|
||||
let[@inline] add_clause_user ~permanent st (c:clause): unit =
|
||||
CCVector.push st.to_add (permanent,c)
|
||||
|
||||
|
|
@ -1491,25 +1380,24 @@ module Make (Th : Theory_intf.S) = struct
|
|||
done
|
||||
|
||||
let act_push_ ~permanent st (l:formula IArray.t) (lemma:lemma): unit =
|
||||
let atoms = IArray.to_array_map (new_atom ~permanent st) l in
|
||||
let atoms = IArray.to_array_map (Atom.make st) l in
|
||||
let c = Clause.make atoms (Lemma lemma) in
|
||||
Log.debugf 3
|
||||
(fun k->k "(@[sat.push_clause_from_theory@ :permanent %B@ %a@])"
|
||||
permanent Clause.debug c);
|
||||
add_clause ~permanent st c
|
||||
|
||||
(* TODO: ensure that the clause is removed upon backtracking *)
|
||||
let act_push_local = act_push_ ~permanent:false
|
||||
let act_push_persistent = act_push_ ~permanent:true
|
||||
|
||||
(* TODO: ensure that the clause is removed upon backtracking *)
|
||||
let act_propagate (st:t) f causes proof : unit =
|
||||
let l = List.rev_map (new_atom ~permanent:false st) causes in
|
||||
let l = List.rev_map (Atom.make st) causes in
|
||||
if List.for_all (fun a -> a.is_true) l then (
|
||||
let p = new_atom ~permanent:false st f in
|
||||
let p = Atom.make st f in
|
||||
let c = Clause.make_l (p :: List.map Atom.neg l) (Lemma proof) in
|
||||
if p.is_true then (
|
||||
) else if p.neg.is_true then (
|
||||
(* propagate other lits in the clause *)
|
||||
add_clause ~permanent:false st c
|
||||
) else (
|
||||
insert_var_order st p.var;
|
||||
|
|
@ -1572,7 +1460,8 @@ module Make (Th : Theory_intf.S) = struct
|
|||
propagate st
|
||||
| Theory_intf.Unsat (l, p) ->
|
||||
(* conflict *)
|
||||
let l = List.rev_map (new_atom ~permanent:false st) l in
|
||||
let l = List.rev_map (Atom.make st) l in
|
||||
(* TODO: do that when the conflict clause is added *)
|
||||
List.iter (fun a -> insert_var_order st a.var) l;
|
||||
let c = Clause.make_l l (Lemma p) in
|
||||
Some c
|
||||
|
|
@ -1680,8 +1569,8 @@ module Make (Th : Theory_intf.S) = struct
|
|||
let solve (st:t) : unit =
|
||||
Log.debug 5 "(sat.solve)";
|
||||
if is_unsat st then raise Unsat;
|
||||
let n_of_conflicts = ref (to_float restart_first) in
|
||||
let n_of_learnts = ref ((to_float (nb_clauses st)) *. learntsize_factor) in
|
||||
let n_of_conflicts = ref (float_of_int restart_first) in
|
||||
let n_of_learnts = ref ((float_of_int (nb_clauses st)) *. learntsize_factor) in
|
||||
(* add clauses that are waiting in [to_add] *)
|
||||
let rec add_clauses () =
|
||||
match
|
||||
|
|
@ -1692,7 +1581,7 @@ module Make (Th : Theory_intf.S) = struct
|
|||
add_boolean_conflict st c;
|
||||
call_solve()
|
||||
and call_solve() =
|
||||
match search st (to_int !n_of_conflicts) (to_int !n_of_learnts) with
|
||||
match search st (int_of_float !n_of_conflicts) (int_of_float !n_of_learnts) with
|
||||
| () -> ()
|
||||
| exception Restart ->
|
||||
n_of_conflicts := !n_of_conflicts *. restart_inc;
|
||||
|
|
@ -1711,7 +1600,7 @@ module Make (Th : Theory_intf.S) = struct
|
|||
raise Sat
|
||||
)
|
||||
| Theory_intf.Unsat (l, p) ->
|
||||
let atoms = List.rev_map (new_atom ~permanent:false st) l in
|
||||
let atoms = List.rev_map (Atom.make st) l in
|
||||
let c = Clause.make_l atoms (Lemma p) in
|
||||
Log.debugf 3
|
||||
(fun k -> k "(@[@{<Yellow>sat.theory_conflict_clause@}@ %a@])" Clause.debug c);
|
||||
|
|
@ -1727,7 +1616,7 @@ module Make (Th : Theory_intf.S) = struct
|
|||
let assume ~permanent st ?tag cnf =
|
||||
List.iter
|
||||
(fun atoms ->
|
||||
let atoms = List.rev_map (new_atom ~permanent st) atoms in
|
||||
let atoms = List.rev_map (Atom.make st) atoms in
|
||||
let c = Clause.make_l ?tag atoms Hyp in
|
||||
add_clause_user st ~permanent c)
|
||||
cnf
|
||||
|
|
@ -1771,7 +1660,7 @@ module Make (Th : Theory_intf.S) = struct
|
|||
(* Add local hyps to the current decision level *)
|
||||
let local (st:t) (assumptions:formula list) : unit =
|
||||
let add_lit lit : unit =
|
||||
let a = new_atom ~permanent:false st lit in
|
||||
let a = Atom.make st lit in
|
||||
Log.debugf 3 (fun k-> k "(@[sat.local_assumption@ %a@])" Atom.debug a);
|
||||
assert (decision_level st = base_level st);
|
||||
if not a.is_true then (
|
||||
|
|
|
|||
|
|
@ -119,10 +119,6 @@ module Make (Th : Theory_intf.S) = struct
|
|||
|
||||
let[@inline] get_tag cl = S.(cl.tag)
|
||||
|
||||
let[@inline] new_atom ~permanent st a =
|
||||
cleanup_ st;
|
||||
ignore (S.new_atom ~permanent st a)
|
||||
|
||||
let actions = S.actions
|
||||
|
||||
let export (st:t) : S.clause export =
|
||||
|
|
@ -131,10 +127,7 @@ module Make (Th : Theory_intf.S) = struct
|
|||
let local = S.temp st in
|
||||
{hyps; history; local}
|
||||
|
||||
module Atom = struct
|
||||
include S.Atom
|
||||
let make = S.new_atom ~permanent:false
|
||||
end
|
||||
module Atom = S.Atom
|
||||
|
||||
module Clause = struct
|
||||
include S.Clause
|
||||
|
|
@ -146,7 +139,7 @@ module Make (Th : Theory_intf.S) = struct
|
|||
let[@inline] make_l ?tag l : t = S.Clause.make_l ?tag l S.Hyp
|
||||
|
||||
let of_formulas st ?tag l =
|
||||
let l = List.map (S.new_atom ~permanent:false st) l in
|
||||
let l = List.map (Atom.make st) l in
|
||||
make_l ?tag l
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -113,12 +113,6 @@ module type S = sig
|
|||
The assumptions are just used for this call to [solve], they are
|
||||
not saved in the solver's state. *)
|
||||
|
||||
val new_atom : permanent:bool -> t -> formula -> unit
|
||||
(** Add a new atom (i.e propositional formula) to the solver.
|
||||
This formula will be decided on at some point during solving,
|
||||
whether it appears in clauses or not.
|
||||
@param permanent if true, kept after backtracking *)
|
||||
|
||||
val unsat_core : proof -> clause list
|
||||
(** Returns the unsat core of a given proof, ie a subset of all the added
|
||||
clauses that is sufficient to establish unsatisfiability. *)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue