(** {1 Theory of Booleans} *) (** {2 Signatures for booleans} *) module View = struct type 'a t = | B_not of 'a | B_and of 'a IArray.t | B_or of 'a IArray.t | B_imply of 'a IArray.t * 'a | B_atom of 'a end module type ARG = sig module S : Sidekick_core.SOLVER type term = S.A.Term.t val view_as_bool : term -> term View.t val mk_bool : S.A.Term.state -> term View.t -> term end module type S = sig module A : ARG val theory : A.S.theory end (** Theory with dynamic reduction to clauses *) module Make_dyn_tseitin(A : ARG) (* : S with module A = A *) = struct (* TODO (long term): relevancy propagation *) (* TODO: Tseitin on the fly when a composite boolean term is asserted. --> maybe, cache the clause inside the literal *) module A = A module SI = A.S.Solver_internal module T = SI.A.Term module Lit = SI.A.Lit type term = T.t module T_tbl = CCHashtbl.Make(T) type t = { expanded: unit T_tbl.t; (* set of literals already expanded *) } let tseitin ~final (self:t) (solver:SI.t) (lit:Lit.t) (lit_t:term) (v:term View.t) : unit = Log.debugf 5 (fun k->k "(@[th_bool.tseitin@ %a@])" Lit.pp lit); let expanded () = T_tbl.mem self.expanded lit_t in let add_axiom c = T_tbl.replace self.expanded lit_t (); SI.add_persistent_axiom solver c in match v with | B_not _ -> assert false (* normalized *) | B_atom _ -> () (* CC will manage *) | B_and subs -> if Lit.sign lit then ( (* propagate [lit => subs_i] *) IArray.iter (fun sub -> let sublit = SI.mk_lit solver sub in SI.propagate_l solver sublit [lit]) subs ) else if final && not @@ expanded () then ( (* axiom [¬lit => ∨_i ¬ subs_i] *) let subs = IArray.to_list subs in let c = Lit.neg lit :: List.map (SI.mk_lit solver ~sign:false) subs in add_axiom c ) | B_or subs -> if not @@ Lit.sign lit then ( (* propagate [¬lit => ¬subs_i] *) IArray.iter (fun sub -> let sublit = SI.mk_lit solver ~sign:false sub in SI.add_local_axiom solver [Lit.neg lit; sublit]) subs ) else if final && not @@ expanded () then ( (* axiom [lit => ∨_i subs_i] *) let subs = IArray.to_list subs in let c = Lit.neg lit :: List.map (SI.mk_lit solver ~sign:true) subs in add_axiom c ) | B_imply (guard,concl) -> if Lit.sign lit && final && not @@ expanded () then ( (* axiom [lit => ∨_i ¬guard_i ∨ concl] *) let guard = IArray.to_list guard in let c = SI.mk_lit solver concl :: Lit.neg lit :: List.map (SI.mk_lit solver ~sign:false) guard in add_axiom c ) else if not @@ Lit.sign lit then ( (* propagate [¬lit => ¬concl] *) SI.propagate_l solver (SI.mk_lit solver ~sign:false concl) [lit]; (* propagate [¬lit => ∧_i guard_i] *) IArray.iter (fun sub -> let sublit = SI.mk_lit solver ~sign:true sub in SI.propagate_l solver sublit [lit]) guard ) let check_ ~final self solver lits = lits (fun lit -> let t = Lit.term lit in match A.view_as_bool t with | B_atom _ -> () | v -> tseitin ~final self solver lit t v) let partial_check (self:t) acts (lits:Lit.t Iter.t) = check_ ~final:false self acts lits let final_check (self:t) acts (lits:Lit.t Iter.t) = check_ ~final:true self acts lits let create_and_setup (solver:SI.t) : t = let self = {expanded=T_tbl.create 24} in SI.on_final_check solver (final_check self); SI.on_partial_check solver (partial_check self); self let theory = A.S.mk_theory ~name:"boolean" ~create_and_setup () end