123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504(*
MSAT is free software, using the Apache license, see file LICENSE
Copyright 2016 Guillaume Bury
Copyright 2016 Simon Cruanes
*)(** Interface for Solvers
This modules defines the safe external interface for solvers.
Solvers that implements this interface can be obtained using the [Make]
functor in {!Solver} or {!Mcsolver}.
*)type'aprinter=Format.formatter->'a->unittype('term,'form,'value)sat_state={eval:'form->bool;(** Returns the valuation of a formula in the current state
of the sat solver.
@raise UndecidedLit if the literal is not decided *)eval_level:'form->bool*int;(** Return the current assignement of the literals, as well as its
decision level. If the level is 0, then it is necessary for
the atom to have this value; otherwise it is due to choices
that can potentially be backtracked.
@raise UndecidedLit if the literal is not decided *)iter_trail:('form->unit)->('term->unit)->unit;(** Iter thorugh the formulas and terms in order of decision/propagation
(starting from the first propagation, to the last propagation). *)model:unit->('term*'value)list;(** Returns the model found if the formula is satisfiable. *)}(** The type of values returned when the solver reaches a SAT state. *)type('atom,'clause,'proof)unsat_state={unsat_conflict:unit->'clause;(** Returns the unsat clause found at the toplevel *)get_proof:unit->'proof;(** returns a persistent proof of the empty clause from the Unsat result. *)unsat_assumptions:unit->'atomlist;(** Subset of assumptions responsible for "unsat" *)}(** The type of values returned when the solver reaches an UNSAT state. *)type'clauseexport={hyps:'clauseVec.t;history:'clauseVec.t;}(** Export internal state *)typenegated=|Negated(** changed sign *)|Same_sign(** kept sign *)(** This type is used during the normalisation of formulas.
See {!val:Expr_intf.S.norm} for more details. *)type'termeval_res=|Unknown(** The given formula does not have an evaluation *)|Valuedofbool*('termlist)(** The given formula can be evaluated to the given bool.
The list of terms to give is the list of terms that
were effectively used for the evaluation. *)(** The type of evaluation results for a given formula.
For instance, let's suppose we want to evaluate the formula [x * y = 0], the
following result are correct:
- [Unknown] if neither [x] nor [y] are assigned to a value
- [Valued (true, [x])] if [x] is assigned to [0]
- [Valued (true, [y])] if [y] is assigned to [0]
- [Valued (false, [x; y])] if [x] and [y] are assigned to 1 (or any non-zero number)
*)type('term,'formula,'value)assumption=|Litof'formula(** The given formula is asserted true by the solver *)|Assignof'term*'value(** The term is assigned to the value *)(** Asusmptions made by the core SAT solver. *)type('term,'formula,'proof)reason=|Evalof'termlist(** The formula can be evalutaed using the terms in the list *)|Consequenceof(unit->'formulalist*'proof)(** [Consequence (l, p)] means that the formulas in [l] imply the propagated
formula [f]. The proof should be a proof of the clause "[l] implies [f]".
invariant: in [Consequence (fun () -> l,p)], all elements of [l] must be true in
the current trail.
{b note} on lazyiness: the justification is suspended (using [unit -> …])
to avoid potentially costly computations that might never be used
if this literal is backtracked without participating in a conflict.
Therefore the function that produces [(l,p)] needs only be safe in
trails (partial models) that are conservative extensions of the current
trail.
If the theory isn't robust w.r.t. extensions of the trail (e.g. if
its internal state undergoes significant changes),
it can be easier to produce the explanation eagerly when
propagating, and then use [Consequence (fun () -> expl, proof)] with
the already produced [(expl,proof)] tuple.
*)(** The type of reasons for propagations of a formula [f]. *)typelbool=L_true|L_false|L_undefined(** Valuation of an atom *)(* TODO: find a way to use atoms instead of formulas here *)type('term,'formula,'value,'proof)acts={acts_iter_assumptions:(('term,'formula,'value)assumption->unit)->unit;(** Traverse the new assumptions on the boolean trail. *)acts_eval_lit:'formula->lbool;(** Obtain current value of the given literal *)acts_mk_lit:'formula->unit;(** Map the given formula to a literal, which will be decided by the
SAT solver. *)acts_mk_term:'term->unit;(** Map the given term (and its subterms) to decision variables,
for the MCSAT solver to decide. *)acts_add_clause:?keep:bool->'formulalist->'proof->unit;(** Add a clause to the solver.
@param keep if true, the clause will be kept by the solver.
Otherwise the solver is allowed to GC the clause and propose this
partial model again.
*)acts_raise_conflict:'b.'formulalist->'proof->'b;(** Raise a conflict, yielding control back to the solver.
The list of atoms must be a valid theory lemma that is false in the
current trail. *)acts_propagate:'formula->('term,'formula,'proof)reason->unit;(** Propagate a formula, i.e. the theory can evaluate the formula to be true
(see the definition of {!type:eval_res} *)}(** The type for a slice of assertions to assume/propagate in the theory. *)type('a,'b)gadt_eq=GADT_EQ:('a,'a)gadt_eqtypevoid=(unit,bool)gadt_eq(** A provably empty type *)exceptionNo_proofmoduletypeFORMULA=sig(** formulas *)typet(** The type of atomic formulas over terms. *)valequal:t->t->bool(** Equality over formulas. *)valhash:t->int(** Hashing function for formulas. Should be such that two formulas equal according
to {!val:Expr_intf.S.equal} have the same hash. *)valpp:tprinter(** Printing function used among other thing for debugging. *)valneg:t->t(** Formula negation *)valnorm:t->t*negated(** Returns a 'normalized' form of the formula, possibly negated
(in which case return [Negated]).
[norm] must be so that [a] and [neg a] normalise to the same formula,
but one returns [Negated] and the other [Same_sign]. *)end(** Formulas and Terms required for mcSAT *)moduletypeEXPR=sigtypeproof(** An abstract type for proofs *)moduleTerm:sigtypet(** The type of terms *)valequal:t->t->bool(** Equality over terms. *)valhash:t->int(** Hashing function for terms. Should be such that two terms equal according
to {!equal} have the same hash. *)valpp:tprinter(** Printing function used among other for debugging. *)endmoduleValue:sigtypet(** The type of semantic values (domain elements) *)valpp:tprinter(** Printing function used among other for debugging. *)endmoduleFormula:FORMULAend(** Signature for theories to be given to the CDCL(T) solver *)moduletypePLUGIN_CDCL_T=sigtypet(** The plugin state itself *)moduleFormula:FORMULAtypeproofvalpush_level:t->unit(** Create a new backtrack level *)valpop_levels:t->int->unit(** Pop [n] levels of the theory *)valpartial_check:t->(void,Formula.t,void,proof)acts->unit(** Assume the formulas in the slice, possibly using the [slice]
to push new formulas to be propagated or to raising a conflict or to add
new lemmas. *)valfinal_check:t->(void,Formula.t,void,proof)acts->unit(** Called at the end of the search in case a model has been found.
If no new clause is pushed, then proof search ends and "sat" is returned;
if lemmas are added, search is resumed;
if a conflict clause is added, search backtracks and then resumes. *)end(** Signature for theories to be given to the Model Constructing Solver. *)moduletypePLUGIN_MCSAT=sigtypet(** The plugin state itself *)includeEXPRvalpush_level:t->unit(** Create a new backtrack level *)valpop_levels:t->int->unit(** Pop [n] levels of the theory *)valpartial_check:t->(Term.t,Formula.t,Value.t,proof)acts->unit(** Assume the formulas in the slice, possibly using the [slice]
to push new formulas to be propagated or to raising a conflict or to add
new lemmas. *)valfinal_check:t->(Term.t,Formula.t,Value.t,proof)acts->unit(** Called at the end of the search in case a model has been found.
If no new clause is pushed, then proof search ends and "sat" is returned;
if lemmas are added, search is resumed;
if a conflict clause is added, search backtracks and then resumes. *)valassign:t->Term.t->Value.t(** Returns an assignment value for the given term. *)valiter_assignable:t->(Term.t->unit)->Formula.t->unit(** An iterator over the subTerm.ts of a Formula.t that should be assigned a value (usually the poure subTerm.ts) *)valeval:t->Formula.t->Term.teval_res(** Returns the evaluation of the Formula.t in the current assignment *)end(** Signature for pure SAT solvers *)moduletypePLUGIN_SAT=sigmoduleFormula:FORMULAtypeproofendmoduletypePROOF=sig(** Signature for a module handling proof by resolution from sat solving traces *)(** {3 Type declarations} *)exceptionResolution_errorofstring(** Raised when resolution failed. *)typeformulatypeatomtypelemmatypeclause(** Abstract types for atoms, clauses and theory-specific lemmas *)typet(** Lazy type for proof trees. Proofs are persistent objects, and can be
extended to proof nodes using functions defined later. *)andproof_node={conclusion:clause;(** The conclusion of the proof *)step:step;(** The reasoning step used to prove the conclusion *)}(** A proof can be expanded into a proof node, which show the first step of the proof. *)(** The type of reasoning steps allowed in a proof. *)andstep=|Hypothesisoflemma(** The conclusion is a user-provided hypothesis *)|Assumption(** The conclusion has been locally assumed by the user *)|Lemmaoflemma(** The conclusion is a tautology provided by the theory, with associated proof *)|Duplicateoft*atomlist(** The conclusion is obtained by eliminating multiple occurences of the atom in
the conclusion of the provided proof. *)|Hyper_resofhyper_res_stepandhyper_res_step={hr_init:t;hr_steps:(atom*t)list;(* list of pivot+clause to resolve against [init] *)}(** {3 Proof building functions} *)valprove:clause->t(** Given a clause, return a proof of that clause.
@raise Resolution_error if it does not succeed. *)valprove_unsat:clause->t(** Given a conflict clause [c], returns a proof of the empty clause.
@raise Resolution_error if it does not succeed. *)valprove_atom:atom->toption(** Given an atom [a], returns a proof of the clause [[a]] if [a] is true at level 0 *)valres_of_hyper_res:hyper_res_step->t*t*atom(** Turn an hyper resolution step into a resolution step.
The conclusion can be deduced by performing a resolution between the conclusions
of the two given proofs.
The atom on which to perform the resolution is also given. *)(** {3 Proof Nodes} *)valparents:step->tlist(** Returns the parents of a proof node. *)valis_leaf:step->bool(** Returns wether the the proof node is a leaf, i.e. an hypothesis,
an assumption, or a lemma.
[true] if and only if {!parents} returns the empty list. *)valexpl:step->string(** Returns a short string description for the proof step; for instance
["hypothesis"] for a [Hypothesis]
(it currently returns the variant name in lowercase). *)(** {3 Proof Manipulation} *)valexpand:t->proof_node(** Return the proof step at the root of a given proof. *)valconclusion:t->clause(** What is proved at the root of the clause *)valfold:('a->proof_node->'a)->'a->t->'a(** [fold f acc p], fold [f] over the proof [p] and all its node. It is guaranteed that
[f] is executed exactly once on each proof node in the tree, and that the execution of
[f] on a proof node happens after the execution on the parents of the nodes. *)valunsat_core:t->clauselist(** Returns the unsat_core of the given proof, i.e the lists of conclusions
of all leafs of the proof.
More efficient than using the [fold] function since it has
access to the internal representation of proofs *)(** {3 Misc} *)valcheck_empty_conclusion:t->unit(** Check that the proof's conclusion is the empty clause,
@raise Resolution_error otherwise *)valcheck:t->unit(** Check the contents of a proof. Mainly for internal use. *)moduleTbl:Hashtbl.Swithtypekey=tend(** The external interface implemented by safe solvers, such as the one
created by the {!Solver.Make} and {!Mcsolver.Make} functors. *)moduletypeS=sig(** {2 Internal modules}
These are the internal modules used, you should probably not use them
if you're not familiar with the internals of mSAT. *)includeEXPRtypeterm=Term.t(** user terms *)typeformula=Formula.t(** user formulas *)typeatom(** The type of atoms given by the module argument for formulas.
An atom is a user-defined atomic formula whose truth value is
picked by Msat. *)typeclausetypetheorytypelemma(** A theory lemma or an input axiom *)typesolvermoduleAtom:sigtypet=atomvalequal:t->t->boolvalcompare:t->t->intvalhash:t->intvalneg:t->tvalsign:t->boolvalabs:t->tvalformula:t->formulavalpp:tprinterendmoduleClause:sigtypet=clausevalatoms:t->atomarrayvalatoms_l:t->atomlistvalequal:t->t->boolvalname:t->stringvalpp:tprintermoduleTbl:Hashtbl.Swithtypekey=tendmoduleProof:PROOFwithtypeclause=clauseandtypeatom=atomandtypeformula=formulaandtypelemma=lemmaandtypet=proof(** A module to manipulate proofs. *)typet=solver(** Main solver type, containing all state for solving. *)valcreate:?store_proof:bool->?size:[`Tiny|`Small|`Big]->theory->t(** Create new solver
@param theory the theory
@param store_proof if true, stores proof (default [true]). Otherwise
the functions that return proofs will fail with [No_proof]
@param size the initial size of internal data structures. The bigger,
the faster, but also the more RAM it uses. *)valtheory:t->theory(** Access the theory state *)(** {2 Types} *)(** Result type for the solver *)typeres=|Satof(term,formula,Value.t)sat_state(** Returned when the solver reaches SAT, with a model *)|Unsatof(atom,clause,Proof.t)unsat_state(** Returned when the solver reaches UNSAT, with a proof *)exceptionUndecidedLit(** Exception raised by the evaluating functions when a literal
has not yet been assigned a value. *)(** {2 Base operations} *)valassume:t->formulalistlist->lemma->unit(** Add the list of clauses to the current set of assumptions.
Modifies the sat solver state in place. *)valadd_clause:t->atomlist->lemma->unit(** Lower level addition of clauses *)valadd_clause_a:t->atomarray->lemma->unit(** Lower level addition of clauses *)valsolve:?assumptions:atomlist->t->res(** Try and solves the current set of clauses.
@param assumptions additional atomic assumptions to be temporarily added.
The assumptions are just used for this call to [solve], they are
not saved in the solver's state. *)valmake_term:t->term->unit(** Add a new term (i.e. decision variable) to the solver. This term will
be decided on at some point during solving, wether it appears
in clauses or not. *)valmake_atom:t->formula->atom(** Add a new atom (i.e propositional formula) to the solver.
This formula will be decided on at some point during solving,
wether it appears in clauses or not. *)valtrue_at_level0:t->atom->bool(** [true_at_level0 a] returns [true] if [a] was proved at level0, i.e.
it must hold in all models *)valeval_atom:t->atom->lbool(** Evaluate atom in current state *)valexport:t->clauseexportend