refactor(cc): merge the two task queues

This commit is contained in:
Simon Cruanes 2018-06-27 21:43:15 -05:00
parent b7518a632a
commit bf70f9688d

View file

@ -16,9 +16,6 @@ end
module Sig_tbl = CCHashtbl.Make(Signature) module Sig_tbl = CCHashtbl.Make(Signature)
type merge_op = node * node * explanation
(* a merge operation to perform *)
module type ACTIONS = sig module type ACTIONS = sig
val on_backtrack: (unit -> unit) -> unit val 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 *)
@ -35,6 +32,10 @@ end
type actions = (module ACTIONS) type actions = (module ACTIONS)
type task =
| T_pending of node
| T_merge of node * node * explanation
type t = { type t = {
tst: Term.state; tst: Term.state;
acts: actions; acts: actions;
@ -48,10 +49,8 @@ type t = {
The critical property is that all members of an equivalence class The critical property is that all members of an equivalence class
that have the same "shape" (including head symbol) that have the same "shape" (including head symbol)
have the same signature *) have the same signature *)
pending: node Vec.t; tasks: task Vec.t;
(* nodes to check, maybe their new signature is in {!signatures_tbl} *) (* tasks to perform *)
combine: merge_op Vec.t;
(* pairs of terms to merge *)
mutable ps_lits: Lit.Set.t; mutable ps_lits: Lit.Set.t;
(* proof state *) (* proof state *)
ps_queue: (node*node) Vec.t; ps_queue: (node*node) Vec.t;
@ -146,22 +145,18 @@ let add_signature cc (t:term) (r:node): unit =
assert (same_class cc r r'); assert (same_class cc r r');
end end
let[@inline] is_done (cc:t): bool =
Vec.is_empty cc.pending &&
Vec.is_empty cc.combine
let push_pending cc t : unit = let push_pending cc t : unit =
if not @@ Equiv_class.get_field Equiv_class.field_is_pending t then ( if not @@ Equiv_class.get_field Equiv_class.field_is_pending t then (
Log.debugf 5 (fun k->k "(@[<hv1>cc.push_pending@ %a@])" Equiv_class.pp t); Log.debugf 5 (fun k->k "(@[<hv1>cc.push_pending@ %a@])" Equiv_class.pp t);
Equiv_class.set_field Equiv_class.field_is_pending true t; Equiv_class.set_field Equiv_class.field_is_pending true t;
Vec.push cc.pending t Vec.push cc.tasks (T_pending t)
) )
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@ :t1 %a@ :t2 %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.tasks @@ T_merge (t,u,e)
(* re-root the explanation tree of the equivalence class of [n] (* re-root the explanation tree of the equivalence class of [n]
so that it points to [n]. so that it points to [n].
@ -296,11 +291,17 @@ let add_tag_n cc (n:node) (tag:int) (expl:explanation) : unit =
(* main CC algo: add terms from [pending] to the signature table, (* main CC algo: add terms from [pending] to the signature table,
check for collisions *) check for collisions *)
let rec update_pending (cc:t): unit = let rec update_tasks (cc:t): unit =
(* step 2 deal with pending (parent) terms whose equiv class (* step 2 deal with pending (parent) terms whose equiv class
might have changed *) might have changed *)
while not (Vec.is_empty cc.pending) do while not (Vec.is_empty cc.tasks) do
let n = Vec.pop_last cc.pending in let task = Vec.pop_last cc.tasks in
match task with
| T_pending n -> task_pending_ cc n
| T_merge (t,u,expl) -> task_merge_ cc t u expl
done
and task_pending_ cc n =
Equiv_class.set_field Equiv_class.field_is_pending false n; Equiv_class.set_field Equiv_class.field_is_pending false n;
(* check if some parent collided *) (* check if some parent collided *)
begin match find_by_signature cc n.n_term with begin match find_by_signature cc n.n_term with
@ -328,16 +329,11 @@ let rec update_pending (cc:t): unit =
(* FIXME: when to actually evaluate? (* FIXME: when to actually evaluate?
eval_pending cc; eval_pending cc;
*) *)
done; ()
if not (is_done cc) then (
update_combine cc (* repeat *)
)
(* main CC algo: merge equivalence classes in [st.combine]. (* main CC algo: merge equivalence classes in [st.combine].
@raise Exn_unsat if merge fails *) @raise Exn_unsat if merge fails *)
and update_combine cc = and task_merge_ cc a b e_ab : unit =
while not (Vec.is_empty cc.combine) do
let a, b, e_ab = Vec.pop_last cc.combine in
let ra = find cc a in let ra = find cc a in
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 (
@ -395,9 +391,6 @@ and update_combine cc =
(* notify listeners of the merge *) (* notify listeners of the merge *)
notify_merge cc r_from ~into:r_into e_ab; notify_merge cc r_from ~into:r_into e_ab;
) )
done;
(* now update pending terms again *)
update_pending cc
(* Checks if [ra] and [~into] have compatible normal forms and can (* Checks if [ra] and [~into] have compatible normal forms and can
be merged w.r.t. the theories. be merged w.r.t. the theories.
@ -452,12 +445,12 @@ and[@inline] add_ cc t : node =
let add cc t : node = let add cc t : node =
let n = add_ cc t in let n = add_ cc t in
update_pending cc; update_tasks cc;
n n
let add_seq cc seq = let add_seq cc seq =
seq (fun t -> ignore @@ add_ cc t); seq (fun t -> ignore @@ add_ cc t);
update_pending cc update_tasks cc
(* assert that this boolean literal holds *) (* assert that this boolean literal holds *)
let assert_lit cc lit : unit = let assert_lit cc lit : unit =
@ -473,7 +466,7 @@ let assert_lit cc lit : unit =
the corresponding value, so its superterms (like [ite]) can evaluate the corresponding value, so its superterms (like [ite]) can evaluate
properly *) properly *)
push_combine cc n rhs (E_lit lit); push_combine cc n rhs (E_lit lit);
update_combine cc update_tasks cc
let assert_eq cc (t:term) (u:term) e : unit = let assert_eq cc (t:term) (u:term) e : unit =
let n1 = add_ cc t in let n1 = add_ cc t in
@ -482,7 +475,7 @@ let assert_eq cc (t:term) (u:term) e : unit =
let e = Explanation.E_lits e in let e = Explanation.E_lits e in
push_combine cc n1 n2 e; push_combine cc n1 n2 e;
); );
update_pending cc update_tasks cc
let assert_distinct cc (l:term list) ~neq (lit:Lit.t) : unit = let assert_distinct cc (l:term list) ~neq (lit:Lit.t) : unit =
assert (match l with[] | [_] -> false | _ -> true); assert (match l with[] | [_] -> false | _ -> true);
@ -513,8 +506,7 @@ let create ?(size=2048) ~actions (tst:Term.state) : t =
acts=actions; acts=actions;
tbl = Term.Tbl.create size; tbl = Term.Tbl.create size;
signatures_tbl = Sig_tbl.create size; signatures_tbl = Sig_tbl.create size;
pending=Vec.make_empty Equiv_class.dummy; tasks=Vec.make_empty (T_pending Equiv_class.dummy);
combine= Vec.make_empty (nd,nd,E_reduction);
ps_lits=Lit.Set.empty; ps_lits=Lit.Set.empty;
ps_queue=Vec.make_empty (nd,nd); ps_queue=Vec.make_empty (nd,nd);
true_ = Equiv_class.dummy; true_ = Equiv_class.dummy;
@ -526,7 +518,7 @@ let create ?(size=2048) ~actions (tst:Term.state) : t =
let final_check cc : unit = let final_check cc : unit =
Log.debug 5 "(CC.final_check)"; Log.debug 5 "(CC.final_check)";
update_pending cc update_tasks cc
(* model: map each uninterpreted equiv class to some ID *) (* model: map each uninterpreted equiv class to some ID *)
let mk_model (cc:t) (m:Model.t) : Model.t = let mk_model (cc:t) (m:Model.t) : Model.t =