diff --git a/dev/sidekick-arith/Sidekick_arith_lra/Make/argument-1-A/S/Atom/index.html b/dev/sidekick-arith/Sidekick_arith_lra/Make/argument-1-A/S/Atom/index.html index f5487505..8541bddb 100644 --- a/dev/sidekick-arith/Sidekick_arith_lra/Make/argument-1-A/S/Atom/index.html +++ b/dev/sidekick-arith/Sidekick_arith_lra/Make/argument-1-A/S/Atom/index.html @@ -1,2 +1,2 @@ -
S.AtomAtoms are the SAT solver's version of our boolean literals (they may have a different representation).
S.Litmodule T = TS.Litmodule T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.ModelS.ModelModels
A model can be produced when the solver is found to be in a satisfiable state after a call to solve.
CC.Actionsmodule T = Tmodule P = Pmodule Lit = Littype t = actionsAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see Sidekick_core.CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC.ExplCC.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
CC.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
CC.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See Sidekick_core.CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.Simplifyval tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
Solver_internal.SimplifySimplify terms
val tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
S.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule CC : Sidekick_core.CC_S with module T = T and module P = P and module Lit = Lit and type Actions.t = actionsmodule Simplify : sig ... endtype simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitval simplifier : t -> Simplify.tval simp_t : t -> term -> termval propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unittype model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class.
val on_model_gen : t -> model_hook -> unitS.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule Simplify : sig ... endSimplify terms
type simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitAdd a simplifier hook for preprocessing.
val simplifier : t -> Simplify.tval simp_t : t -> term -> termSimplify the term using the solver's simplifier (see simplifier)
val propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
Preprocessing might transform terms to make them more amenable to reasoning, e.g. by removing boolean formulas via Tseitin encoding, adding clauses that encode their meaning in the same move.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unitAdd a hook that will be called when terms are preprocessed
type model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class. For example, an arithmetic theory might detect that a class contains a numeric constant, and return this constant as a model value.
If no hook assigns a value to a class, a fake value is created for it.
val on_model_gen : t -> model_hook -> unitAdd a hook that will be called when a model is being produced
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.Termval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
val equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.Tyval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
1-A.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory
module Atom : sig ... endmodule Model : sig ... endmodule Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
theories to load from the start.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tval mk_atom_t : t -> ?sign:bool -> term -> Atom.tval add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitval add_clause_l : t -> Atom.t list -> unittype res = | Sat of Model.t | ||
| Unsat of {
} | ||
| Unknown of Unknown.t | Result of solving for the current set of clauses |
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the statement added so far to s
if true, the model is checked before returning
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printer1-A.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory.
module Atom : sig ... endmodule Model : sig ... endModels
module Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
It needs a term state and a type state to manipulate terms and types. All terms and types interacting with this solver will need to come from these exact states.
if true, proofs from the SAT solver and theories are retained and potentially accessible after solve returns UNSAT.
influences the size of initial allocations.
theories to load from the start. Other theories can be added using add_theory.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tTurn a literal into a SAT solver literal.
val mk_atom_t : t -> ?sign:bool -> term -> Atom.tTurn a boolean term, with a sign, into a SAT solver's literal.
val add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitadd_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 -> Atom.t list -> unitSame as add_clause but with a list of atoms.
type res = | Sat of Model.t | Satisfiable | ||||
| Unsat of {
} | Unsatisfiable | ||||
| Unknown of Unknown.t | Unknown, obtained after a timeout, memory limit, etc. |
Result of solving for the current set of clauses
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the clauses added so far to s.
if true, the model is checked before returning.
called regularly during solving.
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerPrint some statistics. What it prints exactly is unspecified.
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitPop backtracking levels, restoring the theory to its former state
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
Called once for every solver this theory is added to.
val push_level : t -> unitPush backtracking level. When the corresponding pop is called, the theory's state should be restored to a state equivalent to what it was just before push_level.
it does not have to be exactly the same state, it just needs to be equivalent.
val pop_levels : t -> int -> unitpop_levels theory n pops n backtracking levels, restoring theory to its state before calling push_level n times.
S.AtomAtoms are the SAT solver's version of our boolean literals (they may have a different representation).
S.Litmodule T = TS.Litmodule T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.ModelS.ModelModels
A model can be produced when the solver is found to be in a satisfiable state after a call to solve.
CC.Actionsmodule T = Tmodule P = Pmodule Lit = Littype t = actionsAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see Sidekick_core.CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC.ExplCC.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
CC.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
CC.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See Sidekick_core.CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.Simplifyval tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
Solver_internal.SimplifySimplify terms
val tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
S.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule CC : Sidekick_core.CC_S with module T = T and module P = P and module Lit = Lit and type Actions.t = actionsmodule Simplify : sig ... endtype simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitval simplifier : t -> Simplify.tval simp_t : t -> term -> termval propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unittype model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class.
val on_model_gen : t -> model_hook -> unitS.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule Simplify : sig ... endSimplify terms
type simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitAdd a simplifier hook for preprocessing.
val simplifier : t -> Simplify.tval simp_t : t -> term -> termSimplify the term using the solver's simplifier (see simplifier)
val propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
Preprocessing might transform terms to make them more amenable to reasoning, e.g. by removing boolean formulas via Tseitin encoding, adding clauses that encode their meaning in the same move.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unitAdd a hook that will be called when terms are preprocessed
type model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class. For example, an arithmetic theory might detect that a class contains a numeric constant, and return this constant as a model value.
If no hook assigns a value to a class, a fake value is created for it.
val on_model_gen : t -> model_hook -> unitAdd a hook that will be called when a model is being produced
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.Termval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
val equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.Tyval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
ARG.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory
module Atom : sig ... endmodule Model : sig ... endmodule Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
theories to load from the start.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tval mk_atom_t : t -> ?sign:bool -> term -> Atom.tval add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitval add_clause_l : t -> Atom.t list -> unittype res = | Sat of Model.t | ||
| Unsat of {
} | ||
| Unknown of Unknown.t | Result of solving for the current set of clauses |
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the statement added so far to s
if true, the model is checked before returning
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerARG.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory.
module Atom : sig ... endmodule Model : sig ... endModels
module Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
It needs a term state and a type state to manipulate terms and types. All terms and types interacting with this solver will need to come from these exact states.
if true, proofs from the SAT solver and theories are retained and potentially accessible after solve returns UNSAT.
influences the size of initial allocations.
theories to load from the start. Other theories can be added using add_theory.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tTurn a literal into a SAT solver literal.
val mk_atom_t : t -> ?sign:bool -> term -> Atom.tTurn a boolean term, with a sign, into a SAT solver's literal.
val add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitadd_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 -> Atom.t list -> unitSame as add_clause but with a list of atoms.
type res = | Sat of Model.t | Satisfiable | ||||
| Unsat of {
} | Unsatisfiable | ||||
| Unknown of Unknown.t | Unknown, obtained after a timeout, memory limit, etc. |
Result of solving for the current set of clauses
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the clauses added so far to s.
if true, the model is checked before returning.
called regularly during solving.
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerPrint some statistics. What it prints exactly is unspecified.
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitPop backtracking levels, restoring the theory to its former state
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
Called once for every solver this theory is added to.
val push_level : t -> unitPush backtracking level. When the corresponding pop is called, the theory's state should be restored to a state equivalent to what it was just before push_level.
it does not have to be exactly the same state, it just needs to be equivalent.
val pop_levels : t -> int -> unitpop_levels theory n pops n backtracking levels, restoring theory to its state before calling push_level n times.
S.AtomAtoms are the SAT solver's version of our boolean literals (they may have a different representation).
S.Litmodule T = TS.Litmodule T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.ModelS.ModelModels
A model can be produced when the solver is found to be in a satisfiable state after a call to solve.
CC.Actionsmodule T = Tmodule P = Pmodule Lit = Littype t = actionsAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see Sidekick_core.CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC.ExplCC.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
CC.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
CC.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See Sidekick_core.CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.Simplifyval tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
Solver_internal.SimplifySimplify terms
val tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
S.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule CC : Sidekick_core.CC_S with module T = T and module P = P and module Lit = Lit and type Actions.t = actionsmodule Simplify : sig ... endtype simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitval simplifier : t -> Simplify.tval simp_t : t -> term -> termval propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unittype model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class.
val on_model_gen : t -> model_hook -> unitS.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule Simplify : sig ... endSimplify terms
type simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitAdd a simplifier hook for preprocessing.
val simplifier : t -> Simplify.tval simp_t : t -> term -> termSimplify the term using the solver's simplifier (see simplifier)
val propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
Preprocessing might transform terms to make them more amenable to reasoning, e.g. by removing boolean formulas via Tseitin encoding, adding clauses that encode their meaning in the same move.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unitAdd a hook that will be called when terms are preprocessed
type model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class. For example, an arithmetic theory might detect that a class contains a numeric constant, and return this constant as a model value.
If no hook assigns a value to a class, a fake value is created for it.
val on_model_gen : t -> model_hook -> unitAdd a hook that will be called when a model is being produced
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.Termval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
val equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.Tyval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
A.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory
module Atom : sig ... endmodule Model : sig ... endmodule Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
theories to load from the start.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tval mk_atom_t : t -> ?sign:bool -> term -> Atom.tval add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitval add_clause_l : t -> Atom.t list -> unittype res = | Sat of Model.t | ||
| Unsat of {
} | ||
| Unknown of Unknown.t | Result of solving for the current set of clauses |
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the statement added so far to s
if true, the model is checked before returning
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerA.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory.
module Atom : sig ... endmodule Model : sig ... endModels
module Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
It needs a term state and a type state to manipulate terms and types. All terms and types interacting with this solver will need to come from these exact states.
if true, proofs from the SAT solver and theories are retained and potentially accessible after solve returns UNSAT.
influences the size of initial allocations.
theories to load from the start. Other theories can be added using add_theory.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tTurn a literal into a SAT solver literal.
val mk_atom_t : t -> ?sign:bool -> term -> Atom.tTurn a boolean term, with a sign, into a SAT solver's literal.
val add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitadd_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 -> Atom.t list -> unitSame as add_clause but with a list of atoms.
type res = | Sat of Model.t | Satisfiable | ||||
| Unsat of {
} | Unsatisfiable | ||||
| Unknown of Unknown.t | Unknown, obtained after a timeout, memory limit, etc. |
Result of solving for the current set of clauses
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the clauses added so far to s.
if true, the model is checked before returning.
called regularly during solving.
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerPrint some statistics. What it prints exactly is unspecified.
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitPop backtracking levels, restoring the theory to its former state
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
Called once for every solver this theory is added to.
val push_level : t -> unitPush backtracking level. When the corresponding pop is called, the theory's state should be restored to a state equivalent to what it was just before push_level.
it does not have to be exactly the same state, it just needs to be equivalent.
val pop_levels : t -> int -> unitpop_levels theory n pops n backtracking levels, restoring theory to its state before calling push_level n times.
Sidekick_arith_lra__.Simplex_intf.
module type S = sig ... endmodule type S_FULL = sig ... endSidekick_arith_lra__.Simplex_intfmodule type S = sig ... endmodule type S_FULL = sig ... endSidekick_arith_lra__Simplex_intf.
module type S = sig ... endmodule type S_FULL = sig ... endSidekick_arith_lra__Simplex_intfmodule type S = sig ... endmodule type S_FULL = sig ... endArg.Funtype t = Fun.tval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerArg.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
type t = Fun.tval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerArg.Termtype t = Term.tval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype state = Term.stateArg.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
type t = Term.tval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype state = Term.stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3Arg.Tytype t = Ty.tval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerArg.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
type t = Ty.tval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerSidekick_base_term.Argmodule Term : sig ... end with type t = Term.t and type state = Term.stateSidekick_base_term.Argmodule Fun : sig ... end with type t = Fun.tA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Term : sig ... end with type t = Term.t and type state = Term.stateTerm structure.
Solver.AtomAtoms are the SAT solver's version of our boolean literals (they may have a different representation).
Solver.Litmodule T = TSolver.Litmodule T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerSolver.ModelSolver.ModelModels
A model can be produced when the solver is found to be in a satisfiable state after a call to solve.
CC.Actionsmodule T = Tmodule P = Pmodule Lit = Littype t = actionsAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see Sidekick_core.CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC.ExplCC.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
CC.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
CC.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See Sidekick_core.CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.Simplifyval tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
Solver_internal.SimplifySimplify terms
val tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
Solver.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule CC : Sidekick_core.CC_S with module T = T and module P = P and module Lit = Lit and type Actions.t = actionsmodule Simplify : sig ... endtype simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitval simplifier : t -> Simplify.tval simp_t : t -> term -> termval propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unittype model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class.
val on_model_gen : t -> model_hook -> unitSolver.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule Simplify : sig ... endSimplify terms
type simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitAdd a simplifier hook for preprocessing.
val simplifier : t -> Simplify.tval simp_t : t -> term -> termSimplify the term using the solver's simplifier (see simplifier)
val propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
Preprocessing might transform terms to make them more amenable to reasoning, e.g. by removing boolean formulas via Tseitin encoding, adding clauses that encode their meaning in the same move.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unitAdd a hook that will be called when terms are preprocessed
type model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class. For example, an arithmetic theory might detect that a class contains a numeric constant, and return this constant as a model value.
If no hook assigns a value to a class, a fake value is created for it.
val on_model_gen : t -> model_hook -> unitAdd a hook that will be called when a model is being produced
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.Termtype t = Sidekick_base_term.Term.tval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype state = Sidekick_base_term.Term.stateT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
type t = Sidekick_base_term.Term.tval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype state = Sidekick_base_term.Term.stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.Tytype t = Sidekick_base_term.Ty.tval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printertype state = Sidekick_base_term.Ty.stateT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
type t = Sidekick_base_term.Ty.tval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printertype state = Sidekick_base_term.Ty.stateSolver.Tmodule Fun : sig ... endmodule Ty : sig ... end with type t = Sidekick_base_term.Ty.t and type state = Sidekick_base_term.Ty.statemodule Term : sig ... end with type t = Sidekick_base_term.Term.t and type state = Sidekick_base_term.Term.stateSolver.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... end with type t = Sidekick_base_term.Ty.t and type state = Sidekick_base_term.Ty.stateTypes
module Term : sig ... end with type t = Sidekick_base_term.Term.t and type state = Sidekick_base_term.Term.stateTerm structure.
Process.Solvermodule T : Sidekick_core.TERM with type Term.t = Sidekick_base_term.Term.t and type Term.state = Sidekick_base_term.Term.state and type Ty.t = Sidekick_base_term.Ty.t and type Ty.state = Sidekick_base_term.Ty.statemodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory
module Atom : sig ... endmodule Model : sig ... endmodule Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
theories to load from the start.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tval mk_atom_t : t -> ?sign:bool -> term -> Atom.tval add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitval add_clause_l : t -> Atom.t list -> unittype res = | Sat of Model.t | ||
| Unsat of {
} | ||
| Unknown of Unknown.t | Result of solving for the current set of clauses |
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the statement added so far to s
if true, the model is checked before returning
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerProcess.Solvermodule T : Sidekick_core.TERM with type Term.t = Sidekick_base_term.Term.t and type Term.state = Sidekick_base_term.Term.state and type Ty.t = Sidekick_base_term.Ty.t and type Ty.state = Sidekick_base_term.Ty.statemodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory.
module Atom : sig ... endmodule Model : sig ... endModels
module Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
It needs a term state and a type state to manipulate terms and types. All terms and types interacting with this solver will need to come from these exact states.
if true, proofs from the SAT solver and theories are retained and potentially accessible after solve returns UNSAT.
influences the size of initial allocations.
theories to load from the start. Other theories can be added using add_theory.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tTurn a literal into a SAT solver literal.
val mk_atom_t : t -> ?sign:bool -> term -> Atom.tTurn a boolean term, with a sign, into a SAT solver's literal.
val add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitadd_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 -> Atom.t list -> unitSame as add_clause but with a list of atoms.
type res = | Sat of Model.t | Satisfiable | ||||
| Unsat of {
} | Unsatisfiable | ||||
| Unknown of Unknown.t | Unknown, obtained after a timeout, memory limit, etc. |
Result of solving for the current set of clauses
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the clauses added so far to s.
if true, the model is checked before returning.
called regularly during solving.
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerPrint some statistics. What it prints exactly is unspecified.
Solver.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitPop backtracking levels, restoring the theory to its former state
Solver.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
Called once for every solver this theory is added to.
val push_level : t -> unitPush backtracking level. When the corresponding pop is called, the theory's state should be restored to a state equivalent to what it was just before push_level.
it does not have to be exactly the same state, it just needs to be equivalent.
val pop_levels : t -> int -> unitpop_levels theory n pops n backtracking levels, restoring theory to its state before calling push_level n times.
Solver.AtomAtoms are the SAT solver's version of our boolean literals (they may have a different representation).
Solver.Litmodule T = TSolver.Litmodule T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerSolver.ModelSolver.ModelModels
A model can be produced when the solver is found to be in a satisfiable state after a call to solve.
CC.Actionsmodule T = Tmodule P = Pmodule Lit = Littype t = actionsAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see Sidekick_core.CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC.ExplCC.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
CC.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
CC.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See Sidekick_core.CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.Simplifyval tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
Solver_internal.SimplifySimplify terms
val tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
Solver.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule CC : Sidekick_core.CC_S with module T = T and module P = P and module Lit = Lit and type Actions.t = actionsmodule Simplify : sig ... endtype simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitval simplifier : t -> Simplify.tval simp_t : t -> term -> termval propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unittype model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class.
val on_model_gen : t -> model_hook -> unitSolver.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule Simplify : sig ... endSimplify terms
type simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitAdd a simplifier hook for preprocessing.
val simplifier : t -> Simplify.tval simp_t : t -> term -> termSimplify the term using the solver's simplifier (see simplifier)
val propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
Preprocessing might transform terms to make them more amenable to reasoning, e.g. by removing boolean formulas via Tseitin encoding, adding clauses that encode their meaning in the same move.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unitAdd a hook that will be called when terms are preprocessed
type model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class. For example, an arithmetic theory might detect that a class contains a numeric constant, and return this constant as a model value.
If no hook assigns a value to a class, a fake value is created for it.
val on_model_gen : t -> model_hook -> unitAdd a hook that will be called when a model is being produced
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.Termtype t = Sidekick_base_term.Term.tval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype state = Sidekick_base_term.Term.stateT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
type t = Sidekick_base_term.Term.tval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype state = Sidekick_base_term.Term.stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.Tytype t = Sidekick_base_term.Ty.tval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printertype state = Sidekick_base_term.Ty.stateT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
type t = Sidekick_base_term.Ty.tval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printertype state = Sidekick_base_term.Ty.stateSolver.Tmodule Fun : sig ... endmodule Ty : sig ... end with type t = Sidekick_base_term.Ty.t and type state = Sidekick_base_term.Ty.statemodule Term : sig ... end with type t = Sidekick_base_term.Term.t and type state = Sidekick_base_term.Term.stateSolver.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... end with type t = Sidekick_base_term.Ty.t and type state = Sidekick_base_term.Ty.stateTypes
module Term : sig ... end with type t = Sidekick_base_term.Term.t and type state = Sidekick_base_term.Term.stateTerm structure.
Process.Solvermodule T : Sidekick_core.TERM with type Term.t = Sidekick_base_term.Term.t and type Term.state = Sidekick_base_term.Term.state and type Ty.t = Sidekick_base_term.Ty.t and type Ty.state = Sidekick_base_term.Ty.statemodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory
module Atom : sig ... endmodule Model : sig ... endmodule Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
theories to load from the start.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tval mk_atom_t : t -> ?sign:bool -> term -> Atom.tval add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitval add_clause_l : t -> Atom.t list -> unittype res = | Sat of Model.t | ||
| Unsat of {
} | ||
| Unknown of Unknown.t | Result of solving for the current set of clauses |
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the statement added so far to s
if true, the model is checked before returning
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerProcess.Solvermodule T : Sidekick_core.TERM with type Term.t = Sidekick_base_term.Term.t and type Term.state = Sidekick_base_term.Term.state and type Ty.t = Sidekick_base_term.Ty.t and type Ty.state = Sidekick_base_term.Ty.statemodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory.
module Atom : sig ... endmodule Model : sig ... endModels
module Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
It needs a term state and a type state to manipulate terms and types. All terms and types interacting with this solver will need to come from these exact states.
if true, proofs from the SAT solver and theories are retained and potentially accessible after solve returns UNSAT.
influences the size of initial allocations.
theories to load from the start. Other theories can be added using add_theory.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tTurn a literal into a SAT solver literal.
val mk_atom_t : t -> ?sign:bool -> term -> Atom.tTurn a boolean term, with a sign, into a SAT solver's literal.
val add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitadd_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 -> Atom.t list -> unitSame as add_clause but with a list of atoms.
type res = | Sat of Model.t | Satisfiable | ||||
| Unsat of {
} | Unsatisfiable | ||||
| Unknown of Unknown.t | Unknown, obtained after a timeout, memory limit, etc. |
Result of solving for the current set of clauses
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the clauses added so far to s.
if true, the model is checked before returning.
called regularly during solving.
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerPrint some statistics. What it prints exactly is unspecified.
Solver.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitPop backtracking levels, restoring the theory to its former state
Solver.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
Called once for every solver this theory is added to.
val push_level : t -> unitPush backtracking level. When the corresponding pop is called, the theory's state should be restored to a state equivalent to what it was just before push_level.
it does not have to be exactly the same state, it just needs to be equivalent.
val pop_levels : t -> int -> unitpop_levels theory n pops n backtracking levels, restoring theory to its state before calling push_level n times.
Solver.AtomAtoms are the SAT solver's version of our boolean literals (they may have a different representation).
Solver.Litmodule T = TSolver.Litmodule T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerSolver.ModelSolver.ModelModels
A model can be produced when the solver is found to be in a satisfiable state after a call to solve.
CC.Actionsmodule T = Tmodule P = Pmodule Lit = Littype t = actionsAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see Sidekick_core.CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC.ExplCC.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
CC.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
CC.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See Sidekick_core.CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.Simplifyval tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
Solver_internal.SimplifySimplify terms
val tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
Solver.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule CC : Sidekick_core.CC_S with module T = T and module P = P and module Lit = Lit and type Actions.t = actionsmodule Simplify : sig ... endtype simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitval simplifier : t -> Simplify.tval simp_t : t -> term -> termval propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unittype model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class.
val on_model_gen : t -> model_hook -> unitSolver.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule Simplify : sig ... endSimplify terms
type simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitAdd a simplifier hook for preprocessing.
val simplifier : t -> Simplify.tval simp_t : t -> term -> termSimplify the term using the solver's simplifier (see simplifier)
val propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
Preprocessing might transform terms to make them more amenable to reasoning, e.g. by removing boolean formulas via Tseitin encoding, adding clauses that encode their meaning in the same move.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unitAdd a hook that will be called when terms are preprocessed
type model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class. For example, an arithmetic theory might detect that a class contains a numeric constant, and return this constant as a model value.
If no hook assigns a value to a class, a fake value is created for it.
val on_model_gen : t -> model_hook -> unitAdd a hook that will be called when a model is being produced
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.Termtype t = Sidekick_base_term.Term.tval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype state = Sidekick_base_term.Term.stateT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
type t = Sidekick_base_term.Term.tval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype state = Sidekick_base_term.Term.stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.Tytype t = Sidekick_base_term.Ty.tval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printertype state = Sidekick_base_term.Ty.stateT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
type t = Sidekick_base_term.Ty.tval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printertype state = Sidekick_base_term.Ty.stateSolver.Tmodule Fun : sig ... endmodule Ty : sig ... end with type t = Sidekick_base_term.Ty.t and type state = Sidekick_base_term.Ty.statemodule Term : sig ... end with type t = Sidekick_base_term.Term.t and type state = Sidekick_base_term.Term.stateSolver.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... end with type t = Sidekick_base_term.Ty.t and type state = Sidekick_base_term.Ty.stateTypes
module Term : sig ... end with type t = Sidekick_base_term.Term.t and type state = Sidekick_base_term.Term.stateTerm structure.
Sidekick_smtlib__Process.Solvermodule T : Sidekick_core.TERM with type Term.t = Sidekick_base_term.Term.t and type Term.state = Sidekick_base_term.Term.state and type Ty.t = Sidekick_base_term.Ty.t and type Ty.state = Sidekick_base_term.Ty.statemodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory
module Atom : sig ... endmodule Model : sig ... endmodule Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
theories to load from the start.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tval mk_atom_t : t -> ?sign:bool -> term -> Atom.tval add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitval add_clause_l : t -> Atom.t list -> unittype res = | Sat of Model.t | ||
| Unsat of {
} | ||
| Unknown of Unknown.t | Result of solving for the current set of clauses |
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the statement added so far to s
if true, the model is checked before returning
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerSidekick_smtlib__Process.Solvermodule T : Sidekick_core.TERM with type Term.t = Sidekick_base_term.Term.t and type Term.state = Sidekick_base_term.Term.state and type Ty.t = Sidekick_base_term.Ty.t and type Ty.state = Sidekick_base_term.Ty.statemodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory.
module Atom : sig ... endmodule Model : sig ... endModels
module Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
It needs a term state and a type state to manipulate terms and types. All terms and types interacting with this solver will need to come from these exact states.
if true, proofs from the SAT solver and theories are retained and potentially accessible after solve returns UNSAT.
influences the size of initial allocations.
theories to load from the start. Other theories can be added using add_theory.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tTurn a literal into a SAT solver literal.
val mk_atom_t : t -> ?sign:bool -> term -> Atom.tTurn a boolean term, with a sign, into a SAT solver's literal.
val add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitadd_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 -> Atom.t list -> unitSame as add_clause but with a list of atoms.
type res = | Sat of Model.t | Satisfiable | ||||
| Unsat of {
} | Unsatisfiable | ||||
| Unknown of Unknown.t | Unknown, obtained after a timeout, memory limit, etc. |
Result of solving for the current set of clauses
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the clauses added so far to s.
if true, the model is checked before returning.
called regularly during solving.
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerPrint some statistics. What it prints exactly is unspecified.
Solver.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitPop backtracking levels, restoring the theory to its former state
Solver.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
Called once for every solver this theory is added to.
val push_level : t -> unitPush backtracking level. When the corresponding pop is called, the theory's state should be restored to a state equivalent to what it was just before push_level.
it does not have to be exactly the same state, it just needs to be equivalent.
val pop_levels : t -> int -> unitpop_levels theory n pops n backtracking levels, restoring theory to its state before calling push_level n times.
Make.ExplMake.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
Make.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
Make.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
1-A.Actionsmodule T = Tmodule P = Pmodule Lit = Littype tAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see Sidekick_core.CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
1-A.Litmodule T = T1-A.Litmodule T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.Termval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
val equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.Tyval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printer1-A.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
Sidekick_cc.Makemodule T = A.Tmodule P = A.Pmodule Lit = A.Litmodule Actions = A.Actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Sidekick_cc.Makemodule T = A.Tmodule P = A.Pmodule Lit = A.Litmodule Actions = A.Actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See Sidekick_core.CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
S.Actionsmodule T = Tmodule P = Pmodule Lit = Littype tAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see Sidekick_core.CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
S.ExplS.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
S.Litmodule T = TS.Litmodule T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
S.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.Termval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
val equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.Tyval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
Sidekick_cc.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Ttype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Sidekick_cc.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Ttype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See Sidekick_core.CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
CC.Actionsmodule T = Tmodule P = Pmodule Lit = Littype t = actionsAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC.ExplCC.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
CC.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
CC.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
SI.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
SI.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
SI.LitA literal is a (preprocessed) term along with its sign. It is directly manipulated by the SAT solver.
module T = TSI.LitA literal is a (preprocessed) term along with its sign. It is directly manipulated by the SAT solver.
module T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
SI.Simplifyval tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
SI.SimplifySimplify terms
val tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
T.TermT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
type stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.TyT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
SI.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
1-M.SItype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.ttype lit = Lit.tmodule CC : CC_S with module T = T and module P = P and module Lit = Lit and type Actions.t = actionsmodule Simplify : sig ... endtype simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitval simplifier : t -> Simplify.tval simp_t : t -> term -> termval propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unittype model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class.
val on_model_gen : t -> model_hook -> unit1-M.SItype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.ttype lit = Lit.tmodule Simplify : sig ... endSimplify terms
type simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitAdd a simplifier hook for preprocessing.
val simplifier : t -> Simplify.tval simp_t : t -> term -> termSimplify the term using the solver's simplifier (see simplifier)
val propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
Preprocessing might transform terms to make them more amenable to reasoning, e.g. by removing boolean formulas via Tseitin encoding, adding clauses that encode their meaning in the same move.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unitAdd a hook that will be called when terms are preprocessed
type model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class. For example, an arithmetic theory might detect that a class contains a numeric constant, and return this constant as a model value.
If no hook assigns a value to a class, a fake value is created for it.
val on_model_gen : t -> model_hook -> unitAdd a hook that will be called when a model is being produced
Monoid_of_repr.1-Mmodule SI : SOLVER_INTERNALval of_term : SI.CC.t -> SI.CC.N.t -> SI.T.Term.t -> t option * (SI.CC.N.t * t) listof_term n t, where t is the term annotating node n, returns maybe_m, l, where:
maybe_m = Some m if t has monoid value m; otherwise maybe_m=Nonel is a list of (u, m_u) where each u's term is a direct subterm of t and m_u is the monoid value attached to u.val merge : SI.CC.t -> SI.CC.N.t -> t -> SI.CC.N.t -> t -> SI.CC.Expl.t -> (t, SI.CC.Expl.t) Stdlib.resultMonoid_of_repr.1-Mmodule SI : SOLVER_INTERNALval of_term : SI.CC.t -> SI.CC.N.t -> SI.T.Term.t -> t option * (SI.CC.N.t * t) listof_term n t, where t is the term annotating node n, must return maybe_m, l, where:
maybe_m = Some m if t has monoid value m; otherwise maybe_m=Nonel is a list of (u, m_u) where each u's term is a direct subterm of t and m_u is the monoid value attached to u.val merge : SI.CC.t -> SI.CC.N.t -> t -> SI.CC.N.t -> t -> SI.CC.Expl.t -> (t, SI.CC.Expl.t) Stdlib.resultMonoidal combination of two values.
merge cc n1 mon1 n2 mon2 expl returns the result of merging monoid values mon1 (for class n1) and mon2 (for class n2) when n1 and n2 are merged with explanation expl.
Ok mon if the merge is acceptable, annotating the class of n1 ∪ n2; or Error expl' if the merge is unsatisfiable. expl' can then be used to trigger a conflict and undo the merge.
Sidekick_core.Monoid_of_reprKeep track of monoid state per equivalence class
M : MONOID_ARGval create_and_setup : ?size:int -> M.SI.t -> tval push_level : t -> unitval pop_levels : t -> int -> unitval mem : t -> M.SI.CC.N.t -> boolval get : t -> M.SI.CC.N.t -> M.t optionval iter_all : t -> (M.SI.CC.repr * M.t) Iter.tval pp : t Fmt.printerSidekick_core.Monoid_of_reprState for a per-equivalence-class monoid.
Helps keep track of monoid state per equivalence class. A theory might use one or more instance(s) of this to aggregate some theory-specific state over all terms, with the information of what terms are already known to be equal potentially saving work for the theory.
M : MONOID_ARGval push_level : t -> unitPush backtracking point
val pop_levels : t -> int -> unitPop n backtracking points
val mem : t -> M.SI.CC.N.t -> boolDoes the CC node have a monoid value?
val get : t -> M.SI.CC.N.t -> M.t optionGet monoid value for this CC node, if any
val iter_all : t -> (M.SI.CC.repr * M.t) Iter.tval pp : t Fmt.printerSidekick_coreTheories and concrete solvers rely on an environment that defines several important types:
module CC_view : sig ... endView terms through the lens of the Congruence Closure
module type TERM = sig ... endMain representation of Terms and Types
module type PROOF = sig ... endmodule type LIT = sig ... endmodule type CC_ACTIONS = sig ... endActions provided to the congruence closure.
module type CC_ARG = sig ... endmodule type CC_S = sig ... endmodule type SOLVER_INTERNAL = sig ... endA view of the solver from a theory's point of view
module type SOLVER = sig ... endPublic view of the solver
module type MONOID_ARG = sig ... endmodule Monoid_of_repr : functor (M : MONOID_ARG) -> sig ... endKeep track of monoid state per equivalence class
Sidekick_coreTheories and concrete solvers rely on an environment that defines several important types:
In this module we define most of the main signatures used throughout Sidekick.
module CC_view : sig ... endView terms through the lens of the Congruence Closure
module type TERM = sig ... endMain representation of Terms and Types
module type PROOF = sig ... endmodule type LIT = sig ... endLiterals
module type CC_ACTIONS = sig ... endActions provided to the congruence closure.
module type CC_ARG = sig ... endArguments to a congruence closure's implementation
module type CC_S = sig ... endSignature of the congruence closure
module type SOLVER_INTERNAL = sig ... endA view of the solver from a theory's point of view.
module type SOLVER = sig ... endUser facing view of the solver
module type MONOID_ARG = sig ... endHelper for the congruence closure
module Monoid_of_repr : functor (M : MONOID_ARG) -> sig ... endState for a per-equivalence-class monoid.
CC_ACTIONS.Litmodule T = TCC_ACTIONS.Litmodule T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
T.TermT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
type stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.TyT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
CC_ACTIONS.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
Sidekick_core.CC_ACTIONSActions provided to the congruence closure.
The congruence closure must be able to propagate literals when it detects that they are true or false; it must also be able to create conflicts when the set of (dis)equalities is inconsistent
Sidekick_core.CC_ACTIONSActions provided to the congruence closure.
The congruence closure must be able to propagate literals when it detects that they are true or false; it must also be able to create conflicts when the set of (dis)equalities is inconsistent
type tAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC_ARG.Actionsmodule T = Tmodule P = Pmodule Lit = Littype tAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC_ARG.Litmodule T = TCC_ARG.Litmodule T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
T.TermT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
type stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.TyT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
CC_ARG.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
Sidekick_core.CC_ARGSidekick_core.CC_ARGArguments to a congruence closure's implementation
CC_S.Actionsmodule T = Tmodule P = Pmodule Lit = Littype tAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC_S.ExplCC_S.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
CC_S.Litmodule T = TCC_S.Litmodule T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
CC_S.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
CC_S.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
T.TermT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
type stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.TyT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
CC_S.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
Sidekick_core.CC_Stype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Sidekick_core.CC_SSignature of the congruence closure
type term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
T.TermT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
type stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.TyT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
LIT.TLiterals depend on terms
module Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
Sidekick_core.LITSidekick_core.LITLiterals
Literals are a pair of a boolean-sorted term, and a sign. Positive literals are the same as their term, and negative literals are the negation of their term.
The SAT solver deals only in literals and clauses (sets of literals). Everything else belongs in the SMT solver.
val sign : t -> boolGet the sign. A negated literal has sign false.
CC.Actionsmodule T = Tmodule P = Pmodule Lit = Littype t = actionsAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC.ExplCC.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
CC.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
CC.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
SI.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
SI.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
SI.LitA literal is a (preprocessed) term along with its sign. It is directly manipulated by the SAT solver.
module T = TSI.LitA literal is a (preprocessed) term along with its sign. It is directly manipulated by the SAT solver.
module T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
SI.Simplifyval tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
SI.SimplifySimplify terms
val tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
T.TermT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
type stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.TyT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
SI.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
MONOID_ARG.SItype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.ttype lit = Lit.tmodule CC : CC_S with module T = T and module P = P and module Lit = Lit and type Actions.t = actionsmodule Simplify : sig ... endtype simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitval simplifier : t -> Simplify.tval simp_t : t -> term -> termval propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unittype model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class.
val on_model_gen : t -> model_hook -> unitMONOID_ARG.SItype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.ttype lit = Lit.tmodule Simplify : sig ... endSimplify terms
type simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitAdd a simplifier hook for preprocessing.
val simplifier : t -> Simplify.tval simp_t : t -> term -> termSimplify the term using the solver's simplifier (see simplifier)
val propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
Preprocessing might transform terms to make them more amenable to reasoning, e.g. by removing boolean formulas via Tseitin encoding, adding clauses that encode their meaning in the same move.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unitAdd a hook that will be called when terms are preprocessed
type model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class. For example, an arithmetic theory might detect that a class contains a numeric constant, and return this constant as a model value.
If no hook assigns a value to a class, a fake value is created for it.
val on_model_gen : t -> model_hook -> unitAdd a hook that will be called when a model is being produced
Sidekick_core.MONOID_ARGmodule SI : SOLVER_INTERNALval of_term : SI.CC.t -> SI.CC.N.t -> SI.T.Term.t -> t option * (SI.CC.N.t * t) listof_term n t, where t is the term annotating node n, returns maybe_m, l, where:
maybe_m = Some m if t has monoid value m; otherwise maybe_m=Nonel is a list of (u, m_u) where each u's term is a direct subterm of t and m_u is the monoid value attached to u.val merge : SI.CC.t -> SI.CC.N.t -> t -> SI.CC.N.t -> t -> SI.CC.Expl.t -> (t, SI.CC.Expl.t) Stdlib.resultSidekick_core.MONOID_ARGHelper for the congruence closure
This helps theories keeping track of some state for each class. The state of a class is the monoidal combination of the state for each term in the class (for example, the set of terms in the class whose head symbol is a datatype constructor).
module SI : SOLVER_INTERNALval of_term : SI.CC.t -> SI.CC.N.t -> SI.T.Term.t -> t option * (SI.CC.N.t * t) listof_term n t, where t is the term annotating node n, must return maybe_m, l, where:
maybe_m = Some m if t has monoid value m; otherwise maybe_m=Nonel is a list of (u, m_u) where each u's term is a direct subterm of t and m_u is the monoid value attached to u.val merge : SI.CC.t -> SI.CC.N.t -> t -> SI.CC.N.t -> t -> SI.CC.Expl.t -> (t, SI.CC.Expl.t) Stdlib.resultMonoidal combination of two values.
merge cc n1 mon1 n2 mon2 expl returns the result of merging monoid values mon1 (for class n1) and mon2 (for class n2) when n1 and n2 are merged with explanation expl.
Ok mon if the merge is acceptable, annotating the class of n1 ∪ n2; or Error expl' if the merge is unsatisfiable. expl' can then be used to trigger a conflict and undo the merge.
SOLVER.AtomAtoms are the SAT solver's version of our boolean literals (they may have a different representation).
SOLVER.Litmodule T = TSOLVER.Litmodule T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
SOLVER.ModelSOLVER.ModelModels
A model can be produced when the solver is found to be in a satisfiable state after a call to solve.
CC.Actionsmodule T = Tmodule P = Pmodule Lit = Littype t = actionsAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC.ExplCC.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
CC.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
CC.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.Simplifyval tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
Solver_internal.SimplifySimplify terms
val tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
SOLVER.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule CC : CC_S with module T = T and module P = P and module Lit = Lit and type Actions.t = actionsmodule Simplify : sig ... endtype simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitval simplifier : t -> Simplify.tval simp_t : t -> term -> termval propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unittype model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class.
val on_model_gen : t -> model_hook -> unitSOLVER.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule Simplify : sig ... endSimplify terms
type simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitAdd a simplifier hook for preprocessing.
val simplifier : t -> Simplify.tval simp_t : t -> term -> termSimplify the term using the solver's simplifier (see simplifier)
val propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
Preprocessing might transform terms to make them more amenable to reasoning, e.g. by removing boolean formulas via Tseitin encoding, adding clauses that encode their meaning in the same move.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unitAdd a hook that will be called when terms are preprocessed
type model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class. For example, an arithmetic theory might detect that a class contains a numeric constant, and return this constant as a model value.
If no hook assigns a value to a class, a fake value is created for it.
val on_model_gen : t -> model_hook -> unitAdd a hook that will be called when a model is being produced
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
T.TermT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
type stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.TyT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
SOLVER.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
Sidekick_core.SOLVERPublic view of the solver
module Solver_internal : SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory
module Atom : sig ... endmodule Model : sig ... endmodule Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
theories to load from the start.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tval mk_atom_t : t -> ?sign:bool -> term -> Atom.tval add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitval add_clause_l : t -> Atom.t list -> unittype res = | Sat of Model.t | ||
| Unsat of {
} | ||
| Unknown of Unknown.t | Result of solving for the current set of clauses |
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the statement added so far to s
if true, the model is checked before returning
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerSidekick_core.SOLVERUser facing view of the solver
This is the solver a user of sidekick can see, after instantiating everything. The user can add some theories, clauses, etc. and asks the solver to check satisfiability.
Theory implementors will mostly interact with SOLVER_INTERNAL.
module Solver_internal : SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory.
module Atom : sig ... endmodule Model : sig ... endModels
module Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
It needs a term state and a type state to manipulate terms and types. All terms and types interacting with this solver will need to come from these exact states.
if true, proofs from the SAT solver and theories are retained and potentially accessible after solve returns UNSAT.
influences the size of initial allocations.
theories to load from the start. Other theories can be added using add_theory.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tTurn a literal into a SAT solver literal.
val mk_atom_t : t -> ?sign:bool -> term -> Atom.tTurn a boolean term, with a sign, into a SAT solver's literal.
val add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitadd_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 -> Atom.t list -> unitSame as add_clause but with a list of atoms.
type res = | Sat of Model.t | Satisfiable | ||||
| Unsat of {
} | Unsatisfiable | ||||
| Unknown of Unknown.t | Unknown, obtained after a timeout, memory limit, etc. |
Result of solving for the current set of clauses
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the clauses added so far to s.
if true, the model is checked before returning.
called regularly during solving.
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerPrint some statistics. What it prints exactly is unspecified.
SOLVER.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitPop backtracking levels, restoring the theory to its former state
SOLVER.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
Called once for every solver this theory is added to.
val push_level : t -> unitPush backtracking level. When the corresponding pop is called, the theory's state should be restored to a state equivalent to what it was just before push_level.
it does not have to be exactly the same state, it just needs to be equivalent.
val pop_levels : t -> int -> unitpop_levels theory n pops n backtracking levels, restoring theory to its state before calling push_level n times.
CC.Actionsmodule T = Tmodule P = Pmodule Lit = Littype t = actionsAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC.ExplCC.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
CC.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
CC.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
SOLVER_INTERNAL.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
SOLVER_INTERNAL.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
SOLVER_INTERNAL.LitA literal is a (preprocessed) term along with its sign. It is directly manipulated by the SAT solver.
module T = TSOLVER_INTERNAL.LitA literal is a (preprocessed) term along with its sign. It is directly manipulated by the SAT solver.
module T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
SOLVER_INTERNAL.Simplifyval tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
SOLVER_INTERNAL.SimplifySimplify terms
val tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
T.TermT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
type stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.TyT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
SOLVER_INTERNAL.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
Sidekick_core.SOLVER_INTERNALA view of the solver from a theory's point of view
type ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.ttype lit = Lit.tmodule CC : CC_S with module T = T and module P = P and module Lit = Lit and type Actions.t = actionsmodule Simplify : sig ... endtype simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitval simplifier : t -> Simplify.tval simp_t : t -> term -> termval propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unittype model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class.
val on_model_gen : t -> model_hook -> unitSidekick_core.SOLVER_INTERNALA view of the solver from a theory's point of view.
Theories should interact with the solver via this module, to assert new lemmas, propagate literals, access the congruence closure, etc.
type ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.ttype lit = Lit.tmodule Simplify : sig ... endSimplify terms
type simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitAdd a simplifier hook for preprocessing.
val simplifier : t -> Simplify.tval simp_t : t -> term -> termSimplify the term using the solver's simplifier (see simplifier)
val propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
Preprocessing might transform terms to make them more amenable to reasoning, e.g. by removing boolean formulas via Tseitin encoding, adding clauses that encode their meaning in the same move.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unitAdd a hook that will be called when terms are preprocessed
type model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class. For example, an arithmetic theory might detect that a class contains a numeric constant, and return this constant as a model value.
If no hook assigns a value to a class, a fake value is created for it.
val on_model_gen : t -> model_hook -> unitAdd a hook that will be called when a model is being produced
TERM.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
TERM.TermTERM.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
type stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3TERM.TyTERM.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
Sidekick_core.TERMMain representation of Terms and Types
module Fun : sig ... endmodule Ty : sig ... endmodule Term : sig ... endSidekick_core.TERMMain representation of Terms and Types
module Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.Termval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
val equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.Tyval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printer1-A.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
Sidekick_mini_cc.Maketype term = A.T.Term.ttype fun_ = A.T.Fun.ttype term_state = A.T.Term.statetype tval create : term_state -> tval clear : t -> unitFully reset the congruence closure's state
val check_sat : t -> boolcheck_sat cc returns true if the current state is satisfiable, false if it's unsatisfiable
Sidekick_mini_cc.MakeInstantiate the congruence closure for the given term structure.
type term = A.T.Term.ttype fun_ = A.T.Fun.ttype term_state = A.T.Term.statetype tAn instance of the congruence closure. Mutable
val create : term_state -> tNew instance
val clear : t -> unitFully reset the congruence closure's state
val add_lit : t -> term -> bool -> unitadd_lit cc p sign asserts that p is true if sign, or p is false if not sign. If p is an equation and sign is true, this adds a new equation to the congruence relation.
val check_sat : t -> boolcheck_sat cc returns true if the current state is satisfiable, false if it's unsatisfiable.
Sidekick_mini_ccThis implementation is as simple as possible, and doesn't provide backtracking, theories, or explanations. It just decides the satisfiability of a set of (dis)equations.
module CC_view = Sidekick_core.CC_viewmodule type ARG = sig ... endmodule type S = sig ... endmodule Make : functor (A : ARG) -> S with type term = A.T.Term.t and type fun_ = A.T.Fun.t and type term_state = A.T.Term.stateSidekick_mini_ccThis implementation is as simple as possible, and doesn't provide backtracking, theories, or explanations. It just decides the satisfiability of a set of (dis)equations.
module CC_view = Sidekick_core.CC_viewmodule type S = sig ... endMain signature for an instance of the mini congruence closure
module Make : functor (A : ARG) -> S with type term = A.T.Term.t and type fun_ = A.T.Fun.t and type term_state = A.T.Term.stateInstantiate the congruence closure for the given term structure.
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.Termval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
val equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.Tyval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerARG.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
Sidekick_mini_cc.ARGmodule T : Sidekick_core.TERMSidekick_mini_cc.ARGArgument for the functor Make
It only requires a term structure, and a congruence-oriented view.
module T : Sidekick_core.TERMSidekick_mini_cc.Sval create : term_state -> tval clear : t -> unitFully reset the congruence closure's state
val check_sat : t -> boolcheck_sat cc returns true if the current state is satisfiable, false if it's unsatisfiable
Sidekick_mini_cc.SMain signature for an instance of the mini congruence closure
val create : term_state -> tNew instance
val clear : t -> unitFully reset the congruence closure's state
val add_lit : t -> term -> bool -> unitadd_lit cc p sign asserts that p is true if sign, or p is false if not sign. If p is an equation and sign is true, this adds a new equation to the congruence relation.
val check_sat : t -> boolcheck_sat cc returns true if the current state is satisfiable, false if it's unsatisfiable.
Make.AtomAtoms are the SAT solver's version of our boolean literals (they may have a different representation).
Make.Litmodule T = TMake.Litmodule T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerMake.ModelMake.ModelModels
A model can be produced when the solver is found to be in a satisfiable state after a call to solve.
CC.Actionsmodule T = Tmodule P = Pmodule Lit = Littype t = actionsAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see Sidekick_core.CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC.ExplCC.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
CC.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
CC.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See Sidekick_core.CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.Simplifyval tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
Solver_internal.SimplifySimplify terms
val tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
Make.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule CC : Sidekick_core.CC_S with module T = T and module P = P and module Lit = Lit and type Actions.t = actionsmodule Simplify : sig ... endtype simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitval simplifier : t -> Simplify.tval simp_t : t -> term -> termval propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unittype model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class.
val on_model_gen : t -> model_hook -> unitMake.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule Simplify : sig ... endSimplify terms
type simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitAdd a simplifier hook for preprocessing.
val simplifier : t -> Simplify.tval simp_t : t -> term -> termSimplify the term using the solver's simplifier (see simplifier)
val propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
Preprocessing might transform terms to make them more amenable to reasoning, e.g. by removing boolean formulas via Tseitin encoding, adding clauses that encode their meaning in the same move.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unitAdd a hook that will be called when terms are preprocessed
type model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class. For example, an arithmetic theory might detect that a class contains a numeric constant, and return this constant as a model value.
If no hook assigns a value to a class, a fake value is created for it.
val on_model_gen : t -> model_hook -> unitAdd a hook that will be called when a model is being produced
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.Termval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
val equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.Tyval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printer1-A.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
Sidekick_msat_solver.Makemodule T = A.Tmodule P = A.Pmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory
module Atom : sig ... endmodule Model : sig ... endmodule Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
theories to load from the start.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tval mk_atom_t : t -> ?sign:bool -> term -> Atom.tval add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitval add_clause_l : t -> Atom.t list -> unittype res = | Sat of Model.t | ||
| Unsat of {
} | ||
| Unknown of Unknown.t | Result of solving for the current set of clauses |
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the statement added so far to s
if true, the model is checked before returning
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerSidekick_msat_solver.Makemodule T = A.Tmodule P = A.Pmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory.
module Atom : sig ... endmodule Model : sig ... endModels
module Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
It needs a term state and a type state to manipulate terms and types. All terms and types interacting with this solver will need to come from these exact states.
if true, proofs from the SAT solver and theories are retained and potentially accessible after solve returns UNSAT.
influences the size of initial allocations.
theories to load from the start. Other theories can be added using add_theory.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tTurn a literal into a SAT solver literal.
val mk_atom_t : t -> ?sign:bool -> term -> Atom.tTurn a boolean term, with a sign, into a SAT solver's literal.
val add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitadd_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 -> Atom.t list -> unitSame as add_clause but with a list of atoms.
type res = | Sat of Model.t | Satisfiable | ||||
| Unsat of {
} | Unsatisfiable | ||||
| Unknown of Unknown.t | Unknown, obtained after a timeout, memory limit, etc. |
Result of solving for the current set of clauses
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the clauses added so far to s.
if true, the model is checked before returning.
called regularly during solving.
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerPrint some statistics. What it prints exactly is unspecified.
Make.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitPop backtracking levels, restoring the theory to its former state
Make.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
Called once for every solver this theory is added to.
val push_level : t -> unitPush backtracking level. When the corresponding pop is called, the theory's state should be restored to a state equivalent to what it was just before push_level.
it does not have to be exactly the same state, it just needs to be equivalent.
val pop_levels : t -> int -> unitpop_levels theory n pops n backtracking levels, restoring theory to its state before calling push_level n times.
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.Termval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
val equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.Tyval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerARG.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
S.AtomAtoms are the SAT solver's version of our boolean literals (they may have a different representation).
S.Litmodule T = TS.Litmodule T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.ModelS.ModelModels
A model can be produced when the solver is found to be in a satisfiable state after a call to solve.
CC.Actionsmodule T = Tmodule P = Pmodule Lit = Littype t = actionsAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see Sidekick_core.CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC.ExplCC.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
CC.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
CC.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See Sidekick_core.CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.Simplifyval tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
Solver_internal.SimplifySimplify terms
val tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
S.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule CC : Sidekick_core.CC_S with module T = T and module P = P and module Lit = Lit and type Actions.t = actionsmodule Simplify : sig ... endtype simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitval simplifier : t -> Simplify.tval simp_t : t -> term -> termval propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unittype model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class.
val on_model_gen : t -> model_hook -> unitS.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule Simplify : sig ... endSimplify terms
type simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitAdd a simplifier hook for preprocessing.
val simplifier : t -> Simplify.tval simp_t : t -> term -> termSimplify the term using the solver's simplifier (see simplifier)
val propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
Preprocessing might transform terms to make them more amenable to reasoning, e.g. by removing boolean formulas via Tseitin encoding, adding clauses that encode their meaning in the same move.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unitAdd a hook that will be called when terms are preprocessed
type model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class. For example, an arithmetic theory might detect that a class contains a numeric constant, and return this constant as a model value.
If no hook assigns a value to a class, a fake value is created for it.
val on_model_gen : t -> model_hook -> unitAdd a hook that will be called when a model is being produced
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.Termval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
val equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.Tyval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
Sidekick_msat_solver.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory
module Atom : sig ... endmodule Model : sig ... endmodule Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
theories to load from the start.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tval mk_atom_t : t -> ?sign:bool -> term -> Atom.tval add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitval add_clause_l : t -> Atom.t list -> unittype res = | Sat of Model.t | ||
| Unsat of {
} | ||
| Unknown of Unknown.t | Result of solving for the current set of clauses |
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the statement added so far to s
if true, the model is checked before returning
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerSidekick_msat_solver.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory.
module Atom : sig ... endmodule Model : sig ... endModels
module Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
It needs a term state and a type state to manipulate terms and types. All terms and types interacting with this solver will need to come from these exact states.
if true, proofs from the SAT solver and theories are retained and potentially accessible after solve returns UNSAT.
influences the size of initial allocations.
theories to load from the start. Other theories can be added using add_theory.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tTurn a literal into a SAT solver literal.
val mk_atom_t : t -> ?sign:bool -> term -> Atom.tTurn a boolean term, with a sign, into a SAT solver's literal.
val add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitadd_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 -> Atom.t list -> unitSame as add_clause but with a list of atoms.
type res = | Sat of Model.t | Satisfiable | ||||
| Unsat of {
} | Unsatisfiable | ||||
| Unknown of Unknown.t | Unknown, obtained after a timeout, memory limit, etc. |
Result of solving for the current set of clauses
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the clauses added so far to s.
if true, the model is checked before returning.
called regularly during solving.
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerPrint some statistics. What it prints exactly is unspecified.
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitPop backtracking levels, restoring the theory to its former state
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
Called once for every solver this theory is added to.
val push_level : t -> unitPush backtracking level. When the corresponding pop is called, the theory's state should be restored to a state equivalent to what it was just before push_level.
it does not have to be exactly the same state, it just needs to be equivalent.
val pop_levels : t -> int -> unitpop_levels theory n pops n backtracking levels, restoring theory to its state before calling push_level n times.
S.AtomAtoms are the SAT solver's version of our boolean literals (they may have a different representation).
S.Litmodule T = TS.Litmodule T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.ModelS.ModelModels
A model can be produced when the solver is found to be in a satisfiable state after a call to solve.
CC.Actionsmodule T = Tmodule P = Pmodule Lit = Littype t = actionsAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see Sidekick_core.CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC.ExplCC.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
CC.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
CC.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See Sidekick_core.CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.Simplifyval tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
Solver_internal.SimplifySimplify terms
val tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
S.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule CC : Sidekick_core.CC_S with module T = T and module P = P and module Lit = Lit and type Actions.t = actionsmodule Simplify : sig ... endtype simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitval simplifier : t -> Simplify.tval simp_t : t -> term -> termval propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unittype model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class.
val on_model_gen : t -> model_hook -> unitS.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule Simplify : sig ... endSimplify terms
type simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitAdd a simplifier hook for preprocessing.
val simplifier : t -> Simplify.tval simp_t : t -> term -> termSimplify the term using the solver's simplifier (see simplifier)
val propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
Preprocessing might transform terms to make them more amenable to reasoning, e.g. by removing boolean formulas via Tseitin encoding, adding clauses that encode their meaning in the same move.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unitAdd a hook that will be called when terms are preprocessed
type model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class. For example, an arithmetic theory might detect that a class contains a numeric constant, and return this constant as a model value.
If no hook assigns a value to a class, a fake value is created for it.
val on_model_gen : t -> model_hook -> unitAdd a hook that will be called when a model is being produced
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.Termval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
val equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.Tyval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
1-A.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory
module Atom : sig ... endmodule Model : sig ... endmodule Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
theories to load from the start.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tval mk_atom_t : t -> ?sign:bool -> term -> Atom.tval add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitval add_clause_l : t -> Atom.t list -> unittype res = | Sat of Model.t | ||
| Unsat of {
} | ||
| Unknown of Unknown.t | Result of solving for the current set of clauses |
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the statement added so far to s
if true, the model is checked before returning
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printer1-A.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory.
module Atom : sig ... endmodule Model : sig ... endModels
module Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
It needs a term state and a type state to manipulate terms and types. All terms and types interacting with this solver will need to come from these exact states.
if true, proofs from the SAT solver and theories are retained and potentially accessible after solve returns UNSAT.
influences the size of initial allocations.
theories to load from the start. Other theories can be added using add_theory.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tTurn a literal into a SAT solver literal.
val mk_atom_t : t -> ?sign:bool -> term -> Atom.tTurn a boolean term, with a sign, into a SAT solver's literal.
val add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitadd_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 -> Atom.t list -> unitSame as add_clause but with a list of atoms.
type res = | Sat of Model.t | Satisfiable | ||||
| Unsat of {
} | Unsatisfiable | ||||
| Unknown of Unknown.t | Unknown, obtained after a timeout, memory limit, etc. |
Result of solving for the current set of clauses
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the clauses added so far to s.
if true, the model is checked before returning.
called regularly during solving.
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerPrint some statistics. What it prints exactly is unspecified.
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitPop backtracking levels, restoring the theory to its former state
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
Called once for every solver this theory is added to.
val push_level : t -> unitPush backtracking level. When the corresponding pop is called, the theory's state should be restored to a state equivalent to what it was just before push_level.
it does not have to be exactly the same state, it just needs to be equivalent.
val pop_levels : t -> int -> unitpop_levels theory n pops n backtracking levels, restoring theory to its state before calling push_level n times.
S.AtomAtoms are the SAT solver's version of our boolean literals (they may have a different representation).
S.Litmodule T = TS.Litmodule T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.ModelS.ModelModels
A model can be produced when the solver is found to be in a satisfiable state after a call to solve.
CC.Actionsmodule T = Tmodule P = Pmodule Lit = Littype t = actionsAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see Sidekick_core.CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC.ExplCC.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
CC.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
CC.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See Sidekick_core.CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.Simplifyval tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
Solver_internal.SimplifySimplify terms
val tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
S.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule CC : Sidekick_core.CC_S with module T = T and module P = P and module Lit = Lit and type Actions.t = actionsmodule Simplify : sig ... endtype simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitval simplifier : t -> Simplify.tval simp_t : t -> term -> termval propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unittype model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class.
val on_model_gen : t -> model_hook -> unitS.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule Simplify : sig ... endSimplify terms
type simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitAdd a simplifier hook for preprocessing.
val simplifier : t -> Simplify.tval simp_t : t -> term -> termSimplify the term using the solver's simplifier (see simplifier)
val propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
Preprocessing might transform terms to make them more amenable to reasoning, e.g. by removing boolean formulas via Tseitin encoding, adding clauses that encode their meaning in the same move.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unitAdd a hook that will be called when terms are preprocessed
type model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class. For example, an arithmetic theory might detect that a class contains a numeric constant, and return this constant as a model value.
If no hook assigns a value to a class, a fake value is created for it.
val on_model_gen : t -> model_hook -> unitAdd a hook that will be called when a model is being produced
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.Termval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
val equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.Tyval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
ARG.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory
module Atom : sig ... endmodule Model : sig ... endmodule Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
theories to load from the start.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tval mk_atom_t : t -> ?sign:bool -> term -> Atom.tval add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitval add_clause_l : t -> Atom.t list -> unittype res = | Sat of Model.t | ||
| Unsat of {
} | ||
| Unknown of Unknown.t | Result of solving for the current set of clauses |
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the statement added so far to s
if true, the model is checked before returning
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerARG.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory.
module Atom : sig ... endmodule Model : sig ... endModels
module Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
It needs a term state and a type state to manipulate terms and types. All terms and types interacting with this solver will need to come from these exact states.
if true, proofs from the SAT solver and theories are retained and potentially accessible after solve returns UNSAT.
influences the size of initial allocations.
theories to load from the start. Other theories can be added using add_theory.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tTurn a literal into a SAT solver literal.
val mk_atom_t : t -> ?sign:bool -> term -> Atom.tTurn a boolean term, with a sign, into a SAT solver's literal.
val add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitadd_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 -> Atom.t list -> unitSame as add_clause but with a list of atoms.
type res = | Sat of Model.t | Satisfiable | ||||
| Unsat of {
} | Unsatisfiable | ||||
| Unknown of Unknown.t | Unknown, obtained after a timeout, memory limit, etc. |
Result of solving for the current set of clauses
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the clauses added so far to s.
if true, the model is checked before returning.
called regularly during solving.
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerPrint some statistics. What it prints exactly is unspecified.
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitPop backtracking levels, restoring the theory to its former state
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
Called once for every solver this theory is added to.
val push_level : t -> unitPush backtracking level. When the corresponding pop is called, the theory's state should be restored to a state equivalent to what it was just before push_level.
it does not have to be exactly the same state, it just needs to be equivalent.
val pop_levels : t -> int -> unitpop_levels theory n pops n backtracking levels, restoring theory to its state before calling push_level n times.
S.AtomAtoms are the SAT solver's version of our boolean literals (they may have a different representation).
S.Litmodule T = TS.Litmodule T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.ModelS.ModelModels
A model can be produced when the solver is found to be in a satisfiable state after a call to solve.
CC.Actionsmodule T = Tmodule P = Pmodule Lit = Littype t = actionsAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see Sidekick_core.CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC.ExplCC.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
CC.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
CC.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See Sidekick_core.CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.Simplifyval tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
Solver_internal.SimplifySimplify terms
val tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
S.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule CC : Sidekick_core.CC_S with module T = T and module P = P and module Lit = Lit and type Actions.t = actionsmodule Simplify : sig ... endtype simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitval simplifier : t -> Simplify.tval simp_t : t -> term -> termval propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unittype model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class.
val on_model_gen : t -> model_hook -> unitS.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule Simplify : sig ... endSimplify terms
type simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitAdd a simplifier hook for preprocessing.
val simplifier : t -> Simplify.tval simp_t : t -> term -> termSimplify the term using the solver's simplifier (see simplifier)
val propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
Preprocessing might transform terms to make them more amenable to reasoning, e.g. by removing boolean formulas via Tseitin encoding, adding clauses that encode their meaning in the same move.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unitAdd a hook that will be called when terms are preprocessed
type model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class. For example, an arithmetic theory might detect that a class contains a numeric constant, and return this constant as a model value.
If no hook assigns a value to a class, a fake value is created for it.
val on_model_gen : t -> model_hook -> unitAdd a hook that will be called when a model is being produced
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.Termval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
val equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.Tyval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
A.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory
module Atom : sig ... endmodule Model : sig ... endmodule Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
theories to load from the start.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tval mk_atom_t : t -> ?sign:bool -> term -> Atom.tval add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitval add_clause_l : t -> Atom.t list -> unittype res = | Sat of Model.t | ||
| Unsat of {
} | ||
| Unknown of Unknown.t | Result of solving for the current set of clauses |
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the statement added so far to s
if true, the model is checked before returning
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerA.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory.
module Atom : sig ... endmodule Model : sig ... endModels
module Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
It needs a term state and a type state to manipulate terms and types. All terms and types interacting with this solver will need to come from these exact states.
if true, proofs from the SAT solver and theories are retained and potentially accessible after solve returns UNSAT.
influences the size of initial allocations.
theories to load from the start. Other theories can be added using add_theory.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tTurn a literal into a SAT solver literal.
val mk_atom_t : t -> ?sign:bool -> term -> Atom.tTurn a boolean term, with a sign, into a SAT solver's literal.
val add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitadd_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 -> Atom.t list -> unitSame as add_clause but with a list of atoms.
type res = | Sat of Model.t | Satisfiable | ||||
| Unsat of {
} | Unsatisfiable | ||||
| Unknown of Unknown.t | Unknown, obtained after a timeout, memory limit, etc. |
Result of solving for the current set of clauses
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the clauses added so far to s.
if true, the model is checked before returning.
called regularly during solving.
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerPrint some statistics. What it prints exactly is unspecified.
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitPop backtracking levels, restoring the theory to its former state
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
Called once for every solver this theory is added to.
val push_level : t -> unitPush backtracking level. When the corresponding pop is called, the theory's state should be restored to a state equivalent to what it was just before push_level.
it does not have to be exactly the same state, it just needs to be equivalent.
val pop_levels : t -> int -> unitpop_levels theory n pops n backtracking levels, restoring theory to its state before calling push_level n times.
S.AtomAtoms are the SAT solver's version of our boolean literals (they may have a different representation).
S.Litmodule T = TS.Litmodule T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.ModelS.ModelModels
A model can be produced when the solver is found to be in a satisfiable state after a call to solve.
CC.Actionsmodule T = Tmodule P = Pmodule Lit = Littype t = actionsAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see Sidekick_core.CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC.ExplCC.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
CC.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
CC.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See Sidekick_core.CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.Simplifyval tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
Solver_internal.SimplifySimplify terms
val tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
S.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule CC : Sidekick_core.CC_S with module T = T and module P = P and module Lit = Lit and type Actions.t = actionsmodule Simplify : sig ... endtype simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitval simplifier : t -> Simplify.tval simp_t : t -> term -> termval propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unittype model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class.
val on_model_gen : t -> model_hook -> unitS.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule Simplify : sig ... endSimplify terms
type simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitAdd a simplifier hook for preprocessing.
val simplifier : t -> Simplify.tval simp_t : t -> term -> termSimplify the term using the solver's simplifier (see simplifier)
val propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
Preprocessing might transform terms to make them more amenable to reasoning, e.g. by removing boolean formulas via Tseitin encoding, adding clauses that encode their meaning in the same move.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unitAdd a hook that will be called when terms are preprocessed
type model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class. For example, an arithmetic theory might detect that a class contains a numeric constant, and return this constant as a model value.
If no hook assigns a value to a class, a fake value is created for it.
val on_model_gen : t -> model_hook -> unitAdd a hook that will be called when a model is being produced
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.Termval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
val equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.Tyval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
1-A.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory
module Atom : sig ... endmodule Model : sig ... endmodule Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
theories to load from the start.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tval mk_atom_t : t -> ?sign:bool -> term -> Atom.tval add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitval add_clause_l : t -> Atom.t list -> unittype res = | Sat of Model.t | ||
| Unsat of {
} | ||
| Unknown of Unknown.t | Result of solving for the current set of clauses |
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the statement added so far to s
if true, the model is checked before returning
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printer1-A.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory.
module Atom : sig ... endmodule Model : sig ... endModels
module Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
It needs a term state and a type state to manipulate terms and types. All terms and types interacting with this solver will need to come from these exact states.
if true, proofs from the SAT solver and theories are retained and potentially accessible after solve returns UNSAT.
influences the size of initial allocations.
theories to load from the start. Other theories can be added using add_theory.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tTurn a literal into a SAT solver literal.
val mk_atom_t : t -> ?sign:bool -> term -> Atom.tTurn a boolean term, with a sign, into a SAT solver's literal.
val add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitadd_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 -> Atom.t list -> unitSame as add_clause but with a list of atoms.
type res = | Sat of Model.t | Satisfiable | ||||
| Unsat of {
} | Unsatisfiable | ||||
| Unknown of Unknown.t | Unknown, obtained after a timeout, memory limit, etc. |
Result of solving for the current set of clauses
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the clauses added so far to s.
if true, the model is checked before returning.
called regularly during solving.
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerPrint some statistics. What it prints exactly is unspecified.
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitPop backtracking levels, restoring the theory to its former state
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
Called once for every solver this theory is added to.
val push_level : t -> unitPush backtracking level. When the corresponding pop is called, the theory's state should be restored to a state equivalent to what it was just before push_level.
it does not have to be exactly the same state, it just needs to be equivalent.
val pop_levels : t -> int -> unitpop_levels theory n pops n backtracking levels, restoring theory to its state before calling push_level n times.
S.AtomAtoms are the SAT solver's version of our boolean literals (they may have a different representation).
S.Litmodule T = TS.Litmodule T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.ModelS.ModelModels
A model can be produced when the solver is found to be in a satisfiable state after a call to solve.
CC.Actionsmodule T = Tmodule P = Pmodule Lit = Littype t = actionsAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see Sidekick_core.CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC.ExplCC.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
CC.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
CC.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See Sidekick_core.CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.Simplifyval tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
Solver_internal.SimplifySimplify terms
val tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
S.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule CC : Sidekick_core.CC_S with module T = T and module P = P and module Lit = Lit and type Actions.t = actionsmodule Simplify : sig ... endtype simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitval simplifier : t -> Simplify.tval simp_t : t -> term -> termval propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unittype model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class.
val on_model_gen : t -> model_hook -> unitS.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule Simplify : sig ... endSimplify terms
type simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitAdd a simplifier hook for preprocessing.
val simplifier : t -> Simplify.tval simp_t : t -> term -> termSimplify the term using the solver's simplifier (see simplifier)
val propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
Preprocessing might transform terms to make them more amenable to reasoning, e.g. by removing boolean formulas via Tseitin encoding, adding clauses that encode their meaning in the same move.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unitAdd a hook that will be called when terms are preprocessed
type model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class. For example, an arithmetic theory might detect that a class contains a numeric constant, and return this constant as a model value.
If no hook assigns a value to a class, a fake value is created for it.
val on_model_gen : t -> model_hook -> unitAdd a hook that will be called when a model is being produced
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.Termval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
val equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.Tyval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
ARG.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory
module Atom : sig ... endmodule Model : sig ... endmodule Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
theories to load from the start.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tval mk_atom_t : t -> ?sign:bool -> term -> Atom.tval add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitval add_clause_l : t -> Atom.t list -> unittype res = | Sat of Model.t | ||
| Unsat of {
} | ||
| Unknown of Unknown.t | Result of solving for the current set of clauses |
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the statement added so far to s
if true, the model is checked before returning
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerARG.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory.
module Atom : sig ... endmodule Model : sig ... endModels
module Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
It needs a term state and a type state to manipulate terms and types. All terms and types interacting with this solver will need to come from these exact states.
if true, proofs from the SAT solver and theories are retained and potentially accessible after solve returns UNSAT.
influences the size of initial allocations.
theories to load from the start. Other theories can be added using add_theory.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tTurn a literal into a SAT solver literal.
val mk_atom_t : t -> ?sign:bool -> term -> Atom.tTurn a boolean term, with a sign, into a SAT solver's literal.
val add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitadd_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 -> Atom.t list -> unitSame as add_clause but with a list of atoms.
type res = | Sat of Model.t | Satisfiable | ||||
| Unsat of {
} | Unsatisfiable | ||||
| Unknown of Unknown.t | Unknown, obtained after a timeout, memory limit, etc. |
Result of solving for the current set of clauses
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the clauses added so far to s.
if true, the model is checked before returning.
called regularly during solving.
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerPrint some statistics. What it prints exactly is unspecified.
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitPop backtracking levels, restoring the theory to its former state
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
Called once for every solver this theory is added to.
val push_level : t -> unitPush backtracking level. When the corresponding pop is called, the theory's state should be restored to a state equivalent to what it was just before push_level.
it does not have to be exactly the same state, it just needs to be equivalent.
val pop_levels : t -> int -> unitpop_levels theory n pops n backtracking levels, restoring theory to its state before calling push_level n times.
S.AtomAtoms are the SAT solver's version of our boolean literals (they may have a different representation).
S.Litmodule T = TS.Litmodule T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.ModelS.ModelModels
A model can be produced when the solver is found to be in a satisfiable state after a call to solve.
CC.Actionsmodule T = Tmodule P = Pmodule Lit = Littype t = actionsAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see Sidekick_core.CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC.ExplCC.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
CC.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
CC.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See Sidekick_core.CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.Simplifyval tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
Solver_internal.SimplifySimplify terms
val tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
S.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule CC : Sidekick_core.CC_S with module T = T and module P = P and module Lit = Lit and type Actions.t = actionsmodule Simplify : sig ... endtype simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitval simplifier : t -> Simplify.tval simp_t : t -> term -> termval propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unittype model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class.
val on_model_gen : t -> model_hook -> unitS.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule Simplify : sig ... endSimplify terms
type simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitAdd a simplifier hook for preprocessing.
val simplifier : t -> Simplify.tval simp_t : t -> term -> termSimplify the term using the solver's simplifier (see simplifier)
val propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
Preprocessing might transform terms to make them more amenable to reasoning, e.g. by removing boolean formulas via Tseitin encoding, adding clauses that encode their meaning in the same move.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unitAdd a hook that will be called when terms are preprocessed
type model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class. For example, an arithmetic theory might detect that a class contains a numeric constant, and return this constant as a model value.
If no hook assigns a value to a class, a fake value is created for it.
val on_model_gen : t -> model_hook -> unitAdd a hook that will be called when a model is being produced
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.Termval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
val equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.Tyval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
A.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory
module Atom : sig ... endmodule Model : sig ... endmodule Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
theories to load from the start.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tval mk_atom_t : t -> ?sign:bool -> term -> Atom.tval add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitval add_clause_l : t -> Atom.t list -> unittype res = | Sat of Model.t | ||
| Unsat of {
} | ||
| Unknown of Unknown.t | Result of solving for the current set of clauses |
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the statement added so far to s
if true, the model is checked before returning
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerA.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory.
module Atom : sig ... endmodule Model : sig ... endModels
module Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
It needs a term state and a type state to manipulate terms and types. All terms and types interacting with this solver will need to come from these exact states.
if true, proofs from the SAT solver and theories are retained and potentially accessible after solve returns UNSAT.
influences the size of initial allocations.
theories to load from the start. Other theories can be added using add_theory.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tTurn a literal into a SAT solver literal.
val mk_atom_t : t -> ?sign:bool -> term -> Atom.tTurn a boolean term, with a sign, into a SAT solver's literal.
val add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitadd_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 -> Atom.t list -> unitSame as add_clause but with a list of atoms.
type res = | Sat of Model.t | Satisfiable | ||||
| Unsat of {
} | Unsatisfiable | ||||
| Unknown of Unknown.t | Unknown, obtained after a timeout, memory limit, etc. |
Result of solving for the current set of clauses
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the clauses added so far to s.
if true, the model is checked before returning.
called regularly during solving.
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerPrint some statistics. What it prints exactly is unspecified.
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitPop backtracking levels, restoring the theory to its former state
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
Called once for every solver this theory is added to.
val push_level : t -> unitPush backtracking level. When the corresponding pop is called, the theory's state should be restored to a state equivalent to what it was just before push_level.
it does not have to be exactly the same state, it just needs to be equivalent.
val pop_levels : t -> int -> unitpop_levels theory n pops n backtracking levels, restoring theory to its state before calling push_level n times.
S.AtomAtoms are the SAT solver's version of our boolean literals (they may have a different representation).
S.Litmodule T = TS.Litmodule T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.ModelS.ModelModels
A model can be produced when the solver is found to be in a satisfiable state after a call to solve.
CC.Actionsmodule T = Tmodule P = Pmodule Lit = Littype t = actionsAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see Sidekick_core.CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC.ExplCC.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
CC.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
CC.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See Sidekick_core.CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.Simplifyval tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
Solver_internal.SimplifySimplify terms
val tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
S.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule CC : Sidekick_core.CC_S with module T = T and module P = P and module Lit = Lit and type Actions.t = actionsmodule Simplify : sig ... endtype simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitval simplifier : t -> Simplify.tval simp_t : t -> term -> termval propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unittype model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class.
val on_model_gen : t -> model_hook -> unitS.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule Simplify : sig ... endSimplify terms
type simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitAdd a simplifier hook for preprocessing.
val simplifier : t -> Simplify.tval simp_t : t -> term -> termSimplify the term using the solver's simplifier (see simplifier)
val propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
Preprocessing might transform terms to make them more amenable to reasoning, e.g. by removing boolean formulas via Tseitin encoding, adding clauses that encode their meaning in the same move.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unitAdd a hook that will be called when terms are preprocessed
type model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class. For example, an arithmetic theory might detect that a class contains a numeric constant, and return this constant as a model value.
If no hook assigns a value to a class, a fake value is created for it.
val on_model_gen : t -> model_hook -> unitAdd a hook that will be called when a model is being produced
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.Termval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
val equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.Tyval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
1-A.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory
module Atom : sig ... endmodule Model : sig ... endmodule Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
theories to load from the start.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tval mk_atom_t : t -> ?sign:bool -> term -> Atom.tval add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitval add_clause_l : t -> Atom.t list -> unittype res = | Sat of Model.t | ||
| Unsat of {
} | ||
| Unknown of Unknown.t | Result of solving for the current set of clauses |
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the statement added so far to s
if true, the model is checked before returning
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printer1-A.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory.
module Atom : sig ... endmodule Model : sig ... endModels
module Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
It needs a term state and a type state to manipulate terms and types. All terms and types interacting with this solver will need to come from these exact states.
if true, proofs from the SAT solver and theories are retained and potentially accessible after solve returns UNSAT.
influences the size of initial allocations.
theories to load from the start. Other theories can be added using add_theory.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tTurn a literal into a SAT solver literal.
val mk_atom_t : t -> ?sign:bool -> term -> Atom.tTurn a boolean term, with a sign, into a SAT solver's literal.
val add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitadd_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 -> Atom.t list -> unitSame as add_clause but with a list of atoms.
type res = | Sat of Model.t | Satisfiable | ||||
| Unsat of {
} | Unsatisfiable | ||||
| Unknown of Unknown.t | Unknown, obtained after a timeout, memory limit, etc. |
Result of solving for the current set of clauses
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the clauses added so far to s.
if true, the model is checked before returning.
called regularly during solving.
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerPrint some statistics. What it prints exactly is unspecified.
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitPop backtracking levels, restoring the theory to its former state
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
Called once for every solver this theory is added to.
val push_level : t -> unitPush backtracking level. When the corresponding pop is called, the theory's state should be restored to a state equivalent to what it was just before push_level.
it does not have to be exactly the same state, it just needs to be equivalent.
val pop_levels : t -> int -> unitpop_levels theory n pops n backtracking levels, restoring theory to its state before calling push_level n times.
S.AtomAtoms are the SAT solver's version of our boolean literals (they may have a different representation).
S.Litmodule T = TS.Litmodule T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.ModelS.ModelModels
A model can be produced when the solver is found to be in a satisfiable state after a call to solve.
CC.Actionsmodule T = Tmodule P = Pmodule Lit = Littype t = actionsAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see Sidekick_core.CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC.ExplCC.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
CC.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
CC.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See Sidekick_core.CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.Simplifyval tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
Solver_internal.SimplifySimplify terms
val tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
S.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule CC : Sidekick_core.CC_S with module T = T and module P = P and module Lit = Lit and type Actions.t = actionsmodule Simplify : sig ... endtype simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitval simplifier : t -> Simplify.tval simp_t : t -> term -> termval propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unittype model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class.
val on_model_gen : t -> model_hook -> unitS.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule Simplify : sig ... endSimplify terms
type simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitAdd a simplifier hook for preprocessing.
val simplifier : t -> Simplify.tval simp_t : t -> term -> termSimplify the term using the solver's simplifier (see simplifier)
val propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
Preprocessing might transform terms to make them more amenable to reasoning, e.g. by removing boolean formulas via Tseitin encoding, adding clauses that encode their meaning in the same move.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unitAdd a hook that will be called when terms are preprocessed
type model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class. For example, an arithmetic theory might detect that a class contains a numeric constant, and return this constant as a model value.
If no hook assigns a value to a class, a fake value is created for it.
val on_model_gen : t -> model_hook -> unitAdd a hook that will be called when a model is being produced
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.Termval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
val equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.Tyval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
ARG.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory
module Atom : sig ... endmodule Model : sig ... endmodule Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
theories to load from the start.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tval mk_atom_t : t -> ?sign:bool -> term -> Atom.tval add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitval add_clause_l : t -> Atom.t list -> unittype res = | Sat of Model.t | ||
| Unsat of {
} | ||
| Unknown of Unknown.t | Result of solving for the current set of clauses |
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the statement added so far to s
if true, the model is checked before returning
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerARG.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory.
module Atom : sig ... endmodule Model : sig ... endModels
module Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
It needs a term state and a type state to manipulate terms and types. All terms and types interacting with this solver will need to come from these exact states.
if true, proofs from the SAT solver and theories are retained and potentially accessible after solve returns UNSAT.
influences the size of initial allocations.
theories to load from the start. Other theories can be added using add_theory.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tTurn a literal into a SAT solver literal.
val mk_atom_t : t -> ?sign:bool -> term -> Atom.tTurn a boolean term, with a sign, into a SAT solver's literal.
val add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitadd_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 -> Atom.t list -> unitSame as add_clause but with a list of atoms.
type res = | Sat of Model.t | Satisfiable | ||||
| Unsat of {
} | Unsatisfiable | ||||
| Unknown of Unknown.t | Unknown, obtained after a timeout, memory limit, etc. |
Result of solving for the current set of clauses
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the clauses added so far to s.
if true, the model is checked before returning.
called regularly during solving.
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerPrint some statistics. What it prints exactly is unspecified.
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitPop backtracking levels, restoring the theory to its former state
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
Called once for every solver this theory is added to.
val push_level : t -> unitPush backtracking level. When the corresponding pop is called, the theory's state should be restored to a state equivalent to what it was just before push_level.
it does not have to be exactly the same state, it just needs to be equivalent.
val pop_levels : t -> int -> unitpop_levels theory n pops n backtracking levels, restoring theory to its state before calling push_level n times.
S.AtomAtoms are the SAT solver's version of our boolean literals (they may have a different representation).
S.Litmodule T = TS.Litmodule T = TLiterals depend on terms
val sign : t -> boolGet the sign. A negated literal has sign false.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.ModelS.ModelModels
A model can be produced when the solver is found to be in a satisfiable state after a call to solve.
CC.Actionsmodule T = Tmodule P = Pmodule Lit = Littype t = actionsAn 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 -> P.t -> 'araise_conflict acts c pr declares that c is a tautology of the theory of congruence. This does not return (it should raise an exception).
the proof of c being a tautology
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list) -> P.t -> unitpropagate acts lit ~reason pr declares that reason() => lit is a tautology.
reason() should return a list of literals that are currently true.lit should be a literal of interest (see Sidekick_core.CC_S.set_as_lit).This function might never be called, a congruence closure has the right to not propagate and only trigger conflicts.
CC.ExplCC.ExplExplanations
Explanations are specialized proofs, created by the congruence closure when asked to justify why 2 terms are equal.
CC.NAn 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.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)?
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
CC.NAn 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.
type tAn equivalent class, containing terms that are proved to be equal.
A value of type t points to a particular term, but see find to get the representative of the class.
val term : t -> termval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerval is_root : t -> boolIs the node a root (ie the representative of its class)? See find to get the root.
val iter_class : t -> t Iter.tTraverse the congruence class. Precondition: is_root n (see find below)
val iter_parents : t -> t Iter.tTraverse the parents of the class. Precondition: is_root n (see find below)
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tGlobal state of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endtype node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
val add_term : t -> term -> nodeAdd the term to the congruence closure, if not present already. Will be backtracked.
val mem_term : t -> term -> boolReturns true if the term is explicitly present in the congruence closure
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unittype ev_on_post_merge = t -> actions -> N.t -> N.t -> unittype ev_on_new_term = t -> N.t -> term -> unittype ev_on_conflict = t -> th:bool -> lit list -> unittype ev_on_propagate = t -> lit -> (unit -> lit list) -> unittype ev_on_is_subterm = N.t -> term -> unitval create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val n_true : t -> N.tval n_false : t -> N.tval n_bool : t -> bool -> N.tval merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.CCmodule T = Tmodule P = Pmodule Lit = Litmodule Actions : Sidekick_core.CC_ACTIONS with module T = T and module Lit = Lit and module P = P and type t = actionstype term_state = T.Term.statetype term = T.Term.ttype fun_ = T.Fun.ttype lit = Lit.ttype proof = P.ttype actions = Actions.ttype tState of the congruence closure
module N : sig ... endAn 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".
module Expl : sig ... endExplanations
type node = N.tA node of the congruence closure
type repr = N.tNode that is currently a representative
type explanation = Expl.tval term_state : t -> term_stateval find : t -> node -> reprCurrent representative
Events triggered by the congruence closure, to which other plugins can subscribe.
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unitev_on_pre_merge cc acts n1 n2 expl is called right before n1 and n2 are merged with explanation expl.
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unitev_on_post_merge cc acts n1 n2 is called right after n1 and n2 were merged. find cc n1 and find cc n2 will return the same node.
type ev_on_new_term = t -> N.t -> term -> unitev_on_new_term cc n t is called whenever a new term t is added to the congruence closure. Its node is n.
type ev_on_conflict = t -> th:bool -> lit list -> unitev_on_conflict acts ~th c is called when the congruence closure triggers a conflict by asserting the tautology c.
true if the explanation for this conflict involves at least one "theory" explanation; i.e. some of the equations participating in the conflict are purely syntactic theories like injectivity of constructors.
type ev_on_propagate = t -> lit -> (unit -> lit list) -> unitev_on_propagate cc lit reason is called whenever reason() => lit is a propagated lemma. See Sidekick_core.CC_ACTIONS.propagate.
type ev_on_is_subterm = N.t -> term -> unitev_on_is_subterm n t is called when n is a subterm of another node for the first time. t is the term corresponding to the node n. This can be useful for theory combination.
val create : ?stat:Sidekick_util.Stat.t -> ?on_pre_merge:ev_on_pre_merge list -> ?on_post_merge:ev_on_post_merge list -> ?on_new_term:ev_on_new_term list -> ?on_conflict:ev_on_conflict list -> ?on_propagate:ev_on_propagate list -> ?on_is_subterm:ev_on_is_subterm list -> ?size:[ `Small | `Big ] -> term_state -> tCreate a new congruence closure.
used to be able to create new terms. All terms interacting with this congruence closure must belong in this term state as well.
val allocate_bitfield : descr:string -> t -> N.bitfieldAllocate a new bitfield for the nodes. See N.bitfield.
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unitSet the bitfield for the node. This will be backtracked. See N.bitfield.
val on_pre_merge : t -> ev_on_pre_merge -> unitAdd a function to be called when two classes are merged
val on_post_merge : t -> ev_on_post_merge -> unitAdd a function to be called when two classes are merged
val on_new_term : t -> ev_on_new_term -> unitAdd a function to be called when a new node is created
val on_conflict : t -> ev_on_conflict -> unitCalled when the congruence closure finds a conflict
val on_propagate : t -> ev_on_propagate -> unitCalled when the congruence closure propagates a literal
val on_is_subterm : t -> ev_on_is_subterm -> unitCalled on terms that are subterms of function symbols
val find_t : t -> term -> reprCurrent representative of the term.
if the term is not already add-ed.
val all_classes : t -> repr Iter.tAll current classes. This is costly, only use if there is no other solution
val assert_lit : t -> lit -> unitGiven 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 explain_eq : t -> N.t -> N.t -> lit listExplain why the two nodes are equal. Fails if they are not, in an unspecified way
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'aRaise a conflict with the given explanation it must be a theory tautology that expl ==> absurd. To be used in theories.
val merge : t -> N.t -> N.t -> Expl.t -> unitMerge these two nodes given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val check : t -> actions -> unitPerform all pending operations done via assert_eq, assert_lit, etc. Will use the actions to propagate literals, declare conflicts, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitRestore to state n calls to push_level earlier. Used during backtracking.
Solver_internal.Simplifyval tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
Solver_internal.SimplifySimplify terms
val tst : t -> term_stateval ty_st : t -> ty_stateval clear : t -> unitReset internal cache, etc.
S.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule CC : Sidekick_core.CC_S with module T = T and module P = P and module Lit = Lit and type Actions.t = actionsmodule Simplify : sig ... endtype simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitval simplifier : t -> Simplify.tval simp_t : t -> term -> termval propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unittype model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class.
val on_model_gen : t -> model_hook -> unitS.Solver_internalInternal solver, available to theories.
module T = Tmodule P = Ptype ty = T.Ty.ttype term = T.Term.ttype term_state = T.Term.statetype ty_state = T.Ty.statetype proof = P.ttype ttype solver = tval tst : t -> term_stateval ty_st : t -> ty_stateval stats : t -> Sidekick_util.Stat.tmodule Lit = Littype lit = Lit.tmodule Simplify : sig ... endSimplify terms
type simplify_hook = Simplify.hookval add_simplifier : t -> Simplify.hook -> unitAdd a simplifier hook for preprocessing.
val simplifier : t -> Simplify.tval simp_t : t -> term -> termSimplify the term using the solver's simplifier (see simplifier)
val propagate : t -> actions -> lit -> reason:(unit -> lit list) -> proof -> unitPropagate a literal for a reason. This is similar to asserting the clause reason => lit, but more lightweight, and in a way that is backtrackable.
val push_decision : t -> actions -> lit -> unitAsk the SAT solver to decide the given literal in an extension of the current trail. This is useful for theory combination. If the SAT solver backtracks, this (potential) decision is removed and forgotten.
val propagate : t -> actions -> lit -> (unit -> lit list) -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val propagate_l : t -> actions -> lit -> lit list -> unitPropagate a boolean using a unit clause. expl => lit must be a theory lemma, that is, a T-tautology
val add_clause_temp : t -> actions -> lit list -> unitAdd local clause to the SAT solver. This clause will be removed when the solver backtracks.
val add_clause_permanent : t -> actions -> lit list -> unitAdd toplevel clause to the SAT solver. This clause will not be backtracked.
val mk_lit : t -> actions -> ?sign:bool -> term -> litCreate a literal. This automatically preprocesses the term.
val add_lit : t -> actions -> lit -> unitAdd the given literal to the SAT solver, so it gets assigned a boolean value
val add_lit_t : t -> actions -> ?sign:bool -> term -> unitAdd the given (signed) bool term to the SAT solver, so it gets assigned a boolean value
val cc_raise_conflict_expl : t -> actions -> CC.Expl.t -> 'aRaise a conflict with the given congruence closure explanation. it must be a theory tautology that expl ==> absurd. To be used in theories.
val cc_merge : t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unitMerge these two nodes in the congruence closure, given this explanation. It must be a theory tautology that expl ==> n1 = n2. To be used in theories.
val cc_merge_t : t -> actions -> term -> term -> CC.Expl.t -> unitMerge these two terms in the congruence closure, given this explanation. See cc_merge
val cc_add_term : t -> term -> CC.N.tAdd/retrieve congruence closure node for this term. To be used in theories
val cc_mem_term : t -> term -> boolReturn true if the term is explicitly in the congruence closure. To be used in theories
val on_cc_pre_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> CC.Expl.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called before)
val on_cc_post_merge : t -> (CC.t -> actions -> CC.N.t -> CC.N.t -> unit) -> unitCallback for when two classes containing data for this key are merged (called after)
val on_cc_new_term : t -> (CC.t -> CC.N.t -> term -> unit) -> unitCallback to add data on terms when they are added to the congruence closure
val on_cc_is_subterm : t -> (CC.N.t -> term -> unit) -> unitCallback for when a term is a subterm of another term in the congruence closure
val on_cc_conflict : t -> (CC.t -> th:bool -> lit list -> unit) -> unitCallback called on every CC conflict
val on_cc_propagate : t -> (CC.t -> lit -> (unit -> lit list) -> unit) -> unitCallback called on every CC propagation
val on_partial_check : t -> (t -> actions -> lit Iter.t -> unit) -> unitRegister callbacked to be called with the slice of literals newly added on the trail.
This is called very often and should be efficient. It doesn't have to be complete, only correct. It's given only the slice of the trail consisting in new literals.
These preprocessors turn mixed, raw literals (possibly simplified) into literals suitable for reasoning. Typically some clauses are also added to the solver.
type preprocess_hook = t -> mk_lit:(term -> lit) -> add_clause:(lit list -> unit) -> term -> term optionGiven a term, try to preprocess it. Return None if it didn't change. Can also add clauses to define new terms.
Preprocessing might transform terms to make them more amenable to reasoning, e.g. by removing boolean formulas via Tseitin encoding, adding clauses that encode their meaning in the same move.
creates a new literal for a boolean term.
pushes a new clause into the SAT solver.
val on_preprocess : t -> preprocess_hook -> unitAdd a hook that will be called when terms are preprocessed
type model_hook = recurse:(t -> CC.N.t -> term) -> t -> CC.N.t -> term optionA model-production hook. It takes the solver, a class, and returns a term for this class. For example, an arithmetic theory might detect that a class contains a numeric constant, and return this constant as a model value.
If no hook assigns a value to a class, a fake value is created for it.
val on_model_gen : t -> model_hook -> unitAdd a hook that will be called when a model is being produced
T.FunA function symbol, like "f" or "plus" or "is_human" or "socrates"
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.Termval equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TermTerm structure.
Terms should be hashconsed, with perfect sharing. This allows, for example, Term.Tbl and Term.iter_dag to be efficient.
val equal : t -> t -> boolval compare : t -> t -> intval hash : t -> intval pp : t Sidekick_core.Fmt.printertype stateA state used to create new terms. It is where the hashconsing table should live, along with other all-terms related state.
val as_bool : t -> bool optionas_bool t is Some true if t is the term true, and similarly for false. For other terms it is None.
val abs : state -> t -> t * boolabs t returns an "absolute value" for the term, along with the sign of t.
The idea is that we want to turn not a into (a, false), or (a != b) into (a=b, false). For terms without a negation this should return (t, true).
The state is passed in case a new term needs to be created.
val map_shallow : state -> (t -> t) -> t -> tMap function on immediate subterms. This should not be recursive.
val iter_dag : t -> (t -> unit) -> unititer_dag t f calls f once on each subterm of t, t included. It must not traverse t as a tree, but rather as a perfectly shared DAG.
For example, in:
let x = 2 in
+let y = f x x in
+let z = g y x in
+z = zthe DAG has the following nodes:
n1: 2
+n2: f n1 n1
+n3: g n2 n1
+n4: = n3 n3T.Tyval equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerT.TyTypes
Types should be comparable (ideally, in O(1)), and have at least a boolean type available.
val equal : t -> t -> boolval hash : t -> intval pp : t Sidekick_core.Fmt.printerS.Tmodule Fun : sig ... endA function symbol, like "f" or "plus" or "is_human" or "socrates"
module Ty : sig ... endTypes
module Term : sig ... endTerm structure.
A.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory
module Atom : sig ... endmodule Model : sig ... endmodule Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
theories to load from the start.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tval mk_atom_t : t -> ?sign:bool -> term -> Atom.tval add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitval add_clause_l : t -> Atom.t list -> unittype res = | Sat of Model.t | ||
| Unsat of {
} | ||
| Unknown of Unknown.t | Result of solving for the current set of clauses |
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the statement added so far to s
if true, the model is checked before returning
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerA.Smodule T : Sidekick_core.TERMmodule P : Sidekick_core.PROOFmodule Lit : Sidekick_core.LIT with module T = Tmodule Solver_internal : Sidekick_core.SOLVER_INTERNAL with module T = T and module P = P and module Lit = LitInternal solver, available to theories.
module type THEORY = sig ... endtype theory = (module THEORY)A theory that can be used for this particular solver.
type 'a theory_p = (module THEORY with type t = 'a)A theory that can be used for this particular solver, with state of type 'a.
val mk_theory : name:string -> create_and_setup:(Solver_internal.t -> 'th) -> ?push_level:('th -> unit) -> ?pop_levels:('th -> int -> unit) -> unit -> theoryHelper to create a theory.
module Atom : sig ... endmodule Model : sig ... endModels
module Unknown : sig ... endmodule Proof : sig ... endtype proof = Proof.tval stats : t -> Sidekick_util.Stat.tval tst : t -> T.Term.stateval ty_st : t -> T.Ty.stateval create : ?stat:Sidekick_util.Stat.t -> ?size:[ `Big | `Tiny | `Small ] -> ?store_proof:bool -> theories:theory list -> T.Term.state -> T.Ty.state -> unit -> tCreate a new solver.
It needs a term state and a type state to manipulate terms and types. All terms and types interacting with this solver will need to come from these exact states.
if true, proofs from the SAT solver and theories are retained and potentially accessible after solve returns UNSAT.
influences the size of initial allocations.
theories to load from the start. Other theories can be added using add_theory.
val add_theory : t -> theory -> unitAdd a theory to the solver. This should be called before any call to solve or to add_clause and the likes (otherwise the theory will have a partial view of the problem).
val add_theory_l : t -> theory list -> unitval mk_atom_lit : t -> lit -> Atom.tTurn a literal into a SAT solver literal.
val mk_atom_t : t -> ?sign:bool -> term -> Atom.tTurn a boolean term, with a sign, into a SAT solver's literal.
val add_clause : t -> Atom.t Sidekick_util.IArray.t -> unitadd_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 -> Atom.t list -> unitSame as add_clause but with a list of atoms.
type res = | Sat of Model.t | Satisfiable | ||||
| Unsat of {
} | Unsatisfiable | ||||
| Unknown of Unknown.t | Unknown, obtained after a timeout, memory limit, etc. |
Result of solving for the current set of clauses
val solve : ?on_exit:(unit -> unit) list -> ?check:bool -> ?on_progress:(t -> unit) -> assumptions:Atom.t list -> t -> ressolve s checks the satisfiability of the clauses added so far to s.
if true, the model is checked before returning.
called regularly during solving.
a set of atoms held to be true. The unsat core, if any, will be a subset of assumptions.
functions to be run before this returns
val pp_stats : t CCFormat.printerPrint some statistics. What it prints exactly is unspecified.
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
val push_level : t -> unitPush backtracking level
val pop_levels : t -> int -> unitPop backtracking levels, restoring the theory to its former state
S.THEORYTheories are abstracted over the concrete implementation of the solver, so they can work with any implementation.
Typically a theory should be a functor taking an argument containing a SOLVER_INTERNAL or even a full SOLVER, and some additional views on terms, literals, etc. that are specific to the theory (e.g. to map terms to linear expressions). The theory can then be instantiated on any kind of solver for any term representation that also satisfies the additional theory-specific requirements. Instantiated theories (ie values of type Sidekick_core.SOLVER.theory) can be added to the solver.
val create_and_setup : Solver_internal.t -> tInstantiate the theory's state for the given (internal) solver, register callbacks, create keys, etc.
Called once for every solver this theory is added to.
val push_level : t -> unitPush backtracking level. When the corresponding pop is called, the theory's state should be restored to a state equivalent to what it was just before push_level.
it does not have to be exactly the same state, it just needs to be equivalent.
val pop_levels : t -> int -> unitpop_levels theory n pops n backtracking levels, restoring theory to its state before calling push_level n times.