sidekick/src/cc/Congruence_closure_intf.ml
2019-03-22 19:43:05 -05:00

220 lines
6.5 KiB
OCaml

module type ARG = CC_types.FULL
module type THEORY_KEY = sig
type ('term,'lit,'a) t
(** An access key for theories which have per-class data ['a] *)
val create :
?pp:'a Fmt.printer ->
name:string ->
eq:('a -> 'a -> bool) ->
merge:('a -> 'a -> 'a) ->
unit ->
('term,'lit,'a) t
(** Generative creation of keys for the given theory data.
@param eq : Equality. This is used to optimize backtracking info.
@param merge :
[merge d1 d2] is called when merging classes with data [d1] and [d2]
respectively. The theory should already have checked that the merge
is compatible, and this produces the combined data for terms in the
merged class.
@param name name of the theory which owns this data
@param pp a printer for the data
*)
val equal : ('t,'lit,_) t -> ('t,'lit,_) t -> bool
(** Checks if two keys are equal (generatively) *)
val pp : _ t Fmt.printer
(** Prints the name of the key. *)
end
module type S = sig
type term_state
type term
type fun_
type lit
type proof
type model
(** Implementation of theory keys *)
module Key : THEORY_KEY
type t
(** Global state of the congruence closure *)
(** An equivalence class is a set of terms that are currently equal
in the partial model built by the solver.
The class is represented by a collection of nodes, one of which is
distinguished and is called the "representative".
All information pertaining to the whole equivalence class is stored
in this representative's node.
When two classes become equal (are "merged"), one of the two
representatives is picked as the representative of the new class.
The new class contains the union of the two old classes' nodes.
We also allow theories to store additional information in the
representative. This information can be used when two classes are
merged, to detect conflicts and solve equations à la Shostak.
*)
module N : sig
type t
val term : t -> term
val equal : t -> t -> bool
val hash : t -> int
val pp : t Fmt.printer
val is_root : t -> bool
(** Is the node a root (ie the representative of its class)? *)
val iter_class : t -> t Iter.t
(** Traverse the congruence class.
Invariant: [is_root n] (see {!find} below) *)
val iter_parents : t -> t Iter.t
(** Traverse the parents of the class.
Invariant: [is_root n] (see {!find} below) *)
end
module Expl : sig
type t
val pp : t Fmt.printer
val mk_merge : N.t -> N.t -> t
val mk_lit : lit -> t
val mk_list : t list -> t
end
type node = N.t
(** A node of the congruence closure *)
type repr = N.t
(** Node that is currently a representative *)
type explanation = Expl.t
type conflict = lit list
(** Accessors *)
val term_state : t -> term_state
val find : t -> node -> repr
(** Current representative *)
val add_term : t -> term -> node
(** Add the term to the congruence closure, if not present already.
Will be backtracked. *)
(** Actions available to the theory *)
type sat_actions = (Msat.void, lit, Msat.void, proof) Msat.acts
module Theory : sig
type cc = t
type t
type 'a key = (term,lit,'a) Key.t
val raise_conflict : cc -> Expl.t -> unit
(** Raise a conflict with the given explanation
it must be a theory tautology that [expl ==> absurd].
To be used in theories. *)
val merge : cc -> N.t -> N.t -> Expl.t -> unit
(** Merge these two nodes given this explanation.
It must be a theory tautology that [expl ==> n1 = n2].
To be used in theories. *)
val add_term : cc -> term -> N.t
(** Add/retrieve node for this term.
To be used in theories *)
val get_data : cc -> N.t -> 'a key -> 'a option
(** Get data information for this particular representative *)
val add_data : cc -> N.t -> 'a key -> 'a -> unit
(** Add data to this particular representative. Will be backtracked. *)
val make :
key:'a key ->
on_merge:(cc -> N.t -> 'a -> N.t -> 'a -> Expl.t -> unit) ->
on_new_term:(cc -> N.t -> term -> 'a option) ->
unit ->
t
(** Build a micro theory. It can use the callbacks above. *)
end
val create :
?th:Theory.t list ->
?on_merge:(t -> N.t -> N.t -> Expl.t -> unit) list ->
?size:[`Small | `Big] ->
term_state ->
t
(** Create a new congruence closure. *)
val add_th : t -> Theory.t -> unit
(** Add a (micro) theory to the congruence closure.
@raise Error.Error if there is already a theory with
the same key. *)
val on_merge : t -> (t -> N.t -> N.t -> Expl.t -> unit) -> unit
(** Add a function to be called when two classes are merged *)
val set_as_lit : t -> N.t -> lit -> unit
(** map the given node to a literal. *)
val find_t : t -> term -> repr
(** Current representative of the term.
@raise Not_found if the term is not already {!add}-ed. *)
val add_seq : t -> term Iter.t -> unit
(** Add a sequence of terms to the congruence closure *)
val all_classes : t -> repr Iter.t
(** All current classes. This is costly, only use if there is no other solution *)
val assert_lit : t -> lit -> unit
(** Given a literal, assume it in the congruence closure and propagate
its consequences. Will be backtracked.
Useful for the theory combination or the SAT solver's functor *)
val assert_lits : t -> lit Iter.t -> unit
(** Addition of many literals *)
val assert_eq : t -> term -> term -> lit list -> unit
(** merge the given terms with some explanations *)
(* TODO: remove and move into its own library as a micro theory
val assert_distinct : t -> term list -> neq:term -> lit -> unit
(** [assert_distinct l ~neq:u e] asserts all elements of [l] are distinct
because [lit] is true
precond: [u = distinct l] *)
*)
val check : t -> sat_actions -> unit
(** Perform all pending operations done via {!assert_eq}, {!assert_lit}, etc.
Will use the [sat_actions] to propagate literals, declare conflicts, etc. *)
val push_level : t -> unit
(** Push backtracking level *)
val pop_levels : t -> int -> unit
(** Restore to state [n] calls to [push_level] earlier. Used during backtracking. *)
val mk_model : t -> model -> model
(** Enrich a model by mapping terms to their representative's value,
if any. Otherwise map the representative to a fresh value *)
(**/**)
val check_invariants : t -> unit
val pp_full : t Fmt.printer
(**/**)
end