wip: feat(proof): insert proof constructs in most of sidekick

This commit is contained in:
Simon Cruanes 2021-04-15 00:03:15 -04:00
parent 683909c6ef
commit ff7a87f3fb
8 changed files with 385 additions and 180 deletions

View file

@ -95,6 +95,7 @@ module Make (A: CC_ARG)
| E_congruence of node * node (* caused by normal congruence *) | E_congruence of node * node (* caused by normal congruence *)
| E_and of explanation * explanation | E_and of explanation * explanation
| E_theory of explanation | E_theory of explanation
| E_proof of P.t
type repr = node type repr = node
@ -168,6 +169,7 @@ module Make (A: CC_ARG)
| E_merge (a,b) -> Fmt.fprintf out "(@[merge@ %a@ %a@])" N.pp a N.pp b | E_merge (a,b) -> Fmt.fprintf out "(@[merge@ %a@ %a@])" N.pp a N.pp b
| E_merge_t (a,b) -> Fmt.fprintf out "(@[<hv>merge@ @[:n1 %a@]@ @[:n2 %a@]@])" Term.pp a Term.pp b | E_merge_t (a,b) -> Fmt.fprintf out "(@[<hv>merge@ @[:n1 %a@]@ @[:n2 %a@]@])" Term.pp a Term.pp b
| E_theory e -> Fmt.fprintf out "(@[th@ %a@])" pp e | E_theory e -> Fmt.fprintf out "(@[th@ %a@])" pp e
| E_proof p -> Fmt.fprintf out "(@[proof@ %a@])" P.pp p
| E_and (a,b) -> | E_and (a,b) ->
Format.fprintf out "(@[<hv1>and@ %a@ %a@])" pp a pp b Format.fprintf out "(@[<hv1>and@ %a@ %a@])" pp a pp b
@ -177,6 +179,7 @@ module Make (A: CC_ARG)
let[@inline] mk_merge_t a b : t = if Term.equal a b then mk_reduction else E_merge_t (a,b) let[@inline] mk_merge_t a b : t = if Term.equal a b then mk_reduction else E_merge_t (a,b)
let[@inline] mk_lit l : t = E_lit l let[@inline] mk_lit l : t = E_lit l
let mk_theory e = E_theory e let mk_theory e = E_theory e
let mk_proof p = E_proof p
let rec mk_list l = let rec mk_list l =
match l with match l with
@ -280,7 +283,7 @@ module Make (A: CC_ARG)
and ev_on_post_merge = t -> actions -> N.t -> N.t -> unit 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_new_term = t -> N.t -> term -> unit
and ev_on_conflict = t -> th:bool -> lit list -> unit and ev_on_conflict = t -> th:bool -> lit list -> unit
and ev_on_propagate = t -> lit -> (unit -> lit list) -> unit and ev_on_propagate = t -> lit -> (unit -> lit list * P.t) -> unit
and ev_on_is_subterm = N.t -> term -> unit and ev_on_is_subterm = N.t -> term -> unit
let[@inline] size_ (r:repr) = r.n_size let[@inline] size_ (r:repr) = r.n_size
@ -458,6 +461,9 @@ module Make (A: CC_ARG)
| E_lit lit -> lit :: acc | E_lit lit -> lit :: acc
| E_theory e' -> | E_theory e' ->
th := true; explain_decompose cc ~th acc e' th := true; explain_decompose cc ~th acc e'
| E_proof _p ->
(* FIXME: need to also return pairs of [t, u, |-t=u] as part of explanation *)
assert false
| E_merge (a,b) -> explain_pair cc ~th acc a b | E_merge (a,b) -> explain_pair cc ~th acc a b
| E_merge_t (a,b) -> | E_merge_t (a,b) ->
(* find nodes for [a] and [b] on the fly *) (* find nodes for [a] and [b] on the fly *)
@ -657,7 +663,7 @@ module Make (A: CC_ARG)
Iter.of_list lits Iter.of_list lits
|> Iter.map (fun lit -> Lit.term lit, Lit.sign lit) |> Iter.map (fun lit -> Lit.term lit, Lit.sign lit)
in in
P.make_cc lits P.cc_lemma lits
in in
raise_conflict_ cc ~th:!th acts (List.rev_map Lit.neg lits) proof raise_conflict_ cc ~th:!th acts (List.rev_map Lit.neg lits) proof
); );
@ -773,13 +779,18 @@ module Make (A: CC_ARG)
let reason = let reason =
let e = lazy ( let e = lazy (
let lazy (th, acc) = half_expl in let lazy (th, acc) = half_expl in
explain_pair cc ~th acc u1 t1 let lits = explain_pair cc ~th acc u1 t1 in
let p =
A.P.cc_lemma
(Iter.of_list lits |> Iter.map (fun l -> Lit.term l, Lit.sign l))
in
lits, p
) in ) in
fun () -> Lazy.force e fun () -> Lazy.force e
in in
List.iter (fun f -> f cc lit reason) cc.on_propagate; List.iter (fun f -> f cc lit reason) cc.on_propagate;
Stat.incr cc.count_props; Stat.incr cc.count_props;
Actions.propagate acts lit ~reason P.default Actions.propagate acts lit ~reason
| _ -> ()) | _ -> ())
module Debug_ = struct module Debug_ = struct
@ -845,7 +856,7 @@ module Make (A: CC_ARG)
Iter.of_list lits Iter.of_list lits
|> Iter.map (fun lit -> Lit.term lit, Lit.sign lit) |> Iter.map (fun lit -> Lit.term lit, Lit.sign lit)
in in
P.make_cc lits P.cc_lemma lits
in in
raise_conflict_ cc ~th:!th acts lits proof raise_conflict_ cc ~th:!th acts lits proof

View file

@ -147,10 +147,24 @@ end
module type PROOF = sig module type PROOF = sig
type term type term
type clause
type t type t
val pp : t Fmt.printer val pp : t Fmt.printer
val default : t
val make_cc : (term*bool) Iter.t -> t (** hyper-resolution steps: resolution, unit resolution; bool paramodulation, unit bool paramodulation *)
type hres_step = | R | R1 | P | P1
val hres_iter : t -> (hres_step * t) Iter.t -> t (* hyper-res *)
val hres_l : t -> (hres_step * t) list -> t (* hyper-res *)
val refl : term -> t (* proof of [| t=t] *)
val true_is_true : t (* proof of [|- true] *)
val true_neq_false : t (* proof of [|- not (true=false)] *)
val cc_lemma : (term*bool) Iter.t -> t (* equality tautology, unsigned *)
val cc_imply2 : t -> t -> term -> term -> t (* tautology [p1, p2 |- t=u] *)
val cc_imply_l : t list -> term -> term -> t (* tautology [hyps |- t=u] *)
val sorry : t [@@alert cstor "sorry used"] (* proof hole when we don't know how to produce a proof *)
val default : t [@@alert cstor "do not use default constructor"]
end end
(** Literals (** Literals
@ -208,7 +222,7 @@ module type CC_ACTIONS = sig
exception). exception).
@param pr the proof of [c] being a tautology *) @param pr the proof of [c] being a tautology *)
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unit val propagate : t -> Lit.t -> reason:(unit -> Lit.t list * P.t) -> unit
(** [propagate acts lit ~reason pr] declares that [reason() => lit] (** [propagate acts lit ~reason pr] declares that [reason() => lit]
is a tautology. is a tautology.
@ -310,6 +324,7 @@ module type CC_S = sig
val mk_merge_t : term -> term -> t val mk_merge_t : term -> term -> t
val mk_lit : lit -> t val mk_lit : lit -> t
val mk_list : t list -> t val mk_list : t list -> t
val mk_proof : P.t -> t
val mk_theory : t -> t (* TODO: indicate what theory, or even provide a lemma *) val mk_theory : t -> t (* TODO: indicate what theory, or even provide a lemma *)
end end
@ -362,7 +377,7 @@ module type CC_S = sig
participating in the conflict are purely syntactic theories participating in the conflict are purely syntactic theories
like injectivity of constructors. *) like injectivity of constructors. *)
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unit type ev_on_propagate = t -> lit -> (unit -> lit list * P.t) -> unit
(** [ev_on_propagate cc lit reason] is called whenever [reason() => lit] (** [ev_on_propagate cc lit reason] is called whenever [reason() => lit]
is a propagated lemma. See {!CC_ACTIONS.propagate}. *) is a propagated lemma. See {!CC_ACTIONS.propagate}. *)
@ -437,6 +452,8 @@ module type CC_S = sig
val assert_lits : t -> lit Iter.t -> unit val assert_lits : t -> lit Iter.t -> unit
(** Addition of many literals *) (** Addition of many literals *)
(* FIXME: this needs to return [lit list * (term*term*P.t) list].
the explanation is [/\_i lit_i /\ /\_j (|- t_j=u_j) |- n1=n2] *)
val explain_eq : t -> N.t -> N.t -> lit list val explain_eq : t -> N.t -> N.t -> lit list
(** Explain why the two nodes are equal. (** Explain why the two nodes are equal.
Fails if they are not, in an unspecified way *) Fails if they are not, in an unspecified way *)
@ -546,7 +563,7 @@ module type SOLVER_INTERNAL = sig
val clear : t -> unit val clear : t -> unit
(** Reset internal cache, etc. *) (** Reset internal cache, etc. *)
type hook = t -> term -> term option type hook = t -> term -> (term * proof) option
(** Given a term, try to simplify it. Return [None] if it didn't change. (** Given a term, try to simplify it. Return [None] if it didn't change.
A simple example could be a hook that takes a term [t], A simple example could be a hook that takes a term [t],
@ -557,6 +574,11 @@ module type SOLVER_INTERNAL = sig
(** Normalize a term using all the hooks. This performs (** Normalize a term using all the hooks. This performs
a fixpoint, i.e. it only stops when no hook applies anywhere inside a fixpoint, i.e. it only stops when no hook applies anywhere inside
the term. *) the term. *)
val normalize_t : t -> term -> term * P.t
(** Normalize a term using all the hooks, along with a proof that the
simplification is correct.
returns [t, refl t] if no simplification occurred. *)
end end
type simplify_hook = Simplify.hook type simplify_hook = Simplify.hook
@ -566,12 +588,18 @@ module type SOLVER_INTERNAL = sig
val simplifier : t -> Simplify.t val simplifier : t -> Simplify.t
val simp_t : t -> term -> term val simplify_t : t -> term -> (term * proof) option
(** Simplify the term using the solver's simplifier (see {!simplifier}) *) (** Simplify input term, returns [Some (u, |- t=u)] if some
simplification occurred. *)
val simp_t : t -> term -> term * proof
(** [simp_t si t] returns [u, |- t=u] even if no simplification occurred
(in which case [t == u] syntactically).
(see {!simplifier}) *)
(** {3 hooks for the theory} *) (** {3 hooks for the theory} *)
val propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unit val propagate : t -> actions -> lit -> reason:(unit -> lit list * proof) -> unit
(** Propagate a literal for a reason. This is similar to asserting (** Propagate a literal for a reason. This is similar to asserting
the clause [reason => lit], but more lightweight, and in a way the clause [reason => lit], but more lightweight, and in a way
that is backtrackable. *) that is backtrackable. *)
@ -585,19 +613,19 @@ module type SOLVER_INTERNAL = sig
If the SAT solver backtracks, this (potential) decision is removed If the SAT solver backtracks, this (potential) decision is removed
and forgotten. *) and forgotten. *)
val propagate: t -> actions -> lit -> (unit -> lit list) -> unit val propagate: t -> actions -> lit -> (unit -> lit list * proof) -> unit
(** Propagate a boolean using a unit clause. (** Propagate a boolean using a unit clause.
[expl => lit] must be a theory lemma, that is, a T-tautology *) [expl => lit] must be a theory lemma, that is, a T-tautology *)
val propagate_l: t -> actions -> lit -> lit list -> unit val propagate_l: t -> actions -> lit -> lit list -> proof -> unit
(** Propagate a boolean using a unit clause. (** Propagate a boolean using a unit clause.
[expl => lit] must be a theory lemma, that is, a T-tautology *) [expl => lit] must be a theory lemma, that is, a T-tautology *)
val add_clause_temp : t -> actions -> lit list -> unit val add_clause_temp : t -> actions -> lit list -> proof -> unit
(** Add local clause to the SAT solver. This clause will be (** Add local clause to the SAT solver. This clause will be
removed when the solver backtracks. *) removed when the solver backtracks. *)
val add_clause_permanent : t -> actions -> lit list -> unit val add_clause_permanent : t -> actions -> lit list -> proof -> unit
(** Add toplevel clause to the SAT solver. This clause will (** Add toplevel clause to the SAT solver. This clause will
not be backtracked. *) not be backtracked. *)
@ -605,7 +633,7 @@ module type SOLVER_INTERNAL = sig
(** Create a literal. This automatically preprocesses the term. *) (** Create a literal. This automatically preprocesses the term. *)
val preprocess_term : val preprocess_term :
t -> add_clause:(Lit.t list -> unit) -> term -> term t -> add_clause:(Lit.t list -> proof -> unit) -> term -> term * proof
(** Preprocess a term. *) (** Preprocess a term. *)
val add_lit : t -> actions -> lit -> unit val add_lit : t -> actions -> lit -> unit
@ -661,7 +689,7 @@ module type SOLVER_INTERNAL = sig
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unit val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unit
(** Callback called on every CC conflict *) (** Callback called on every CC conflict *)
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unit val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list * proof) -> unit) -> unit
(** Callback called on every CC propagation *) (** Callback called on every CC propagation *)
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unit val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unit
@ -688,9 +716,10 @@ module type SOLVER_INTERNAL = sig
type preprocess_hook = type preprocess_hook =
t -> t ->
mk_lit:(term -> lit) -> mk_lit:(term -> lit) ->
add_clause:(lit list -> unit) -> add_clause:(lit list -> proof -> unit) ->
term -> term option term -> (term * proof) option
(** Given a term, try to preprocess it. Return [None] if it didn't change. (** Given a term, try to preprocess it. Return [None] if it didn't change,
or [Some (u,p)] if [t=u] and [p] is a proof of [t=u].
Can also add clauses to define new terms. Can also add clauses to define new terms.
Preprocessing might transform terms to make them more amenable Preprocessing might transform terms to make them more amenable
@ -853,6 +882,11 @@ module type SOLVER = sig
*) *)
end end
(** {3 Proof type}
The representation of a full proof, including toplevel steps
with intermediate, named, clauses. Each clause is justified by
a {!P.t} lemma. *)
module Proof : sig module Proof : sig
type t type t
val check : t -> unit val check : t -> unit
@ -906,11 +940,11 @@ module type SOLVER = sig
val mk_atom_t : t -> ?sign:bool -> term -> Atom.t val mk_atom_t : t -> ?sign:bool -> term -> Atom.t
(** Turn a boolean term, with a sign, into a SAT solver's literal. *) (** Turn a boolean term, with a sign, into a SAT solver's literal. *)
val add_clause : t -> Atom.t IArray.t -> unit val add_clause : t -> Atom.t IArray.t -> P.t -> unit
(** [add_clause solver cs] adds a boolean clause to the solver. (** [add_clause solver cs] adds a boolean clause to the solver.
Subsequent calls to {!solve} will need to satisfy this clause. *) Subsequent calls to {!solve} will need to satisfy this clause. *)
val add_clause_l : t -> Atom.t list -> unit val add_clause_l : t -> Atom.t list -> P.t -> unit
(** Same as {!add_clause} but with a list of atoms. *) (** Same as {!add_clause} but with a list of atoms. *)
(** Result of solving for the current set of clauses *) (** Result of solving for the current set of clauses *)

View file

@ -57,6 +57,9 @@ module type ARG = sig
val has_ty_real : term -> bool val has_ty_real : term -> bool
(** Does this term have the type [Real] *) (** Does this term have the type [Real] *)
(** TODO: actual proof *)
val proof_lra : S.lemma
module Gensym : sig module Gensym : sig
type t type t
@ -228,12 +231,22 @@ module Make(A : ARG) : S with module A = A = struct
proxy proxy
(* preprocess linear expressions away *) (* preprocess linear expressions away *)
let preproc_lra (self:state) si ~mk_lit ~add_clause (t:T.t) : T.t option = let preproc_lra (self:state) si ~mk_lit ~add_clause
(t:T.t) : (T.t * _) option =
Log.debugf 50 (fun k->k "lra.preprocess %a" T.pp t); Log.debugf 50 (fun k->k "lra.preprocess %a" T.pp t);
let tst = SI.tst si in let tst = SI.tst si in
let sub_proofs_ = ref [] in
(* preprocess subterm *) (* preprocess subterm *)
let preproc_t = SI.preprocess_term ~add_clause si in let preproc_t t =
let u, p_t_eq_u = SI.preprocess_term ~add_clause si t in
if t != u then (
(* add [|- t=u] to hyps *)
sub_proofs_ := (t,u,p_t_eq_u) :: !sub_proofs_;
);
u
in
(* tell the CC this term exists *) (* tell the CC this term exists *)
let declare_term_to_cc t = let declare_term_to_cc t =
@ -255,9 +268,9 @@ module Make(A : ARG) : S with module A = A = struct
let lit_t = mk_lit t in let lit_t = mk_lit t in
let lit_u1 = mk_lit u1 in let lit_u1 = mk_lit u1 in
let lit_u2 = mk_lit u2 in let lit_u2 = mk_lit u2 in
add_clause [SI.Lit.neg lit_t; lit_u1]; add_clause [SI.Lit.neg lit_t; lit_u1] A.proof_lra;
add_clause [SI.Lit.neg lit_t; lit_u2]; add_clause [SI.Lit.neg lit_t; lit_u2] A.proof_lra;
add_clause [SI.Lit.neg lit_u1; SI.Lit.neg lit_u2; lit_t]; add_clause [SI.Lit.neg lit_u1; SI.Lit.neg lit_u2; lit_t] A.proof_lra;
); );
None None
@ -292,7 +305,9 @@ module Make(A : ARG) : S with module A = A = struct
end; end;
Log.debugf 10 (fun k->k "lra.preprocess:@ %a@ :into %a" T.pp t T.pp new_t); Log.debugf 10 (fun k->k "lra.preprocess:@ %a@ :into %a" T.pp t T.pp new_t);
Some new_t (* FIXME: by def proxy + LRA *)
let proof = A.S.P.sorry in
Some (new_t, proof)
| Some (coeff, v), pred -> | Some (coeff, v), pred ->
(* [c . v <= const] becomes a direct simplex constraint [v <= const/c] *) (* [c . v <= const] becomes a direct simplex constraint [v <= const/c] *)
@ -317,7 +332,8 @@ module Make(A : ARG) : S with module A = A = struct
end; end;
Log.debugf 10 (fun k->k "lra.preprocess@ :%a@ :into %a" T.pp t T.pp new_t); Log.debugf 10 (fun k->k "lra.preprocess@ :%a@ :into %a" T.pp t T.pp new_t);
Some new_t let proof = A.proof_lra in
Some (new_t, proof)
end end
| LRA_op _ | LRA_mult _ -> | LRA_op _ | LRA_mult _ ->
@ -330,7 +346,10 @@ module Make(A : ARG) : S with module A = A = struct
let proxy = var_encoding_comb self ~pre:"_le" le_comb in let proxy = var_encoding_comb self ~pre:"_le" le_comb in
declare_term_to_cc proxy; declare_term_to_cc proxy;
Some proxy (* TODO: proof by def of proxy *)
let proof = A.S.P.sorry in
Some (proxy, proof)
) else ( ) else (
(* a bit more complicated: we cannot just define [proxy := le_comb] (* a bit more complicated: we cannot just define [proxy := le_comb]
because of the coefficient. because of the coefficient.
@ -355,24 +374,26 @@ module Make(A : ARG) : S with module A = A = struct
add_clause [ add_clause [
mk_lit (A.mk_lra tst (LRA_simplex_pred (proxy2, Leq, Q.neg le_const))) mk_lit (A.mk_lra tst (LRA_simplex_pred (proxy2, Leq, Q.neg le_const)))
]; ] A.S.P.sorry; (* TODO: by-def proxy2 + LRA *)
add_clause [ add_clause [
mk_lit (A.mk_lra tst (LRA_simplex_pred (proxy2, Geq, Q.neg le_const))) mk_lit (A.mk_lra tst (LRA_simplex_pred (proxy2, Geq, Q.neg le_const)))
]; ] A.S.P.sorry; (* TODO: by-def proxy2 + LRA *)
Some proxy (* FIXME: actual proof *)
let proof = A.S.P.sorry in
Some (proxy, proof)
) )
| LRA_other t when A.has_ty_real t -> None | LRA_other t when A.has_ty_real t -> None
| LRA_const _ | LRA_simplex_pred _ | LRA_simplex_var _ | LRA_other _ -> None | LRA_const _ | LRA_simplex_pred _ | LRA_simplex_var _ | LRA_other _ -> None
let simplify (self:state) (_recurse:_) (t:T.t) : T.t option = let simplify (self:state) (_recurse:_) (t:T.t) : (T.t * SI.proof) option =
match A.view_as_lra t with match A.view_as_lra t with
| LRA_op _ | LRA_mult _ -> | LRA_op _ | LRA_mult _ ->
let le = as_linexp_id t in let le = as_linexp_id t in
if LE.is_const le then ( if LE.is_const le then (
let c = LE.const le in let c = LE.const le in
Some (A.mk_lra self.tst (LRA_const c)) Some (A.mk_lra self.tst (LRA_const c), A.proof_lra)
) else None ) else None
| LRA_pred (pred, l1, l2) -> | LRA_pred (pred, l1, l2) ->
let le = LE.(as_linexp_id l1 - as_linexp_id l2) in let le = LE.(as_linexp_id l1 - as_linexp_id l2) in
@ -386,7 +407,7 @@ module Make(A : ARG) : S with module A = A = struct
| Eq -> Q.(c = zero) | Eq -> Q.(c = zero)
| Neq -> Q.(c <> zero) | Neq -> Q.(c <> zero)
in in
Some (A.mk_bool self.tst is_true) Some (A.mk_bool self.tst is_true, A.proof_lra)
) else None ) else None
| _ -> None | _ -> None
@ -400,13 +421,15 @@ module Make(A : ARG) : S with module A = A = struct
|> CCList.flat_map (Tag.to_lits si) |> CCList.flat_map (Tag.to_lits si)
|> List.rev_map SI.Lit.neg |> List.rev_map SI.Lit.neg
in in
SI.raise_conflict si acts confl SI.P.default (* TODO: more detailed proof certificate *)
SI.raise_conflict si acts confl A.proof_lra
let on_propagate_ si acts lit ~reason = let on_propagate_ si acts lit ~reason =
match lit with match lit with
| Tag.Lit lit -> | Tag.Lit lit ->
(* TODO: more detailed proof certificate *)
SI.propagate si acts lit SI.propagate si acts lit
(fun() -> CCList.flat_map (Tag.to_lits si) reason) (fun() -> CCList.flat_map (Tag.to_lits si) reason, A.proof_lra)
| _ -> () | _ -> ()
let check_simplex_ self si acts : SimpSolver.Subst.t = let check_simplex_ self si acts : SimpSolver.Subst.t =

View file

@ -93,8 +93,8 @@ module Make(A : ARG)
type t = msat_acts type t = msat_acts
let[@inline] raise_conflict a lits pr = let[@inline] raise_conflict a lits pr =
a.Msat.acts_raise_conflict lits pr a.Msat.acts_raise_conflict lits pr
let[@inline] propagate a lit ~reason pr = let[@inline] propagate a lit ~reason =
let reason = Msat.Consequence (fun () -> reason(), pr) in let reason = Msat.Consequence reason in
a.Msat.acts_propagate lit reason a.Msat.acts_propagate lit reason
end end
end end
@ -134,7 +134,7 @@ module Make(A : ARG)
mutable hooks: hook list; mutable hooks: hook list;
cache: Term.t Term.Tbl.t; cache: Term.t Term.Tbl.t;
} }
and hook = t -> term -> term option and hook = t -> term -> (term * P.t) option
let create tst ty_st : t = let create tst ty_st : t =
{tst; ty_st; hooks=[]; cache=Term.Tbl.create 32;} {tst; ty_st; hooks=[]; cache=Term.Tbl.create 32;}
@ -143,9 +143,11 @@ module Make(A : ARG)
let add_hook self f = self.hooks <- f :: self.hooks let add_hook self f = self.hooks <- f :: self.hooks
let clear self = Term.Tbl.clear self.cache let clear self = Term.Tbl.clear self.cache
let normalize (self:t) (t:Term.t) : Term.t = let normalize (self:t) (t:Term.t) : (Term.t * P.t) option =
let sub_proofs_ = ref [] in
(* compute and cache normal form of [t] *) (* compute and cache normal form of [t] *)
let rec aux t = let rec aux t : Term.t =
match Term.Tbl.find self.cache t with match Term.Tbl.find self.cache t with
| u -> u | u -> u
| exception Not_found -> | exception Not_found ->
@ -160,10 +162,23 @@ module Make(A : ARG)
| h :: hooks_tl -> | h :: hooks_tl ->
match h self t with match h self t with
| None -> aux_rec t hooks_tl | None -> aux_rec t hooks_tl
| Some u when Term.equal t u -> aux_rec t hooks_tl | Some (u, _) when Term.equal t u -> aux_rec t hooks_tl
| Some u -> aux u | Some (u, pr_t_u) ->
sub_proofs_ := pr_t_u :: !sub_proofs_;
aux u
in in
aux t let u = aux t in
if Term.equal t u then None
else (
(* proof: [sub_proofs |- t=u] by CC *)
let pr = P.cc_imply_l !sub_proofs_ t u in
Some (u, pr)
)
let normalize_t self t =
match normalize self t with
| None -> t, P.refl t
| Some (u,pr) -> u, pr
end end
type simplify_hook = Simplify.hook type simplify_hook = Simplify.hook
@ -180,7 +195,7 @@ module Make(A : ARG)
simp: Simplify.t; simp: Simplify.t;
mutable preprocess: preprocess_hook list; mutable preprocess: preprocess_hook list;
mutable mk_model: model_hook list; mutable mk_model: model_hook list;
preprocess_cache: Term.t Term.Tbl.t; preprocess_cache: (Term.t * P.t) Term.Tbl.t;
mutable th_states : th_states; (** Set of theories *) mutable th_states : th_states; (** Set of theories *)
mutable on_partial_check: (t -> actions -> lit Iter.t -> unit) list; mutable on_partial_check: (t -> actions -> lit Iter.t -> unit) list;
mutable on_final_check: (t -> actions -> lit Iter.t -> unit) list; mutable on_final_check: (t -> actions -> lit Iter.t -> unit) list;
@ -190,8 +205,8 @@ module Make(A : ARG)
and preprocess_hook = and preprocess_hook =
t -> t ->
mk_lit:(term -> lit) -> mk_lit:(term -> lit) ->
add_clause:(lit list -> unit) -> add_clause:(lit list -> P.t -> unit) ->
term -> term option term -> (term * P.t) option
and model_hook = and model_hook =
recurse:(t -> CC.N.t -> term) -> recurse:(t -> CC.N.t -> term) ->
@ -216,7 +231,9 @@ module Make(A : ARG)
let stats t = t.stat let stats t = t.stat
let simplifier self = self.simp let simplifier self = self.simp
let simp_t self (t:Term.t) : Term.t = Simplify.normalize self.simp t let simplify_t self (t:Term.t) : _ option = Simplify.normalize self.simp t
let simp_t self (t:Term.t) : Term.t * P.t = Simplify.normalize_t self.simp t
let add_simplifier (self:t) f : unit = Simplify.add_hook self.simp f let add_simplifier (self:t) f : unit = Simplify.add_hook self.simp f
let on_preprocess self f = self.preprocess <- f :: self.preprocess let on_preprocess self f = self.preprocess <- f :: self.preprocess
@ -226,34 +243,58 @@ module Make(A : ARG)
let sign = Lit.sign lit in let sign = Lit.sign lit in
acts.Msat.acts_add_decision_lit (Lit.abs lit) sign acts.Msat.acts_add_decision_lit (Lit.abs lit) sign
let[@inline] raise_conflict self acts c : 'a = let[@inline] raise_conflict self acts c proof : 'a =
Stat.incr self.count_conflict; Stat.incr self.count_conflict;
acts.Msat.acts_raise_conflict c P.default acts.Msat.acts_raise_conflict c proof
let[@inline] propagate self acts p cs : unit = let[@inline] propagate self acts p reason : unit =
Stat.incr self.count_propagate; Stat.incr self.count_propagate;
acts.Msat.acts_propagate p (Msat.Consequence (fun () -> cs(), P.default)) acts.Msat.acts_propagate p (Msat.Consequence reason)
let[@inline] propagate_l self acts p cs : unit = let[@inline] propagate_l self acts p cs proof : unit =
propagate self acts p (fun()->cs) propagate self acts p (fun()->cs,proof)
let add_sat_clause_ self acts ~keep lits : unit = let add_sat_clause_ self acts ~keep lits (proof:P.t) : unit =
Stat.incr self.count_axiom; Stat.incr self.count_axiom;
acts.Msat.acts_add_clause ~keep lits P.default acts.Msat.acts_add_clause ~keep lits proof
let preprocess_term_ (self:t) ~add_clause (t:term) : term = let preprocess_term_ (self:t) ~add_clause (t:term) : term * proof =
let mk_lit t = Lit.atom self.tst t in (* no further simplification *) let mk_lit t = Lit.atom self.tst t in (* no further simplification *)
(* compute and cache normal form of [t] *) (* compute and cache normal form of [t] *)
let rec aux t = let rec aux t : term * proof =
match Term.Tbl.find self.preprocess_cache t with match Term.Tbl.find self.preprocess_cache t with
| u -> u | up -> up
| exception Not_found -> | exception Not_found ->
let sub_p = ref [] in
(* try rewrite at root *) (* try rewrite at root *)
let t1 = aux_rec t self.preprocess in let t1 = aux_rec ~sub_p t self.preprocess in
(* then map subterms *)
let t2 = Term.map_shallow self.tst aux t1 in (* map subterms *) (* map subterms *)
let u = if t != t2 then aux t2 (* fixpoint *) else t2 in let t2 =
Term.map_shallow self.tst
(fun t_sub ->
let u_sub, p_t = aux t_sub in
if not (Term.equal t_sub u_sub) then (
sub_p := p_t :: !sub_p;
);
u_sub)
t1
in
let u =
if not (Term.equal t t2) then (
(* fixpoint *)
let v, p_t2_v = aux t2 in
if not (Term.equal t2 v) then (
sub_p := p_t2_v :: !sub_p
);
v
) else (
t2
)
in
if t != u then ( if t != u then (
Log.debugf 5 Log.debugf 5
@ -261,46 +302,77 @@ module Make(A : ARG)
:from %a@ :to %a@])" Term.pp t Term.pp u); :from %a@ :to %a@])" Term.pp t Term.pp u);
); );
Term.Tbl.add self.preprocess_cache t u; let p_t_u =
u if t != u then (
P.cc_imply_l !sub_p t u (* proof: [sub_p |- t=u] *)
) else P.refl t
in
Term.Tbl.add self.preprocess_cache t (u,p_t_u);
u, p_t_u
(* try each function in [hooks] successively *) (* try each function in [hooks] successively *)
and aux_rec t hooks = match hooks with and aux_rec ~sub_p t hooks : term =
match hooks with
| [] -> t | [] -> t
| h :: hooks_tl -> | h :: hooks_tl ->
match h self ~mk_lit ~add_clause t with match h self ~mk_lit ~add_clause t with
| None -> aux_rec t hooks_tl | None -> aux_rec ~sub_p t hooks_tl
| Some u -> aux u | Some (u, p_t_u) ->
sub_p := p_t_u :: !sub_p;
let v, p_u_v = aux u in
if t != v then (
sub_p := p_u_v :: !sub_p;
);
v
in in
t |> simp_t self |> aux
let preprocess_lit_ (self:t) ~add_clause (lit:lit) : lit = let t1, p_t_t1 = simp_t self t in
let t = Lit.term lit |> preprocess_term_ self ~add_clause in
let u, p_t1_u = aux t1 in
if t != u then (
let pr = P.cc_imply2 p_t_t1 p_t1_u t u in
u, pr
) else (
u, P.refl u
)
(* return preprocessed lit + proof they are equal *)
let preprocess_lit_ (self:t) ~add_clause (lit:lit) : lit * proof =
let t, p = Lit.term lit |> preprocess_term_ self ~add_clause in
let lit' = Lit.atom self.tst ~sign:(Lit.sign lit) t in let lit' = Lit.atom self.tst ~sign:(Lit.sign lit) t in
Log.debugf 10 Log.debugf 10
(fun k->k "(@[msat-solver.preprocess.lit@ :lit %a@ :into %a@])" Lit.pp lit Lit.pp lit'); (fun k->k "(@[msat-solver.preprocess.lit@ :lit %a@ :into %a@ :proof %a@])"
lit' Lit.pp lit Lit.pp lit' P.pp p);
lit', p
(* add a clause using [acts] *) (* add a clause using [acts] *)
let add_clause_ self acts lits : unit = let add_clause_ self acts lits (proof:P.t) : unit =
Stat.incr self.count_preprocess_clause; Stat.incr self.count_preprocess_clause;
add_sat_clause_ self acts ~keep:true lits add_sat_clause_ self acts ~keep:true lits proof
let mk_lit self acts ?sign t = (* FIXME: should we store the proof somewhere? *)
let mk_lit self acts ?sign t : Lit.t =
let add_clause = add_clause_ self acts in let add_clause = add_clause_ self acts in
preprocess_lit_ self ~add_clause @@ Lit.atom self.tst ?sign t let lit, _p =
preprocess_lit_ self ~add_clause @@ Lit.atom self.tst ?sign t
in
lit
let[@inline] preprocess_term self ~add_clause (t:term) : term = let[@inline] preprocess_term self ~add_clause (t:term) : term * proof =
preprocess_term_ self ~add_clause t preprocess_term_ self ~add_clause t
let[@inline] add_clause_temp self acts lits : unit = let[@inline] add_clause_temp self acts lits (proof:P.t) : unit =
add_sat_clause_ self acts ~keep:false lits add_sat_clause_ self acts ~keep:false lits proof
let[@inline] add_clause_permanent self acts lits : unit = let[@inline] add_clause_permanent self acts lits (proof:P.t) : unit =
add_sat_clause_ self acts ~keep:true lits add_sat_clause_ self acts ~keep:true lits proof
let add_lit _self acts lit : unit = acts.Msat.acts_mk_lit lit let add_lit _self acts lit : unit = acts.Msat.acts_mk_lit lit
let add_lit_t self acts ?sign t = add_lit self acts (mk_lit self acts ?sign t) let add_lit_t self acts ?sign t =
let lit = mk_lit self acts ?sign t in
add_lit self acts lit
let on_final_check self f = self.on_final_check <- f :: self.on_final_check let on_final_check self f = self.on_final_check <- f :: self.on_final_check
let on_partial_check self f = self.on_partial_check <- f :: self.on_partial_check let on_partial_check self f = self.on_partial_check <- f :: self.on_partial_check
@ -475,24 +547,29 @@ module Make(A : ARG)
| Lemma l -> Fmt.fprintf out "(@[lemma@ %a@])" P.pp l | Lemma l -> Fmt.fprintf out "(@[lemma@ %a@])" P.pp l
| Duplicate (c, _) -> | Duplicate (c, _) ->
let n = find_idx_of_proof_ c in let n = find_idx_of_proof_ c in
Fmt.fprintf out "(@[dedup@ %d@])" n Fmt.fprintf out "(@[dedup@ c%d@])" n
| Hyper_res { hr_init=init; hr_steps=steps } -> | Hyper_res { hr_init=init; hr_steps=steps } ->
let n_init = find_idx_of_proof_ init in let n_init = find_idx_of_proof_ init in
let pp_step out (pivot,p') = let pp_step out (pivot,p') =
let unit_res =
Array.length (SC.atoms (conclusion p')) = 1 in
let n_p' = find_idx_of_proof_ p' in let n_p' = find_idx_of_proof_ p' in
Fmt.fprintf out "(@[%d@ :pivot %a@])" n_p' pp_atom pivot if unit_res then (
Fmt.fprintf out "(@[r1 c%d@])" n_p'
) else (
Fmt.fprintf out "(@[r c%d@ :pivot %a@])" n_p' pp_atom pivot
)
in in
Fmt.fprintf out "(@[hres@ %d@ (@[%a@])@])" Fmt.fprintf out "(@[hres@ %d@ (@[%a@])@])"
n_init Fmt.(list ~sep:(return "@ ") pp_step) steps n_init Fmt.(list ~sep:(return "@ ") pp_step) steps
in in
Fmt.fprintf out "(@[step %d@ (@[cl %a@])@ (@[<1>src@ %a@])@])@ " Fmt.fprintf out "(@[defc c%d@ (@[cl %a@])@ (@[<1>src@ %a@])@])@ "
idx Fmt.(list ~sep:(return "@ ") pp_atom) atoms idx Fmt.(list ~sep:(return "@ ") pp_atom) atoms
pp_step step; pp_step step;
) )
in in
Fmt.fprintf out "(@[<v>proof@ "; Fmt.fprintf out "(@[<v>quip 1@ ";
Sat_solver.Proof.fold pp_node () self; Sat_solver.Proof.fold pp_node () self;
Fmt.fprintf out "@])@."; Fmt.fprintf out "@])@.";
() ()
@ -564,7 +641,7 @@ module Make(A : ARG)
let tst = Solver_internal.tst self.si in let tst = Solver_internal.tst self.si in
Sat_solver.assume self.solver [ Sat_solver.assume self.solver [
[Lit.atom tst @@ Term.bool tst true]; [Lit.atom tst @@ Term.bool tst true];
] P.default; ] P.true_is_true
end; end;
self self
@ -600,18 +677,18 @@ module Make(A : ARG)
()) ())
let rec mk_atom_lit self lit : Atom.t = let rec mk_atom_lit self lit : Atom.t =
let lit = preprocess_lit_ self lit in let lit, _proof = preprocess_lit_ self lit in
add_bool_subterms_ self (Lit.term lit); add_bool_subterms_ self (Lit.term lit);
Sat_solver.make_atom self.solver lit Sat_solver.make_atom self.solver lit
and preprocess_lit_ self lit : Lit.t = and preprocess_lit_ self lit : Lit.t * P.t =
Solver_internal.preprocess_lit_ Solver_internal.preprocess_lit_
~add_clause:(fun lits -> ~add_clause:(fun lits proof ->
(* recursively add these sub-literals, so they're also properly processed *) (* recursively add these sub-literals, so they're also properly processed *)
Stat.incr self.si.count_preprocess_clause; Stat.incr self.si.count_preprocess_clause;
let atoms = List.map (mk_atom_lit self) lits in let atoms = List.map (mk_atom_lit self) lits in
Sat_solver.add_clause self.solver atoms P.default) Sat_solver.add_clause self.solver atoms proof)
self.si lit self.si lit
let[@inline] mk_atom_t self ?sign t : Atom.t = let[@inline] mk_atom_t self ?sign t : Atom.t =
let lit = Lit.atom (tst self) ?sign t in let lit = Lit.atom (tst self) ?sign t in
@ -667,11 +744,12 @@ module Make(A : ARG)
let pp_stats out (self:t) : unit = let pp_stats out (self:t) : unit =
Stat.pp_all out (Stat.all @@ stats self) Stat.pp_all out (Stat.all @@ stats self)
let add_clause (self:t) (c:Atom.t IArray.t) : unit = let add_clause (self:t) (c:Atom.t IArray.t) (proof:P.t) : unit =
Stat.incr self.count_clause; Stat.incr self.count_clause;
Log.debugf 50 (fun k->k "(@[solver.add-clause@ %a@])" (Util.pp_iarray Atom.pp) c); Log.debugf 50 (fun k->k "(@[solver.add-clause@ %a@ :proof %a@])"
(Util.pp_iarray Atom.pp) c P.pp proof);
let pb = Profile.begin_ "add-clause" in let pb = Profile.begin_ "add-clause" in
Sat_solver.add_clause_a self.solver (c:> Atom.t array) P.default; Sat_solver.add_clause_a self.solver (c:> Atom.t array) proof;
Profile.exit pb Profile.exit pb
let add_clause_l self c = add_clause self (IArray.of_list c) let add_clause_l self c = add_clause self (IArray.of_list c)

View file

@ -271,7 +271,7 @@ let process_stmt
); );
let atom = Solver.mk_atom_t solver t in let atom = Solver.mk_atom_t solver t in
CCOpt.iter (fun h -> Vec.push h [atom]) hyps; CCOpt.iter (fun h -> Vec.push h [atom]) hyps;
Solver.add_clause solver (IArray.singleton atom); Solver.add_clause solver (IArray.singleton atom) (Proof.assertion t);
E.return() E.return()
| Statement.Stmt_assert_clause c -> | Statement.Stmt_assert_clause c ->
if pp_cnf then ( if pp_cnf then (

View file

@ -4,14 +4,26 @@ type term = T.t
type t = type t =
| Unspecified | Unspecified
| Sorry of term
| CC_lemma_imply of t list * term * term
| CC_lemma of (term * bool) list | CC_lemma of (term * bool) list
| Assertion of term
let default=Unspecified let default=Unspecified
let sorry t = Sorry t
let make_cc iter : t = CC_lemma (Iter.to_rev_list iter) let make_cc iter : t = CC_lemma (Iter.to_rev_list iter)
let pp out = function let assertion t = Assertion t
let rec pp out (self:t) : unit =
let pp_signed_t_ out (t,b) =
if b then T.pp out t else Fmt.fprintf out "(@[not@ %a@])" T.pp t
in
match self with
| Unspecified -> Fmt.string out "<unspecified>" | Unspecified -> Fmt.string out "<unspecified>"
| CC_lemma_imply (l,t,u) ->
Fmt.fprintf out "(@[cc-lemma@ (@[%a@])@])"
| CC_lemma l -> | CC_lemma l ->
let pp_lit out (t,b) =
if b then T.pp out t else Fmt.fprintf out "(@[not@ %a@])" T.pp t
in
Fmt.fprintf out "(@[cc-lemma@ (@[%a@])@])" Fmt.fprintf out "(@[cc-lemma@ (@[%a@])@])"
Fmt.(list ~sep:(return "@ ") pp_lit) l Fmt.(list ~sep:(return "@ ") pp_lit) l

View file

@ -27,6 +27,15 @@ module type ARG = sig
val view_as_bool : term -> (term, term Iter.t) bool_view val view_as_bool : term -> (term, term Iter.t) bool_view
(** Project the term into the boolean view. *) (** Project the term into the boolean view. *)
(** [proof_ite_true (ite a b c)] is [a=true |- ite a b c = b] *)
val proof_ite_true : S.T.Term.t -> S.P.t
(** [proof_ite_false (ite a b c)] is [a=false |- ite a b c = c] *)
val proof_ite_false : S.T.Term.t -> S.P.t
(** By basic boolean logic to prove [a=b] *)
val proof_bool : S.T.Term.t -> S.T.Term.t -> S.P.t
val mk_bool : S.T.Term.state -> (term, term IArray.t) bool_view -> term val mk_bool : S.T.Term.state -> (term, term IArray.t) bool_view -> term
(** Make a term from the given boolean view. *) (** Make a term from the given boolean view. *)
@ -86,13 +95,12 @@ module Make(A : ARG) : S with module A = A = struct
type state = { type state = {
tst: T.state; tst: T.state;
ty_st: Ty.state; ty_st: Ty.state;
simps: T.t T.Tbl.t; (* cache *) cnf: (Lit.t * SI.P.t) T.Tbl.t; (* tseitin CNF *)
cnf: Lit.t T.Tbl.t; (* tseitin CNF *)
gensym: A.Gensym.t; gensym: A.Gensym.t;
} }
let create tst ty_st : state = let create tst ty_st : state =
{ tst; ty_st; simps=T.Tbl.create 128; { tst; ty_st;
cnf=T.Tbl.create 128; cnf=T.Tbl.create 128;
gensym=A.Gensym.create tst; gensym=A.Gensym.create tst;
} }
@ -107,47 +115,52 @@ module Make(A : ARG) : S with module A = A = struct
let is_true t = match T.as_bool t with Some true -> true | _ -> false let is_true t = match T.as_bool t with Some true -> true | _ -> false
let is_false t = match T.as_bool t with Some false -> true | _ -> false let is_false t = match T.as_bool t with Some false -> true | _ -> false
let simplify (self:state) (simp:SI.Simplify.t) (t:T.t) : T.t option = let simplify (self:state) (simp:SI.Simplify.t) (t:T.t) : (T.t * SI.P.t) option =
let tst = self.tst in let tst = self.tst in
let ret u = Some (u, A.proof_bool t u) in
match A.view_as_bool t with match A.view_as_bool t with
| B_bool _ -> None | B_bool _ -> None
| B_not u when is_true u -> Some (T.bool tst false) | B_not u when is_true u -> ret (T.bool tst false)
| B_not u when is_false u -> Some (T.bool tst true) | B_not u when is_false u -> ret (T.bool tst true)
| B_not _ -> None | B_not _ -> None
| B_opaque_bool _ -> None | B_opaque_bool _ -> None
| B_and a -> | B_and a ->
if Iter.exists is_false a then Some (T.bool tst false) if Iter.exists is_false a then ret (T.bool tst false)
else if Iter.for_all is_true a then Some (T.bool tst true) else if Iter.for_all is_true a then ret (T.bool tst true)
else None else None
| B_or a -> | B_or a ->
if Iter.exists is_true a then Some (T.bool tst true) if Iter.exists is_true a then ret (T.bool tst true)
else if Iter.for_all is_false a then Some (T.bool tst false) else if Iter.for_all is_false a then ret (T.bool tst false)
else None else None
| B_imply (args, u) -> | B_imply (args, u) ->
if Iter.exists is_false args then Some (T.bool tst true) if Iter.exists is_false args then ret (T.bool tst true)
else if is_true u then Some (T.bool tst true) else if is_true u then ret (T.bool tst true)
else None else None
| B_ite (a,b,c) -> | B_ite (a,b,c) ->
(* directly simplify [a] so that maybe we never will simplify one (* directly simplify [a] so that maybe we never will simplify one
of the branches *) of the branches *)
let a = SI.Simplify.normalize simp a in let a, pr_a = SI.Simplify.normalize_t simp a in
begin match A.view_as_bool a with begin match A.view_as_bool a with
| B_bool true -> Some b | B_bool true ->
| B_bool false -> Some c let pr = SI.P.(hres_l (A.proof_ite_true t) [R1, pr_a]) in
Some (b, pr)
| B_bool false ->
let pr = SI.P.(hres_l (A.proof_ite_false t) [R1, pr_a]) in
Some (c, pr)
| _ -> | _ ->
None None
end end
| B_equiv (a,b) when is_true a -> Some b | B_equiv (a,b) when is_true a -> ret b
| B_equiv (a,b) when is_false a -> Some (not_ tst b) | B_equiv (a,b) when is_false a -> ret (not_ tst b)
| B_equiv (a,b) when is_true b -> Some a | B_equiv (a,b) when is_true b -> ret a
| B_equiv (a,b) when is_false b -> Some (not_ tst a) | B_equiv (a,b) when is_false b -> ret (not_ tst a)
| B_xor (a,b) when is_false a -> Some b | B_xor (a,b) when is_false a -> ret b
| B_xor (a,b) when is_true a -> Some (not_ tst b) | B_xor (a,b) when is_true a -> ret (not_ tst b)
| B_xor (a,b) when is_false b -> Some a | B_xor (a,b) when is_false b -> ret a
| B_xor (a,b) when is_true b -> Some (not_ tst a) | B_xor (a,b) when is_true b -> ret (not_ tst a)
| B_equiv _ | B_xor _ -> None | B_equiv _ | B_xor _ -> None
| B_eq (a,b) when T.equal a b -> Some (T.bool tst true) | B_eq (a,b) when T.equal a b -> ret (T.bool tst true)
| B_neq (a,b) when T.equal a b -> Some (T.bool tst true) | B_neq (a,b) when T.equal a b -> ret (T.bool tst true)
| B_eq _ | B_neq _ -> None | B_eq _ | B_neq _ -> None
| B_atom _ -> None | B_atom _ -> None
@ -164,76 +177,101 @@ module Make(A : ARG) : S with module A = A = struct
mk_lit proxy mk_lit proxy
(* preprocess "ite" away *) (* preprocess "ite" away *)
let preproc_ite self si ~mk_lit ~add_clause (t:T.t) : T.t option = let preproc_ite self si ~mk_lit ~add_clause (t:T.t) : (T.t * SI.P.t) option =
match A.view_as_bool t with match A.view_as_bool t with
| B_ite (a,b,c) -> | B_ite (a,b,c) ->
let a = SI.simp_t si a in let a, pr_a = SI.simp_t si a in
begin match A.view_as_bool a with begin match A.view_as_bool a with
| B_bool true -> Some b | B_bool true ->
| B_bool false -> Some c (* [a=true |- ite a b c=b], [|- a=true] ==> [|- t=b] *)
let proof = SI.P.(hres_l (A.proof_ite_true t) [P1, pr_a]) in
Some (b, proof)
| B_bool false ->
(* [a=false |- ite a b c=c], [|- a=false] ==> [|- t=c] *)
let proof = SI.P.(hres_l (A.proof_ite_false t) [P1, pr_a]) in
Some (c, proof)
| _ -> | _ ->
let t_ite = fresh_term self ~for_:t ~pre:"ite" (T.ty b) in let t_ite = fresh_term self ~for_:t ~pre:"ite" (T.ty b) in
let lit_a = mk_lit a in let lit_a = mk_lit a in
add_clause [Lit.neg lit_a; mk_lit (eq self.tst t_ite b)]; let pr = SI.P.sorry in (* FIXME: proper proof by-def(t_ite) + bool *)
add_clause [lit_a; mk_lit (eq self.tst t_ite c)]; add_clause [Lit.neg lit_a; mk_lit (eq self.tst t_ite b)] pr;
Some t_ite add_clause [lit_a; mk_lit (eq self.tst t_ite c)] pr;
(* TODO: by def t_ite + ite-true + ite-false
+ case split [a=true \/ a=false] *)
Some (t_ite, SI.P.sorry)
end end
| _ -> None | _ -> None
(* TODO: polarity? *) (* TODO: polarity? *)
let cnf (self:state) (_si:SI.t) ~mk_lit ~add_clause (t:T.t) : T.t option = let cnf (self:state) (_si:SI.t) ~mk_lit ~add_clause (t:T.t) : (T.t * SI.P.t) option =
let rec get_lit (t:T.t) : Lit.t = let rec get_lit_and_proof_ (t:T.t) : Lit.t * SI.P.t =
let t_abs, t_sign = T.abs self.tst t in let t_abs, t_sign = T.abs self.tst t in
let lit = let lit_abs, pr =
match T.Tbl.find self.cnf t_abs with match T.Tbl.find self.cnf t_abs with
| lit -> lit (* cached *) | lit_pr -> lit_pr (* cached *)
| exception Not_found -> | exception Not_found ->
(* compute and cache *) (* compute and cache *)
let lit = get_lit_uncached t_abs in let lit, pr = get_lit_uncached t_abs in
if not (T.equal (Lit.term lit) t_abs) then ( if not (T.equal (Lit.term lit) t_abs) then (
T.Tbl.add self.cnf t_abs lit; T.Tbl.add self.cnf t_abs (lit, pr);
Log.debugf 20 Log.debugf 20
(fun k->k "(@[sidekick.bool.add-lit@ :lit %a@ :for-t %a@])" (fun k->k "(@[sidekick.bool.add-lit@ :lit %a@ :for-t %a@])"
Lit.pp lit T.pp t_abs); Lit.pp lit T.pp t_abs);
); );
lit lit, pr
in in
if t_sign then lit else Lit.neg lit
and equiv_ ~is_xor ~for_ a b : Lit.t = let lit = if t_sign then lit_abs else Lit.neg lit_abs in
lit, pr
and equiv_ ~get_lit ~is_xor ~for_ a b : Lit.t =
let a = get_lit a in let a = get_lit a in
let b = get_lit b in let b = get_lit b in
let a = if is_xor then Lit.neg a else a in (* [a xor b] is [(¬a) = b] *) let a = if is_xor then Lit.neg a else a in (* [a xor b] is [(¬a) = b] *)
let proxy = fresh_lit ~for_ ~mk_lit ~pre:"equiv_" self in let proxy = fresh_lit ~for_ ~mk_lit ~pre:"equiv_" self in
(* proxy => a<=> b, (* proxy => a<=> b,
¬proxy => a xor b *) ¬proxy => a xor b *)
add_clause [Lit.neg proxy; Lit.neg a; b]; let proof = SI.P.sorry in (* FIXME: by_def(proxy) + bool *)
add_clause [Lit.neg proxy; Lit.neg b; a]; add_clause [Lit.neg proxy; Lit.neg a; b] proof;
add_clause [proxy; a; b]; add_clause [Lit.neg proxy; Lit.neg b; a] proof;
add_clause [proxy; Lit.neg a; Lit.neg b]; add_clause [proxy; a; b] proof;
add_clause [proxy; Lit.neg a; Lit.neg b] proof;
proxy proxy
and get_lit_uncached t : Lit.t = (* make a literal for [t], with a proof of [|- abs(t) = abs(lit)] *)
and get_lit_uncached t : Lit.t * SI.P.t =
let sub_p = ref [] in
let get_lit t =
let lit, pr = get_lit_and_proof_ t in
if Lit.term lit != t then (
sub_p := pr :: !sub_p;
);
lit
in
match A.view_as_bool t with match A.view_as_bool t with
| B_bool b -> mk_lit (T.bool self.tst b) | B_bool b -> mk_lit (T.bool self.tst b), SI.P.refl t
| B_opaque_bool t -> mk_lit t | B_opaque_bool t -> mk_lit t, SI.P.refl t
| B_not u -> | B_not u ->
let lit = get_lit u in let lit, pr = get_lit_and_proof_ u in
Lit.neg lit Lit.neg lit, pr
| B_and l -> | B_and l ->
let subs = l |> Iter.map get_lit |> Iter.to_list in let subs = l |> Iter.map get_lit |> Iter.to_list in
let proxy = fresh_lit ~for_:t ~mk_lit ~pre:"and_" self in let proxy = fresh_lit ~for_:t ~mk_lit ~pre:"and_" self in
(* add clauses *) (* add clauses *)
List.iter (fun u -> add_clause [Lit.neg proxy; u]) subs; let proof = SI.P.sorry in (* FIXME: by_def(proxy) + bool *)
add_clause (proxy :: List.map Lit.neg subs); List.iter (fun u -> add_clause [Lit.neg proxy; u] proof) subs;
proxy add_clause (proxy :: List.map Lit.neg subs) proof;
proxy, proof (* FIXME: use sub_p, by-def(proxy), A.proof_bool *)
| B_or l -> | B_or l ->
let subs = l |> Iter.map get_lit |> Iter.to_list in let subs = l |> Iter.map get_lit |> Iter.to_list in
let proxy = fresh_lit ~for_:t ~mk_lit ~pre:"or_" self in let proxy = fresh_lit ~for_:t ~mk_lit ~pre:"or_" self in
(* add clauses *) (* add clauses *)
List.iter (fun u -> add_clause [Lit.neg u; proxy]) subs; let proof = SI.P.sorry in (* FIXME: by_def(proxy) + bool *)
add_clause (Lit.neg proxy :: subs); List.iter (fun u -> add_clause [Lit.neg u; proxy] proof) subs;
proxy add_clause (Lit.neg proxy :: subs) proof;
proxy, proof (* FIXME: use sub_p, by-def(proxy), A.proof_bool *)
| B_imply (args, u) -> | B_imply (args, u) ->
(* transform into [¬args \/ u] on the fly *) (* transform into [¬args \/ u] on the fly *)
let args = args |> Iter.map get_lit |> Iter.map Lit.neg |> Iter.to_list in let args = args |> Iter.map get_lit |> Iter.map Lit.neg |> Iter.to_list in
@ -242,19 +280,21 @@ module Make(A : ARG) : S with module A = A = struct
(* now the or-encoding *) (* now the or-encoding *)
let proxy = fresh_lit ~for_:t ~mk_lit ~pre:"implies_" self in let proxy = fresh_lit ~for_:t ~mk_lit ~pre:"implies_" self in
(* add clauses *) (* add clauses *)
List.iter (fun u -> add_clause [Lit.neg u; proxy]) subs; let proof = SI.P.sorry in (* FIXME: by_def(proxy) + bool *)
add_clause (Lit.neg proxy :: subs); List.iter (fun u -> add_clause [Lit.neg u; proxy] proof) subs;
proxy add_clause (Lit.neg proxy :: subs) proof;
| B_ite _ | B_eq _ | B_neq _ -> mk_lit t proxy, proof (* FIXME: by_def(proxy) + sub_p + A.proof_bool *)
| B_equiv (a,b) -> equiv_ ~for_:t ~is_xor:false a b | B_ite _ | B_eq _ | B_neq _ -> mk_lit t, SI.P.refl t
| B_xor (a,b) -> equiv_ ~for_:t ~is_xor:true a b | B_equiv (a,b) -> equiv_ ~get_lit ~for_:t ~is_xor:false a b, SI.P.sorry (* FIXME *)
| B_atom u -> mk_lit u | B_xor (a,b) -> equiv_ ~get_lit ~for_:t ~is_xor:true a b, SI.P.sorry (* FIXME *)
| B_atom u -> mk_lit u, SI.P.refl u
in in
let lit = get_lit t in
let lit, pr = get_lit_and_proof_ t in
let u = Lit.term lit in let u = Lit.term lit in
(* put sign back as a "not" *) (* put sign back as a "not" *)
let u = if Lit.sign lit then u else A.mk_bool self.tst (B_not u) in let u = if Lit.sign lit then u else A.mk_bool self.tst (B_not u) in
if T.equal u t then None else Some u if T.equal u t then None else Some (u, pr)
(* check if new terms were added to the congruence closure, that can be turned (* check if new terms were added to the congruence closure, that can be turned
into clauses *) into clauses *)
@ -274,10 +314,10 @@ module Make(A : ARG) : S with module A = A = struct
all_terms all_terms
(fun t -> match cnf_of t with (fun t -> match cnf_of t with
| None -> () | None -> ()
| Some u -> | Some (u, pr_t_u) ->
Log.debugf 5 Log.debugf 5
(fun k->k "(@[th-bool-static.final-check.cnf@ %a@ :yields %a@])" (fun k->k "(@[th-bool-static.final-check.cnf@ %a@ :yields %a@ :pr %a@])"
T.pp t T.pp u); T.pp t T.pp u SI.P.pp pr_t_u);
SI.CC.merge_t cc_ t u (SI.CC.Expl.mk_list []); SI.CC.merge_t cc_ t u (SI.CC.Expl.mk_list []);
()); ());
end; end;

View file

@ -24,6 +24,7 @@ let name = "th-data"
module type DATA_TY = sig module type DATA_TY = sig
type t type t
type cstor type cstor
type proof
val equal : t -> t -> bool val equal : t -> t -> bool
@ -73,6 +74,10 @@ module type ARG = sig
val mk_eq : S.T.Term.state -> S.T.Term.t -> S.T.Term.t -> S.T.Term.t val mk_eq : S.T.Term.state -> S.T.Term.t -> S.T.Term.t -> S.T.Term.t
val ty_is_finite : S.T.Ty.t -> bool val ty_is_finite : S.T.Ty.t -> bool
val ty_set_is_finite : S.T.Ty.t -> bool -> unit val ty_set_is_finite : S.T.Ty.t -> bool -> unit
val proof_isa_split : S.T.Term.t Iter.t -> S.P.t
val proof_isa_disj : S.T.Term.t -> S.T.Term.t -> S.P.t
val proof_cstor_inj : Cstor.t -> S.T.Term.t list -> S.T.Term.t list -> S.P.t
end end
(** Helper to compute the cardinality of types *) (** Helper to compute the cardinality of types *)
@ -565,11 +570,13 @@ module Make(A : ARG) : S with module A = A = struct
lit) lit)
|> Iter.to_rev_list |> Iter.to_rev_list
in in
SI.add_clause_permanent solver acts c; SI.add_clause_permanent solver acts c
(A.proof_isa_split @@ (Iter.of_list c|>Iter.map SI.Lit.term));
Iter.diagonal_l c Iter.diagonal_l c
(fun (c1,c2) -> (fun (c1,c2) ->
let proof = A.proof_isa_disj (SI.Lit.term c1) (SI.Lit.term c2) in
SI.add_clause_permanent solver acts SI.add_clause_permanent solver acts
[SI.Lit.neg c1; SI.Lit.neg c2]); [SI.Lit.neg c1; SI.Lit.neg c2] proof);
) )
(* on final check, check acyclicity, (* on final check, check acyclicity,