mirror of
https://github.com/c-cube/sidekick.git
synced 2025-12-06 11:15:43 -05:00
wip: cleanup and documentation of internal.ml
This commit is contained in:
parent
8b1d657695
commit
c1915ba2d3
1 changed files with 169 additions and 102 deletions
|
|
@ -26,7 +26,7 @@ module Make
|
||||||
type user_level = {
|
type user_level = {
|
||||||
(* User levels always refer to decision_level 0 *)
|
(* User levels always refer to decision_level 0 *)
|
||||||
ul_elt_lvl : int; (* Number of atoms in trail at decision level 0 *)
|
ul_elt_lvl : int; (* Number of atoms in trail at decision level 0 *)
|
||||||
ul_th_lvl : int; (* Number of atoms known by the theory at decicion level 0 *)
|
ul_th_lvl : int; (* Number of atoms known by the theory at decision level 0 *)
|
||||||
ul_th_env : Plugin.level; (* Theory state at level 0 *)
|
ul_th_env : Plugin.level; (* Theory state at level 0 *)
|
||||||
ul_clauses : int; (* number of clauses *)
|
ul_clauses : int; (* number of clauses *)
|
||||||
ul_learnt : int; (* number of learnt clauses *)
|
ul_learnt : int; (* number of learnt clauses *)
|
||||||
|
|
@ -38,9 +38,9 @@ module Make
|
||||||
clauses_hyps : clause Vec.t;
|
clauses_hyps : clause Vec.t;
|
||||||
(* clauses assumed (subject to user levels) *)
|
(* clauses assumed (subject to user levels) *)
|
||||||
clauses_learnt : clause Vec.t;
|
clauses_learnt : clause Vec.t;
|
||||||
(* learnt clauses (true at anytime, whatever the user level) *)
|
(* learnt clauses (tautologies true at any time, whatever the user level) *)
|
||||||
clauses_pushed : clause Stack.t;
|
clauses_pushed : clause Stack.t;
|
||||||
(* Clauses pushed, waiting to be added as learnt. *)
|
(* Clauses pushed by the theory, waiting to be added as learnt. *)
|
||||||
|
|
||||||
|
|
||||||
mutable unsat_conflict : clause option;
|
mutable unsat_conflict : clause option;
|
||||||
|
|
@ -49,7 +49,8 @@ module Make
|
||||||
(* When the last conflict was a semantic one, this stores the next decision to make *)
|
(* When the last conflict was a semantic one, this stores the next decision to make *)
|
||||||
|
|
||||||
elt_queue : t Vec.t;
|
elt_queue : t Vec.t;
|
||||||
(* decision stack + propagated elements (atoms or assignments) *)
|
(* decision stack + propagated elements (atoms or assignments).
|
||||||
|
Also called "trail" in some solvers. *)
|
||||||
|
|
||||||
elt_levels : int Vec.t;
|
elt_levels : int Vec.t;
|
||||||
(* decision levels in [trail] *)
|
(* decision levels in [trail] *)
|
||||||
|
|
@ -59,9 +60,20 @@ module Make
|
||||||
(* user-defined levels, for {!push} and {!pop} *)
|
(* user-defined levels, for {!push} and {!pop} *)
|
||||||
|
|
||||||
mutable th_head : int;
|
mutable th_head : int;
|
||||||
(* Start offset in the queue of unit fact not yet seen by the theory *)
|
(* Start offset in the queue {!elt_queue} of
|
||||||
|
unit facts not yet seen by the theory. *)
|
||||||
mutable elt_head : int;
|
mutable elt_head : int;
|
||||||
(* Start offset in the queue of unit facts to propagate, within the trail *)
|
(* Start offset in the queue {!elt_queue} of
|
||||||
|
unit facts to propagate, within the trail *)
|
||||||
|
|
||||||
|
(* invariant:
|
||||||
|
- during propagation, th_head <= elt_head
|
||||||
|
- then, once elt_head reaches length elt_queue, Th.assume is
|
||||||
|
called so that th_head can catch up with elt_head
|
||||||
|
- this is repeated until a fixpoint is reached;
|
||||||
|
- before a decision (and after the fixpoint),
|
||||||
|
th_head = elt_head = length elt_queue
|
||||||
|
*)
|
||||||
|
|
||||||
|
|
||||||
mutable simpDB_props : int;
|
mutable simpDB_props : int;
|
||||||
|
|
@ -83,10 +95,6 @@ module Make
|
||||||
mutable clause_incr : float;
|
mutable clause_incr : float;
|
||||||
(* increment for clauses' activity *)
|
(* increment for clauses' activity *)
|
||||||
|
|
||||||
|
|
||||||
mutable progress_estimate : float;
|
|
||||||
(* progression estimate, updated by [search ()] *)
|
|
||||||
|
|
||||||
remove_satisfied : bool;
|
remove_satisfied : bool;
|
||||||
(* Wether to remove satisfied learnt clauses when simplifying *)
|
(* Wether to remove satisfied learnt clauses when simplifying *)
|
||||||
|
|
||||||
|
|
@ -146,7 +154,6 @@ module Make
|
||||||
simpDB_assigns = -1;
|
simpDB_assigns = -1;
|
||||||
simpDB_props = 0;
|
simpDB_props = 0;
|
||||||
|
|
||||||
progress_estimate = 0.;
|
|
||||||
remove_satisfied = false;
|
remove_satisfied = false;
|
||||||
|
|
||||||
restart_inc = 1.5;
|
restart_inc = 1.5;
|
||||||
|
|
@ -197,15 +204,17 @@ module Make
|
||||||
we need to save enough to be able to restore the current decision
|
we need to save enough to be able to restore the current decision
|
||||||
level 0. *)
|
level 0. *)
|
||||||
let res = current_level () in
|
let res = current_level () in
|
||||||
(* To restore decision level 0, we need the stolver queue, and theory state. *)
|
(* To restore decision level 0, we need the solver queue, and theory state. *)
|
||||||
let ul_elt_lvl, ul_th_lvl =
|
let ul_elt_lvl, ul_th_lvl =
|
||||||
if Vec.is_empty env.elt_levels then
|
if Vec.is_empty env.elt_levels then
|
||||||
env.elt_head, env.th_head
|
env.elt_head, env.th_head
|
||||||
else
|
else (
|
||||||
let l = Vec.get env.elt_levels 0 in
|
let l = Vec.get env.elt_levels 0 in
|
||||||
l, l
|
l, l
|
||||||
|
)
|
||||||
and ul_th_env =
|
and ul_th_env =
|
||||||
if Vec.is_empty env.th_levels then Plugin.current_level ()
|
if Vec.is_empty env.th_levels
|
||||||
|
then Plugin.current_level ()
|
||||||
else Vec.get env.th_levels 0
|
else Vec.get env.th_levels 0
|
||||||
in
|
in
|
||||||
(* Keep in mind what are the current assumptions. *)
|
(* Keep in mind what are the current assumptions. *)
|
||||||
|
|
@ -240,17 +249,17 @@ module Make
|
||||||
Hashtbl.add iter_map v.vid !l;
|
Hashtbl.add iter_map v.vid !l;
|
||||||
List.iter f !l
|
List.iter f !l
|
||||||
|
|
||||||
(* When we have a new litteral,
|
(* When we have a new literal,
|
||||||
we need to first create the list of its subterms. *)
|
we need to first create the list of its subterms. *)
|
||||||
let atom lit : atom =
|
let atom (f:St.formula) : atom =
|
||||||
let res = add_atom lit in
|
let res = add_atom f in
|
||||||
iter_sub ignore res.var;
|
iter_sub ignore res.var;
|
||||||
res
|
res
|
||||||
|
|
||||||
(* Variable and litteral activity.
|
(* Variable and literal activity.
|
||||||
Activity is used to decide on which variable to decide when propagation
|
Activity is used to decide on which variable to decide when propagation
|
||||||
is done. Uses a heap (implemented in Iheap), to keep track of variable activity.
|
is done. Uses a heap (implemented in Iheap), to keep track of variable activity.
|
||||||
To be more general, the heap only stores the variable/litteral id (i.e an int).
|
To be more general, the heap only stores the variable/literal id (i.e an int).
|
||||||
When we add a variable (which wraps a formula), we also need to add all
|
When we add a variable (which wraps a formula), we also need to add all
|
||||||
its subterms.
|
its subterms.
|
||||||
*)
|
*)
|
||||||
|
|
@ -269,6 +278,7 @@ module Make
|
||||||
let clause_decay_activity () =
|
let clause_decay_activity () =
|
||||||
env.clause_incr <- env.clause_incr *. env.clause_decay
|
env.clause_incr <- env.clause_incr *. env.clause_decay
|
||||||
|
|
||||||
|
(* increase activity of [v] *)
|
||||||
let var_bump_activity_aux v =
|
let var_bump_activity_aux v =
|
||||||
v.v_weight <- v.v_weight +. env.var_incr;
|
v.v_weight <- v.v_weight +. env.var_incr;
|
||||||
if v.v_weight > 1e100 then begin
|
if v.v_weight > 1e100 then begin
|
||||||
|
|
@ -280,7 +290,8 @@ module Make
|
||||||
if Iheap.in_heap env.order v.vid then
|
if Iheap.in_heap env.order v.vid then
|
||||||
Iheap.decrease f_weight env.order v.vid
|
Iheap.decrease f_weight env.order v.vid
|
||||||
|
|
||||||
let lit_bump_activity_aux l =
|
(* increase activity of literal [l] *)
|
||||||
|
let lit_bump_activity_aux (l:lit): unit =
|
||||||
l.l_weight <- l.l_weight +. env.var_incr;
|
l.l_weight <- l.l_weight +. env.var_incr;
|
||||||
if l.l_weight > 1e100 then begin
|
if l.l_weight > 1e100 then begin
|
||||||
for i = 0 to (St.nb_elt ()) - 1 do
|
for i = 0 to (St.nb_elt ()) - 1 do
|
||||||
|
|
@ -291,11 +302,13 @@ module Make
|
||||||
if Iheap.in_heap env.order l.lid then
|
if Iheap.in_heap env.order l.lid then
|
||||||
Iheap.decrease f_weight env.order l.lid
|
Iheap.decrease f_weight env.order l.lid
|
||||||
|
|
||||||
let var_bump_activity v =
|
(* increase activity of var [v] *)
|
||||||
|
let var_bump_activity (v:var): unit =
|
||||||
var_bump_activity_aux v;
|
var_bump_activity_aux v;
|
||||||
iter_sub lit_bump_activity_aux v
|
iter_sub lit_bump_activity_aux v
|
||||||
|
|
||||||
let clause_bump_activity c =
|
(* increase activity of clause [c] *)
|
||||||
|
let clause_bump_activity (c:clause) : unit =
|
||||||
c.activity <- c.activity +. env.clause_incr;
|
c.activity <- c.activity +. env.clause_incr;
|
||||||
if c.activity > 1e20 then begin
|
if c.activity > 1e20 then begin
|
||||||
for i = 0 to (Vec.size env.clauses_learnt) - 1 do
|
for i = 0 to (Vec.size env.clauses_learnt) - 1 do
|
||||||
|
|
@ -309,14 +322,16 @@ module Make
|
||||||
When adding new clauses, it is desirable to 'simplify' them, i.e:
|
When adding new clauses, it is desirable to 'simplify' them, i.e:
|
||||||
- remove variables that are false at level 0, since it is a fact
|
- remove variables that are false at level 0, since it is a fact
|
||||||
that they cannot be true, and therefore can not help to satisfy the clause
|
that they cannot be true, and therefore can not help to satisfy the clause
|
||||||
|
- return the list of undecided atoms, and the list of clauses that
|
||||||
|
justify why the other atoms are false (and will remain so).
|
||||||
|
|
||||||
Aditionally, since we can do push/pop on the assumptions, we need to
|
Aditionally, since we can do push/pop on the assumptions, we need to
|
||||||
keep track of what assumptions were used to simplify a given clause.
|
keep track of what assumptions were used to simplify a given clause.
|
||||||
*)
|
*)
|
||||||
exception Trivial
|
exception Trivial
|
||||||
|
|
||||||
let simplify_zero atoms =
|
let simplify_zero atoms : atom list * clause list=
|
||||||
(* Eliminates dead litterals from clauses when at decision level 0 (see above) *)
|
(* Eliminates dead literals from clauses when at decision level 0 (see above) *)
|
||||||
assert (decision_level () = 0);
|
assert (decision_level () = 0);
|
||||||
let aux (atoms, history) a =
|
let aux (atoms, history) a =
|
||||||
if a.is_true then raise Trivial;
|
if a.is_true then raise Trivial;
|
||||||
|
|
@ -346,12 +361,17 @@ module Make
|
||||||
(* TODO: Why do we sort the atoms here ? *)
|
(* TODO: Why do we sort the atoms here ? *)
|
||||||
List.fast_sort (fun a b -> a.var.vid - b.var.vid) atoms, init
|
List.fast_sort (fun a b -> a.var.vid - b.var.vid) atoms, init
|
||||||
|
|
||||||
let arr_to_list arr i =
|
(* [arr_to_list a i] converts [a.(i), ... a.(length a-1)] into a list *)
|
||||||
|
let arr_to_list arr i : _ list =
|
||||||
if i >= Array.length arr then []
|
if i >= Array.length arr then []
|
||||||
else Array.to_list (Array.sub arr i (Array.length arr - i))
|
else Array.to_list (Array.sub arr i (Array.length arr - i))
|
||||||
|
|
||||||
let partition atoms =
|
(* Partition literals for new clauses, into:
|
||||||
(* Parittion litterals for new clauses *)
|
- true literals (maybe makes the clause trivial if the lit is proved true)
|
||||||
|
- false literals (-> removed, also return the list of reasons those are false)
|
||||||
|
- unassigned literals, yet to be decided
|
||||||
|
*)
|
||||||
|
let partition atoms : atom list * clause list =
|
||||||
let rec partition_aux trues unassigned falses history i =
|
let rec partition_aux trues unassigned falses history i =
|
||||||
if i >= Array.length atoms then
|
if i >= Array.length atoms then
|
||||||
trues @ unassigned @ falses, history
|
trues @ unassigned @ falses, history
|
||||||
|
|
@ -387,26 +407,12 @@ module Make
|
||||||
else
|
else
|
||||||
partition_aux [] [] [] [] 0
|
partition_aux [] [] [] [] 0
|
||||||
|
|
||||||
(* Compute a progess estimate.
|
|
||||||
TODO: remove it or use it ? *)
|
|
||||||
let progress_estimate () =
|
|
||||||
let prg = ref 0. in
|
|
||||||
let nbv = to_float (nb_vars()) in
|
|
||||||
let lvl = decision_level () in
|
|
||||||
let _F = 1. /. nbv in
|
|
||||||
for i = 0 to lvl do
|
|
||||||
let _beg = if i = 0 then 0 else Vec.get env.elt_levels (i-1) in
|
|
||||||
let _end = if i=lvl then Vec.size env.elt_queue else Vec.get env.elt_levels i in
|
|
||||||
prg := !prg +. _F**(to_float i) *. (to_float (_end - _beg))
|
|
||||||
done;
|
|
||||||
!prg /. nbv
|
|
||||||
|
|
||||||
|
|
||||||
(* Making a decision.
|
(* Making a decision.
|
||||||
Before actually creatig a new decision level, we check that
|
Before actually creatig a new decision level, we check that
|
||||||
all propagations have been done and propagated to the theory,
|
all propagations have been done and propagated to the theory,
|
||||||
i.e that the theoriy state indeed takes into account the whole
|
i.e that the theoriy state indeed takes into account the whole
|
||||||
stack of litterals
|
stack of literals
|
||||||
i.e we have indeed reached a propagation fixpoint before making
|
i.e we have indeed reached a propagation fixpoint before making
|
||||||
a new decision *)
|
a new decision *)
|
||||||
let new_decision_level() =
|
let new_decision_level() =
|
||||||
|
|
@ -417,9 +423,12 @@ module Make
|
||||||
()
|
()
|
||||||
|
|
||||||
(* Attach/Detach a clause.
|
(* Attach/Detach a clause.
|
||||||
Clauses that become satisfied are detached, i.e we remove
|
|
||||||
their watchers, while clauses that loose their satisfied status
|
A clause is attached (to its watching lits) when it is first added,
|
||||||
have to be reattached, adding watchers. *)
|
either because it is assumed or learnt.
|
||||||
|
|
||||||
|
A clause is detached once it dies (because of pop())
|
||||||
|
*)
|
||||||
let attach_clause c =
|
let attach_clause c =
|
||||||
if not c.attached then begin
|
if not c.attached then begin
|
||||||
Log.debugf 60 "Attaching %a" (fun k -> k St.pp_clause c);
|
Log.debugf 60 "Attaching %a" (fun k -> k St.pp_clause c);
|
||||||
|
|
@ -465,7 +474,7 @@ module Make
|
||||||
if a.var.v_level <= lvl then begin
|
if a.var.v_level <= lvl then begin
|
||||||
(* It is a semantic propagation, which can be late, and has a level
|
(* It is a semantic propagation, which can be late, and has a level
|
||||||
lower than where we backtrack, so we just move it to the head
|
lower than where we backtrack, so we just move it to the head
|
||||||
of the queue. *)
|
of the queue, to be propagated again. *)
|
||||||
Vec.set env.elt_queue env.elt_head (of_atom a);
|
Vec.set env.elt_queue env.elt_head (of_atom a);
|
||||||
env.elt_head <- env.elt_head + 1
|
env.elt_head <- env.elt_head + 1
|
||||||
end else begin
|
end else begin
|
||||||
|
|
@ -491,7 +500,7 @@ module Make
|
||||||
|
|
||||||
(* Unsatisfiability is signaled through an exception, since it can happen
|
(* Unsatisfiability is signaled through an exception, since it can happen
|
||||||
in multiple places (adding new clauses, or solving for instance). *)
|
in multiple places (adding new clauses, or solving for instance). *)
|
||||||
let report_unsat ({atoms=atoms} as confl) =
|
let report_unsat ({atoms=atoms} as confl) : _ =
|
||||||
Log.debugf 5 "@[Unsat conflict: %a@]" (fun k -> k St.pp_clause confl);
|
Log.debugf 5 "@[Unsat conflict: %a@]" (fun k -> k St.pp_clause confl);
|
||||||
env.unsat_conflict <- Some confl;
|
env.unsat_conflict <- Some confl;
|
||||||
raise Unsat
|
raise Unsat
|
||||||
|
|
@ -502,7 +511,7 @@ module Make
|
||||||
other formulas, but has been simplified. in which case, we
|
other formulas, but has been simplified. in which case, we
|
||||||
need to rebuild a clause with correct history, in order to
|
need to rebuild a clause with correct history, in order to
|
||||||
be able to build a correct proof at the end of proof search. *)
|
be able to build a correct proof at the end of proof search. *)
|
||||||
let simpl_reason = function
|
let simpl_reason : reason -> reason = function
|
||||||
| (Bcp cl) as r ->
|
| (Bcp cl) as r ->
|
||||||
let l, history = partition cl.atoms in
|
let l, history = partition cl.atoms in
|
||||||
begin match l with
|
begin match l with
|
||||||
|
|
@ -522,9 +531,9 @@ module Make
|
||||||
|
|
||||||
(* Boolean propagation.
|
(* Boolean propagation.
|
||||||
Wrapper function for adding a new propagated formula. *)
|
Wrapper function for adding a new propagated formula. *)
|
||||||
let enqueue_bool a lvl reason =
|
let enqueue_bool a ~level:lvl reason : unit =
|
||||||
if a.neg.is_true then begin
|
if a.neg.is_true then begin
|
||||||
Log.debugf 0 "Trying to enqueue a false litteral: %a" (fun k->k St.pp_atom a);
|
Log.debugf 0 "Trying to enqueue a false literal: %a" (fun k->k St.pp_atom a);
|
||||||
assert false
|
assert false
|
||||||
end;
|
end;
|
||||||
if not a.is_true then begin
|
if not a.is_true then begin
|
||||||
|
|
@ -541,37 +550,67 @@ module Make
|
||||||
(fun k->k (Vec.size env.elt_queue) pp_atom a)
|
(fun k->k (Vec.size env.elt_queue) pp_atom a)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
(* MCsat semantic assignment *)
|
||||||
let enqueue_assign l value lvl =
|
let enqueue_assign l value lvl =
|
||||||
l.assigned <- Some value;
|
l.assigned <- Some value;
|
||||||
l.l_level <- lvl;
|
l.l_level <- lvl;
|
||||||
Vec.push env.elt_queue (of_lit l);
|
Vec.push env.elt_queue (of_lit l);
|
||||||
()
|
()
|
||||||
|
|
||||||
let th_eval a =
|
(* evaluate an atom for MCsat, if it's not assigned
|
||||||
|
by boolean propagation/decision *)
|
||||||
|
let th_eval a : bool option =
|
||||||
if a.is_true || a.neg.is_true then None
|
if a.is_true || a.neg.is_true then None
|
||||||
else match Plugin.eval a.lit with
|
else match Plugin.eval a.lit with
|
||||||
| Plugin_intf.Unknown -> None
|
| Plugin_intf.Unknown -> None
|
||||||
| Plugin_intf.Valued (b, lvl) ->
|
| Plugin_intf.Valued (b, lvl) ->
|
||||||
let atom = if b then a else a.neg in
|
let atom = if b then a else a.neg in
|
||||||
enqueue_bool atom lvl (Semantic lvl);
|
enqueue_bool atom ~level:lvl (Semantic lvl);
|
||||||
Some b
|
Some b
|
||||||
|
|
||||||
(* conflict analysis *)
|
(* conflict analysis: find the list of atoms of [l] that have the
|
||||||
let max_lvl_atoms l =
|
maximal level *)
|
||||||
List.fold_left (fun (max_lvl, acc) a ->
|
let max_lvl_atoms (l:atom list) : int * atom list =
|
||||||
|
List.fold_left
|
||||||
|
(fun (max_lvl, acc) a ->
|
||||||
if a.var.v_level = max_lvl then (max_lvl, a :: acc)
|
if a.var.v_level = max_lvl then (max_lvl, a :: acc)
|
||||||
else if a.var.v_level > max_lvl then (a.var.v_level, [a])
|
else if a.var.v_level > max_lvl then (a.var.v_level, [a])
|
||||||
else (max_lvl, acc)) (0, []) l
|
else (max_lvl, acc))
|
||||||
|
(0, []) l
|
||||||
|
|
||||||
let backtrack_lvl is_uip = function
|
(* find which level to backtrack to, given a conflict clause
|
||||||
|
and a boolean stating whether it is
|
||||||
|
a UIP ("Unique Implication Point")
|
||||||
|
precond: the atom list is sorted by decreasing decision level *)
|
||||||
|
let backtrack_lvl ~is_uip : atom list -> int = function
|
||||||
| [] -> 0
|
| [] -> 0
|
||||||
| a :: r when not is_uip -> max (a.var.v_level - 1) 0
|
| [a] ->
|
||||||
| a :: [] -> 0
|
assert is_uip;
|
||||||
|
0
|
||||||
| a :: b :: r ->
|
| a :: b :: r ->
|
||||||
assert(a.var.v_level <> b.var.v_level);
|
if is_uip then (
|
||||||
|
(* backtrack below [a], so we can propagate [not a] *)
|
||||||
|
assert(a.var.v_level > b.var.v_level);
|
||||||
b.var.v_level
|
b.var.v_level
|
||||||
|
) else (
|
||||||
|
assert (a.var.v_level = b.var.v_level);
|
||||||
|
max (a.var.v_level - 1) 0
|
||||||
|
)
|
||||||
|
|
||||||
let analyze_mcsat c_clause =
|
(* result of conflict analysis, containing the learnt clause and some
|
||||||
|
additional info.
|
||||||
|
|
||||||
|
invariant: cr_history's order matters
|
||||||
|
TODO zozozo explain *)
|
||||||
|
type conflict_res = {
|
||||||
|
cr_backtrack_lvl : int; (* level to backtrack to *)
|
||||||
|
cr_learnt: atom list; (* lemma learnt from conflict *)
|
||||||
|
cr_history: clause list; (* justification *)
|
||||||
|
cr_is_uip: bool; (* conflict is UIP? *)
|
||||||
|
}
|
||||||
|
|
||||||
|
(* conflict analysis for MCsat *)
|
||||||
|
let analyze_mcsat c_clause : conflict_res =
|
||||||
let tr_ind = ref (Vec.size env.elt_queue) in
|
let tr_ind = ref (Vec.size env.elt_queue) in
|
||||||
let is_uip = ref false in
|
let is_uip = ref false in
|
||||||
let c = ref (Proof.to_list c_clause) in
|
let c = ref (Proof.to_list c_clause) in
|
||||||
|
|
@ -581,11 +620,12 @@ module Make
|
||||||
| Some Semantic _ -> true
|
| Some Semantic _ -> true
|
||||||
| _ -> false
|
| _ -> false
|
||||||
in
|
in
|
||||||
try while true do
|
try
|
||||||
|
while true do
|
||||||
let lvl, atoms = max_lvl_atoms !c in
|
let lvl, atoms = max_lvl_atoms !c in
|
||||||
if lvl = 0 then raise Exit;
|
if lvl = 0 then raise Exit;
|
||||||
match atoms with
|
match atoms with
|
||||||
| [] | _ :: [] ->
|
| [] | [_] ->
|
||||||
is_uip := true;
|
is_uip := true;
|
||||||
raise Exit
|
raise Exit
|
||||||
| _ when List.for_all is_semantic atoms ->
|
| _ when List.for_all is_semantic atoms ->
|
||||||
|
|
@ -598,6 +638,7 @@ module Make
|
||||||
| Atom a ->
|
| Atom a ->
|
||||||
begin match a.var.reason with
|
begin match a.var.reason with
|
||||||
| Some (Bcp d) ->
|
| Some (Bcp d) ->
|
||||||
|
(* resolution step *)
|
||||||
let tmp, res = Proof.resolve (Proof.merge !c (Proof.to_list d)) in
|
let tmp, res = Proof.resolve (Proof.merge !c (Proof.to_list d)) in
|
||||||
begin match tmp with
|
begin match tmp with
|
||||||
| [] -> ()
|
| [] -> ()
|
||||||
|
|
@ -612,16 +653,23 @@ module Make
|
||||||
end
|
end
|
||||||
done; assert false
|
done; assert false
|
||||||
with Exit ->
|
with Exit ->
|
||||||
let learnt = List.fast_sort (
|
let learnt =
|
||||||
fun a b -> Pervasives.compare b.var.v_level a.var.v_level) !c in
|
List.fast_sort
|
||||||
|
(fun a b -> Pervasives.compare b.var.v_level a.var.v_level) !c
|
||||||
|
in
|
||||||
let blevel = backtrack_lvl !is_uip learnt in
|
let blevel = backtrack_lvl !is_uip learnt in
|
||||||
blevel, learnt, List.rev !history, !is_uip
|
{ cr_backtrack_lvl = blevel;
|
||||||
|
cr_learnt= learnt;
|
||||||
|
cr_history = List.rev !history;
|
||||||
|
cr_is_uip = !is_uip;
|
||||||
|
}
|
||||||
|
|
||||||
let get_atom i =
|
let get_atom i =
|
||||||
match Vec.get env.elt_queue i with
|
match Vec.get env.elt_queue i with
|
||||||
| Lit _ -> assert false | Atom x -> x
|
| Lit _ -> assert false | Atom x -> x
|
||||||
|
|
||||||
let analyze_sat c_clause =
|
(* conflict analysis for SAT *)
|
||||||
|
let analyze_sat c_clause : conflict_res =
|
||||||
let pathC = ref 0 in
|
let pathC = ref 0 in
|
||||||
let learnt = ref [] in
|
let learnt = ref [] in
|
||||||
let cond = ref true in
|
let cond = ref true in
|
||||||
|
|
@ -641,7 +689,7 @@ module Make
|
||||||
(* visit the current predecessors *)
|
(* visit the current predecessors *)
|
||||||
for j = 0 to Array.length !c.atoms - 1 do
|
for j = 0 to Array.length !c.atoms - 1 do
|
||||||
let q = !c.atoms.(j) in
|
let q = !c.atoms.(j) in
|
||||||
assert (q.is_true || q.neg.is_true && q.var.v_level >= 0); (* Pas sur *)
|
assert (q.is_true || q.neg.is_true && q.var.v_level >= 0); (* unsure? *)
|
||||||
if q.var.v_level = 0 then begin
|
if q.var.v_level = 0 then begin
|
||||||
assert (q.neg.is_true);
|
assert (q.neg.is_true);
|
||||||
match q.var.reason with
|
match q.var.reason with
|
||||||
|
|
@ -678,35 +726,40 @@ module Make
|
||||||
| n, _ -> assert false
|
| n, _ -> assert false
|
||||||
done;
|
done;
|
||||||
List.iter (fun q -> q.var.seen <- false) !seen;
|
List.iter (fun q -> q.var.seen <- false) !seen;
|
||||||
!blevel, !learnt, List.rev !history, true
|
{ cr_backtrack_lvl= !blevel;
|
||||||
|
cr_learnt= !learnt;
|
||||||
|
cr_history= List.rev !history;
|
||||||
|
cr_is_uip = true;
|
||||||
|
}
|
||||||
|
|
||||||
let analyze c_clause =
|
let analyze c_clause : conflict_res =
|
||||||
if St.mcsat then
|
if St.mcsat
|
||||||
analyze_mcsat c_clause
|
then analyze_mcsat c_clause
|
||||||
else
|
else analyze_sat c_clause
|
||||||
analyze_sat c_clause
|
|
||||||
|
|
||||||
let record_learnt_clause confl blevel learnt history is_uip =
|
(* add the learnt clause to the clause database, propagate, etc. *)
|
||||||
begin match learnt with
|
let record_learnt_clause (confl:clause) (cr:conflict_res): unit =
|
||||||
|
begin match cr.cr_learnt with
|
||||||
| [] -> assert false
|
| [] -> assert false
|
||||||
| [fuip] ->
|
| [fuip] ->
|
||||||
assert (blevel = 0);
|
assert (cr.cr_backtrack_lvl = 0);
|
||||||
if fuip.neg.is_true then
|
if fuip.neg.is_true then
|
||||||
report_unsat confl
|
report_unsat confl
|
||||||
else begin
|
else begin
|
||||||
let name = fresh_lname () in
|
let name = fresh_lname () in
|
||||||
let uclause = make_clause name learnt history in
|
let uclause = make_clause name cr.cr_learnt (History cr.cr_history) in
|
||||||
Vec.push env.clauses_learnt uclause;
|
Vec.push env.clauses_learnt uclause;
|
||||||
enqueue_bool fuip 0 (Bcp uclause)
|
(* no need to attach [uclause], it is true at level 0 *)
|
||||||
|
enqueue_bool fuip ~level:0 (Bcp uclause)
|
||||||
end
|
end
|
||||||
| fuip :: _ ->
|
| fuip :: _ ->
|
||||||
let name = fresh_lname () in
|
let name = fresh_lname () in
|
||||||
let lclause = make_clause name learnt history in
|
let lclause = make_clause name cr.cr_learnt (History cr.cr_history) in
|
||||||
Vec.push env.clauses_learnt lclause;
|
Vec.push env.clauses_learnt lclause;
|
||||||
attach_clause lclause;
|
attach_clause lclause;
|
||||||
clause_bump_activity lclause;
|
clause_bump_activity lclause;
|
||||||
if is_uip then
|
if cr.cr_is_uip then
|
||||||
enqueue_bool fuip blevel (Bcp lclause)
|
enqueue_bool fuip ~level:cr.cr_backtrack_lvl (Bcp lclause)
|
||||||
else begin
|
else begin
|
||||||
env.next_decision <- Some fuip.neg
|
env.next_decision <- Some fuip.neg
|
||||||
end
|
end
|
||||||
|
|
@ -714,17 +767,23 @@ module Make
|
||||||
var_decay_activity ();
|
var_decay_activity ();
|
||||||
clause_decay_activity ()
|
clause_decay_activity ()
|
||||||
|
|
||||||
let add_boolean_conflict confl =
|
(* process a conflict:
|
||||||
|
- learn clause
|
||||||
|
- backtrack
|
||||||
|
- report unsat if conflict at level 0
|
||||||
|
*)
|
||||||
|
let add_boolean_conflict (confl:clause): unit =
|
||||||
env.next_decision <- None;
|
env.next_decision <- None;
|
||||||
env.conflicts <- env.conflicts + 1;
|
env.conflicts <- env.conflicts + 1;
|
||||||
if decision_level() = 0 || Array_util.for_all (fun a -> a.var.v_level = 0) confl.atoms then
|
if decision_level() = 0 || Array_util.for_all (fun a -> a.var.v_level = 0) confl.atoms then
|
||||||
report_unsat confl; (* Top-level conflict *)
|
report_unsat confl; (* Top-level conflict *)
|
||||||
let blevel, learnt, history, is_uip = analyze confl in
|
let cr = analyze confl in
|
||||||
cancel_until blevel;
|
cancel_until cr.cr_backtrack_lvl;
|
||||||
record_learnt_clause confl blevel learnt (History history) is_uip
|
record_learnt_clause confl cr
|
||||||
|
|
||||||
(* Add a new clause *)
|
(* Add a new clause, simplifying, propagating, and backtracking if
|
||||||
let add_clause ?(force=false) init =
|
the clause is false in the current trail *)
|
||||||
|
let add_clause ?(force=false) (init:clause) : unit =
|
||||||
Log.debugf 90 "Adding clause:@[<hov>%a@]" (fun k -> k St.pp_clause init);
|
Log.debugf 90 "Adding clause:@[<hov>%a@]" (fun k -> k St.pp_clause init);
|
||||||
let vec = match init.cpremise with
|
let vec = match init.cpremise with
|
||||||
| Hyp _ -> env.clauses_hyps
|
| Hyp _ -> env.clauses_hyps
|
||||||
|
|
@ -744,8 +803,9 @@ module Make
|
||||||
report_unsat clause
|
report_unsat clause
|
||||||
| a::b::_ ->
|
| a::b::_ ->
|
||||||
if a.neg.is_true then begin
|
if a.neg.is_true then begin
|
||||||
Array.sort (fun a b ->
|
Array.sort
|
||||||
compare b.var.v_level a.var.v_level) clause.atoms;
|
(fun a b -> compare b.var.v_level a.var.v_level)
|
||||||
|
clause.atoms;
|
||||||
attach_clause clause;
|
attach_clause clause;
|
||||||
add_boolean_conflict init
|
add_boolean_conflict init
|
||||||
end else begin
|
end else begin
|
||||||
|
|
@ -764,17 +824,22 @@ module Make
|
||||||
Vec.push vec init;
|
Vec.push vec init;
|
||||||
Log.debugf 5 "Trivial clause ignored : @[%a@]" (fun k->k St.pp_clause init)
|
Log.debugf 5 "Trivial clause ignored : @[%a@]" (fun k->k St.pp_clause init)
|
||||||
|
|
||||||
let propagate_in_clause a c i watched new_sz =
|
(* boolean propagation.
|
||||||
|
[a] is the false atom, one of [c]'s two watch literals
|
||||||
|
[i] is the index of [c] in [a.watched]
|
||||||
|
*)
|
||||||
|
let propagate_in_clause (a:atom) (c:clause) (i:int) new_sz: unit =
|
||||||
let atoms = c.atoms in
|
let atoms = c.atoms in
|
||||||
let first = atoms.(0) in
|
let first = atoms.(0) in
|
||||||
if first == a.neg then begin (* false lit must be at index 1 *)
|
if first == a.neg then (
|
||||||
|
(* false lit must be at index 1 *)
|
||||||
atoms.(0) <- atoms.(1);
|
atoms.(0) <- atoms.(1);
|
||||||
atoms.(1) <- first
|
atoms.(1) <- first
|
||||||
end;
|
) else assert (a.neg == atoms.(1));
|
||||||
let first = atoms.(0) in
|
let first = atoms.(0) in
|
||||||
if first.is_true then begin
|
if first.is_true then begin
|
||||||
(* true clause, keep it in watched *)
|
(* true clause, keep it in watched *)
|
||||||
Vec.set watched !new_sz c;
|
Vec.set a.watched !new_sz c;
|
||||||
incr new_sz;
|
incr new_sz;
|
||||||
end else
|
end else
|
||||||
try (* look for another watch lit *)
|
try (* look for another watch lit *)
|
||||||
|
|
@ -792,27 +857,30 @@ module Make
|
||||||
if first.neg.is_true || (th_eval first = Some false) then begin
|
if first.neg.is_true || (th_eval first = Some false) then begin
|
||||||
(* clause is false *)
|
(* clause is false *)
|
||||||
env.elt_head <- Vec.size env.elt_queue;
|
env.elt_head <- Vec.size env.elt_queue;
|
||||||
for k = i to Vec.size watched - 1 do
|
(* TODO: here, just swap [i] and last element of [watched];
|
||||||
Vec.set watched !new_sz (Vec.get watched k);
|
then update the last element's position since it changed *)
|
||||||
|
for k = i to Vec.size a.watched - 1 do
|
||||||
|
Vec.set a.watched !new_sz (Vec.get a.watched k);
|
||||||
incr new_sz;
|
incr new_sz;
|
||||||
done;
|
done;
|
||||||
raise (Conflict c)
|
raise (Conflict c)
|
||||||
end else begin
|
end else begin
|
||||||
(* clause is unit *)
|
(* clause is unit *)
|
||||||
Vec.set watched !new_sz c;
|
Vec.set a.watched !new_sz c;
|
||||||
incr new_sz;
|
incr new_sz;
|
||||||
enqueue_bool first (decision_level ()) (Bcp c)
|
enqueue_bool first (decision_level ()) (Bcp c)
|
||||||
end
|
end
|
||||||
with Exit -> ()
|
with Exit -> ()
|
||||||
|
|
||||||
let propagate_atom a res =
|
let propagate_atom a res : unit =
|
||||||
let watched = a.watched in
|
let watched = a.watched in
|
||||||
let new_sz_w = ref 0 in
|
let new_sz_w = ref 0 in
|
||||||
begin
|
begin
|
||||||
try
|
try
|
||||||
for i = 0 to Vec.size watched - 1 do
|
for i = 0 to Vec.size watched - 1 do
|
||||||
let c = Vec.get watched i in
|
let c = Vec.get watched i in
|
||||||
if c.attached then propagate_in_clause a c i watched new_sz_w
|
assert c.attached;
|
||||||
|
propagate_in_clause a c i new_sz_w
|
||||||
done;
|
done;
|
||||||
with Conflict c ->
|
with Conflict c ->
|
||||||
assert (!res = None);
|
assert (!res = None);
|
||||||
|
|
@ -987,7 +1055,7 @@ module Make
|
||||||
end
|
end
|
||||||
*)
|
*)
|
||||||
|
|
||||||
(* Decide on a new litteral *)
|
(* Decide on a new literal *)
|
||||||
let rec pick_branch_aux atom =
|
let rec pick_branch_aux atom =
|
||||||
let v = atom.var in
|
let v = atom.var in
|
||||||
if v.v_level >= 0 then begin
|
if v.v_level >= 0 then begin
|
||||||
|
|
@ -1040,7 +1108,6 @@ module Make
|
||||||
assert (env.elt_head = Vec.size env.elt_queue);
|
assert (env.elt_head = Vec.size env.elt_queue);
|
||||||
if Vec.size env.elt_queue = St.nb_elt () (* env.nb_init_vars *) then raise Sat;
|
if Vec.size env.elt_queue = St.nb_elt () (* env.nb_init_vars *) then raise Sat;
|
||||||
if n_of_conflicts > 0 && !conflictC >= n_of_conflicts then begin
|
if n_of_conflicts > 0 && !conflictC >= n_of_conflicts then begin
|
||||||
env.progress_estimate <- progress_estimate();
|
|
||||||
cancel_until 0;
|
cancel_until 0;
|
||||||
raise Restart
|
raise Restart
|
||||||
end;
|
end;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue