sidekick/src/th-bool-static/Sidekick_th_bool_static.ml
2019-10-29 15:06:19 -05:00

236 lines
7.1 KiB
OCaml

(** {2 Signatures for booleans} *)
type 'a bool_view =
| B_bool of bool
| B_not of 'a
| B_and of 'a IArray.t
| B_or of 'a IArray.t
| B_imply of 'a IArray.t * 'a
| B_equiv of 'a * 'a
| B_eq of 'a * 'a
| B_ite of 'a * 'a * 'a
| B_opaque_bool of 'a (* do not enter *)
| B_atom of 'a
module type ARG = sig
module S : Sidekick_core.SOLVER
type term = S.T.Term.t
val view_as_bool : term -> term bool_view
(** Project the term into the boolean view *)
val mk_bool : S.T.Term.state -> term bool_view -> term
(** Make a term from the given boolean view *)
val check_congruence_classes : bool
(** Configuration: add final-check handler to verify if new boolean formulas
are present in the congruence closure.
Only enable if some theories are susceptible to
create boolean formulas during the proof search. *)
module Gensym : sig
type t
val create : S.T.Term.state -> t
val fresh_term : t -> pre:string -> S.T.Ty.t -> term
(** Make a fresh term of the given type *)
end
end
module type S = sig
module A : ARG
type state
val create : A.S.T.Term.state -> state
val simplify : state -> A.S.Solver_internal.simplify_hook
(** Simplify given term *)
val cnf : state -> A.S.Solver_internal.preprocess_hook
(** add clauses for the booleans within the term. *)
val theory : A.S.theory
end
module Make(A : ARG) : S with module A = A = struct
module A = A
module Ty = A.S.T.Ty
module T = A.S.T.Term
module Lit = A.S.Solver_internal.Lit
module SI = A.S.Solver_internal
type state = {
tst: T.state;
simps: T.t T.Tbl.t; (* cache *)
cnf: Lit.t T.Tbl.t; (* tseitin CNF *)
gensym: A.Gensym.t;
}
let create tst : state =
{ tst; simps=T.Tbl.create 128;
cnf=T.Tbl.create 128;
gensym=A.Gensym.create tst;
}
let[@inline] not_ tst t = A.mk_bool tst (B_not t)
let[@inline] and_a tst a = A.mk_bool tst (B_and a)
let[@inline] or_a tst a = A.mk_bool tst (B_or a)
let[@inline] ite tst a b c = A.mk_bool tst (B_ite (a,b,c))
let[@inline] equiv tst a b = A.mk_bool tst (B_equiv (a,b))
let[@inline] eq tst a b = A.mk_bool tst (B_eq (a,b))
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 simplify (self:state) (simp:SI.Simplify.t) (t:T.t) : T.t option =
let tst = self.tst in
match A.view_as_bool t with
| B_bool _ -> None
| B_not u when is_true u -> Some (T.bool tst false)
| B_not u when is_false u -> Some (T.bool tst true)
| B_not _ -> None
| B_opaque_bool _ -> None
| B_and a ->
if IArray.exists is_false a then Some (T.bool tst false)
else if IArray.for_all is_true a then Some (T.bool tst true)
else None
| B_or a ->
if IArray.exists is_true a then Some (T.bool tst true)
else if IArray.for_all is_false a then Some (T.bool tst false)
else None
| B_imply (args, u) ->
(* turn into a disjunction *)
let u =
or_a tst @@
IArray.append (IArray.map (not_ tst) args) (IArray.singleton u)
in
Some u
| B_ite (a,b,c) ->
(* directly simplify [a] so that maybe we never will simplify one
of the branches *)
let a = SI.Simplify.normalize simp a in
begin match A.view_as_bool a with
| B_bool true -> Some b
| B_bool false -> Some c
| _ -> None
end
| B_equiv (a,b) when is_true a -> Some b
| B_equiv (a,b) when is_false a -> Some (not_ tst b)
| B_equiv (a,b) when is_true b -> Some a
| B_equiv (a,b) when is_false b -> Some (not_ tst a)
| B_equiv _ -> None
| B_eq (a,b) when T.equal a b -> Some (T.bool tst true)
| B_eq _ -> None
| B_atom _ -> None
let fresh_term self ~pre ty = A.Gensym.fresh_term self.gensym ~pre ty
let fresh_lit (self:state) ~mk_lit ~pre : Lit.t =
let t = fresh_term ~pre self Ty.bool in
mk_lit t
(* TODO: polarity? *)
let cnf (self:state) (_si:SI.t) ~mk_lit ~add_clause (t:T.t) : T.t option =
let rec get_lit (t:T.t) : Lit.t =
match T.Tbl.find self.cnf t with
| lit -> lit (* cached *)
| exception Not_found ->
(* compute and cache *)
let lit = get_lit_uncached t in
if not (T.equal (Lit.term lit) (T.abs self.tst t |> fst)) then (
T.Tbl.add self.cnf t lit;
);
lit
and get_lit_uncached t : Lit.t =
match A.view_as_bool t with
| B_bool b -> mk_lit (T.bool self.tst b)
| B_opaque_bool t -> mk_lit t
| B_not u ->
let lit = get_lit u in
Lit.neg lit
| B_and l ->
let subs = IArray.to_list_map get_lit l in
let proxy = fresh_lit ~mk_lit ~pre:"and_" self in
(* add clauses *)
List.iter (fun u -> add_clause [Lit.neg proxy; u]) subs;
add_clause (proxy :: List.map Lit.neg subs);
proxy
| B_or l ->
let subs = IArray.to_list_map get_lit l in
let proxy = fresh_lit ~mk_lit ~pre:"or_" self in
(* add clauses *)
List.iter (fun u -> add_clause [Lit.neg u; proxy]) subs;
add_clause (Lit.neg proxy :: subs);
proxy
| B_imply (args, u) ->
let t' =
or_a self.tst @@
IArray.append (IArray.map (not_ self.tst) args) (IArray.singleton u) in
get_lit t'
| B_ite _ | B_eq _ ->
mk_lit t
| B_equiv (a,b) ->
let a = get_lit a in
let b = get_lit b in
let proxy = fresh_lit ~mk_lit ~pre:"equiv_" self in
(* proxy => a<=> b,
¬proxy => a xor b *)
add_clause [Lit.neg proxy; Lit.neg a; b];
add_clause [Lit.neg proxy; Lit.neg b; a];
add_clause [proxy; a; b];
add_clause [proxy; Lit.neg a; Lit.neg b];
proxy
| B_atom u -> mk_lit u
in
let lit = get_lit t in
let u = Lit.term lit in
(* put sign back as a "not" *)
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
(* check if new terms were added to the congruence closure, that can be turned
into clauses *)
let check_new_terms (self:state) si (acts:SI.actions) (_trail:_ Iter.t) : unit =
let cc_ = SI.cc si in
let all_terms =
let open SI in
CC.all_classes cc_
|> Iter.flat_map CC.N.iter_class
|> Iter.map CC.N.term
in
let cnf_of t =
cnf self si t
~mk_lit:(SI.mk_lit si acts) ~add_clause:(SI.add_clause_permanent si acts)
in
begin
all_terms
(fun t -> match cnf_of t with
| None -> ()
| Some u ->
Log.debugf 5
(fun k->k "(@[th-bool-static.final-check.cnf@ %a@ :yields %a@])"
T.pp t T.pp u);
SI.CC.merge_t cc_ t u (SI.CC.Expl.mk_list []);
());
end;
()
let create_and_setup si =
Log.debug 2 "(th-bool.setup)";
let st = create (SI.tst si) in
SI.add_simplifier si (simplify st);
SI.add_preprocess si (cnf st);
if A.check_congruence_classes then (
Log.debug 5 "(th-bool.add-final-check)";
SI.on_final_check si (check_new_terms st);
);
st
let theory =
A.S.mk_theory
~name:"th-bool"
~create_and_setup
()
end