mirror of
https://github.com/c-cube/sidekick.git
synced 2025-12-09 20:55:39 -05:00
fix(cc): fix bugs in congruence closure and explanations
also, simplify API for backtracking
This commit is contained in:
parent
bc1a573407
commit
4e215e3d01
8 changed files with 63 additions and 88 deletions
|
|
@ -43,7 +43,6 @@ type ('form, 'proof) actions = ('form,'proof) Theory_intf.actions = Actions of {
|
||||||
push_persistent : 'form IArray.t -> 'proof -> unit;
|
push_persistent : 'form IArray.t -> 'proof -> unit;
|
||||||
push_local : 'form IArray.t -> 'proof -> unit;
|
push_local : 'form IArray.t -> 'proof -> unit;
|
||||||
on_backtrack: (unit -> unit) -> unit;
|
on_backtrack: (unit -> unit) -> unit;
|
||||||
at_level_0 : unit -> bool;
|
|
||||||
propagate : 'form -> 'form list -> 'proof -> unit;
|
propagate : 'form -> 'form list -> 'proof -> unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -146,14 +146,10 @@ module Make
|
||||||
}
|
}
|
||||||
|
|
||||||
let[@inline] theory st = Lazy.force st.th
|
let[@inline] theory st = Lazy.force st.th
|
||||||
let[@inline] on_backtrack st f : unit = Vec.push st.backtrack f
|
|
||||||
let[@inline] at_level_0 st : bool = Vec.is_empty st.backtrack_levels
|
let[@inline] at_level_0 st : bool = Vec.is_empty st.backtrack_levels
|
||||||
|
let[@inline] on_backtrack st f : unit =
|
||||||
(* schedule [f] as a backtrack action, iff the solver's current
|
|
||||||
level is not 0. *)
|
|
||||||
let[@inline] on_backtrack_if_not_at_0 st f =
|
|
||||||
if not (at_level_0 st) then (
|
if not (at_level_0 st) then (
|
||||||
on_backtrack st f;
|
Vec.push st.backtrack f
|
||||||
)
|
)
|
||||||
|
|
||||||
let[@inline] st t = t.st
|
let[@inline] st t = t.st
|
||||||
|
|
@ -196,7 +192,7 @@ module Make
|
||||||
if not (Var.in_heap v) && Var.level v < 0 then (
|
if not (Var.in_heap v) && Var.level v < 0 then (
|
||||||
(* new variable that is not assigned, add to heap.
|
(* new variable that is not assigned, add to heap.
|
||||||
remember to remove variable when we backtrack *)
|
remember to remove variable when we backtrack *)
|
||||||
on_backtrack_if_not_at_0 st (fun () -> H.remove st.order v);
|
on_backtrack st (fun () -> H.remove st.order v);
|
||||||
H.insert st.order v;
|
H.insert st.order v;
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -350,11 +346,10 @@ module Make
|
||||||
let attach_clause (self:t) (c:clause) : unit =
|
let attach_clause (self:t) (c:clause) : unit =
|
||||||
if not (Clause.attached c) then (
|
if not (Clause.attached c) then (
|
||||||
Log.debugf 5 (fun k -> k "(@[sat.attach_clause@ %a@])" Clause.debug c);
|
Log.debugf 5 (fun k -> k "(@[sat.attach_clause@ %a@])" Clause.debug c);
|
||||||
if not (at_level_0 self) then (
|
on_backtrack self
|
||||||
on_backtrack self
|
(fun () ->
|
||||||
(fun () ->
|
Log.debugf 5 (fun k->k "(@[sat.detach_clause@ %a@])" Clause.debug c);
|
||||||
Clause.set_attached c false)
|
Clause.set_attached c false);
|
||||||
);
|
|
||||||
Vec.push c.atoms.(0).neg.watched c;
|
Vec.push c.atoms.(0).neg.watched c;
|
||||||
Vec.push c.atoms.(1).neg.watched c;
|
Vec.push c.atoms.(1).neg.watched c;
|
||||||
Clause.set_attached c true;
|
Clause.set_attached c true;
|
||||||
|
|
@ -380,7 +375,8 @@ module Make
|
||||||
i.e we want to go back to the state the solver was in
|
i.e we want to go back to the state the solver was in
|
||||||
when decision level [lvl] was created. *)
|
when decision level [lvl] was created. *)
|
||||||
let cancel_until st lvl =
|
let cancel_until st lvl =
|
||||||
Log.debugf 5 (fun k -> k "(@[@{<yellow>sat.cancel_until@}@ :level %d@])" lvl);
|
Log.debugf 5
|
||||||
|
(fun k -> k "(@[@{<yellow>sat.cancel_until@}@ :lvl %d :cur-decision-lvl %d@])" lvl @@ decision_level st);
|
||||||
assert (lvl >= base_level st);
|
assert (lvl >= base_level st);
|
||||||
(* Nothing to do if we try to backtrack to a non-existent level. *)
|
(* Nothing to do if we try to backtrack to a non-existent level. *)
|
||||||
if decision_level st <= lvl then (
|
if decision_level st <= lvl then (
|
||||||
|
|
@ -471,15 +467,15 @@ module Make
|
||||||
Wrapper function for adding a new propagated formula. *)
|
Wrapper function for adding a new propagated formula. *)
|
||||||
let enqueue_bool st a reason : unit =
|
let enqueue_bool st a reason : unit =
|
||||||
if a.neg.is_true then (
|
if a.neg.is_true then (
|
||||||
Util.errorf "(@[sat.enqueue_bool@ :false-literal %a@])" Atom.debug a
|
Util.errorf "(@[sat.enqueue_bool.error@ :false-literal %a@])" Atom.debug a
|
||||||
);
|
);
|
||||||
let level = Vec.size st.trail in
|
let level = decision_level st in
|
||||||
Log.debugf 5
|
Log.debugf 5
|
||||||
(fun k->k "(@[sat.enqueue_bool@ :lvl %d@ %a@])" level Atom.debug a);
|
(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
|
let reason = if at_level_0 st then simpl_reason reason else reason in
|
||||||
(* backtrack assignment if needed. Trail is backtracked automatically. *)
|
(* backtrack assignment if needed. Trail is backtracked automatically. *)
|
||||||
assert (not a.is_true && a.var.v_level < 0 && a.var.reason = None);
|
assert (not a.is_true && a.var.v_level < 0 && a.var.reason = None);
|
||||||
on_backtrack_if_not_at_0 st
|
on_backtrack st
|
||||||
(fun () ->
|
(fun () ->
|
||||||
a.var.v_level <- -1;
|
a.var.v_level <- -1;
|
||||||
a.is_true <- false;
|
a.is_true <- false;
|
||||||
|
|
@ -653,7 +649,7 @@ module Make
|
||||||
| fuip :: _ ->
|
| fuip :: _ ->
|
||||||
let lclause = Clause.make_l cr.cr_learnt (History cr.cr_history) in
|
let lclause = Clause.make_l cr.cr_learnt (History cr.cr_history) in
|
||||||
Vec.push st.clauses_learnt lclause;
|
Vec.push st.clauses_learnt lclause;
|
||||||
attach_clause st lclause;
|
redo_down_to_level_0 st (fun () -> attach_clause st lclause);
|
||||||
clause_bump_activity st lclause;
|
clause_bump_activity st lclause;
|
||||||
if cr.cr_is_uip then (
|
if cr.cr_is_uip then (
|
||||||
enqueue_bool st fuip (Bcp lclause)
|
enqueue_bool st fuip (Bcp lclause)
|
||||||
|
|
@ -712,21 +708,23 @@ module Make
|
||||||
(* Since we cannot propagate the atom [a], in order to not lose
|
(* 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
|
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. *)
|
of clauses to add, so that it will be e-examined later. *)
|
||||||
Log.debug 5 "Unit clause, report failure";
|
Log.debug 5 "(sat.false-unit-clause@ report failure)";
|
||||||
report_unsat st clause
|
report_unsat st clause
|
||||||
) else if a.is_true then (
|
) else if a.is_true then (
|
||||||
(* If the atom is already true, then it should be because of a local hyp.
|
(* 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
|
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
|
that information, we store the clause in a stack of clauses that we will
|
||||||
add to the solver at the next pop. *)
|
add to the solver at the next pop. *)
|
||||||
Log.debug 5 "Unit clause, adding to root clauses";
|
Log.debug 5 "(sat.unit-clause@ adding to root clauses)";
|
||||||
assert (0 < a.var.v_level && a.var.v_level <= base_level st);
|
assert (0 < a.var.v_level && a.var.v_level <= base_level st);
|
||||||
on_backtrack_if_not_at_0 st (fun () -> Vec.pop vec_for_clause);
|
on_backtrack st
|
||||||
|
(fun () ->
|
||||||
|
Vec.pop vec_for_clause);
|
||||||
Vec.push vec_for_clause clause;
|
Vec.push vec_for_clause clause;
|
||||||
) else (
|
) else (
|
||||||
Log.debugf 5
|
Log.debugf 5
|
||||||
(fun k->k "(@[sat.add_clause.unit-clause@ :propagating %a@])" Atom.debug a);
|
(fun k->k "(@[sat.add_clause.unit-clause@ :propagating %a@])" Atom.debug a);
|
||||||
on_backtrack_if_not_at_0 st (fun () -> Vec.pop vec_for_clause);
|
on_backtrack st (fun () -> Vec.pop vec_for_clause);
|
||||||
Vec.push vec_for_clause clause;
|
Vec.push vec_for_clause clause;
|
||||||
enqueue_bool st a (Bcp clause)
|
enqueue_bool st a (Bcp clause)
|
||||||
)
|
)
|
||||||
|
|
@ -803,7 +801,7 @@ module Make
|
||||||
(fun () -> enqueue_bool st a (Bcp clause))
|
(fun () -> enqueue_bool st a (Bcp clause))
|
||||||
)
|
)
|
||||||
| a::b::_ ->
|
| a::b::_ ->
|
||||||
on_backtrack_if_not_at_0 st (fun () -> Vec.pop vec_for_clause);
|
on_backtrack st (fun () -> Vec.pop vec_for_clause);
|
||||||
Vec.push vec_for_clause clause;
|
Vec.push vec_for_clause clause;
|
||||||
(* Atoms need to be sorted in decreasing order of decision level,
|
(* Atoms need to be sorted in decreasing order of decision level,
|
||||||
or we might watch the wrong literals. *)
|
or we might watch the wrong literals. *)
|
||||||
|
|
@ -902,7 +900,9 @@ module Make
|
||||||
let act_push_ ~permanent st (l:formula IArray.t) (lemma:proof): unit =
|
let act_push_ ~permanent st (l:formula IArray.t) (lemma:proof): unit =
|
||||||
let atoms = IArray.to_array_map (new_atom ~permanent st) l in
|
let atoms = IArray.to_array_map (new_atom ~permanent st) l in
|
||||||
let c = Clause.make atoms (Lemma lemma) in
|
let c = Clause.make atoms (Lemma lemma) in
|
||||||
Log.debugf 3 (fun k->k "(@[sat.push_clause@ %a@])" Clause.debug c);
|
Log.debugf 3
|
||||||
|
(fun k->k "(@[sat.push_clause_from_theory@ :permanent %B@ %a@])"
|
||||||
|
permanent Clause.debug c);
|
||||||
add_clause ~permanent st c
|
add_clause ~permanent st c
|
||||||
|
|
||||||
(* TODO: ensure that the clause is removed upon backtracking *)
|
(* TODO: ensure that the clause is removed upon backtracking *)
|
||||||
|
|
@ -943,7 +943,6 @@ module Make
|
||||||
push_persistent = act_push_persistent st;
|
push_persistent = act_push_persistent st;
|
||||||
push_local = act_push_local st;
|
push_local = act_push_local st;
|
||||||
on_backtrack = on_backtrack st;
|
on_backtrack = on_backtrack st;
|
||||||
at_level_0 = act_at_level_0 st;
|
|
||||||
propagate = act_propagate st;
|
propagate = act_propagate st;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1000,7 +999,7 @@ module Make
|
||||||
while st.elt_head < Vec.size st.trail do
|
while st.elt_head < Vec.size st.trail do
|
||||||
let a = Vec.get st.trail st.elt_head in
|
let a = Vec.get st.trail st.elt_head in
|
||||||
incr num_props;
|
incr num_props;
|
||||||
Log.debugf 5 (fun k->k "(@[sat.propagate_atom@ %a@])" Atom.pp a);
|
Log.debugf 5 (fun k->k "(@[sat.bcp.propagate_atom@ %a@])" Atom.pp a);
|
||||||
propagate_atom st a;
|
propagate_atom st a;
|
||||||
st.elt_head <- st.elt_head + 1;
|
st.elt_head <- st.elt_head + 1;
|
||||||
done;
|
done;
|
||||||
|
|
@ -1052,7 +1051,7 @@ module Make
|
||||||
if Vec.size st.trail = St.nb_elt st.st
|
if Vec.size st.trail = St.nb_elt st.st
|
||||||
then raise Sat;
|
then raise Sat;
|
||||||
if n_of_conflicts > 0 && !conflictC >= n_of_conflicts then (
|
if n_of_conflicts > 0 && !conflictC >= n_of_conflicts then (
|
||||||
Log.debug 3 "Restarting...";
|
Log.debug 3 "(sat.restarting)";
|
||||||
cancel_until st (base_level st);
|
cancel_until st (base_level st);
|
||||||
raise Restart
|
raise Restart
|
||||||
);
|
);
|
||||||
|
|
@ -1088,7 +1087,7 @@ module Make
|
||||||
(* fixpoint of propagation and decisions until a model is found, or a
|
(* fixpoint of propagation and decisions until a model is found, or a
|
||||||
conflict is reached *)
|
conflict is reached *)
|
||||||
let solve (st:t) : unit =
|
let solve (st:t) : unit =
|
||||||
Log.debug 5 "solve";
|
Log.debug 5 "(sat.solve)";
|
||||||
if is_unsat st then raise Unsat;
|
if is_unsat st then raise Unsat;
|
||||||
let n_of_conflicts = ref (to_float restart_first) in
|
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_learnts = ref ((to_float (nb_clauses st)) *. learntsize_factor) in
|
||||||
|
|
@ -1145,17 +1144,17 @@ module Make
|
||||||
|
|
||||||
(* create a factice decision level for local assumptions *)
|
(* create a factice decision level for local assumptions *)
|
||||||
let push st : unit =
|
let push st : unit =
|
||||||
Log.debug 5 "Pushing a new user level";
|
Log.debug 5 "(sat.push-new-user-level)";
|
||||||
cancel_until st (base_level st);
|
cancel_until st (base_level st);
|
||||||
Log.debugf 5
|
Log.debugf 5
|
||||||
(fun k -> k "@[<v>Status:@,@[<hov 2>trail: %d - %d@,%a@]"
|
(fun k -> k "(@[<v>sat.status@ :trail %d - %d@ %a@]"
|
||||||
st.elt_head st.th_head (Vec.print ~sep:"" Atom.debug) st.trail);
|
st.elt_head st.th_head (Vec.print ~sep:"" Atom.debug) st.trail);
|
||||||
begin match propagate st with
|
begin match propagate st with
|
||||||
| Some confl ->
|
| Some confl ->
|
||||||
report_unsat st confl
|
report_unsat st confl
|
||||||
| None ->
|
| None ->
|
||||||
pp_trail st;
|
pp_trail st;
|
||||||
Log.debug 3 "Creating new user level";
|
Log.debug 3 "(sat.create-new-user-level)";
|
||||||
new_decision_level st;
|
new_decision_level st;
|
||||||
Vec.push st.user_levels (Vec.size st.clauses_temp);
|
Vec.push st.user_levels (Vec.size st.clauses_temp);
|
||||||
assert (decision_level st = base_level st)
|
assert (decision_level st = base_level st)
|
||||||
|
|
@ -1215,7 +1214,7 @@ module Make
|
||||||
let res = Array.exists (fun x -> x) tmp in
|
let res = Array.exists (fun x -> x) tmp in
|
||||||
if not res then (
|
if not res then (
|
||||||
Log.debugf 5
|
Log.debugf 5
|
||||||
(fun k -> k "Clause not satisfied: @[<hov>%a@]" Clause.debug c);
|
(fun k -> k "(@[sat.check-clause.error@ :not-satisfied %a@])" Clause.debug c);
|
||||||
false
|
false
|
||||||
) else
|
) else
|
||||||
true
|
true
|
||||||
|
|
|
||||||
|
|
@ -52,9 +52,6 @@ type ('form, 'proof) actions = Actions of {
|
||||||
on_backtrack: (unit -> unit) -> unit;
|
on_backtrack: (unit -> unit) -> unit;
|
||||||
(** [on_backtrack f] calls [f] when the main solver backtracks *)
|
(** [on_backtrack f] calls [f] when the main solver backtracks *)
|
||||||
|
|
||||||
at_level_0 : unit -> bool;
|
|
||||||
(** Are we at level 0? *)
|
|
||||||
|
|
||||||
propagate : 'form -> 'form list -> 'proof -> unit;
|
propagate : 'form -> 'form list -> 'proof -> unit;
|
||||||
(** [propagate lit causes proof] informs the solver to propagate [lit], with the reason
|
(** [propagate lit causes proof] informs the solver to propagate [lit], with the reason
|
||||||
that the clause [causes => lit] is a theory tautology. It is faster than pushing
|
that the clause [causes => lit] is a theory tautology. It is faster than pushing
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,6 @@ type actions = {
|
||||||
on_backtrack:(unit -> unit) -> unit;
|
on_backtrack:(unit -> unit) -> unit;
|
||||||
(** Register a callback to be invoked upon backtracking below the current level *)
|
(** Register a callback to be invoked upon backtracking below the current level *)
|
||||||
|
|
||||||
at_lvl_0:unit -> bool;
|
|
||||||
(** Are we currently at backtracking level 0? *)
|
|
||||||
|
|
||||||
on_merge:repr -> repr -> explanation -> unit;
|
on_merge:repr -> repr -> explanation -> unit;
|
||||||
(** Call this when two classes are merged *)
|
(** Call this when two classes are merged *)
|
||||||
|
|
||||||
|
|
@ -65,10 +62,7 @@ type t = {
|
||||||
several times.
|
several times.
|
||||||
See "fast congruence closure and extensions", Nieuwenhis&al, page 14 *)
|
See "fast congruence closure and extensions", Nieuwenhis&al, page 14 *)
|
||||||
|
|
||||||
let[@inline] on_backtrack_if_not_lvl_0 cc f : unit =
|
let[@inline] on_backtrack cc f : unit = cc.acts.on_backtrack f
|
||||||
if not (cc.acts.at_lvl_0 ()) then (
|
|
||||||
cc.acts.on_backtrack f
|
|
||||||
)
|
|
||||||
|
|
||||||
let[@inline] is_root_ (n:node) : bool = n.n_root == n
|
let[@inline] is_root_ (n:node) : bool = n.n_root == n
|
||||||
|
|
||||||
|
|
@ -90,7 +84,7 @@ let rec find_rec cc (n:node) : repr =
|
||||||
let root = find_rec cc old_root in
|
let root = find_rec cc old_root in
|
||||||
(* path compression *)
|
(* path compression *)
|
||||||
if (root :> node) != old_root then (
|
if (root :> node) != old_root then (
|
||||||
on_backtrack_if_not_lvl_0 cc (fun () -> n.n_root <- old_root);
|
on_backtrack cc (fun () -> n.n_root <- old_root);
|
||||||
n.n_root <- (root :> node);
|
n.n_root <- (root :> node);
|
||||||
);
|
);
|
||||||
root
|
root
|
||||||
|
|
@ -155,7 +149,7 @@ let add_signature cc (t:term) (r:repr): unit = match signature cc t with
|
||||||
(* add, but only if not present already *)
|
(* add, but only if not present already *)
|
||||||
begin match Sig_tbl.get cc.signatures_tbl s with
|
begin match Sig_tbl.get cc.signatures_tbl s with
|
||||||
| None ->
|
| None ->
|
||||||
on_backtrack_if_not_lvl_0 cc
|
on_backtrack cc
|
||||||
(fun () -> Sig_tbl.remove cc.signatures_tbl s);
|
(fun () -> Sig_tbl.remove cc.signatures_tbl s);
|
||||||
Sig_tbl.add cc.signatures_tbl s r;
|
Sig_tbl.add cc.signatures_tbl s r;
|
||||||
| Some r' ->
|
| Some r' ->
|
||||||
|
|
@ -172,13 +166,13 @@ let push_pending cc t : unit =
|
||||||
|
|
||||||
let push_combine cc t u e : unit =
|
let push_combine cc t u e : unit =
|
||||||
Log.debugf 5
|
Log.debugf 5
|
||||||
(fun k->k "(@[<hv1>cc.push_combine@ %a@ %a@ expl: %a@])"
|
(fun k->k "(@[<hv1>cc.push_combine@ :t1 %a@ :t2 %a@ :expl %a@])"
|
||||||
Equiv_class.pp t Equiv_class.pp u Explanation.pp e);
|
Equiv_class.pp t Equiv_class.pp u Explanation.pp e);
|
||||||
Vec.push cc.combine (t,u,e)
|
Vec.push cc.combine (t,u,e)
|
||||||
|
|
||||||
let push_propagation cc (lit:lit) (expl:explanation Bag.t): unit =
|
let push_propagation cc (lit:lit) (expl:explanation Bag.t): unit =
|
||||||
Log.debugf 5
|
Log.debugf 5
|
||||||
(fun k->k "(@[<hv1>cc.push_propagate@ %a@ expl: (@[<hv>%a@])@])"
|
(fun k->k "(@[<hv1>cc.push_propagate@ %a@ :expl (@[<hv>%a@])@])"
|
||||||
Lit.pp lit (Util.pp_seq Explanation.pp) @@ Bag.to_seq expl);
|
Lit.pp lit (Util.pp_seq Explanation.pp) @@ Bag.to_seq expl);
|
||||||
cc.acts.propagate lit expl
|
cc.acts.propagate lit expl
|
||||||
|
|
||||||
|
|
@ -192,8 +186,7 @@ let[@inline] union cc (a:node) (b:node) (e:explanation): unit =
|
||||||
postcondition: [n.n_expl = None] *)
|
postcondition: [n.n_expl = None] *)
|
||||||
let rec reroot_expl (cc:t) (n:node): unit =
|
let rec reroot_expl (cc:t) (n:node): unit =
|
||||||
let old_expl = n.n_expl in
|
let old_expl = n.n_expl in
|
||||||
on_backtrack_if_not_lvl_0 cc
|
on_backtrack cc (fun () -> n.n_expl <- old_expl);
|
||||||
(fun () -> n.n_expl <- old_expl);
|
|
||||||
begin match old_expl with
|
begin match old_expl with
|
||||||
| E_none -> () (* already root *)
|
| E_none -> () (* already root *)
|
||||||
| E_some {next=u; expl=e_n_u} ->
|
| E_some {next=u; expl=e_n_u} ->
|
||||||
|
|
@ -254,7 +247,7 @@ let ps_clear (cc:t) =
|
||||||
()
|
()
|
||||||
|
|
||||||
let rec decompose_explain cc (e:explanation): unit =
|
let rec decompose_explain cc (e:explanation): unit =
|
||||||
Log.debugf 5 (fun k->k "(@[decompose_expl@ %a@])" Explanation.pp e);
|
Log.debugf 5 (fun k->k "(@[cc.decompose_expl@ %a@])" Explanation.pp e);
|
||||||
begin match e with
|
begin match e with
|
||||||
| E_reduction -> ()
|
| E_reduction -> ()
|
||||||
| E_lit lit -> ps_add_lit cc lit
|
| E_lit lit -> ps_add_lit cc lit
|
||||||
|
|
@ -305,7 +298,7 @@ let explain_loop (cc : t) : Lit.Set.t =
|
||||||
while not (Vec.is_empty cc.ps_queue) do
|
while not (Vec.is_empty cc.ps_queue) do
|
||||||
let a, b = Vec.pop_last cc.ps_queue in
|
let a, b = Vec.pop_last cc.ps_queue in
|
||||||
Log.debugf 5
|
Log.debugf 5
|
||||||
(fun k->k "(@[explain_loop at@ %a@ %a@])" Equiv_class.pp a Equiv_class.pp b);
|
(fun k->k "(@[cc.explain_loop at@ %a@ %a@])" Equiv_class.pp a Equiv_class.pp b);
|
||||||
assert (Equiv_class.equal (find cc a) (find cc b));
|
assert (Equiv_class.equal (find cc a) (find cc b));
|
||||||
let c = find_common_ancestor a b in
|
let c = find_common_ancestor a b in
|
||||||
explain_along_path cc a c;
|
explain_along_path cc a c;
|
||||||
|
|
@ -352,9 +345,9 @@ let explain_unfold_bag ?(init=Lit.Set.empty) cc (b:explanation Bag.t) : Lit.Set.
|
||||||
let add_tag_n cc (n:node) (tag:int) (expl:explanation) : unit =
|
let add_tag_n cc (n:node) (tag:int) (expl:explanation) : unit =
|
||||||
assert (is_root_ n);
|
assert (is_root_ n);
|
||||||
if not (Util.Int_map.mem tag n.n_tags) then (
|
if not (Util.Int_map.mem tag n.n_tags) then (
|
||||||
on_backtrack_if_not_lvl_0 cc
|
on_backtrack cc
|
||||||
(fun () -> n.n_tags <- Util.Int_map.remove tag n.n_tags);
|
(fun () -> n.n_tags <- Util.Int_map.remove tag n.n_tags);
|
||||||
n.n_tags <- Util.Int_map.add tag expl n.n_tags;
|
n.n_tags <- Util.Int_map.add tag (n,expl) n.n_tags;
|
||||||
)
|
)
|
||||||
|
|
||||||
(* conflict because [expl => t1 ≠ t2] but they are the same *)
|
(* conflict because [expl => t1 ≠ t2] but they are the same *)
|
||||||
|
|
@ -396,7 +389,7 @@ and update_combine cc =
|
||||||
let rb = find cc b in
|
let rb = find cc b in
|
||||||
if not (Equiv_class.equal ra rb) then (
|
if not (Equiv_class.equal ra rb) then (
|
||||||
assert (is_root_ ra);
|
assert (is_root_ ra);
|
||||||
assert (is_root_ (rb:>node));
|
assert (is_root_ rb);
|
||||||
(* We will merge [r_from] into [r_into].
|
(* We will merge [r_from] into [r_into].
|
||||||
we try to ensure that [size ra <= size rb] in general, unless
|
we try to ensure that [size ra <= size rb] in general, unless
|
||||||
it clashes with the invariant that the representative must
|
it clashes with the invariant that the representative must
|
||||||
|
|
@ -412,13 +405,19 @@ and update_combine cc =
|
||||||
in
|
in
|
||||||
let new_tags =
|
let new_tags =
|
||||||
Util.Int_map.union
|
Util.Int_map.union
|
||||||
(fun _i e1 e2 ->
|
(fun _i (n1,e1) (n2,e2) ->
|
||||||
(* both maps contain same tag [_i]. conflict clause:
|
(* both maps contain same tag [_i]. conflict clause:
|
||||||
[e1 & e2 & e_ab] impossible *)
|
[e1 & e2 & e_ab] impossible *)
|
||||||
Log.debugf 5 (fun k->k "(cc.merge.distinct_conflict@ :tag %d@])" _i);
|
Log.debugf 5
|
||||||
|
(fun k->k "(@[<hv>cc.merge.distinct_conflict@ :tag %d@ \
|
||||||
|
@[:r1 %a@ :e1 %a@]@ @[:r2 %a@ :e2 %a@]@ :e_ab %a@])"
|
||||||
|
_i Equiv_class.pp n1 Explanation.pp e1
|
||||||
|
Equiv_class.pp n2 Explanation.pp e2 Explanation.pp e_ab);
|
||||||
let lits = explain_unfold cc e1 in
|
let lits = explain_unfold cc e1 in
|
||||||
let lits = explain_unfold ~init:lits cc e2 in
|
let lits = explain_unfold ~init:lits cc e2 in
|
||||||
let lits = explain_unfold ~init:lits cc e_ab in
|
let lits = explain_unfold ~init:lits cc e_ab in
|
||||||
|
let lits = explain_eq_n ~init:lits cc a n1 in
|
||||||
|
let lits = explain_eq_n ~init:lits cc b n2 in
|
||||||
raise_conflict cc lits)
|
raise_conflict cc lits)
|
||||||
ra.n_tags rb.n_tags
|
ra.n_tags rb.n_tags
|
||||||
in
|
in
|
||||||
|
|
@ -462,8 +461,11 @@ and update_combine cc =
|
||||||
let r_into = (r_into :> node) in
|
let r_into = (r_into :> node) in
|
||||||
let r_into_old_parents = r_into.n_parents in
|
let r_into_old_parents = r_into.n_parents in
|
||||||
let r_into_old_tags = r_into.n_tags in
|
let r_into_old_tags = r_into.n_tags in
|
||||||
on_backtrack_if_not_lvl_0 cc
|
on_backtrack cc
|
||||||
(fun () ->
|
(fun () ->
|
||||||
|
Log.debugf 5
|
||||||
|
(fun k->k "(@[cc.undo_merge@ :of %a :into %a@])"
|
||||||
|
Term.pp r_from.n_term Term.pp r_into.n_term);
|
||||||
r_from.n_root <- r_from;
|
r_from.n_root <- r_from;
|
||||||
r_into.n_tags <- r_into_old_tags;
|
r_into.n_tags <- r_into_old_tags;
|
||||||
r_into.n_parents <- r_into_old_parents);
|
r_into.n_parents <- r_into_old_parents);
|
||||||
|
|
@ -475,7 +477,7 @@ and update_combine cc =
|
||||||
begin
|
begin
|
||||||
reroot_expl cc a;
|
reroot_expl cc a;
|
||||||
assert (a.n_expl = E_none);
|
assert (a.n_expl = E_none);
|
||||||
on_backtrack_if_not_lvl_0 cc (fun () -> a.n_expl <- E_none);
|
on_backtrack cc (fun () -> a.n_expl <- E_none);
|
||||||
a.n_expl <- E_some {next=b; expl=e_ab};
|
a.n_expl <- E_some {next=b; expl=e_ab};
|
||||||
end;
|
end;
|
||||||
(* notify listeners of the merge *)
|
(* notify listeners of the merge *)
|
||||||
|
|
@ -501,15 +503,6 @@ and eval_pending (t:term): unit =
|
||||||
theories
|
theories
|
||||||
*)
|
*)
|
||||||
|
|
||||||
(* FIXME: remove?
|
|
||||||
(* main CC algo: add missing terms to the congruence class *)
|
|
||||||
and update_add (cc:t) terms () =
|
|
||||||
while not (Queue.is_empty cc.terms_to_add) do
|
|
||||||
let t = Queue.pop cc.terms_to_add in
|
|
||||||
add cc t
|
|
||||||
done
|
|
||||||
*)
|
|
||||||
|
|
||||||
(* add [t] to [cc] when not present already *)
|
(* add [t] to [cc] when not present already *)
|
||||||
and add_new_term cc (t:term) : node =
|
and add_new_term cc (t:term) : node =
|
||||||
assert (not @@ mem cc t);
|
assert (not @@ mem cc t);
|
||||||
|
|
@ -517,7 +510,7 @@ and add_new_term cc (t:term) : node =
|
||||||
(* how to add a subterm *)
|
(* how to add a subterm *)
|
||||||
let add_to_parents_of_sub_node (sub:node) : unit =
|
let add_to_parents_of_sub_node (sub:node) : unit =
|
||||||
let old_parents = sub.n_parents in
|
let old_parents = sub.n_parents in
|
||||||
on_backtrack_if_not_lvl_0 cc
|
on_backtrack cc
|
||||||
(fun () -> sub.n_parents <- old_parents);
|
(fun () -> sub.n_parents <- old_parents);
|
||||||
sub.n_parents <- Bag.cons n sub.n_parents;
|
sub.n_parents <- Bag.cons n sub.n_parents;
|
||||||
push_pending cc sub
|
push_pending cc sub
|
||||||
|
|
@ -541,7 +534,7 @@ and add_new_term cc (t:term) : node =
|
||||||
tc.tc_t_relevant view add_sub_t
|
tc.tc_t_relevant view add_sub_t
|
||||||
end;
|
end;
|
||||||
(* remove term when we backtrack *)
|
(* remove term when we backtrack *)
|
||||||
on_backtrack_if_not_lvl_0 cc (fun () -> Term.Tbl.remove cc.tbl t);
|
on_backtrack cc (fun () -> Term.Tbl.remove cc.tbl t);
|
||||||
(* add term to the table *)
|
(* add term to the table *)
|
||||||
Term.Tbl.add cc.tbl t n;
|
Term.Tbl.add cc.tbl t n;
|
||||||
(* [n] might be merged with other equiv classes *)
|
(* [n] might be merged with other equiv classes *)
|
||||||
|
|
@ -571,6 +564,10 @@ let assert_lit cc lit : unit = match Lit.view lit with
|
||||||
(* equate t and true/false *)
|
(* equate t and true/false *)
|
||||||
let rhs = if sign then true_ cc else false_ cc in
|
let rhs = if sign then true_ cc else false_ cc in
|
||||||
let n = add cc t in
|
let n = add cc t in
|
||||||
|
(* TODO: ensure that this is O(1).
|
||||||
|
basically, just have [n] point to true/false and thus acquire
|
||||||
|
the corresponding value, so its superterms (like [ite]) can evaluate
|
||||||
|
properly *)
|
||||||
push_combine cc n rhs (E_lit lit);
|
push_combine cc n rhs (E_lit lit);
|
||||||
()
|
()
|
||||||
|
|
||||||
|
|
@ -612,14 +609,6 @@ module Distinct_ = struct
|
||||||
mutable tags: Int_set.t;
|
mutable tags: Int_set.t;
|
||||||
}
|
}
|
||||||
|
|
||||||
let get (n:Equiv_class.t) : Int_set.t =
|
|
||||||
Equiv_class.payload_find
|
|
||||||
~f:(function
|
|
||||||
| P_dist {tags} -> Some tags
|
|
||||||
| _ -> None)
|
|
||||||
n
|
|
||||||
|> CCOpt.get_or ~default:Int_set.empty
|
|
||||||
|
|
||||||
let add_tag (tag:int) (n:Equiv_class.t) : unit =
|
let add_tag (tag:int) (n:Equiv_class.t) : unit =
|
||||||
if not @@
|
if not @@
|
||||||
CCList.exists
|
CCList.exists
|
||||||
|
|
@ -633,7 +622,6 @@ module Distinct_ = struct
|
||||||
end
|
end
|
||||||
|
|
||||||
let create ?(size=2048) ~actions (tst:Term.state) : t =
|
let create ?(size=2048) ~actions (tst:Term.state) : t =
|
||||||
assert (actions.at_lvl_0 ());
|
|
||||||
let nd = Equiv_class.dummy in
|
let nd = Equiv_class.dummy in
|
||||||
let rec cc = {
|
let rec cc = {
|
||||||
tst;
|
tst;
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,6 @@ type actions = {
|
||||||
on_backtrack:(unit -> unit) -> unit;
|
on_backtrack:(unit -> unit) -> unit;
|
||||||
(** Register a callback to be invoked upon backtracking below the current level *)
|
(** Register a callback to be invoked upon backtracking below the current level *)
|
||||||
|
|
||||||
at_lvl_0:unit -> bool;
|
|
||||||
(** Are we currently at backtracking level 0? *)
|
|
||||||
|
|
||||||
on_merge:repr -> repr -> explanation -> unit;
|
on_merge:repr -> repr -> explanation -> unit;
|
||||||
(** Call this when two classes are merged *)
|
(** Call this when two classes are merged *)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ and cc_node = {
|
||||||
mutable n_root: cc_node; (* representative of congruence class (itself if a representative) *)
|
mutable n_root: cc_node; (* representative of congruence class (itself if a representative) *)
|
||||||
mutable n_expl: explanation_forest_link; (* the rooted forest for explanations *)
|
mutable n_expl: explanation_forest_link; (* the rooted forest for explanations *)
|
||||||
mutable n_payload: cc_node_payload list; (* list of theory payloads *)
|
mutable n_payload: cc_node_payload list; (* list of theory payloads *)
|
||||||
mutable n_tags: explanation Util.Int_map.t; (* "distinct" tags (i.e. set of `(distinct t1…tn)` terms this belongs to *)
|
mutable n_tags: (cc_node * explanation) Util.Int_map.t; (* "distinct" tags (i.e. set of `(distinct t1…tn)` terms this belongs to *)
|
||||||
}
|
}
|
||||||
|
|
||||||
(** Theory-extensible payloads *)
|
(** Theory-extensible payloads *)
|
||||||
|
|
|
||||||
|
|
@ -38,9 +38,6 @@ type actions = {
|
||||||
on_backtrack: (unit -> unit) -> unit;
|
on_backtrack: (unit -> unit) -> unit;
|
||||||
(** Register an action to do when we backtrack *)
|
(** Register an action to do when we backtrack *)
|
||||||
|
|
||||||
at_lvl_0: unit -> bool;
|
|
||||||
(** Are we at level 0 of backtracking? *)
|
|
||||||
|
|
||||||
raise_conflict: 'a. conflict -> 'a;
|
raise_conflict: 'a. conflict -> 'a;
|
||||||
(** Give a conflict clause to the solver *)
|
(** Give a conflict clause to the solver *)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ let assume_lit (self:t) (lit:Lit.t) : unit =
|
||||||
| Lit_atom {term_cell=Bool true; _} -> ()
|
| Lit_atom {term_cell=Bool true; _} -> ()
|
||||||
| Lit_atom {term_cell=Bool false; _} -> ()
|
| Lit_atom {term_cell=Bool false; _} -> ()
|
||||||
| Lit_atom _ ->
|
| Lit_atom _ ->
|
||||||
(* transmit to CC and theories *)
|
(* transmit to theories. *)
|
||||||
Congruence_closure.assert_lit (cc self) lit;
|
Congruence_closure.assert_lit (cc self) lit;
|
||||||
theories self (fun (Theory.State th) -> th.on_assert th.st lit);
|
theories self (fun (Theory.State th) -> th.on_assert th.st lit);
|
||||||
end
|
end
|
||||||
|
|
@ -148,7 +148,6 @@ let mk_cc_actions (self:t) : Congruence_closure.actions =
|
||||||
let Sat_solver.Actions r = self.cdcl_acts in
|
let Sat_solver.Actions r = self.cdcl_acts in
|
||||||
{ Congruence_closure.
|
{ Congruence_closure.
|
||||||
on_backtrack = r.on_backtrack;
|
on_backtrack = r.on_backtrack;
|
||||||
at_lvl_0 = r.at_level_0;
|
|
||||||
on_merge = on_merge_from_cc self;
|
on_merge = on_merge_from_cc self;
|
||||||
raise_conflict = act_raise_conflict;
|
raise_conflict = act_raise_conflict;
|
||||||
propagate = act_propagate self;
|
propagate = act_propagate self;
|
||||||
|
|
@ -170,7 +169,7 @@ let create (cdcl_acts:_ Sat_solver.actions) : t =
|
||||||
theories = [];
|
theories = [];
|
||||||
conflict = None;
|
conflict = None;
|
||||||
} in
|
} in
|
||||||
ignore @@ Lazy.force @@ self.cc;
|
ignore (Lazy.force @@ self.cc : Congruence_closure.t);
|
||||||
self
|
self
|
||||||
|
|
||||||
(** {2 Interface to individual theories} *)
|
(** {2 Interface to individual theories} *)
|
||||||
|
|
@ -203,7 +202,6 @@ let mk_theory_actions (self:t) : Theory.actions =
|
||||||
let Sat_solver.Actions r = self.cdcl_acts in
|
let Sat_solver.Actions r = self.cdcl_acts in
|
||||||
{ Theory.
|
{ Theory.
|
||||||
on_backtrack = r.on_backtrack;
|
on_backtrack = r.on_backtrack;
|
||||||
at_lvl_0 = r.at_level_0;
|
|
||||||
raise_conflict = act_raise_conflict;
|
raise_conflict = act_raise_conflict;
|
||||||
propagate = act_propagate self;
|
propagate = act_propagate self;
|
||||||
all_classes = act_all_classes self;
|
all_classes = act_all_classes self;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue