wip: refactor proof

This commit is contained in:
Simon Cruanes 2021-10-07 20:49:39 -04:00
parent bbb995b0d5
commit d3537f2c3f
No known key found for this signature in database
GPG key ID: 4AC01D0849AA62B6
10 changed files with 286 additions and 200 deletions

View file

@ -29,7 +29,6 @@ module Make (A: CC_ARG)
type fun_ = T.Fun.t
type proof = A.proof
type proof_step = A.proof_step
type proof_rule = proof -> proof_step
type actions = Actions.t
module Term = T.Term
@ -281,7 +280,7 @@ module Make (A: CC_ARG)
and ev_on_post_merge = t -> actions -> N.t -> N.t -> unit
and ev_on_new_term = t -> N.t -> term -> unit
and ev_on_conflict = t -> th:bool -> lit list -> unit
and ev_on_propagate = t -> lit -> (unit -> lit list * (proof -> unit)) -> unit
and ev_on_propagate = t -> lit -> (unit -> lit list * proof_step) -> unit
and ev_on_is_subterm = N.t -> term -> unit
let[@inline] size_ (r:repr) = r.n_size
@ -374,7 +373,7 @@ module Make (A: CC_ARG)
n.n_expl <- FL_none;
end
let raise_conflict_ (cc:t) ~th (acts:actions) (e:lit list) p : _ =
let raise_conflict_ (cc:t) ~th (acts:actions) (e:lit list) (p:proof_step) : _ =
Profile.instant "cc.conflict";
(* clear tasks queue *)
Vec.clear cc.pending;
@ -658,10 +657,11 @@ module Make (A: CC_ARG)
let lits = explain_decompose_expl cc ~th [] e_ab in
let lits = explain_equal_rec_ cc ~th lits a ra in
let lits = explain_equal_rec_ cc ~th lits b rb in
let emit_proof p =
let pr =
let p_lits = Iter.of_list lits |> Iter.map Lit.neg in
P.lemma_cc p_lits p in
raise_conflict_ cc ~th:!th acts (List.rev_map Lit.neg lits) emit_proof
P.lemma_cc p_lits @@ Actions.proof acts
in
raise_conflict_ cc ~th:!th acts (List.rev_map Lit.neg lits) pr
);
(* We will merge [r_from] into [r_into].
we try to ensure that [size ra <= size rb] in general, but always
@ -776,12 +776,12 @@ module Make (A: CC_ARG)
let e = lazy (
let lazy (th, acc) = half_expl in
let lits = explain_equal_rec_ cc ~th acc u1 t1 in
let emit_proof p =
let pr =
(* make a tautology, not a true guard *)
let p_lits = Iter.cons lit (Iter.of_list lits |> Iter.map Lit.neg) in
P.lemma_cc p_lits p
P.lemma_cc p_lits @@ Actions.proof acts
in
lits, emit_proof
lits, pr
) in
fun () -> Lazy.force e
in
@ -848,11 +848,11 @@ module Make (A: CC_ARG)
let th = ref true in
let lits = explain_decompose_expl cc ~th [] expl in
let lits = List.rev_map Lit.neg lits in
let emit_proof p =
let pr =
let p_lits = Iter.of_list lits in
P.lemma_cc p_lits p
P.lemma_cc p_lits @@ Actions.proof acts
in
raise_conflict_ cc ~th:!th acts lits emit_proof
raise_conflict_ cc ~th:!th acts lits pr
let merge cc n1 n2 expl =
Log.debugf 5

View file

@ -162,7 +162,7 @@ module type SAT_PROOF = sig
(** The stored proof (possibly nil, possibly on disk, possibly in memory) *)
type proof_step
(** identifier for a proof proof_rule *)
(** identifier for a proof *)
module Step_vec : Vec_sig.S with type elt = proof_step
(** A vector of steps *)
@ -176,11 +176,6 @@ module type SAT_PROOF = sig
val enabled : t -> bool
(** Returns true if proof production is enabled *)
val with_proof : t -> (t -> 'a) -> 'a
(** If proof is enabled, call [f] on it to emit steps.
if proof is disabled, the callback won't even be called, and
a dummy proof_rule is returned. *)
val emit_input_clause : lit Iter.t -> proof_rule
(** Emit an input clause. *)
@ -193,7 +188,7 @@ module type SAT_PROOF = sig
FIXME: probably needs the list of proof_step that disprove the lits? *)
val emit_unsat : proof_step -> t -> unit
(** Signal "unsat" result at the given proof_rule *)
(** Signal "unsat" result at the given proof *)
val del_clause : proof_step -> t -> unit
(** Forget a clause. Only useful for performance considerations. *)
@ -248,6 +243,12 @@ module type PROOF = sig
From now on, [t] and [u] will be used interchangeably.
@return a proof_rule ID for the clause [(t=u)]. *)
val lemma_rw_clause : proof_step -> lit_rw:proof_step Iter.t -> proof_rule
(** [lemma_rw_clause prc ~lit_rw], where [prc] is the proof of [|- c],
uses the equations [|- p_i = q_i] from [lit_rw]
to rewrite some literals of [c] into [c']. This is used to preprocess
literals of a clause (using {!lemma_preprocess} individually). *)
end
(** Literals
@ -307,21 +308,24 @@ module type CC_ACTIONS = sig
type proof
type proof_step
type proof_rule = proof -> proof_step
module P : CC_PROOF with type lit = Lit.t and type t = proof
module P : CC_PROOF with type lit = Lit.t
and type t = proof
and type proof_step = proof_step
type t
(** An action handle. It is used by the congruence closure
to perform the actions below. How it performs the actions
is not specified and is solver-specific. *)
val raise_conflict : t -> Lit.t list -> proof_rule -> 'a
val proof : t -> proof
val raise_conflict : t -> Lit.t list -> proof_step -> 'a
(** [raise_conflict acts c pr] declares that [c] is a tautology of
the theory of congruence. This does not return (it should raise an
exception).
@param pr the proof of [c] being a tautology *)
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list * proof_rule) -> unit
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list * proof_step) -> unit
(** [propagate acts lit ~reason pr] declares that [reason() => lit]
is a tautology.
@ -375,7 +379,6 @@ module type CC_S = sig
module Lit : LIT with module T = T
type proof
type proof_step
type proof_rule = proof -> proof_step
module P : CC_PROOF
with type lit = Lit.t
and type t = proof
@ -525,7 +528,7 @@ module type CC_S = sig
participating in the conflict are purely syntactic theories
like injectivity of constructors. *)
type ev_on_propagate = t -> lit -> (unit -> lit list * proof_rule) -> unit
type ev_on_propagate = t -> lit -> (unit -> lit list * proof_step) -> unit
(** [ev_on_propagate cc lit reason] is called whenever [reason() => lit]
is a propagated lemma. See {!CC_ACTIONS.propagate}. *)
@ -680,8 +683,6 @@ module type SOLVER_INTERNAL = sig
type clause_pool
type proof
type proof_step
type proof_rule = proof -> proof_step
(** Delayed proof. This is used to build a proof proof_rule on demand. *)
(** {3 Proofs} *)
module P : PROOF
@ -698,7 +699,8 @@ module type SOLVER_INTERNAL = sig
val ty_st : t -> ty_store
val stats : t -> Stat.t
val with_proof : t -> (proof -> unit) -> unit
val proof : t -> proof
(** Access the proof object *)
(** {3 Actions for the theories} *)
@ -740,7 +742,8 @@ module type SOLVER_INTERNAL = sig
val clear : t -> unit
(** Reset internal cache, etc. *)
val with_proof : t -> (proof -> 'a) -> 'a
val proof : t -> proof
(** Access proof *)
type hook = t -> term -> (term * proof_step Iter.t) option
(** Given a term, try to simplify it. Return [None] if it didn't change.
@ -749,12 +752,12 @@ module type SOLVER_INTERNAL = sig
and if [t] is [app "+" (const x) (const y)] where [x] and [y] are number,
returns [Some (const (x+y))], and [None] otherwise. *)
val normalize : t -> term -> (term * proof_step Iter.t) option
val normalize : t -> term -> (term * proof_step) option
(** Normalize a term using all the hooks. This performs
a fixpoint, i.e. it only stops when no hook applies anywhere inside
the term. *)
val normalize_t : t -> term -> term * proof_step Iter.t
val normalize_t : t -> term -> term * proof_step option
(** Normalize a term using all the hooks, along with a proof that the
simplification is correct.
returns [t, ø] if no simplification occurred. *)
@ -781,10 +784,14 @@ module type SOLVER_INTERNAL = sig
Typically some clauses are also added to the solver. *)
module type PREPROCESS_ACTS = sig
val mk_lit : ?sign:bool -> term -> lit
(** creates a new literal for a boolean term. *)
val proof : proof
val add_clause : lit list -> proof_rule -> unit
val mk_lit : ?sign:bool -> term -> lit * proof_step option
(** [mk_lit t] creates a new literal for a boolean term [t].
Also returns an optional proof of preprocessing, which if present
is the proof of [|- t = lit] with [lit] the result. *)
val add_clause : lit list -> proof_step -> unit
(** pushes a new clause into the SAT solver. *)
val add_lit : ?default_pol:bool -> lit -> unit
@ -817,7 +824,7 @@ module type SOLVER_INTERNAL = sig
(** {3 hooks for the theory} *)
val raise_conflict : t -> theory_actions -> lit list -> proof_rule -> 'a
val raise_conflict : t -> theory_actions -> lit list -> proof_step -> 'a
(** Give a conflict clause to the solver *)
val push_decision : t -> theory_actions -> lit -> unit
@ -826,27 +833,27 @@ module type SOLVER_INTERNAL = sig
If the SAT solver backtracks, this (potential) decision is removed
and forgotten. *)
val propagate: t -> theory_actions -> lit -> reason:(unit -> lit list * proof_rule) -> unit
val propagate: t -> theory_actions -> lit -> reason:(unit -> lit list * proof_step) -> unit
(** Propagate a boolean using a unit clause.
[expl => lit] must be a theory lemma, that is, a T-tautology *)
val propagate_l: t -> theory_actions -> lit -> lit list -> proof_rule -> unit
val propagate_l: t -> theory_actions -> lit -> lit list -> proof_step -> unit
(** Propagate a boolean using a unit clause.
[expl => lit] must be a theory lemma, that is, a T-tautology *)
val add_clause_temp : t -> theory_actions -> lit list -> proof_rule -> unit
val add_clause_temp : t -> theory_actions -> lit list -> proof_step -> unit
(** Add local clause to the SAT solver. This clause will be
removed when the solver backtracks. *)
val add_clause_permanent : t -> theory_actions -> lit list -> proof_rule -> unit
val add_clause_permanent : t -> theory_actions -> lit list -> proof_step -> unit
(** Add toplevel clause to the SAT solver. This clause will
not be backtracked. *)
val mk_lit : t -> theory_actions -> ?sign:bool -> term -> lit
(** Create a literal. This automatically preprocesses the term. *)
val preprocess_term : t -> preprocess_actions -> term -> term
(** Preprocess a term. The preprocessing proof is automatically emitted. *)
val preprocess_term : t -> preprocess_actions -> term -> term * proof_step option
(** Preprocess a term. *)
val add_lit : t -> theory_actions -> ?default_pol:bool -> lit -> unit
(** Add the given literal to the SAT solver, so it gets assigned
@ -902,7 +909,7 @@ module type SOLVER_INTERNAL = sig
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unit
(** Callback called on every CC conflict *)
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list * proof_rule) -> unit) -> unit
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list * proof_step) -> unit) -> unit
(** Callback called on every CC propagation *)
val on_partial_check : t -> (t -> theory_actions -> lit Iter.t -> unit) -> unit
@ -972,8 +979,6 @@ module type SOLVER = sig
type term = T.Term.t
type ty = T.Ty.t
type lit = Lit.t
type proof_rule = proof -> proof_step
(** Proof proof_rule. *)
(** {3 A theory}
@ -1108,11 +1113,11 @@ module type SOLVER = sig
The proof of [|- lit = lit'] is directly added to the solver's proof. *)
val add_clause : t -> lit IArray.t -> proof_rule -> unit
val add_clause : t -> lit IArray.t -> proof_step -> unit
(** [add_clause solver cs] adds a boolean clause to the solver.
Subsequent calls to {!solve} will need to satisfy this clause. *)
val add_clause_l : t -> lit list -> proof_rule -> unit
val add_clause_l : t -> lit list -> proof_step -> unit
(** Add a clause to the solver, given as a list. *)
val assert_terms : t -> term list -> unit

View file

@ -17,7 +17,7 @@ type ('lit, 'proof) reason = ('lit, 'proof) Solver_intf.reason =
| Consequence of (unit -> 'lit list * 'proof) [@@unboxed]
module type ACTS = Solver_intf.ACTS
type ('lit, 'proof) acts = ('lit, 'proof) Solver_intf.acts
type ('lit, 'proof, 'proof_step) acts = ('lit, 'proof, 'proof_step) Solver_intf.acts
type negated = bool

View file

@ -20,7 +20,6 @@ module Make(Plugin : PLUGIN)
type theory = Plugin.t
type proof = Plugin.proof
type proof_step = Plugin.proof_step
type proof_rule = proof -> proof_step
type clause_pool_id = Clause_pool_id.t
module Lit = Plugin.Lit
@ -1074,7 +1073,7 @@ module Make(Plugin : PLUGIN)
self.unsat_at_0 <- Some c;
(match self.on_learnt with Some f -> f self c | None -> ());
let p = Clause.proof_step self.store c in
Proof.with_proof self.proof (Proof.emit_unsat p);
Proof.emit_unsat p self.proof;
US_false c
| US_local _ -> us
in
@ -1410,10 +1409,10 @@ module Make(Plugin : PLUGIN)
report_unsat self (US_false confl)
) else (
let p =
Proof.with_proof self.proof @@
Proof.emit_redundant_clause
(Iter.of_array cr.cr_learnt |> Iter.map (Atom.lit self.store))
~hyps:(Step_vec.to_iter cr.cr_steps)
self.proof
in
let uclause = Clause.make_a store ~removable:true cr.cr_learnt p in
@ -1425,10 +1424,9 @@ module Make(Plugin : PLUGIN)
| _ ->
let fuip = cr.cr_learnt.(0) in
let p =
Proof.with_proof self.proof @@
Proof.emit_redundant_clause
(Iter.of_array cr.cr_learnt |> Iter.map (Atom.lit self.store))
~hyps:(Step_vec.to_iter cr.cr_steps)
~hyps:(Step_vec.to_iter cr.cr_steps) self.proof
in
let lclause = Clause.make_a store ~removable:true cr.cr_learnt p in
@ -1620,21 +1618,17 @@ module Make(Plugin : PLUGIN)
let[@inline] slice_get st i = AVec.get st.trail i
let acts_add_clause self ?(keep=false) (l:lit list) (proof_rule:proof_rule): unit =
let acts_add_clause self ?(keep=false) (l:lit list) (p:proof_step): unit =
let atoms = List.rev_map (make_atom_ self) l in
let removable = not keep in
let c =
let p = Proof.with_proof self.proof proof_rule in
Clause.make_l self.store ~removable atoms p in
let c = Clause.make_l self.store ~removable atoms p in
Log.debugf 5 (fun k->k "(@[sat.th.add-clause@ %a@])" (Clause.debug self.store) c);
CVec.push self.clauses_to_add_learnt c
let acts_add_clause_in_pool self ~pool (l:lit list) (proof_rule:proof_rule): unit =
let acts_add_clause_in_pool self ~pool (l:lit list) (p:proof_step): unit =
let atoms = List.rev_map (make_atom_ self) l in
let removable = true in
let c =
let p = Proof.with_proof self.proof proof_rule in
Clause.make_l self.store ~removable atoms p in
let c = Clause.make_l self.store ~removable atoms p in
let pool = Vec.get self.clause_pools (pool:clause_pool_id:>int) in
Log.debugf 5 (fun k->k "(@[sat.th.add-clause-in-pool@ %a@ :pool %s@])"
(Clause.debug self.store) c
@ -1650,12 +1644,10 @@ module Make(Plugin : PLUGIN)
self.next_decisions <- a :: self.next_decisions
)
let acts_raise self (l:lit list) (proof_rule:proof_rule) : 'a =
let acts_raise self (l:lit list) (p:proof_step) : 'a =
let atoms = List.rev_map (make_atom_ self) l in
(* conflicts can be removed *)
let c =
let p = Proof.with_proof self.proof proof_rule in
Clause.make_l self.store ~removable:true atoms p in
let c = Clause.make_l self.store ~removable:true atoms p in
Log.debugf 5 (fun k->k "(@[@{<yellow>sat.th.raise-conflict@}@ %a@])"
(Clause.debug self.store) c);
raise_notrace (Th_conflict c)
@ -1669,24 +1661,22 @@ module Make(Plugin : PLUGIN)
(Atom.debug store) (Atom.neg a)
| exception Not_found -> ()
let acts_propagate (self:t) f expl =
let acts_propagate (self:t) f (expl:(_,proof_step) Solver_intf.reason) =
let store = self.store in
match expl with
| Solver_intf.Consequence mk_expl ->
let p = make_atom_ self f in
if Atom.is_true store p then ()
else if Atom.is_false store p then (
let lits, proof_rule = mk_expl() in
let lits, proof = mk_expl() in
let l = List.rev_map (fun f -> Atom.neg @@ make_atom_ self f) lits in
check_consequence_lits_false_ self l;
let c =
let proof = Proof.with_proof self.proof proof_rule in
Clause.make_l store ~removable:true (p :: l) proof in
let c = Clause.make_l store ~removable:true (p :: l) proof in
raise_notrace (Th_conflict c)
) else (
insert_var_order self (Atom.var p);
let c = lazy (
let lits, proof_rule = mk_expl () in
let lits, proof = mk_expl () in
let l = List.rev_map (fun f -> Atom.neg @@ make_atom_ self f) lits in
(* note: we can check that invariant here in the [lazy] block,
as conflict analysis will run in an environment where
@ -1695,7 +1685,6 @@ module Make(Plugin : PLUGIN)
(otherwise the propagated lit would have been backtracked and
discarded already.) *)
check_consequence_lits_false_ self l;
let proof = Proof.with_proof self.proof proof_rule in
Clause.make_l store ~removable:true (p :: l) proof
) in
let level = decision_level self in
@ -1725,9 +1714,9 @@ module Make(Plugin : PLUGIN)
let module M = struct
type nonrec proof = proof
type nonrec proof_step = proof_step
type proof_rule = proof -> proof_step
type nonrec clause_pool_id = clause_pool_id
type nonrec lit = lit
let proof = st.proof
let iter_assumptions=acts_iter st ~full:false st.th_head
let eval_lit= acts_eval_lit st
let add_lit=acts_add_lit st
@ -1744,9 +1733,9 @@ module Make(Plugin : PLUGIN)
let module M = struct
type nonrec proof = proof
type nonrec proof_step = proof_step
type proof_rule = proof -> proof_step
type nonrec clause_pool_id = clause_pool_id
type nonrec lit = lit
let proof = st.proof
let iter_assumptions=acts_iter st ~full:true st.th_head
let eval_lit= acts_eval_lit st
let add_lit=acts_add_lit st
@ -1901,8 +1890,7 @@ module Make(Plugin : PLUGIN)
(match self.on_gc with
| Some f -> let lits = Clause.lits_a store c in f self lits
| None -> ());
Proof.with_proof self.proof
(Proof.del_clause (Clause.proof_step store c));
Proof.del_clause (Clause.proof_step store c) self.proof;
in
let gc_arg =
@ -2098,9 +2086,7 @@ module Make(Plugin : PLUGIN)
List.iter
(fun l ->
let atoms = Util.array_of_list_map (make_atom_ self) l in
let proof =
Proof.with_proof self.proof (Proof.emit_input_clause (Iter.of_list l))
in
let proof = Proof.emit_input_clause (Iter.of_list l) self.proof in
let c = Clause.make_a self.store ~removable:false atoms proof in
Log.debugf 10 (fun k -> k "(@[sat.assume-clause@ @[<hov 2>%a@]@])"
(Clause.debug self.store) c);
@ -2162,7 +2148,7 @@ module Make(Plugin : PLUGIN)
assert (Atom.equal first @@ List.hd core);
let proof =
let lits = Iter.of_list core |> Iter.map (Atom.lit self.store) in
Proof.with_proof self.proof (Proof.emit_unsat_core lits) in
Proof.emit_unsat_core lits self.proof in
Clause.make_l self.store ~removable:false [] proof
) in
fun () -> Lazy.force c
@ -2179,27 +2165,26 @@ module Make(Plugin : PLUGIN)
end in
(module M)
let add_clause_atoms_ self ~pool ~removable (c:atom array) proof_rule : unit =
let add_clause_atoms_ self ~pool ~removable (c:atom array) (pr:proof_step) : unit =
try
let proof = Proof.with_proof self.proof proof_rule in
let c = Clause.make_a self.store ~removable c proof in
let c = Clause.make_a self.store ~removable c pr in
add_clause_ ~pool self c
with
| E_unsat (US_false c) ->
self.unsat_at_0 <- Some c
let add_clause_a self c dp : unit =
let add_clause_a self c pr : unit =
let c = Array.map (make_atom_ self) c in
add_clause_atoms_ ~pool:self.clauses_learnt ~removable:false self c dp
add_clause_atoms_ ~pool:self.clauses_learnt ~removable:false self c pr
let add_clause self (c:lit list) dp : unit =
let add_clause self (c:lit list) (pr:proof_step) : unit =
let c = Util.array_of_list_map (make_atom_ self) c in
add_clause_atoms_ ~pool:self.clauses_learnt ~removable:false self c dp
add_clause_atoms_ ~pool:self.clauses_learnt ~removable:false self c pr
let add_clause_a_in_pool self ~pool c dp : unit =
let add_clause_a_in_pool self ~pool c (pr:proof_step) : unit =
let c = Array.map (make_atom_ self) c in
let pool = Vec.get self.clause_pools (pool:clause_pool_id:>int) in
add_clause_atoms_ ~pool ~removable:true self c dp
add_clause_atoms_ ~pool ~removable:true self c pr
let add_clause_in_pool self ~pool (c:lit list) dp : unit =
let c = Util.array_of_list_map (make_atom_ self) c in
@ -2207,12 +2192,12 @@ module Make(Plugin : PLUGIN)
add_clause_atoms_ ~pool ~removable:true self c dp
let add_input_clause self (c:lit list) =
let dp = Proof.emit_input_clause (Iter.of_list c) in
add_clause self c dp
let pr = Proof.emit_input_clause (Iter.of_list c) self.proof in
add_clause self c pr
let add_input_clause_a self c =
let dp = Proof.emit_input_clause (Iter.of_array c) in
add_clause_a self c dp
let pr = Proof.emit_input_clause (Iter.of_array c) self.proof in
add_clause_a self c pr
let new_clause_pool_gc_fixed_size ~descr ~size (self:t) : clause_pool_id =
let p =

View file

@ -6,6 +6,7 @@ module Make_pure_sat(Th: Solver_intf.PLUGIN_SAT)
: S with type lit = Th.lit
and module Lit = Th.Lit
and type proof = Th.proof
and type proof_step = Th.proof_step
and module Proof = Th.Proof
and type theory = unit
@ -13,5 +14,6 @@ module Make_cdcl_t(Th : Solver_intf.PLUGIN_CDCL_T)
: S with type lit = Th.lit
and module Lit = Th.Lit
and type proof = Th.proof
and type proof_step = Th.proof_step
and module Proof = Th.Proof
and type theory = Th.t

View file

@ -104,7 +104,8 @@ module type ACTS = sig
type proof
type proof_step
type clause_pool_id = Clause_pool_id.t
type proof_rule = proof -> proof_step
val proof : proof
val iter_assumptions: (lit -> unit) -> unit
(** Traverse the new assumptions on the boolean trail. *)
@ -116,7 +117,7 @@ module type ACTS = sig
(** Map the given lit to an internal atom, which will be decided by the
SAT solver. *)
val add_clause: ?keep:bool -> lit list -> proof_rule -> unit
val add_clause: ?keep:bool -> lit list -> proof_step -> unit
(** Add a clause to the solver.
@param keep if true, the clause will be kept by the solver.
Otherwise the solver is allowed to GC the clause and propose this
@ -124,16 +125,16 @@ module type ACTS = sig
- [C_use_allocator alloc] puts the clause in the given allocator.
*)
val add_clause_in_pool : pool:clause_pool_id -> lit list -> proof_rule -> unit
val add_clause_in_pool : pool:clause_pool_id -> lit list -> proof_step -> unit
(** Like {!add_clause} but uses a custom clause pool for the clause,
with its own lifetime. *)
val raise_conflict: lit list -> proof_rule -> 'b
val raise_conflict: lit list -> proof_step -> 'b
(** Raise a conflict, yielding control back to the solver.
The list of atoms must be a valid theory lemma that is false in the
current trail. *)
val propagate: lit -> (lit, proof_rule) reason -> unit
val propagate: lit -> (lit, proof_step) reason -> unit
(** Propagate a lit, i.e. the theory can evaluate the lit to be true
(see the definition of {!type:eval_res} *)
@ -143,9 +144,10 @@ module type ACTS = sig
Useful for theory combination. This will be undone on backtracking. *)
end
type ('lit, 'proof) acts =
type ('lit, 'proof, 'proof_step) acts =
(module ACTS with type lit = 'lit
and type proof = 'proof)
and type proof = 'proof
and type proof_step = 'proof_step)
(** The type for a slice of assertions to assume/propagate in the theory. *)
exception No_proof
@ -203,12 +205,12 @@ module type PLUGIN_CDCL_T = sig
val pop_levels : t -> int -> unit
(** Pop [n] levels of the theory *)
val partial_check : t -> (lit, proof) acts -> unit
val partial_check : t -> (lit, proof, proof_step) acts -> unit
(** Assume the lits in the slice, possibly using the [slice]
to push new lits to be propagated or to raising a conflict or to add
new lemmas. *)
val final_check : t -> (lit, proof) acts -> unit
val final_check : t -> (lit, proof, proof_step) acts -> unit
(** Called at the end of the search in case a model has been found.
If no new clause is pushed, then proof search ends and "sat" is returned;
if lemmas are added, search is resumed;
@ -251,8 +253,6 @@ module type S = sig
type proof_step
type proof_rule = proof -> proof_step
type solver
(** The main solver type. *)
@ -357,10 +357,10 @@ module type S = sig
(** Add the list of clauses to the current set of assumptions.
Modifies the sat solver state in place. *)
val add_clause : t -> lit list -> proof_rule -> unit
val add_clause : t -> lit list -> proof_step -> unit
(** Lower level addition of clauses *)
val add_clause_a : t -> lit array -> proof_rule -> unit
val add_clause_a : t -> lit array -> proof_step -> unit
(** Lower level addition of clauses *)
val add_input_clause : t -> lit list -> unit
@ -369,10 +369,10 @@ module type S = sig
val add_input_clause_a : t -> lit array -> unit
(** Like {!add_clause_a} but with justification of being an input clause *)
val add_clause_in_pool : t -> pool:clause_pool_id -> lit list -> proof_rule -> unit
val add_clause_in_pool : t -> pool:clause_pool_id -> lit list -> proof_step -> unit
(** Like {!add_clause} but using a specific clause pool *)
val add_clause_a_in_pool : t -> pool:clause_pool_id -> lit array -> proof_rule -> unit
val add_clause_a_in_pool : t -> pool:clause_pool_id -> lit array -> proof_step -> unit
(** Like {!add_clause_a} but using a specific clause pool *)
(* TODO: API to push/pop/clear assumptions from an inner vector *)

View file

@ -47,11 +47,10 @@ module Make(A : ARG)
type ty = Ty.t
type proof = A.proof
type proof_step = A.proof_step
type proof_rule = proof -> proof_step
type lit = Lit.t
(* actions from the sat solver *)
type sat_acts = (lit, proof) Sidekick_sat.acts
type sat_acts = (lit, proof, proof_step) Sidekick_sat.acts
(* the full argument to the congruence closure *)
module CC_actions = struct
@ -59,6 +58,7 @@ module Make(A : ARG)
module P = P
module Lit = Lit
type nonrec proof = proof
type nonrec proof_step = proof_step
let cc_view = A.cc_view
module Actions = struct
@ -66,11 +66,14 @@ module Make(A : ARG)
module P = P
module Lit = Lit
type nonrec proof = proof
type dproof = proof -> unit
type nonrec proof_step = proof_step
type t = sat_acts
let[@inline] raise_conflict (a:t) lits (dp:dproof) =
let[@inline] proof (a:t) =
let (module A) = a in
A.raise_conflict lits dp
A.proof
let[@inline] raise_conflict (a:t) lits (pr:proof_step) =
let (module A) = a in
A.raise_conflict lits pr
let[@inline] propagate (a:t) lit ~reason =
let (module A) = a in
let reason = Sidekick_sat.Consequence reason in
@ -90,7 +93,6 @@ module Make(A : ARG)
module N = CC.N
type nonrec proof = proof
type nonrec proof_step = proof_step
type proof_rule = proof -> proof_step
type term = Term.t
type ty = Ty.t
type lit = Lit.t
@ -115,57 +117,78 @@ module Make(A : ARG)
ty_st: ty_store;
proof: proof;
mutable hooks: hook list;
cache: Term.t Term.Tbl.t;
(* store [t --> u by proof_steps] in the cache.
We use a bag for the proof steps because it gives us structural
sharing of subproofs. *)
cache: (Term.t * proof_step Bag.t) Term.Tbl.t;
}
and hook = t -> term -> term option
and hook = t -> term -> (term * proof_step Iter.t) option
let create tst ty_st ~proof : t =
{tst; ty_st; proof; hooks=[]; cache=Term.Tbl.create 32;}
let[@inline] tst self = self.tst
let[@inline] ty_st self = self.ty_st
let[@inline] with_proof self f = P.with_proof self.proof f
let[@inline] proof self = self.proof
let add_hook self f = self.hooks <- f :: self.hooks
let clear self = Term.Tbl.clear self.cache
let normalize (self:t) (t:Term.t) : Term.t option =
let normalize (self:t) (t:Term.t) : (Term.t * proof_step) option =
(* compute and cache normal form of [t] *)
let rec aux t : Term.t =
let rec loop t : Term.t * _ Bag.t =
match Term.Tbl.find self.cache t with
| u -> u
| res -> res
| exception Not_found ->
let u = aux_rec t self.hooks in
Term.Tbl.add self.cache t u;
u
let steps_u = ref Bag.empty in
let u = aux_rec ~steps:steps_u t self.hooks in
Term.Tbl.add self.cache t (u, !steps_u);
u, !steps_u
and loop_add ~steps t =
let u, pr_u = loop t in
steps := Bag.append !steps pr_u;
u
(* try each function in [hooks] successively, and rewrite subterms *)
and aux_rec t hooks = match hooks with
and aux_rec ~steps t hooks : Term.t =
match hooks with
| [] ->
let u = Term.map_shallow self.tst aux t in
if Term.equal t u then t else aux u
let u = Term.map_shallow self.tst (loop_add ~steps) t in
if Term.equal t u
then t
else loop_add ~steps u
| h :: hooks_tl ->
match h self t with
| None -> aux_rec t hooks_tl
| Some u when Term.equal t u -> aux_rec t hooks_tl
| Some u -> aux u (* fixpoint *)
| None -> aux_rec ~steps t hooks_tl
| Some (u, _) when Term.equal t u -> aux_rec ~steps t hooks_tl
| Some (u, pr_u) ->
let bag_u = Bag.of_iter pr_u in
steps := Bag.append !steps bag_u;
let v, pr_v = loop u in (* fixpoint *)
steps := Bag.append !steps pr_v;
v
in
let u = aux t in
let u, pr_u = loop t in
if Term.equal t u then None
else (
(* proof: [sub_proofs |- t=u] by CC + subproof *)
P.with_proof self.proof (P.lemma_preprocess t u);
Some u
let step =
P.lemma_preprocess t u ~using:(Bag.to_iter pr_u) self.proof in
Some (u, step)
)
let normalize_t self t =
match normalize self t with
| Some u -> u
| None -> t
| Some (u, pr_u) -> u, Some pr_u
| None -> t, None
end
type simplify_hook = Simplify.hook
module type PREPROCESS_ACTS = sig
val mk_lit : ?sign:bool -> term -> lit
val add_clause : lit list -> proof_rule -> unit
val proof : proof
val mk_lit : ?sign:bool -> term -> lit * proof_step option
val add_clause : lit list -> proof_step -> unit
val add_lit : ?default_pol:bool -> lit -> unit
end
type preprocess_actions = (module PREPROCESS_ACTS)
@ -184,7 +207,7 @@ module Make(A : ARG)
simp: Simplify.t;
mutable preprocess: preprocess_hook list;
mutable mk_model: model_hook list;
preprocess_cache: Term.t Term.Tbl.t;
preprocess_cache: (Term.t * proof_step Bag.t) Term.Tbl.t;
mutable t_defs : (term*term) list; (* term definitions *)
mutable th_states : th_states; (** Set of theories *)
mutable on_partial_check: (t -> theory_actions -> lit Iter.t -> unit) list;
@ -195,7 +218,7 @@ module Make(A : ARG)
and preprocess_hook =
t ->
preprocess_actions ->
term -> term option
term -> (term * proof_step Iter.t) option
and model_hook =
recurse:(t -> CC.N.t -> term) ->
@ -208,7 +231,7 @@ module Make(A : ARG)
let[@inline] cc (t:t) = Lazy.force t.cc
let[@inline] tst t = t.tst
let[@inline] ty_st t = t.ty_st
let[@inline] with_proof self f = Proof.with_proof self.proof f
let[@inline] proof self = self.proof
let stats t = t.stat
let define_const (self:t) ~const ~rhs : unit =
@ -216,7 +239,7 @@ module Make(A : ARG)
let simplifier self = self.simp
let simplify_t self (t:Term.t) : _ option = Simplify.normalize self.simp t
let simp_t self (t:Term.t) : Term.t = Simplify.normalize_t self.simp t
let simp_t self (t:Term.t) : Term.t * _ = Simplify.normalize_t self.simp t
let add_simplifier (self:t) f : unit = Simplify.add_hook self.simp f
@ -241,12 +264,12 @@ module Make(A : ARG)
let[@inline] propagate_l self acts p cs proof : unit =
propagate self acts p ~reason:(fun()->cs,proof)
let add_sat_clause_ self (acts:theory_actions) ~keep lits (proof:proof_rule) : unit =
let add_sat_clause_ self (acts:theory_actions) ~keep lits (proof:proof_step) : unit =
let (module A) = acts in
Stat.incr self.count_axiom;
A.add_clause ~keep lits proof
let add_sat_clause_pool_ self (acts:theory_actions) ~pool lits (proof:proof_rule) : unit =
let add_sat_clause_pool_ self (acts:theory_actions) ~pool lits (proof:proof_step) : unit =
let (module A) = acts in
Stat.incr self.count_axiom;
A.add_clause_in_pool ~pool lits proof
@ -258,7 +281,7 @@ module Make(A : ARG)
(* actual preprocessing logic, acting on terms.
this calls all the preprocessing hooks on subterms, ensuring
a fixpoint. *)
let preprocess_term_ (self:t) (module A0:PREPROCESS_ACTS) (t:term) : term =
let preprocess_term_ (self:t) (module A0:PREPROCESS_ACTS) (t:term) : term * proof_step option =
let mk_lit_nopreproc t = Lit.atom self.tst t in (* no further simplification *)
(* compute and cache normal form [u] of [t].
@ -268,19 +291,23 @@ module Make(A : ARG)
next time we preprocess [t], we will have to re-emit the same
proofs, even though we will not do any actual preprocessing work.
*)
let rec preproc_rec t : term =
let rec preproc_rec ~steps t : term =
match Term.Tbl.find self.preprocess_cache t with
| u -> u
| u, pr_u ->
steps := Bag.append pr_u !steps;
u
| exception Not_found ->
(* try rewrite at root *)
let t1 = preproc_with_hooks t self.preprocess in
let steps = ref Bag.empty in
let t1 = preproc_with_hooks ~steps t self.preprocess in
(* map subterms *)
let t2 = Term.map_shallow self.tst preproc_rec t1 in
let t2 = Term.map_shallow self.tst (preproc_rec ~steps) t1 in
let u =
if not (Term.equal t t2) then (
preproc_rec t2 (* fixpoint *)
preproc_rec ~steps t2 (* fixpoint *)
) else (
t2
)
@ -308,75 +335,103 @@ module Make(A : ARG)
Term.pp t Term.pp u);
);
Term.Tbl.add self.preprocess_cache t u;
let pr_t_u = !steps in
Term.Tbl.add self.preprocess_cache t (u, pr_t_u);
u
(* try each function in [hooks] successively *)
and preproc_with_hooks t hooks : term =
and preproc_with_hooks ~steps t hooks : term =
let[@inline] add_step s = steps := Bag.cons s !steps in
match hooks with
| [] -> t
| h :: hooks_tl ->
(* call hook, using [pacts] which will ensure all new
literals and clauses are also preprocessed *)
match h self (Lazy.force pacts) t with
| None -> preproc_with_hooks t hooks_tl
| Some u -> preproc_rec u
| None -> preproc_with_hooks ~steps t hooks_tl
| Some (u, pr_u) ->
Iter.iter add_step pr_u;
preproc_rec ~steps u
(* create literal and preprocess it with [pacts], which uses [A0]
for the base operations, and preprocesses new literals and clauses
recursively. *)
and mk_lit ?sign t =
let u = preproc_rec t in
if not (Term.equal t u) then (
Log.debugf 10
(fun k->k "(@[smt-solver.preprocess.t@ :t %a@ :into %a@])"
Term.pp t Term.pp u);
);
Lit.atom self.tst ?sign u
and mk_lit ?sign t : _ * proof_step option =
let steps = ref Bag.empty in
let u = preproc_rec ~steps t in
let pr_t_u =
if not (Term.equal t u) then (
Log.debugf 10
(fun k->k "(@[smt-solver.preprocess.t@ :t %a@ :into %a@])"
Term.pp t Term.pp u);
and preprocess_lit (lit:lit) : lit =
let p =
A.P.lemma_preprocess t u ~using:(Bag.to_iter !steps) self.proof
in
Some p
) else None
in
Lit.atom self.tst ?sign u, pr_t_u
and preprocess_lit ~steps (lit:lit) : lit =
let t = Lit.term lit in
let sign = Lit.sign lit in
mk_lit ~sign t
let lit, pr = mk_lit ~sign t in
CCOpt.iter (fun s -> steps := s :: !steps) pr;
lit
(* wrap [A0] so that all operations go throught preprocessing *)
and pacts = lazy (
(module struct
let proof = A0.proof
let add_lit ?default_pol lit =
let lit = preprocess_lit lit in
(* just drop the proof *)
let lit = preprocess_lit ~steps:(ref []) lit in
A0.add_lit ?default_pol lit
let add_clause c pr =
let add_clause c pr_c =
Stat.incr self.count_preprocess_clause;
let c = CCList.map preprocess_lit c in
A0.add_clause c pr
let steps = ref [] in
let c' = CCList.map (preprocess_lit ~steps) c in
let pr_c' =
if !steps=[] then pr_c
else A.P.lemma_rw_clause pr_c ~lit_rw:(Iter.of_list !steps) proof
in
A0.add_clause c' pr_c'
let mk_lit = mk_lit
end : PREPROCESS_ACTS)
) in
P.begin_subproof self.proof;
let steps = ref Bag.empty in
let[@inline] add_step s = steps := Bag.cons s !steps in
(* simplify the term *)
let t1 = simp_t self t in
let t1, pr_1 = simp_t self t in
CCOpt.iter add_step pr_1;
(* preprocess *)
let u = preproc_rec t1 in
(* emit [|- t=u] *)
if not (Term.equal t u) then (
P.with_proof self.proof (P.lemma_preprocess t u);
);
let u = preproc_rec ~steps t1 in
P.end_subproof self.proof;
u
(* emit [|- t=u] *)
let pr_u =
if not (Term.equal t u) then (
let p = P.lemma_preprocess t u ~using:(Bag.to_iter !steps) self.proof in
Some p
) else None
in
u, pr_u
(* return preprocessed lit *)
let preprocess_lit_ (self:t) (pacts:preprocess_actions) (lit:lit) : lit =
let preprocess_lit_ ~steps (self:t) (pacts:preprocess_actions) (lit:lit) : lit =
let t = Lit.term lit in
let sign = Lit.sign lit in
let u = preprocess_term_ self pacts t in
let u, pr_u = preprocess_term_ self pacts t in
CCOpt.iter (fun s -> steps := s :: !steps) pr_u;
Lit.atom self.tst ~sign u
(* add a clause using [acts] *)
let add_clause_ self acts lits (proof:proof_rule) : unit =
let add_clause_ self acts lits (proof:proof_step) : unit =
add_sat_clause_ self acts ~keep:true lits proof
let[@inline] add_lit _self (acts:theory_actions) ?default_pol lit : unit =
@ -385,30 +440,41 @@ module Make(A : ARG)
let preprocess_acts_of_acts (self:t) (acts:theory_actions) : preprocess_actions =
(module struct
let mk_lit ?sign t = Lit.atom self.tst ?sign t
let proof = self.proof
let mk_lit ?sign t = Lit.atom self.tst ?sign t, None
let add_clause = add_clause_ self acts
let add_lit = add_lit self acts
end)
let preprocess_clause_ (self:t) (acts:theory_actions) (c:lit list) : lit list =
let preprocess_clause_ (self:t) (acts:theory_actions)
(c:lit list) (proof:proof_step) : lit list * proof_step =
let steps = ref [] in
let pacts = preprocess_acts_of_acts self acts in
let c = CCList.map (preprocess_lit_ self pacts) c in
c
let c = CCList.map (preprocess_lit_ ~steps self pacts) c in
let pr =
if !steps=[] then proof
else (
P.lemma_rw_clause proof ~lit_rw:(Iter.of_list !steps) self.proof
)
in
c, pr
(* make literal and preprocess it *)
let[@inline] mk_plit (self:t) (pacts:preprocess_actions) ?sign (t:term) : lit =
let lit = Lit.atom self.tst ?sign t in
preprocess_lit_ self pacts lit
let steps = ref [] in
preprocess_lit_ ~steps self pacts lit
let[@inline] preprocess_term self (pacts:preprocess_actions) (t:term) : term =
let[@inline] preprocess_term self
(pacts:preprocess_actions) (t:term) : term * _ option =
preprocess_term_ self pacts t
let[@inline] add_clause_temp self acts c (proof:proof_rule) : unit =
let c = preprocess_clause_ self acts c in
let[@inline] add_clause_temp self acts c (proof:proof_step) : unit =
let c, proof = preprocess_clause_ self acts c proof in
add_sat_clause_ self acts ~keep:false c proof
let[@inline] add_clause_permanent self acts c (proof:proof_rule) : unit =
let c = preprocess_clause_ self acts c in
let[@inline] add_clause_permanent self acts c (proof:proof_step) : unit =
let c, proof = preprocess_clause_ self acts c proof in
add_sat_clause_ self acts ~keep:true c proof
let[@inline] mk_lit (self:t) (acts:theory_actions) ?sign t : lit =
@ -613,7 +679,7 @@ module Make(A : ARG)
let t_true = Term.bool tst true in
Sat_solver.add_clause self.solver
[Lit.atom tst t_true]
(P.lemma_true t_true)
(P.lemma_true t_true self.proof)
end;
self
@ -626,7 +692,8 @@ module Make(A : ARG)
let preprocess_acts_of_solver_
(self:t) : (module Solver_internal.PREPROCESS_ACTS) =
(module struct
let mk_lit ?sign t = Lit.atom ?sign self.si.tst t
let proof = self.proof
let mk_lit ?sign t = Lit.atom ?sign self.si.tst t, None
let add_lit ?default_pol lit =
Sat_solver.add_lit self.solver ?default_pol lit
let add_clause c pr =
@ -634,9 +701,9 @@ module Make(A : ARG)
end)
(* preprocess literal *)
let preprocess_lit_ (self:t) (lit:lit) : lit =
let preprocess_lit_ ~steps (self:t) (lit:lit) : lit =
let pacts = preprocess_acts_of_solver_ self in
Solver_internal.preprocess_lit_ self.si pacts lit
Solver_internal.preprocess_lit_ ~steps self.si pacts lit
(* make a literal from a term, ensuring it is properly preprocessed *)
let mk_lit_t (self:t) ?sign (t:term) : lit =
@ -692,7 +759,7 @@ module Make(A : ARG)
let pp_stats out (self:t) : unit =
Stat.pp_all out (Stat.all @@ stats self)
let add_clause (self:t) (c:lit IArray.t) (proof:proof_rule) : unit =
let add_clause (self:t) (c:lit IArray.t) (proof:proof_step) : unit =
Stat.incr self.count_clause;
Log.debugf 50 (fun k->
k "(@[solver.add-clause@ %a@])"
@ -704,12 +771,16 @@ module Make(A : ARG)
let add_clause_l self c p = add_clause self (IArray.of_list c) p
let assert_terms self c =
let steps = ref [] in
let c = CCList.map (fun t -> Lit.atom (tst self) t) c in
let c = CCList.map (preprocess_lit_ self) c in
let c = CCList.map (preprocess_lit_ ~steps self) c in
(* TODO: if c != c0 then P.emit_redundant_clause c
because we jsut preprocessed it away? *)
let dp = P.emit_input_clause (Iter.of_list c) in
add_clause_l self c dp
let pr = P.emit_input_clause (Iter.of_list c) self.proof in
let pr = if !steps=[] then pr
else P.lemma_rw_clause pr ~lit_rw:(Iter.of_list !steps) self.proof
in
add_clause_l self c pr
let assert_term self t = assert_terms self [t]

View file

@ -189,6 +189,14 @@ module Make(A : ARG) : S with module A = A = struct
let proxy = fresh_term ~for_t ~pre self (Ty.bool self.ty_st) in
proxy, mk_lit proxy
let p1_opt s1 s2 p : SI.proof_step =
let s2 = s2 p in
CCOpt.map_or ~default:s2 (fun s1 -> SI.P.proof_p1 s1 s2 p) s1
let p1_map s1 s2 p =
let s2 = s2 p in
SI.P.proof_p1 s1 s2 p
(* preprocess "ite" away *)
let preproc_ite self si (module PA:SI.PREPROCESS_ACTS) (t:T.t) : (T.t * SI.proof_step Iter.t) option =
let steps = ref [] in
@ -212,14 +220,14 @@ module Make(A : ARG) : S with module A = A = struct
| _ ->
let t_ite = fresh_term self ~for_t:t ~pre:"ite" (T.ty b) in
SI.define_const si ~const:t_ite ~rhs:t;
let pr = SI.with_proof si (SI.P.define_term t_ite t) in
let pr_def = SI.with_proof si (SI.P.define_term t_ite t) in
let lit_a = PA.mk_lit a' in
(* TODO: use unit paramod on each clause with side t=t_ite and on a=a' *)
PA.add_clause [Lit.neg lit_a; PA.mk_lit (eq self.tst t_ite b)]
(fun p -> SI.P.proof_p1 pr_a (A.lemma_ite_true ~a:a' ~ite:t p) p);
(p1_map pr_def @@ p1_opt pr_a (A.lemma_ite_true ~a:a' ~ite:t));
PA.add_clause [lit_a; PA.mk_lit (eq self.tst t_ite c)]
(fun p -> A.lemma_ite_false p ~a:a' ~ite:t);
Some t_ite
(p1_map pr_def @@ p1_opt pr_a (A.lemma_ite_false ~a:a' ~ite:t));
ret t_ite
end
| _ -> None
@ -237,7 +245,7 @@ module Make(A : ARG) : S with module A = A = struct
let t_proxy, proxy = fresh_lit ~for_t ~mk_lit:PA.mk_lit ~pre:"equiv_" self in
SI.define_const si ~const:t_proxy ~rhs:for_t;
SI.with_proof si (SI.P.define_term t_proxy for_t);
let pr_def = SI.with_proof si (SI.P.define_term t_proxy for_t) in
let add_clause c pr =
PA.add_clause c pr

View file

@ -29,6 +29,17 @@ let cons x t = match t with
| L _ -> N (L x, t)
| N (_,_) -> N (L x, t)
let snoc t x = match t with
| E -> L x
| L _ -> N (t, L x)
| N (_, _) -> N (t, L x)
let of_iter i =
let r = ref empty in
i (fun x -> r := snoc !r x);
!r
let rec fold f acc = function
| E -> acc
| L x -> f acc x

View file

@ -19,8 +19,12 @@ val return : 'a -> 'a t
val cons : 'a -> 'a t -> 'a t
val snoc : 'a t -> 'a -> 'a t
val append : 'a t -> 'a t -> 'a t
val of_iter : 'a Iter.t -> 'a t
val to_iter : 'a t -> 'a Iter.t
val fold : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a