wip: migrate to msat 0.8

This commit is contained in:
Simon Cruanes 2019-01-28 21:09:57 -06:00
parent 4fadbeb04d
commit 27d1841f6b
66 changed files with 359 additions and 5686 deletions

View file

@ -1,27 +0,0 @@
(*
MSAT is free software, using the Apache license, see file LICENSE
Copyright 2014 Guillaume Bury
Copyright 2014 Simon Cruanes
*)
(** Backend interface
This modules defines the interface of the modules providing
export of proofs.
*)
module type S = sig
(** Proof exporting
Currently, exporting a proof means printing it into a file
according to the conventions of a given format.
*)
type t
(** The type of proofs. *)
val print : Format.formatter -> t -> unit
(** A function for printing proofs in the desired format. *)
end

View file

@ -1,50 +0,0 @@
(*
MSAT is free software, using the Apache license, see file LICENSE
Copyright 2014 Guillaume Bury
Copyright 2014 Simon Cruanes
*)
module type S = sig
type st
type clause
(** The type of clauses *)
val export :
st ->
Format.formatter ->
clauses:clause Vec.t ->
unit
end
module Make(St : Sidekick_sat.S) = struct
type st = St.t
(* Dimacs & iCNF export *)
let export_vec name fmt vec =
Format.fprintf fmt "c %s@,%a@," name (Vec.print ~sep:"" St.Clause.pp_dimacs) vec
let export_assumption fmt vec =
Format.fprintf fmt "c Local assumptions@,a %a@," St.Clause.pp_dimacs vec
let map_filter_learnt c =
if St.Clause.is_learnt c then Some c else None
let filter_vec learnt =
let lemmas = Vec.make (Vec.size learnt) St.Clause.dummy in
Vec.iter (fun c ->
match map_filter_learnt c with
| None -> ()
| Some d -> Vec.push lemmas d
) learnt;
lemmas
let export st fmt ~clauses : unit =
(* Number of atoms and clauses *)
let n = St.n_vars st in
let m = Vec.size clauses in
Format.fprintf fmt
"@[<v>p cnf %d %d@,%a@]@." n m
(export_vec "Clauses") clauses
end

View file

@ -1,32 +0,0 @@
(*
MSAT is free software, using the Apache license, see file LICENSE
Copyright 2014 Guillaume Bury
Copyright 2014 Simon Cruanes
*)
(** Dimacs backend for problems
This module provides functiosn to export problems to the dimacs and
iCNF formats.
*)
module type S = sig
type st
type clause
(** The type of clauses *)
val export :
st ->
Format.formatter ->
clauses:clause Vec.t ->
unit
(** Export the given clause vectors to the dimacs format.
The arguments should be transmitted directly from the corresponding
function of the {Internal} module. *)
end
module Make(St: Sidekick_sat.S) : S with type clause := St.clause and type st = St.t
(** Functor to create a module for exporting probems to the dimacs (& iCNF) formats. *)

View file

@ -1,150 +0,0 @@
(*
MSAT is free software, using the Apache license, see file LICENSE
Copyright 2014 Guillaume Bury
Copyright 2014 Simon Cruanes
*)
(** Output interface for the backend *)
module type S = Backend_intf.S
(** Input module for the backend *)
module type Arg = sig
type atom
type hyp
type lemma
type assumption
(** Types for hypotheses, lemmas, and assumptions. *)
val print_atom : Format.formatter -> atom -> unit
(** Printing function for atoms *)
val hyp_info : hyp -> string * string option * (Format.formatter -> unit -> unit) list
val lemma_info : lemma -> string * string option * (Format.formatter -> unit -> unit) list
val assumption_info : assumption -> string * string option * (Format.formatter -> unit -> unit) list
(** Functions to return information about hypotheses and lemmas *)
end
module Default(S : Sidekick_sat.S) = struct
module Atom = S.Atom
module Clause = S.Clause
module P = S.Proof
let print_atom = Atom.pp
let hyp_info c =
"hypothesis", Some "LIGHTBLUE",
[ fun fmt () -> Format.fprintf fmt "%s" @@ Clause.name c]
let lemma_info c =
"lemma", Some "BLUE",
[ fun fmt () -> Format.fprintf fmt "%s" @@ Clause.name c]
let assumption_info c =
"assumption", Some "PURPLE",
[ fun fmt () -> Format.fprintf fmt "%s" @@ Clause.name c]
end
(** Functor to provide dot printing *)
module Make(S : Sidekick_sat.S)(A : Arg with type atom := S.atom
and type hyp := S.clause
and type lemma := S.clause
and type assumption := S.clause) = struct
module Atom = S.Atom
module Clause = S.Clause
module P = S.Proof
let node_id n = Clause.name n.P.conclusion
let res_node_id n = (node_id n) ^ "_res"
let proof_id p = node_id (P.expand p)
let print_clause fmt c =
let v = Clause.atoms c in
if Array.length v = 0 then
Format.fprintf fmt ""
else (
let n = Array.length v in
for i = 0 to n - 1 do
Format.fprintf fmt "%a" A.print_atom (Array.get v i);
if i < n - 1 then
Format.fprintf fmt ", "
done
)
let print_edge fmt i j =
Format.fprintf fmt "%s -> %s;@\n" j i
let print_edges fmt n =
match n.P.step with
| P.Resolution (p1, p2, _) ->
print_edge fmt (res_node_id n) (proof_id p1);
print_edge fmt (res_node_id n) (proof_id p2)
| _ -> ()
let table_options fmt color =
Format.fprintf fmt "BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" BGCOLOR=\"%s\"" color
let table fmt (c, rule, color, l) =
Format.fprintf fmt "<TR><TD colspan=\"2\">%a</TD></TR>" print_clause c;
match l with
| [] ->
Format.fprintf fmt "<TR><TD BGCOLOR=\"%s\" colspan=\"2\">%s</TD></TR>" color rule
| f :: r ->
Format.fprintf fmt "<TR><TD BGCOLOR=\"%s\" rowspan=\"%d\">%s</TD><TD>%a</TD></TR>"
color (List.length l) rule f ();
List.iter (fun f -> Format.fprintf fmt "<TR><TD>%a</TD></TR>" f ()) r
let print_dot_node fmt id color c rule rule_color l =
Format.fprintf fmt "%s [shape=plaintext, label=<<TABLE %a>%a</TABLE>>];@\n"
id table_options color table (c, rule, rule_color, l)
let print_dot_res_node fmt id a =
Format.fprintf fmt "%s [label=<%a>];@\n" id A.print_atom a
let ttify f c = fun fmt () -> f fmt c
let print_contents fmt n =
match n.P.step with
(* Leafs of the proof tree *)
| P.Hypothesis ->
let rule, color, l = A.hyp_info P.(n.conclusion) in
let color = match color with None -> "LIGHTBLUE" | Some c -> c in
print_dot_node fmt (node_id n) "LIGHTBLUE" P.(n.conclusion) rule color l
| P.Assumption ->
let rule, color, l = A.assumption_info P.(n.conclusion) in
let color = match color with None -> "LIGHTBLUE" | Some c -> c in
print_dot_node fmt (node_id n) "LIGHTBLUE" P.(n.conclusion) rule color l
| P.Lemma _ ->
let rule, color, l = A.lemma_info P.(n.conclusion) in
let color = match color with None -> "YELLOW" | Some c -> c in
print_dot_node fmt (node_id n) "LIGHTBLUE" P.(n.conclusion) rule color l
(* Tree nodes *)
| P.Duplicate (p, l) ->
print_dot_node fmt (node_id n) "GREY" P.(n.conclusion) "Duplicate" "GREY"
((fun fmt () -> (Format.fprintf fmt "%s" (node_id n))) ::
List.map (ttify A.print_atom) l);
print_edge fmt (node_id n) (node_id (P.expand p))
| P.Resolution (_, _, a) ->
print_dot_node fmt (node_id n) "GREY" P.(n.conclusion) "Resolution" "GREY"
[(fun fmt () -> (Format.fprintf fmt "%s" (node_id n)))];
print_dot_res_node fmt (res_node_id n) a;
print_edge fmt (node_id n) (res_node_id n)
let print_node fmt n =
print_contents fmt n;
print_edges fmt n
let print fmt p =
Format.fprintf fmt "digraph proof {@\n";
P.fold (fun () -> print_node fmt) () p;
Format.fprintf fmt "}@."
end
module Simple(S : Sidekick_sat.S) = Make(S)(Default(S))

View file

@ -1,67 +0,0 @@
(*
MSAT is free software, using the Apache license, see file LICENSE
Copyright 2014 Guillaume Bury
Copyright 2014 Simon Cruanes
*)
(** Dot backend for proofs
This module provides functions to export proofs into the dot graph format.
Graphs in dot format can be used to generates images using the graphviz tool.
*)
module type S = Backend_intf.S
(** Interface for exporting proofs. *)
module type Arg = sig
(** Term printing for DOT
This module defines what functions are required in order to export
a proof to the DOT format.
*)
type atom
(** The type of atomic formuals *)
type hyp
type lemma
type assumption
(** The type of theory-specifi proofs (also called lemmas). *)
val print_atom : Format.formatter -> atom -> unit
(** Print the contents of the given atomic formulas.
WARNING: this function should take care to escape and/or not output special
reserved characters for the dot format (such as quotes and so on). *)
val hyp_info : hyp -> string * string option * (Format.formatter -> unit -> unit) list
val lemma_info : lemma -> string * string option * (Format.formatter -> unit -> unit) list
val assumption_info : assumption -> string * string option * (Format.formatter -> unit -> unit) list
(** Generate some information about the leafs of the proof tree. Currently this backend
print each lemma/assumption/hypothesis as a single leaf of the proof tree.
These function should return a triplet [(rule, color, l)], such that:
- [rule] is a name for the proof (arbitrary, does not need to be unique, but
should rather be descriptive)
- [color] is a color name (optional) understood by DOT
- [l] is a list of printers that will be called to print some additional information
*)
end
module Default(S : Sidekick_sat.S) : Arg with type atom := S.atom
and type hyp := S.clause
and type lemma := S.clause
and type assumption := S.clause
(** Provides a reasonnable default to instantiate the [Make] functor, assuming
the original printing functions are compatible with DOT html labels. *)
module Make(S : Sidekick_sat.S)(A : Arg with type atom := S.atom
and type hyp := S.clause
and type lemma := S.clause
and type assumption := S.clause) : S with type t := S.proof
(** Functor for making a module to export proofs to the DOT format. *)
module Simple(S : Sidekick_sat.S) : S with type t := S.proof
(** Functor for making a module to export proofs to the DOT format.
The substitution of the hyp type is non-destructive due to a restriction
of destructive substitutions on earlier versions of ocaml. *)

View file

@ -1,12 +0,0 @@
(library
(name Sidekick_backend)
(public_name sidekick.backend)
(synopsis "proof backends for Sidekick")
(libraries sidekick.sat)
(flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string
-open Sidekick_sat -open Sidekick_util)
(ocamlopt_flags :standard -O3 -bin-annot
-unbox-closures -unbox-closures-factor 20)
)

View file

@ -4,8 +4,7 @@
(name main)
(public_name sidekick)
(package sidekick)
(libraries containers sequence result
sidekick.sat sidekick.smt sidekick.smtlib sidekick.backend
(libraries containers sequence result msat sidekick.smt sidekick.smtlib
sidekick.dimacs)
(flags :standard -w +a-4-42-44-48-50-58-32-60@8
-safe-string -color always -open Sidekick_util)

View file

@ -1,68 +0,0 @@
# Equality in McSat
## Basics
McSat theories have different interfaces and requirements than classic SMT theories.
The good point of these additional requirements is that it becomes easier to combine
theories, since the assignments allow theories to exchange information about
the equality of terms. In a context where there are multiple theories, they each have
to handle the following operations:
- return an assignment value for a given term
- receive a new assignment value for a term (the assignment may, or not, have been
done by another theory)
- receive a new assertion (i.e an atomic formula asserted to be true by the sat solver)
With assignments, the reason for a theory returning UNSAT now becomes when
some term has no potential assignment value because of past assignments
and assertions, (or in some cases, an assignments decided by a theory A is
incompatible with the possible assignments of the same term according to theory B).
When returning UNSAT, the theory must, as usual return a conflict clause.
The conflict clause must be a tautology, and such that every atomic proposition
in it must evaluate to false using assignments.
## Equality of uninterpreted types
To handle equality on arbitrary values efficiently, we maintain a simple union-find
of known equalities (*NOT* computing congruence closure, only the reflexive-transitive
closure of the equalities), where each class can be tagged with an optional assignment.
When receiving a new assertions by the sat, we update the union-find. When the theory is
asked for an assignment value for a term, we lookup its class. If it is tagged, we return
the tagged value. Else, we take an arbitrary representative $x$ of the class and return it.
When a new assignment $t \mapsto v$ is propagated by the sat solver, there are three cases:
- the class of $t$ if not tagged, we then tag it with $t \mapsto v$ and continue
- the class of $t$ is already tagged with $\_ mapsto v$, we do nothing
- the class of $t$ is tagged with a $t' \mapsto v'$, we raise unsat,
using the explanation of why $t$ and $t'$ are in the same class and the equality
$t' = v'$
Additionally, in order to handle disequalities, each class contains the list of classes
it must be distinct from. There are then two possible reasons to raise unsat, when
a disequality $x <> y$ is invalidated by assignemnts or later equalities:
- when two classes that should be distinct are merged
- when two classes that should be distinct are assigned to the same value
in both cases, we use the union-find structure to get the explanation of why $x$ and $y$
must now be equal (since their class have been merged), and use that to create the
conflict clause.
## Uninterpreted functions
The uninterpreted function theory is much simpler, it doesn't return any assignemnt values
(the equality theory does it already), but rather check that the assignemnts so far are
coherent with the semantics of uninterpreted functions.
So for each function asignment $f(x1,...,xn) \mapsto v$, we wait for all the arguments to
also be assigned to values $x1 \mapsto v1$, etc... $xn \mapsto vn$, and we add the binding
$(f,v1,...,vn) \mapsto (v,x1,...,xn)$ in a map (meaning that in the model $f$ applied to
$v1,...,vn$ is equal to $v$). If a binding $(f,v1,...,vn) \mapsto (v',y1,...,yn)$ already
exists (with $v' <> v$), then we raise UNSAT, with the explanation:
$( x1=y1 /\ ... /\ xn = yn) => f(x1,...,xn) = f(y1,...,yn)$

View file

@ -1,9 +0,0 @@
(executable
(name main_test)
(libraries sidekick_sat sidekick.backend sidekick.th_sat dolmen)
(flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string -open Sidekick_sat)
(ocamlopt_flags :standard -O3 -bin-annot
-unbox-closures -unbox-closures-factor 20)
)

View file

@ -1,251 +0,0 @@
(*
MSAT is free software, using the Apache license, see file LICENSE
Copyright 2014 Guillaume Bury
Copyright 2014 Simon Cruanes
*)
exception Incorrect_model
exception Out_of_time
exception Out_of_space
let file = ref ""
let p_cnf = ref false
let p_check = ref false
let p_dot_proof = ref ""
let p_proof_print = ref false
let time_limit = ref 300.
let size_limit = ref 1000_000_000.
module P =
Dolmen.Logic.Make(Dolmen.ParseLocation)
(Dolmen.Id)(Dolmen.Term)(Dolmen.Statement)
module type S = sig
val do_task : Dolmen.Statement.t -> unit
end
module Make
(S : CDCL.S)
(Th : sig
include CDCL.Theory_intf.S with type t = S.theory
end)
(T : sig
include Type.S with type atom := S.atom
val create : Th.t -> t
end)
: sig
val do_task : Dolmen.Statement.t -> unit
end = struct
module D = CDCL_backend.Dot.Make(S.Proof)(CDCL_backend.Dot.Default(S.Proof))
let hyps = ref []
let st = S.create ~size:`Big ()
let th = S.theory st
let t_st = T.create th
let check_model (CDCL.Sat_state sat) =
let check_clause c =
let l = List.map (function a ->
Log.debugf 99
(fun k -> k "Checking value of %a" S.Formula.pp a);
sat.eval a) c in
List.exists (fun x -> x) l
in
let l = List.map check_clause !hyps in
List.for_all (fun x -> x) l
let prove ~assumptions () =
let res = S.solve st ~assumptions () in
let t = Sys.time () in
begin match res with
| S.Sat state ->
if !p_check then
if not (check_model state) then
raise Incorrect_model;
let t' = Sys.time () -. t in
Format.printf "Sat (%f/%f)@." t t'
| S.Unsat (CDCL.Unsat_state us) ->
if !p_check then begin
let p = us.get_proof () in
S.Proof.check p;
if !p_dot_proof <> "" then begin
let fmt = Format.formatter_of_out_channel (open_out !p_dot_proof) in
D.print fmt p
end
end;
let t' = Sys.time () -. t in
Format.printf "Unsat (%f/%f)@." t t'
end
let do_task s : unit =
match s.Dolmen.Statement.descr with
| Dolmen.Statement.Def (id, t) -> T.def t_st id t
| Dolmen.Statement.Decl (id, t) -> T.decl t_st id t
| Dolmen.Statement.Clause l ->
let cnf = T.antecedent t_st (Dolmen.Term.or_ l) in
hyps := cnf @ !hyps;
S.assume st cnf
| Dolmen.Statement.Consequent t ->
let cnf = T.consequent t_st t in
hyps := cnf @ !hyps;
S.assume st cnf
| Dolmen.Statement.Antecedent t ->
let cnf = T.antecedent t_st t in
hyps := cnf @ !hyps;
S.assume st cnf
| Dolmen.Statement.Pack [
{ Dolmen.Statement.descr = Dolmen.Statement.Push 1;_ };
{ Dolmen.Statement.descr = Dolmen.Statement.Antecedent f;_ };
{ Dolmen.Statement.descr = Dolmen.Statement.Prove;_ };
{ Dolmen.Statement.descr = Dolmen.Statement.Pop 1;_ };
] ->
let assumptions = T.assumptions t_st f in
prove ~assumptions ()
| Dolmen.Statement.Prove ->
prove ~assumptions:[] ()
| Dolmen.Statement.Set_info _
| Dolmen.Statement.Set_logic _ -> ()
| Dolmen.Statement.Exit -> exit 0
| _ ->
Format.printf "Command not supported:@\n%a@."
Dolmen.Statement.print s
end
module Sat = Make(CDCL_sat)(CDCL_sat.Th)(Type_sat)
(*
module Smt = Make(Minismt_smt)(CDCL_sat.Th)(Minismt_smt.Type)
*)
let solver = ref (module Sat : S)
let solver_list = [
"sat", (module Sat : S);
(* FIXME
"smt", (module Smt : S);
*)
]
let error_msg opt arg l =
Format.fprintf Format.str_formatter "'%s' is not a valid argument for '%s', valid arguments are : %a"
arg opt (fun fmt -> List.iter (fun (s, _) -> Format.fprintf fmt "%s, " s)) l;
Format.flush_str_formatter ()
let set_flag opt arg flag l =
try
flag := List.assoc arg l
with Not_found ->
invalid_arg (error_msg opt arg l)
let set_solver s = set_flag "Solver" s solver solver_list
(* Arguments parsing *)
let int_arg r arg =
let l = String.length arg in
let multiplier m =
let arg1 = String.sub arg 0 (l-1) in
r := m *. (float_of_string arg1)
in
if l = 0 then raise (Arg.Bad "bad numeric argument")
else
try
match arg.[l-1] with
| 'k' -> multiplier 1e3
| 'M' -> multiplier 1e6
| 'G' -> multiplier 1e9
| 'T' -> multiplier 1e12
| 's' -> multiplier 1.
| 'm' -> multiplier 60.
| 'h' -> multiplier 3600.
| 'd' -> multiplier 86400.
| '0'..'9' -> r := float_of_string arg
| _ -> raise (Arg.Bad "bad numeric argument")
with Failure _ -> raise (Arg.Bad "bad numeric argument")
let setup_gc_stat () =
at_exit (fun () ->
Gc.print_stat stdout;
)
let input_file = fun s -> file := s
let usage = "Usage : main [options] <file>"
let argspec = Arg.align [
"-bt", Arg.Unit (fun () -> Printexc.record_backtrace true),
" Enable stack traces";
"-cnf", Arg.Set p_cnf,
" Prints the cnf used.";
"-check", Arg.Set p_check,
" Build, check and print the proof (if output is set), if unsat";
"-dot", Arg.Set_string p_dot_proof,
" If provided, print the dot proof in the given file";
"-gc", Arg.Unit setup_gc_stat,
" Outputs statistics about the GC";
"-s", Arg.String set_solver,
"{sat,smt,mcsat} Sets the solver to use (default smt)";
"-size", Arg.String (int_arg size_limit),
"<s>[kMGT] Sets the size limit for the sat solver";
"-time", Arg.String (int_arg time_limit),
"<t>[smhd] Sets the time limit for the sat solver";
"-v", Arg.Int (fun i -> Log.set_debug i),
"<lvl> Sets the debug verbose level";
]
(* Limits alarm *)
let check () =
let t = Sys.time () in
let heap_size = (Gc.quick_stat ()).Gc.heap_words in
let s = float heap_size *. float Sys.word_size /. 8. in
if t > !time_limit then
raise Out_of_time
else if s > !size_limit then
raise Out_of_space
let main () =
(* Administrative duties *)
Arg.parse argspec input_file usage;
if !file = "" then (
Arg.usage argspec usage;
exit 2
);
let al = Gc.create_alarm check in
(* Interesting stuff happening *)
let lang, input = P.parse_file !file in
let module S = (val !solver : S) in
List.iter S.do_task input;
(* Small hack for dimacs, which do not output a "Prove" statement *)
begin match lang with
| P.Dimacs -> S.do_task @@ Dolmen.Statement.check_sat ()
| _ -> ()
end;
Gc.delete_alarm al;
()
let () =
try
main ()
with
| Out_of_time ->
Format.printf "Timeout@.";
exit 2
| Out_of_space ->
Format.printf "Spaceout@.";
exit 3
| Incorrect_model ->
Format.printf "Internal error : incorrect *sat* model@.";
exit 4
(* FIXME
| Minismt_smt.Type.Typing_error (msg, t)
*)
| Type_sat.Typing_error (msg, t) ->
let b = Printexc.get_backtrace () in
let loc = match t.Dolmen.Term.loc with
| Some l -> l | None -> Dolmen.ParseLocation.mk "<>" 0 0 0 0
in
Format.fprintf Format.std_formatter "While typing:@\n%a@\n%a: typing error\n%s@."
Dolmen.Term.print t Dolmen.ParseLocation.fmt loc msg;
if Printexc.backtrace_status () then
Format.fprintf Format.std_formatter "%s@." b

View file

@ -1,18 +0,0 @@
(**************************************************************************)
(* *)
(* Alt-Ergo Zero *)
(* *)
(* Sylvain Conchon and Alain Mebsout *)
(* Universite Paris-Sud 11 *)
(* *)
(* Copyright 2011. This file is distributed under the terms of the *)
(* Apache Software License version 2.0 *)
(* *)
(**************************************************************************)
module type S = Msat.S
module Make (Th : Theory_intf.S) = Msat.Make(Th)

View file

@ -1,23 +0,0 @@
(*
MSAT is free software, using the Apache license, see file LICENSE
Copyright 2014 Guillaume Bury
Copyright 2014 Simon Cruanes
*)
(** Create SAT/SMT Solvers
This module provides a functor to create an SMT solver. Additionally, it also
gives a functor that produce an adequate empty theory that can be given to the [Make]
functor in order to create a pure SAT solver.
*)
module type S = Msat.S
(** The interface of instantiated solvers. *)
module Make (Th : Theory_intf.S)
: S with type formula = Th.formula
and type Proof.lemma = Th.proof
and type theory = Th.t
(** Functor to create a SMT Solver parametrised by the atomic
formulas and a theory. *)

View file

@ -1,709 +0,0 @@
(*
Base modules that defines the terms used in the prover.
*)
(* Type definitions *)
(* ************************************************************************ *)
(* Private aliases *)
type hash = int
type index = int
(* Identifiers, parametrized by the kind of the type of the variable *)
type 'ty id = {
id_type : 'ty;
id_name : string;
index : index; (** unique *)
}
(* Type for first order types *)
type ttype = Type
(* The type of functions *)
type 'ty function_descr = {
fun_vars : ttype id list; (* prenex forall *)
fun_args : 'ty list;
fun_ret : 'ty;
}
(* Types *)
type ty_descr =
| TyVar of ttype id (** Bound variables *)
| TyApp of ttype function_descr id * ty list
and ty = {
ty : ty_descr;
mutable ty_hash : hash; (** lazy hash *)
}
(* Terms & formulas *)
type term_descr =
| Var of ty id
| App of ty function_descr id * ty list * term list
and term = {
term : term_descr;
t_type : ty;
mutable t_hash : hash; (* lazy hash *)
}
type atom_descr =
| Pred of term
| Equal of term * term
and atom = {
sign : bool;
atom : atom_descr;
mutable f_hash : hash; (* lazy hash *)
}
(* Utilities *)
(* ************************************************************************ *)
let rec list_cmp ord l1 l2 =
match l1, l2 with
| [], [] -> 0
| [], _ -> -1
| _, [] -> 1
| x1::l1', x2::l2' ->
let c = ord x1 x2 in
if c = 0
then list_cmp ord l1' l2'
else c
(* Exceptions *)
(* ************************************************************************ *)
exception Type_mismatch of term * ty * ty
exception Bad_arity of ty function_descr id * ty list * term list
exception Bad_ty_arity of ttype function_descr id * ty list
(* Printing functions *)
(* ************************************************************************ *)
module Print = struct
let rec list f sep fmt = function
| [] -> ()
| [x] -> f fmt x
| x :: ((_ :: _) as r) ->
Format.fprintf fmt "%a%s" f x sep;
list f sep fmt r
let id fmt v = Format.fprintf fmt "%s" v.id_name
let ttype fmt = function Type -> Format.fprintf fmt "Type"
let rec ty fmt t = match t.ty with
| TyVar v -> id fmt v
| TyApp (f, []) ->
Format.fprintf fmt "%a" id f
| TyApp (f, l) ->
Format.fprintf fmt "%a(%a)" id f (list ty ", ") l
let params fmt = function
| [] -> ()
| l -> Format.fprintf fmt "∀ %a. " (list id ", ") l
let signature print fmt f =
match f.fun_args with
| [] -> Format.fprintf fmt "%a%a" params f.fun_vars print f.fun_ret
| l -> Format.fprintf fmt "%a%a -> %a" params f.fun_vars
(list print " -> ") l print f.fun_ret
let fun_ty = signature ty
let fun_ttype = signature ttype
let id_type print fmt v = Format.fprintf fmt "%a : %a" id v print v.id_type
let id_ty = id_type ty
let id_ttype = id_type ttype
let const_ty = id_type fun_ty
let const_ttype = id_type fun_ttype
let rec term fmt t = match t.term with
| Var v -> id fmt v
| App (f, [], []) ->
Format.fprintf fmt "%a" id f
| App (f, [], args) ->
Format.fprintf fmt "%a(%a)" id f
(list term ", ") args
| App (f, tys, args) ->
Format.fprintf fmt "%a(%a; %a)" id f
(list ty ", ") tys
(list term ", ") args
let atom_aux fmt f =
match f.atom with
| Equal (a, b) ->
Format.fprintf fmt "%a %s %a"
term a (if f.sign then "=" else "<>") term b
| Pred t ->
Format.fprintf fmt "%s%a" (if f.sign then "" else "¬ ") term t
let atom fmt f = Format.fprintf fmt "⟦%a⟧" atom_aux f
end
(* Substitutions *)
(* ************************************************************************ *)
module Subst = struct
module Mi = Map.Make(struct
type t = int * int
let compare (a, b) (c, d) = match compare a c with 0 -> compare b d | x -> x
end)
type ('a, 'b) t = ('a * 'b) Mi.t
(* Usual functions *)
let empty = Mi.empty
let is_empty = Mi.is_empty
let iter f = Mi.iter (fun _ (key, value) -> f key value)
let fold f = Mi.fold (fun _ (key, value) acc -> f key value acc)
let bindings s = Mi.fold (fun _ (key, value) acc -> (key, value) :: acc) s []
(* Comparisons *)
let equal f = Mi.equal (fun (_, value1) (_, value2) -> f value1 value2)
let compare f = Mi.compare (fun (_, value1) (_, value2) -> f value1 value2)
let hash h s = Mi.fold (fun i (_, value) acc -> Hashtbl.hash (acc, i, h value)) s 1
let choose m = snd (Mi.choose m)
(* Iterators *)
let exists pred s =
try
iter (fun m s -> if pred m s then raise Exit) s;
false
with Exit ->
true
let for_all pred s =
try
iter (fun m s -> if not (pred m s) then raise Exit) s;
true
with Exit ->
false
let print print_key print_value fmt map =
let aux _ (key, value) =
Format.fprintf fmt "%a -> %a@ " print_key key print_value value
in
Format.fprintf fmt "@[<hov 0>%a@]" (fun _ -> Mi.iter aux) map
module type S = sig
type 'a key
val get : 'a key -> ('a key, 'b) t -> 'b
val mem : 'a key -> ('a key, 'b) t -> bool
val bind : 'a key -> 'b -> ('a key, 'b) t -> ('a key, 'b) t
val remove : 'a key -> ('a key, 'b) t -> ('a key, 'b) t
end
(* Variable substitutions *)
module Id = struct
type 'a key = 'a id
let tok v = (v.index, 0)
let get v s = snd (Mi.find (tok v) s)
let mem v s = Mi.mem (tok v) s
let bind v t s = Mi.add (tok v) (v, t) s
let remove v s = Mi.remove (tok v) s
end
end
(* Dummies *)
(* ************************************************************************ *)
module Dummy = struct
let id_ttype =
{ index = -1; id_name = "<dummy>"; id_type = Type; }
let ty =
{ ty = TyVar id_ttype; ty_hash = -1; }
let id =
{ index = -2; id_name = "<dummy>"; id_type = ty; }
let term =
{ term = Var id; t_type = ty; t_hash = -1; }
let atom =
{ atom = Pred term; sign = true; f_hash = -1; }
end
(* Variables *)
(* ************************************************************************ *)
module Id = struct
type 'a t = 'a id
(* Hash & comparisons *)
let hash v = v.index
let compare: 'a. 'a id -> 'a id -> int =
fun v1 v2 -> compare v1.index v2.index
let equal v1 v2 = compare v1 v2 = 0
(* Printing functions *)
let print = Print.id
(* Id count *)
let _count = ref 0
(* Constructors *)
let mk_new id_name id_type =
incr _count;
let index = !_count in
{ index; id_name; id_type }
let ttype name = mk_new name Type
let ty name ty = mk_new name ty
let const name fun_vars fun_args fun_ret =
mk_new name { fun_vars; fun_args; fun_ret; }
let ty_fun name n =
let rec replicate acc n =
if n <= 0 then acc
else replicate (Type :: acc) (n - 1)
in
const name [] (replicate [] n) Type
let term_fun = const
(* Builtin Types *)
let prop = ty_fun "Prop" 0
let base = ty_fun "$i" 0
end
(* Types *)
(* ************************************************************************ *)
module Ty = struct
type t = ty
type subst = (ttype id, ty) Subst.t
(* Hash & Comparisons *)
let rec hash_aux t = match t.ty with
| TyVar v -> Id.hash v
| TyApp (f, args) ->
Hashtbl.hash (Id.hash f, List.map hash args)
and hash t =
if t.ty_hash = -1 then
t.ty_hash <- hash_aux t;
t.ty_hash
let discr ty = match ty.ty with
| TyVar _ -> 1
| TyApp _ -> 2
let rec compare u v =
let hu = hash u and hv = hash v in
if hu <> hv then Pervasives.compare hu hv
else match u.ty, v.ty with
| TyVar v1, TyVar v2 -> Id.compare v1 v2
| TyApp (f1, args1), TyApp (f2, args2) ->
begin match Id.compare f1 f2 with
| 0 -> list_cmp compare args1 args2
| x -> x
end
| _, _ -> Pervasives.compare (discr u) (discr v)
let equal u v =
u == v || (hash u = hash v && compare u v = 0)
(* Printing functions *)
let print = Print.ty
(* Constructors *)
let mk_ty ty = { ty; ty_hash = -1; }
let of_id v = mk_ty (TyVar v)
let apply f args =
assert (f.id_type.fun_vars = []);
if List.length args <> List.length f.id_type.fun_args then
raise (Bad_ty_arity (f, args))
else
mk_ty (TyApp (f, args))
(* Builtin types *)
let prop = apply Id.prop []
let base = apply Id.base []
(* Substitutions *)
let rec subst_aux map t = match t.ty with
| TyVar v -> begin try Subst.Id.get v map with Not_found -> t end
| TyApp (f, args) ->
let new_args = List.map (subst_aux map) args in
if List.for_all2 (==) args new_args then t
else apply f new_args
let subst map t = if Subst.is_empty map then t else subst_aux map t
(* Typechecking *)
let instantiate f tys args =
if List.length f.id_type.fun_vars <> List.length tys ||
List.length f.id_type.fun_args <> List.length args then
raise (Bad_arity (f, tys, args))
else
let map = List.fold_left2 (fun acc v ty -> Subst.Id.bind v ty acc) Subst.empty f.id_type.fun_vars tys in
let fun_args = List.map (subst map) f.id_type.fun_args in
List.iter2 (fun t ty ->
if not (equal t.t_type ty) then raise (Type_mismatch (t, t.t_type, ty)))
args fun_args;
subst map f.id_type.fun_ret
end
(* Terms *)
(* ************************************************************************ *)
module Term = struct
type t = term
type subst = (ty id, term) Subst.t
(* Hash & Comparisons *)
let rec hash_aux t = match t.term with
| Var v -> Id.hash v
| App (f, tys, args) ->
let l = List.map Ty.hash tys in
let l' = List.map hash args in
Hashtbl.hash (Id.hash f, l, l')
and hash t =
if t.t_hash = -1 then
t.t_hash <- hash_aux t;
t.t_hash
let discr t = match t.term with
| Var _ -> 1
| App _ -> 2
let rec compare u v =
let hu = hash u and hv = hash v in
if hu <> hv then Pervasives.compare hu hv
else match u.term, v.term with
| Var v1, Var v2 -> Id.compare v1 v2
| App (f1, tys1, args1), App (f2, tys2, args2) ->
begin match Id.compare f1 f2 with
| 0 ->
begin match list_cmp Ty.compare tys1 tys2 with
| 0 -> list_cmp compare args1 args2
| x -> x
end
| x -> x
end
| _, _ -> Pervasives.compare (discr u) (discr v)
let equal u v =
u == v || (hash u = hash v && compare u v = 0)
(* Printing functions *)
let print = Print.term
(* Constructors *)
let mk_term term t_type =
{ term; t_type; t_hash = -1; }
let of_id v =
mk_term (Var v) v.id_type
let apply f ty_args t_args =
mk_term (App (f, ty_args, t_args)) (Ty.instantiate f ty_args t_args)
(* Substitutions *)
let rec subst_aux ty_map t_map t = match t.term with
| Var v -> begin try Subst.Id.get v t_map with Not_found -> t end
| App (f, tys, args) ->
let new_tys = List.map (Ty.subst ty_map) tys in
let new_args = List.map (subst_aux ty_map t_map) args in
if List.for_all2 (==) new_tys tys && List.for_all2 (==) new_args args then t
else apply f new_tys new_args
let subst ty_map t_map t =
if Subst.is_empty ty_map && Subst.is_empty t_map then
t
else
subst_aux ty_map t_map t
let rec replace (t, t') t'' = match t''.term with
| _ when equal t t'' -> t'
| App (f, ty_args, t_args) ->
apply f ty_args (List.map (replace (t, t')) t_args)
| _ -> t''
end
(* Formulas *)
(* ************************************************************************ *)
module Atom = struct
type t = atom
type proof = unit
(* Hash & Comparisons *)
let h_eq = 2
let h_pred = 3
let rec hash_aux f = match f.atom with
| Equal (t1, t2) ->
Hashtbl.hash (h_eq, Term.hash t1, Term.hash t2)
| Pred t ->
Hashtbl.hash (h_pred, Term.hash t)
and hash f =
if f.f_hash = -1 then
f.f_hash <- hash_aux f;
f.f_hash
let discr f = match f.atom with
| Equal _ -> 1
| Pred _ -> 2
let compare f g =
let hf = hash f and hg = hash g in
if hf <> hg then Pervasives.compare hf hg
else match f.atom, g.atom with
| Equal (u1, v1), Equal(u2, v2) ->
list_cmp Term.compare [u1; v1] [u2; v2]
| Pred t1, Pred t2 -> Term.compare t1 t2
| _, _ -> Pervasives.compare (discr f) (discr g)
let equal u v =
u == v || (hash u = hash v && compare u v = 0)
(* Printing functions *)
let print = Print.atom
(* Constructors *)
let mk_formula f = {
sign = true;
atom = f;
f_hash = -1;
}
let dummy = Dummy.atom
let pred t =
if not (Ty.equal Ty.prop t.t_type) then
raise (Type_mismatch (t, t.t_type, Ty.prop))
else
mk_formula (Pred t)
let neg f =
{ f with sign = not f.sign }
let eq a b =
if not (Ty.equal a.t_type b.t_type) then
raise (Type_mismatch (b, b.t_type, a.t_type))
else if Term.compare a b < 0 then
mk_formula (Equal (a, b))
else
mk_formula (Equal (b, a))
let norm f =
{ f with sign = true },
if f.sign then Msat.Same_sign
else Msat.Negated
end
module Ts_arg = struct
module Form = Atom
type t = unit
let fresh () : Form.t =
let id = Id.ty "fresh" Ty.prop in
Form.pred (Term.of_id id)
end
module Formula = Msat_tseitin.Make(Ts_arg)
(** {2 Theory} *)
module E = Eclosure.Make(Term)
module H = Backtrack.Hashtbl(Term)
module M = Hashtbl.Make(Term)
let uf acts = E.create acts
let assign t =
match E.find_tag uf t with
| _, None -> t
| _, Some (_, v) -> v
(* Propositional constants *)
let true_ = Theory_smt.(Term.of_id (Id.ty "true" Ty.prop))
let false_ = Theory_smt.(Term.of_id (Id.ty "false" Ty.prop))
(* Uninterpreted functions and predicates *)
let map : Theory_smt.term H.t = H.create stack
let watch = M.create 4096
let interpretation = H.create stack
let pop_watches t =
try
let l = M.find watch t in
M.remove watch t;
l
with Not_found ->
[]
let add_job j x =
let l = try M.find watch x with Not_found -> [] in
M.add watch x (j :: l)
let update_job x ((t, watchees) as job) =
try
let y = List.find (fun y -> not (H.mem map y)) watchees in
add_job job y
with Not_found ->
add_job job x;
begin match t with
| { Theory_smt.term = Theory_smt.App (f, tys, l);_ } ->
let is_prop = Theory_smt.(Ty.equal t.t_type Ty.prop) in
let t_v = H.find map t in
let l' = List.map (H.find map) l in
let u = Theory_smt.Term.apply f tys l' in
begin try
let t', u_v = H.find interpretation u in
if not (Theory_smt.Term.equal t_v u_v) then begin
match t' with
| { Theory_smt.term = Theory_smt.App (_, _, r); _ } when is_prop ->
let eqs = List.map2 (fun a b -> Theory_smt.Atom.neg (Theory_smt.Atom.eq a b)) l r in
if Theory_smt.(Term.equal u_v true_) then begin
let res = Theory_smt.Atom.pred t ::
Theory_smt.Atom.neg (Theory_smt.Atom.pred t') :: eqs in
raise (Absurd res)
end else begin
let res = Theory_smt.Atom.pred t' ::
Theory_smt.Atom.neg (Theory_smt.Atom.pred t) :: eqs in
raise (Absurd res)
end
| { Theory_smt.term = Theory_smt.App (_, _, r); _ } ->
let eqs = List.map2 (fun a b -> Theory_smt.Atom.neg (Theory_smt.Atom.eq a b)) l r in
let res = Theory_smt.Atom.eq t t' :: eqs in
raise (Absurd res)
| _ -> assert false
end
with Not_found ->
H.add interpretation u (t, t_v);
end
| _ -> assert false
end
let rec update_watches x = function
| [] -> ()
| job :: r ->
begin
try
update_job x job;
with exn ->
List.iter (fun j -> add_job j x) r;
raise exn
end;
update_watches x r
let add_watch t l =
update_job t (t, l)
let add_assign t v =
H.add map t v;
update_watches t (pop_watches t)
(* Assignemnts *)
let rec iter_aux f = function
| { Theory_smt.term = Theory_smt.Var _; _ } as t ->
Log.debugf 10 (fun k -> k "Adding %a as assignable" Theory_smt.Term.print t);
f t
| { Theory_smt.term = Theory_smt.App (_, _, l); _ } as t ->
if l <> [] then add_watch t (t :: l);
List.iter (iter_aux f) l;
Log.debugf 10 (fun k -> k "Adding %a as assignable" Theory_smt.Term.print t);
f t
let iter_assignable f = function
| { Theory_smt.atom = Theory_smt.Pred { Theory_smt.term = Theory_smt.Var _;_ }; _ } -> ()
| { Theory_smt.atom = Theory_smt.Pred ({ Theory_smt.term = Theory_smt.App (_, _, l);_} as t); _ } ->
if l <> [] then add_watch t (t :: l);
List.iter (iter_aux f) l;
| { Theory_smt.atom = Theory_smt.Equal (a, b);_ } ->
iter_aux f a; iter_aux f b
let eval = function
| { Theory_smt.atom = Theory_smt.Pred t; _ } ->
begin try
let v = H.find map t in
if Theory_smt.Term.equal v true_ then
Plugin_intf.Valued (true, [t])
else if Theory_smt.Term.equal v false_ then
Plugin_intf.Valued (false, [t])
else
Plugin_intf.Unknown
with Not_found ->
Plugin_intf.Unknown
end
| { Theory_smt.atom = Theory_smt.Equal (a, b); sign; _ } ->
begin try
let v_a = H.find map a in
let v_b = H.find map b in
if Theory_smt.Term.equal v_a v_b then
Plugin_intf.Valued(sign, [a; b])
else
Plugin_intf.Valued(not sign, [a; b])
with Not_found ->
Plugin_intf.Unknown
end
(* Theory propagation *)
let rec chain_eq = function
| [] | [_] -> []
| a :: ((b :: _) as l) -> (Theory_smt.Atom.eq a b) :: chain_eq l
let assume s =
let open Plugin_intf in
try
for i = s.start to s.start + s.length - 1 do
match s.get i with
| Assign (t, v) ->
add_assign t v;
E.add_tag uf t v
| Lit f ->
begin match f with
| { Theory_smt.atom = Theory_smt.Equal (u, v); sign = true;_ } ->
E.add_eq uf u v
| { Theory_smt.atom = Theory_smt.Equal (u, v); sign = false;_ } ->
E.add_neq uf u v
| { Theory_smt.atom = Theory_smt.Pred p; sign;_ } ->
let v = if sign then true_ else false_ in
add_assign p v
end
done;
Plugin_intf.Sat
with
| Absurd l ->
Plugin_intf.Unsat (l, ())
| E.Unsat (a, b, l) ->
let c = Theory_smt.Atom.eq a b :: List.map Theory_smt.Atom.neg (chain_eq l) in
Plugin_intf.Unsat (c, ())
let if_sat _ =
Plugin_intf.Sat

View file

@ -1,323 +0,0 @@
(** Expressions for TabSat *)
(** {2 Type definitions} *)
(** These are custom types used in functions later. *)
(** {3 Identifiers} *)
(** Identifiers are the basic building blocks used to build types terms and expressions. *)
type hash
type index = private int
(** Private aliases to provide access. You should not have any need
to use these, instead use the functions provided by this module. *)
type 'ty id = private {
id_type : 'ty;
id_name : string;
index : index; (** unique *)
}
(** The type of identifiers. An ['a id] is an identifier whose solver-type
is represented by an inhabitant of type ['a].
All identifier have an unique [index] which is used for comparison,
so that the name of a variable is only there for tracability
and/or pretty-printing. *)
(** {3 Types} *)
type ttype = Type
(** The caml type of solver-types. *)
type 'ty function_descr = private {
fun_vars : ttype id list; (* prenex forall *)
fun_args : 'ty list;
fun_ret : 'ty;
}
(** This represents the solver-type of a function.
Functions can be polymorphic in the variables described in the
[fun_vars] field. *)
type ty_descr = private
| TyVar of ttype id
(** bound variables (i.e should only appear under a quantifier) *)
| TyApp of ttype function_descr id * ty list
(** application of a constant to some arguments *)
and ty = private {
ty : ty_descr;
mutable ty_hash : hash; (** Use Ty.hash instead *)
}
(** These types defines solver-types, i.e the representation of the types
of terms in the solver. Record definition for [type ty] is shown in order
to be able to use the [ty.ty] field in patter matches. Other fields shoud not
be accessed directly, but throught the functions provided by the [Ty] module. *)
(** {3 Terms} *)
type term_descr = private
| Var of ty id
(** bound variables (i.e should only appear under a quantifier) *)
| App of ty function_descr id * ty list * term list
(** application of a constant to some arguments *)
and term = private {
term : term_descr;
t_type : ty;
mutable t_hash : hash; (** Do not use this filed, call Term.hash instead *)
}
(** Types defining terms in the solver. The definition is vary similar to that
of solver-types, except for type arguments of polymorphic functions which
are explicit. This has the advantage that there is a clear and typed distinction
between solver-types and terms, but may lead to some duplication of code
in some places. *)
(** {3 Formulas} *)
type atom_descr = private
(** Atoms *)
| Pred of term
| Equal of term * term
and atom = private {
sign : bool;
atom : atom_descr;
mutable f_hash : hash; (** Use Formula.hash instead *)
}
(** The type of atoms in the solver. The list of free arguments in quantifiers
is a bit tricky, so you should not touch it (see full doc for further
explanations). *)
(** {3 Exceptions} *)
exception Type_mismatch of term * ty * ty
(* Raised when as Type_mismatch(term, actual_type, expected_type) *)
exception Bad_arity of ty function_descr id * ty list * term list
exception Bad_ty_arity of ttype function_descr id * ty list
(** Raised when trying to build an application with wrong arity *)
(** {2 Printing} *)
module Print : sig
(** Pretty printing functions *)
val id : Format.formatter -> 'a id -> unit
val id_ty : Format.formatter -> ty id -> unit
val id_ttype : Format.formatter -> ttype id -> unit
val const_ty : Format.formatter -> ty function_descr id -> unit
val const_ttype : Format.formatter -> ttype function_descr id -> unit
val ty : Format.formatter -> ty -> unit
val fun_ty : Format.formatter -> ty function_descr -> unit
val ttype : Format.formatter -> ttype -> unit
val fun_ttype : Format.formatter -> ttype function_descr -> unit
val term : Format.formatter -> term -> unit
val atom : Format.formatter -> atom -> unit
end
(** {2 Identifiers & Metas} *)
module Id : sig
type 'a t = 'a id
(* Type alias *)
val hash : 'a t -> int
val equal : 'a t -> 'a t -> bool
val compare : 'a t -> 'a t -> int
(** Usual functions for hash/comparison *)
val print : Format.formatter -> 'a t -> unit
(** Printing for variables *)
val prop : ttype function_descr id
val base : ttype function_descr id
(** Constants representing the type for propositions and a default type
for term, respectively. *)
val ttype : string -> ttype id
(** Create a fresh type variable with the given name. *)
val ty : string -> ty -> ty id
(** Create a fresh variable with given name and type *)
val ty_fun : string -> int -> ttype function_descr id
(** Create a fresh type constructor with given name and arity *)
val term_fun : string -> ttype id list -> ty list -> ty -> ty function_descr id
(** [ty_fun name type_vars arg_types return_type] returns a fresh constant symbol,
possibly polymorphic with respect to the variables in [type_vars] (which may appear in the
types in [arg_types] and in [return_type]). *)
end
(** {2 Substitutions} *)
module Subst : sig
(** Module to handle substitutions *)
type ('a, 'b) t
(** The type of substitutions from values of type ['a] to values of type ['b]. *)
val empty : ('a, 'b) t
(** The empty substitution *)
val is_empty : ('a, 'b) t -> bool
(** Test wether a substitution is empty *)
val iter : ('a -> 'b -> unit) -> ('a, 'b) t -> unit
(** Iterates over the bindings of the substitution. *)
val fold : ('a -> 'b -> 'c -> 'c) -> ('a, 'b) t -> 'c -> 'c
(** Fold over the elements *)
val bindings : ('a, 'b) t -> ('a * 'b) list
(** Returns the list of bindings ofa substitution. *)
val exists : ('a -> 'b -> bool) -> ('a, 'b) t -> bool
(** Tests wether the predicate holds for at least one binding. *)
val for_all : ('a -> 'b -> bool) -> ('a, 'b) t -> bool
(** Tests wether the predicate holds for all bindings. *)
val hash : ('b -> int) -> ('a, 'b) t -> int
val compare : ('b -> 'b -> int) -> ('a, 'b) t -> ('a, 'b) t -> int
val equal : ('b -> 'b -> bool) -> ('a, 'b) t -> ('a, 'b) t -> bool
(** Comparison and hash functions, with a comparison/hash function on values as parameter *)
val print :
(Format.formatter -> 'a -> unit) ->
(Format.formatter -> 'b -> unit) ->
Format.formatter -> ('a, 'b) t -> unit
(** Prints the substitution, using the given functions to print keys and values. *)
val choose : ('a, 'b) t -> 'a * 'b
(** Return one binding of the given substitution, or raise Not_found if the substitution is empty.*)
(** {5 Concrete subtitutions } *)
module type S = sig
type 'a key
val get : 'a key -> ('a key, 'b) t -> 'b
(** [get v subst] returns the value associated with [v] in [subst], if it exists.
@raise Not_found if there is no binding for [v]. *)
val mem : 'a key -> ('a key, 'b) t -> bool
(** [get v subst] returns wether there is a value associated with [v] in [subst]. *)
val bind : 'a key -> 'b -> ('a key, 'b) t -> ('a key, 'b) t
(** [bind v t subst] returns the same substitution as [subst] with the additional binding from [v] to [t].
Erases the previous binding of [v] if it exists. *)
val remove : 'a key -> ('a key, 'b) t -> ('a key, 'b) t
(** [remove v subst] returns the same substitution as [subst] except for [v] which is unbound in the returned substitution. *)
end
module Id : S with type 'a key = 'a id
end
(** {2 Types} *)
module Ty : sig
type t = ty
(** Type alias *)
type subst = (ttype id, ty) Subst.t
(** The type of substitutions over types. *)
val hash : t -> int
val equal : t -> t -> bool
val compare : t -> t -> int
(** Usual hash/compare functions *)
val print : Format.formatter -> t -> unit
val prop : ty
val base : ty
(** The type of propositions and individuals *)
val of_id : ttype id -> ty
(** Creates a type from a variable *)
val apply : ttype function_descr id -> ty list -> ty
(** Applies a constant to a list of types *)
val subst : subst -> ty -> ty
(** Substitution over types. *)
end
(** {2 Terms} *)
module Term : sig
type t = term
(** Type alias *)
type subst = (ty id, term) Subst.t
(** The type of substitutions in types. *)
val hash : t -> int
val equal : t -> t -> bool
val compare : t -> t -> int
(** Usual hash/compare functions *)
val print : Format.formatter -> t -> unit
(** Printing functions *)
val of_id : ty id -> term
(** Create a term from a variable *)
val apply : ty function_descr id -> ty list -> term list -> term
(** Applies a constant function to type arguments, then term arguments *)
val subst : Ty.subst -> subst -> term -> term
(** Substitution over types. *)
val replace : term * term -> term -> term
(** [replace (t, t') t''] returns the term [t''] where every occurence of [t]
has been replace by [t']. *)
end
(** {2 Formulas} *)
module Atom : sig
type t = atom
type proof = unit
(** Type alias *)
val hash : t -> int
val equal : t -> t -> bool
val compare : t -> t -> int
(** Usual hash/compare functions *)
val print : Format.formatter -> t -> unit
(** Printing functions *)
val dummy : atom
(** A dummy atom, different from any other atom. *)
val eq : term -> term -> atom
(** Create an equality over two terms. The two given terms
must have the same type [t], which must be different from {!Ty.prop} *)
val pred : term -> atom
(** Create a atom from a term. The given term must have type {!Ty.prop} *)
val neg : atom -> atom
(** Returns the negation of the given atom *)
val norm : atom -> atom * Msat.negated
(** Normalization functions as required by msat. *)
end
module Formula : Msat_tseitin.S with type atom = atom

View file

@ -1,29 +0,0 @@
(** Typechecking of terms from Dolmen.Term.t
This module defines the requirements for typing expre'ssions from dolmen. *)
module type S = sig
type t
type atom
(** The type of atoms that will be fed to tha sovler. *)
exception Typing_error of string * Dolmen.Term.t
(** Exception raised during typechecking. *)
val decl : t -> Dolmen.Id.t -> Dolmen.Term.t -> unit
(** New declaration, i.e an identifier and its type. *)
val def : t -> Dolmen.Id.t -> Dolmen.Term.t -> unit
(** New definition, i.e an identifier and the term it is equal to. *)
val assumptions : t -> Dolmen.Term.t -> atom list
(** Parse a list of local assumptions. *)
val consequent : t -> Dolmen.Term.t -> atom list list
val antecedent : t -> Dolmen.Term.t -> atom list list
(** Parse a formula, and return a cnf ready to be given to the solver.
Consequent is for hypotheses (left of the sequent), while antecedent
is for goals (i.e formulas on the right of a sequent). *)
end

View file

@ -1,100 +0,0 @@
(*
MSAT is free software, using the Apache license, see file LICENSE
Copyright 2014 Guillaume Bury
Copyright 2014 Simon Cruanes
*)
(* Log&Module Init *)
(* ************************************************************************ *)
module Id = Dolmen.Id
module Ast = Dolmen.Term
module H = Hashtbl.Make(Id)
module Formula = CDCL_tseitin.Make(CDCL_sat.Th)
(* Exceptions *)
(* ************************************************************************ *)
exception Typing_error of string * Dolmen.Term.t
(* Identifiers *)
(* ************************************************************************ *)
type t = {
symbols: Formula.atom H.t;
fresh: Formula.fresh_state;
st: Formula.state;
}
let create th : t = {
symbols = H.create 42;
fresh=th;
st=Formula.create th;
}
let find_id st id =
try
H.find st.symbols id
with Not_found ->
let res = CDCL_sat.Th.fresh st.fresh in
H.add st.symbols id res;
res
(* Actual parsing *)
(* ************************************************************************ *)
[@@@ocaml.warning "-9"]
let rec parse st = function
| { Ast.term = Ast.Builtin Ast.True } ->
Formula.f_true
| { Ast.term = Ast.Builtin Ast.False } ->
Formula.f_false
| { Ast.term = Ast.Symbol id } ->
let s = find_id st id in
Formula.make_atom s
| { Ast.term = Ast.App ({Ast.term = Ast.Builtin Ast.Not }, [p]) }
| { Ast.term = Ast.App ({Ast.term = Ast.Symbol { Id.name = "not" } }, [p]) } ->
Formula.make_not (parse st p)
| { Ast.term = Ast.App ({Ast.term = Ast.Builtin Ast.And }, l) }
| { Ast.term = Ast.App ({Ast.term = Ast.Symbol { Id.name = "and" } }, l) } ->
Formula.make_and (List.map (parse st) l)
| { Ast.term = Ast.App ({Ast.term = Ast.Builtin Ast.Or }, l) }
| { Ast.term = Ast.App ({Ast.term = Ast.Symbol { Id.name = "or" } }, l) } ->
Formula.make_or (List.map (parse st) l)
| { Ast.term = Ast.App ({Ast.term = Ast.Builtin Ast.Imply }, [p; q]) } ->
Formula.make_imply (parse st p) (parse st q)
| { Ast.term = Ast.App ({Ast.term = Ast.Builtin Ast.Equiv }, [p; q]) } ->
Formula.make_equiv (parse st p) (parse st q)
| { Ast.term = Ast.App ({Ast.term = Ast.Builtin Ast.Xor }, [p; q]) } ->
Formula.make_xor (parse st p) (parse st q)
| t ->
raise (Typing_error ("Term is not a pure proposition", t))
[@@@ocaml.warning "+9"]
(* Exported functions *)
(* ************************************************************************ *)
let decl _ _ t =
raise (Typing_error ("Declarations are not allowed in pure sat", t))
let def _ _ t =
raise (Typing_error ("Definitions are not allowed in pure sat", t))
let assumptions st t =
let f = parse st t in
let cnf = Formula.make_cnf st.st f in
List.map (function
| [ x ] -> x
| _ -> assert false
) cnf
let antecedent st t =
let f = parse st t in
Formula.make_cnf st.st f
let consequent st t =
let f = parse st t in
Formula.make_cnf st.st @@ Formula.make_not f

View file

@ -1,14 +0,0 @@
(*
MSAT is free software, using the Apache license, see file LICENSE
Copyright 2014 Guillaume Bury
Copyright 2014 Simon Cruanes
*)
(** Typechecking of terms from Dolmen.Term.t
This module provides functions to parse terms from the untyped syntax tree
defined in Dolmen, and generate formulas as defined in the Expr_sat module. *)
include Type.S with type atom := CDCL_sat.Th.formula
val create : CDCL_sat.Th.t -> t

View file

@ -1,90 +0,0 @@
(*
MSAT is free software, using the Apache license, see file LICENSE
Copyright 2014 Guillaume Bury
Copyright 2014 Simon Cruanes
*)
module type OrderedType = sig
type t
val compare : t -> t -> int
end
(* Union-find Module *)
module Make(T : OrderedType) = struct
exception Unsat of T.t * T.t
type var = T.t
module M = Map.Make(T)
(* TODO: better treatment of inequalities *)
type t = {
rank : int M.t;
forbid : ((var * var) list);
mutable parent : var M.t;
}
let empty = {
rank = M.empty;
forbid = [];
parent = M.empty;
}
let find_map m i default =
try
M.find i m
with Not_found ->
default
let rec find_aux f i =
let fi = find_map f i i in
if fi = i then
(f, i)
else
let f, r = find_aux f fi in
let f = M.add i r f in
(f, r)
let find h x =
let f, cx = find_aux h.parent x in
h.parent <- f;
cx
(* Highly inefficient treatment of inequalities *)
let possible h =
let aux (a, b) =
let ca = find h a in
let cb = find h b in
if T.compare ca cb = 0 then
raise (Unsat (a, b))
in
List.iter aux h.forbid;
h
let union_aux h x y =
let cx = find h x in
let cy = find h y in
if cx != cy then begin
let rx = find_map h.rank cx 0 in
let ry = find_map h.rank cy 0 in
if rx > ry then
{ h with parent = M.add cy cx h.parent }
else if ry > rx then
{ h with parent = M.add cx cy h.parent }
else
{ rank = M.add cx (rx + 1) h.rank;
parent = M.add cy cx h.parent;
forbid = h.forbid; }
end else
h
let union h x y = possible (union_aux h x y)
let forbid h x y =
let cx = find h x in
let cy = find h y in
if cx = cy then
raise (Unsat (x, y))
else
{ h with forbid = (x, y) :: h.forbid }
end

View file

@ -1,20 +0,0 @@
(*
MSAT is free software, using the Apache license, see file LICENSE
Copyright 2014 Guillaume Bury
Copyright 2014 Simon Cruanes
*)
module type OrderedType = sig
type t
val compare : t -> t -> int
end
module Make(T : OrderedType) : sig
type t
exception Unsat of T.t * T.t
val empty : t
val find : t -> T.t -> T.t
val union : t -> T.t -> T.t -> t
val forbid : t -> T.t -> T.t -> t
end

File diff suppressed because it is too large Load diff

View file

@ -1,45 +0,0 @@
(** Main API *)
module Theory_intf = Theory_intf
module type S = Solver_intf.S
type negated = Theory_intf.negated =
| Negated
| Same_sign
type ('formula, 'proof) res = ('formula, 'proof) Theory_intf.res =
| Sat
(** The current set of assumptions is satisfiable. *)
| Unsat of 'formula list * 'proof
(** The current set of assumptions is *NOT* satisfiable, and here is a
theory tautology (with its proof), for which every literal is false
under the current assumptions. *)
(** Type returned by the theory. Formulas in the unsat clause must come from the
current set of assumptions, i.e must have been encountered in a slice. *)
type 'form sat_state = 'form Solver_intf.sat_state = Sat_state of {
eval : 'form -> bool;
eval_level : 'form -> bool * int;
iter_trail : ('form -> unit) -> unit;
}
type ('clause, 'proof) unsat_state = ('clause, 'proof) Solver_intf.unsat_state = Unsat_state of {
unsat_conflict : unit -> 'clause;
get_proof : unit -> 'proof;
}
type 'clause export = 'clause Solver_intf.export = {
clauses : 'clause Vec.t;
}
type ('form, 'proof) actions = ('form,'proof) Theory_intf.actions
type 'form slice_actions = 'form Theory_intf.slice_actions
module Make = Solver.Make
(**/**)
module Vec = Vec
module Log = Log
(**/**)

View file

@ -1,75 +0,0 @@
{1 mSAT}
{2 License}
This code is free, under the {{:https://github.com/c-cube/cdcl/blob/master/LICENSE}Apache 2.0 license}.
{2 Contents}
An ocaml library to implement CDCL(T) solvers.
Most modules in CDCL actually define functors. These functors usually take one
or two arguments, usually an implementation of Terms and formulas used, and an implementation
of the theory used during solving.
{4 Solver creation}
The following modules allow to easily create a SAT or SMT solver (remark: a SAT solver is
simply an SMT solver with an empty theory).
{!modules:
CDCL
}
Finally, mSAT also provides an implementation of Tseitin's CNF conversion:
{!modules:
CDCL_tseitin
}
{4 Proof Management}
CDCL solvers are able to provide detailed proofs when an unsat state is reached. To do
so, it require the provided theory to give proofs of the tautologies it gives the solver.
These proofs will be called lemmas. The type of lemmas is defined by the theory and can
very well be [unit].
In this context a proof is a resolution tree, whose conclusion (i.e. root) is the
empty clause, effectively allowing to deduce [false] from the hypotheses.
A resolution tree is a binary tree whose nodes are clauses. Inner nodes' clauses are
obtained by performing resolution between the two clauses of the children nodes, while
leafs of the tree are either hypotheses, or tautologies (i.e. conflicts returned by
the theory).
{!modules:
CDCL__Res
CDCL__Res_intf
}
Backends for exporting proofs to different formats:
{!modules:
Dot
Coq
Dedukti
Backend_intf
}
{4 Internal modules}
WARNING: for advanced users only ! These modules expose a lot of unsafe functions
that must be used with care to not break the required invariants. Additionally, these
interfaces are not part of the main API and so are subject to a lot more breaking changes
than the safe modules above.
{!modules:
Dimacs
Internal
External
Solver_types
Solver_types_intf
}
{2 Index}
{!indexlist}

View file

@ -1,121 +0,0 @@
(*
MSAT is free software, using the Apache license, see file LICENSE
Copyright 2016 Guillaume Bury
Copyright 2016 Simon Cruanes
*)
module type S = Solver_intf.S
open Solver_intf
module Make (Th : Theory_intf.S) = struct
module S = Internal.Make(Th)
module Proof = S.Proof
exception UndecidedLit = S.UndecidedLit
type formula = S.formula
type atom = S.atom
type clause = S.clause
type theory = Th.t
type proof = S.proof
type lemma = S.lemma
type premise = S.premise =
| Hyp
| Lemma of lemma
| Simplified of clause
| History of clause list
type t = S.t
type solver = t
let[@inline] create ?size () : t = S.create ?size ()
(* Result type *)
type res =
| Sat of formula sat_state
| Unsat of (clause,proof) unsat_state
let pp_all st lvl status =
Log.debugf lvl
(fun k -> k
"@[<v>%s - Full resume:@,@[<hov 2>Trail:@\n%a@]@,\
@[<hov 2>Clauses:@\n%a@]@]@."
status
(Vec.print ~sep:"" S.Atom.debug) (S.trail st)
(Vec.print ~sep:"" S.Clause.debug) st.S.clauses
)
let mk_sat (st:S.t) : _ sat_state =
pp_all st 99 "SAT";
let iter f : unit = Vec.iter (fun a -> f a.S.lit) (S.trail st) in
Sat_state {
eval = S.eval st;
eval_level = S.eval_level st;
iter_trail = iter;
}
let mk_unsat (st:S.t) : (_,_) unsat_state =
pp_all st 99 "UNSAT";
let unsat_conflict () =
match S.unsat_conflict st with
| None -> assert false
| Some c -> c
in
let get_proof () = unsat_conflict () in
Unsat_state { unsat_conflict; get_proof; }
let theory = S.theory
(* Wrappers around internal functions*)
let[@inline] assume ?(permanent=true) st ?tag cls : unit =
S.assume ~permanent st ?tag cls
let[@inline] add_clause ~permanent st c : unit =
S.add_clause_user ~permanent st c
let solve ?(restarts=true) (st:t) ?(assumptions=[]) () =
try
S.solve ~restarts ~assumptions st;
Sat (mk_sat st)
with S.Unsat ->
Unsat (mk_unsat st)
let n_vars = S.n_vars
let unsat_core = S.Proof.unsat_core
let true_at_level0 st a =
try
let b, lev = S.eval_level st a in
b && lev = 0
with S.UndecidedLit -> false
let[@inline] get_tag cl = S.(cl.tag)
let actions = S.actions
let export (st:t) : S.clause export =
let clauses = st.S.clauses in
{clauses}
let check_model = S.check_model
module Atom = S.Atom
module Clause = struct
include S.Clause
let forms c = atoms c |> IArray.of_array_map Atom.get_formula
let forms_l c = atoms_l c |> List.map Atom.get_formula
let[@inline] make ?tag (a:atom array) : t = S.Clause.make ?tag a S.Hyp
let[@inline] make_l ?tag l : t = S.Clause.make_l ?tag l S.Hyp
let of_formulas st ?tag l =
let l = List.map (Atom.make st) l in
make_l ?tag l
end
module Formula = S.Formula
end

View file

@ -1,22 +0,0 @@
(*
MSAT is free software, using the Apache license, see file LICENSE
Copyright 2014 Guillaume Bury
Copyright 2014 Simon Cruanes
*)
(** SAT safe interface
This module defines a safe interface for the core solver.
It is the basis of the {!module:Solver} and {!module:Mcsolver} modules.
*)
module type S = Solver_intf.S
(** Safe external interface of solvers. *)
module Make(Th : Theory_intf.S)
: S with type formula = Th.formula
and type theory = Th.t
and type lemma = Th.proof
(** Functor to make a safe external interface. *)

View file

@ -1,279 +0,0 @@
(** 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}.
*)
module Var_fields = BitField.Make()
module C_fields = BitField.Make()
type 'a printer = Format.formatter -> 'a -> unit
type 'form sat_state = Sat_state of {
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) -> unit;
(** Iter through the formulas and terms in order of decision/propagation
(starting from the first propagation, to the last propagation). *)
}
(** The type of values returned when the solver reaches a SAT state. *)
type ('clause, 'proof) unsat_state = Unsat_state of {
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. *)
}
(** The type of values returned when the solver reaches an UNSAT state. *)
type 'clause export = {
clauses: 'clause Vec.t;
}
(** Export internal state *)
(** The external interface implemented by safe solvers, such as the one
created by the {!Solver.Make} and {!Mcsolver.Make} functors. *)
module type S = 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. *)
type formula
(** 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. *)
type atom (** SAT solver literals *)
type clause (** SAT solver clauses *)
type theory (** user theory *)
type proof
(** Lazy type for proof trees. Proofs are persistent objects, and can be
extended to proof nodes using functions defined later. *)
type lemma (** A theory lemma, used to justify a theory conflict clause *)
type premise =
| Hyp
| Lemma of lemma
| Simplified of clause
| History of clause list
type t
(** Main solver type, containing all state for solving. *)
val create : ?size:[`Tiny|`Small|`Big] -> unit -> t
(** Create new solver
@param size the initial size of internal data structures. The bigger,
the faster, but also the more RAM it uses. *)
(** {2 Types} *)
(** Result type for the solver *)
type res =
| Sat of formula sat_state (** Returned when the solver reaches SAT, with a model *)
| Unsat of (clause,proof) unsat_state (** Returned when the solver reaches UNSAT, with a proof *)
exception UndecidedLit
(** Exception raised by the evaluating functions when a literal
has not yet been assigned a value. *)
(** {2 Base operations} *)
val n_vars : t -> int
val theory : t -> theory
val assume : ?permanent:bool -> t -> ?tag:int -> formula list list -> unit
(** Add the list of clauses to the current set of assumptions.
Modifies the sat solver state in place.
@param permanent if true, kept after backtracking (default true) *)
val add_clause : permanent:bool -> t -> clause -> unit
(** Lower level addition of clauses. See {!Clause} to create clauses.
@param permanent if true, kept after backtracking *)
val solve : ?restarts:bool -> t -> ?assumptions:formula list -> unit -> 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. *)
val unsat_core : proof -> clause list
(** Returns the unsat core of a given proof, ie a subset of all the added
clauses that is sufficient to establish unsatisfiability. *)
val true_at_level0 : t -> formula -> bool
(** [true_at_level0 a] returns [true] if [a] was proved at level0, i.e.
it must hold in all models *)
val get_tag : clause -> int option
(** Recover tag from a clause, if any *)
(* FIXME
val push : t -> unit
(** Push a new save point. Clauses added after this call to [push] will
be added as normal, but the corresponding call to [pop] will
remove these clauses. *)
val pop : t -> unit
(** Return to last save point, discarding clauses added since last
call to [push] *)
*)
val actions : t -> (formula,lemma) Theory_intf.actions
(** Obtain actions *)
val export : t -> clause export
val check_model : t -> unit
(** {2 Re-export some functions} *)
type solver = t
module Atom : sig
type t = atom
val is_pos : t -> bool
val neg : t -> t
val abs : t -> t
val compare : t -> t -> int
val equal : t -> t -> bool
val get_formula : t -> formula
val is_true : t -> bool
val dummy : t
val make : solver -> formula -> t
val pp : t printer
end
(** A module to manipulate proofs. *)
module Proof : sig
type t = proof
type 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. *)
and step =
| Hypothesis
(** The conclusion is a user-provided hypothesis *)
| Assumption
(** The conclusion has been locally assumed by the user *)
| Lemma of lemma
(** The conclusion is a tautology provided by the theory, with associated proof *)
| Duplicate of proof * atom list
(** The conclusion is obtained by eliminating multiple occurences of the atom in
the conclusion of the provided proof. *)
| Resolution of proof * proof * atom
(** 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. *)
exception Insufficient_hyps
(** Raised when a complete resolution derivation cannot be found using the current hypotheses. *)
(** {3 Proof building functions} *)
val prove : clause -> t
(** Given a clause, return a proof of that clause.
@raise Insuficient_hyps if it does not succeed. *)
val prove_atom : atom -> t option
(** Given an atom [a], returns a proof of the clause [[a]] if [a] is true at level 0 *)
(** {3 Proof Nodes} *)
val is_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. *)
val expl : 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). *)
val parents : step -> t list
(** Returns the parents of a proof node. *)
(** {3 Proof Manipulation} *)
val expand : proof -> node
(** Return the proof step at the root of a given proof. *)
val step : proof -> step
val conclusion : proof -> clause
(** What is proved at the root of the clause *)
val fold : ('a -> 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. *)
val unsat_core : t -> clause list
(** 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} *)
val check : t -> unit
(** Check the contents of a proof. Mainly for internal use *)
module Tbl : Hashtbl.S with type key = t
end
module Clause : sig
type t = clause
val atoms : t -> atom array (** do not modify *)
val atoms_l : t -> atom list
val forms : t -> formula IArray.t
val forms_l : t -> formula list
val tag : t -> int option
val equal : t -> t -> bool
val make : ?tag:int -> atom array -> t
(** Make a clause from this array of SAT literals.
The array's ownership is transferred to the clause, do not mutate it *)
val make_l : ?tag:int -> atom list -> t
val of_formulas : solver -> ?tag:int -> formula list -> t
val premise : t -> premise
val is_learnt : t -> bool
val name : t -> string
val pp : t printer
val pp_dimacs : t printer
val dummy : t
module Tbl : Hashtbl.S with type key = t
end
module Formula : sig
type t = formula
val pp : t printer
end
end

View file

@ -1,142 +0,0 @@
(**************************************************************************)
(* *)
(* Cubicle *)
(* Combining model checking algorithms and SMT solvers *)
(* *)
(* Sylvain Conchon, Evelyne Contejean *)
(* Francois Bobot, Mohamed Iguernelala, Alain Mebsout *)
(* CNRS, Universite Paris-Sud 11 *)
(* *)
(* Copyright 2011. This file is distributed under the terms of the *)
(* Apache Software License version 2.0 *)
(* *)
(**************************************************************************)
(*
MSAT is free software, using the Apache license, see file LICENSE
Copyright 2016 Guillaume Bury
Copyright 2016 Simon Cruanes
*)
(** SMT Theory
This module defines what a theory must implement in order to
be used in an SMT solver.
*)
(** This type is used during the normalisation of formulas.
See {!val:Expr_intf.S.norm} for more details. *)
type negated =
| Negated (** changed sign *)
| Same_sign (** kept sign *)
(** Type returned by the theory. Formulas in the unsat clause must come from the
current set of assumptions, i.e must have been encountered in a slice. *)
type ('formula, 'proof) res =
| Sat
(** The current set of assumptions is satisfiable. *)
| Unsat of 'formula list * 'proof
(** The current set of assumptions is *NOT* satisfiable, and here is a
theory tautology (with its proof), for which every literal is false
under the current assumptions. *)
module type ACTIONS = sig
type formula
type proof
val push_persistent : formula IArray.t -> proof -> unit
(** Allows to add a persistent clause to the solver. *)
val push_local : formula IArray.t -> proof -> unit
(** Allows to add a local clause to the solver. The clause
will be removed after backtracking. *)
val on_backtrack: (unit -> unit) -> unit
(** [on_backtrack f] calls [f] when the main solver backtracks *)
val propagate : formula -> formula list -> proof -> unit
(** [propagate lit causes proof] informs the solver to propagate [lit], with the reason
that the clause [causes => lit] is a theory tautology. It is faster than pushing
the associated clause but the clause will not be remembered by the sat solver,
i.e it will not be used by the solver to do boolean propagation. *)
end
(** Actions given to the theory during initialization, to be used
at any time *)
type ('form, 'proof) actions =
(module ACTIONS with type formula = 'form and type proof = 'proof)
module type SLICE_ACTIONS = sig
type form
val slice_iter : (form -> unit) -> unit
(** iterate on the slice of the trail *)
end
type 'form slice_actions = (module SLICE_ACTIONS with type form = 'form)
(** The type for a slice. Slices are some kind of view of the current
propagation queue. They allow to look at the propagated literals,
and to add new clauses to the solver. *)
(** {2 Signature for theories to be given to the Solver.} *)
module type S = sig
type t
(** State of the theory *)
type formula
(** The type of formulas. Should be compatble with Formula_intf.S *)
type proof
(** A custom type for the proofs of lemmas produced by the theory. *)
module Form : sig
type t = formula
(** The type of atomic formulas. *)
val equal : t -> t -> bool
(** Equality over formulas. *)
val hash : t -> int
(** Hashing function for formulas. Should be such that two formulas equal according
to {!val:Expr_intf.S.equal} have the same hash. *)
val print : Format.formatter -> t -> unit
(** Printing function used among other thing for debugging. *)
val dummy : t
(** Formula constant. A valid formula should never be physically equal to [dummy] *)
val neg : t -> t
(** Formula negation. Should be an involution, i.e. [equal a (neg neg a)] should
always hold. *)
val norm : t -> t * negated
(** Returns a 'normalized' form of the formula, possibly negated
(in which case return [Negated]). This function is used to recognize
the link between a formula [a] and its negation [neg a], so the goal is
that [a] and [neg a] normalise to the same formula,
but one returns [Same_sign] and the other one returns [Negated] *)
end
val create : (formula, proof) actions -> t
(** Create a new instance of the theory *)
val assume : t -> formula slice_actions -> (formula, proof) res
(** Assume the formulas in the slice, possibly pushing new formulas to be propagated,
and returns the result of the new assumptions. *)
val add_formula : t -> formula -> unit
(** Internalize formula for later use *)
val if_sat : t -> formula slice_actions -> (formula, proof) res
(** Called at the end of the search in case a model has been found. If no new clause is
pushed, then 'sat' is returned, else search is resumed. *)
val post_backtrack : t -> unit
(** After backtracking, this is called (can be used to invalidate
caches, reset task lists, etc.) *)
(**/**)
val check_invariants : t -> unit
(**/**)
end

View file

@ -1,11 +0,0 @@
(library
(name Sidekick_sat)
(public_name sidekick.sat)
(synopsis "core SAT solver for Sidekick")
(libraries sidekick.util)
(flags :standard -w +a-4-42-44-48-50-58-32-60@8
-color always -safe-string -open Sidekick_util)
(ocamlopt_flags :standard -O3 -bin-annot
-unbox-closures -unbox-closures-factor 20)
)

View file

@ -3,9 +3,9 @@
type 'a sequence = ('a -> unit) -> unit
module Key = Het_map.Key
module Key = CCHet.Key
type pair = Het_map.pair =
type pair = CCHet.pair =
| Pair : 'a Key.t * 'a -> pair
include Het_map.Map
include CCHet.Map

View file

@ -1,6 +1,4 @@
module Vec = Sidekick_sat.Vec
module Log = Sidekick_sat.Log
open Solver_types
module N = Equiv_class
@ -18,27 +16,14 @@ end
module Sig_tbl = CCHashtbl.Make(Signature)
module type ACTIONS = sig
val on_backtrack: (unit -> unit) -> unit
(** Register a callback to be invoked upon backtracking below the current level *)
val on_merge: repr -> repr -> explanation -> unit
(** Call this when two classes are merged *)
val raise_conflict: conflict -> 'a
(** Report a conflict *)
val propagate: Lit.t -> Lit.t list -> unit
(** Propagate a literal *)
end
type actions = (module ACTIONS)
type explanation_thunk = explanation lazy_t
type combine_task =
| CT_merge of node * node * explanation_thunk
| CT_distinct of node list * int * explanation
type t = {
tst: Term.state;
acts: actions;
tbl: node Term.Tbl.t;
(* internalization [term -> node] *)
signatures_tbl : node Sig_tbl.t;
@ -50,14 +35,15 @@ type t = {
that have the same "shape" (including head symbol)
have the same signature *)
pending: node Vec.t;
combine: (node * node * explanation_thunk) Vec.t;
on_backtrack:(unit->unit)->unit;
combine: combine_task Vec.t;
undo: (unit -> unit) Backtrack_stack.t;
on_merge: (repr -> repr -> explanation -> unit) option;
mutable ps_lits: Lit.Set.t;
(* proof state *)
ps_queue: (node*node) Vec.t;
(* pairs to explain *)
mutable true_ : node;
mutable false_ : node;
true_ : node lazy_t;
false_ : node lazy_t;
}
(* TODO: an additional union-find to keep track, for each term,
of the terms they are known to be equal to, according
@ -65,18 +51,18 @@ type t = {
several times.
See "fast congruence closure and extensions", Nieuwenhis&al, page 14 *)
let[@inline] on_backtrack cc f : unit = cc.on_backtrack f
let[@inline] is_root_ (n:node) : bool = n.n_root == n
let[@inline] size_ (r:repr) = r.n_size
let[@inline] true_ cc = Lazy.force cc.true_
let[@inline] false_ cc = Lazy.force cc.false_
let[@inline] on_backtrack cc f : unit =
Backtrack_stack.push_if_nonzero_level cc.undo f
(* check if [t] is in the congruence closure.
Invariant: [in_cc t do_cc t => forall u subterm t, in_cc u] *)
let[@inline] mem (cc:t) (t:term): bool = Term.Tbl.mem cc.tbl t
let[@inline] post_backtrack cc =
Vec.clear cc.pending;
Vec.clear cc.combine
(* find representative, recursively *)
let rec find_rec cc (n:node) : repr =
if n==n.n_root then (
@ -95,9 +81,6 @@ let iter_class_ (n:node) : node Sequence.t =
in
aux n
let[@inline] true_ cc = cc.true_
let[@inline] false_ cc = cc.false_
(* get term that should be there *)
let[@inline] get_ cc (t:term) : node =
try Term.Tbl.find cc.tbl t
@ -179,7 +162,7 @@ let push_combine cc t u e : unit =
Log.debugf 5
(fun k->k "(@[<hv1>cc.push_combine@ :t1 %a@ :t2 %a@ :expl %a@])"
N.pp t N.pp u Explanation.pp (Lazy.force e));
Vec.push cc.combine (t,u,e)
Vec.push cc.combine @@ CT_merge (t,u,e)
(* re-root the explanation tree of the equivalence class of [n]
so that it points to [n].
@ -194,13 +177,12 @@ let rec reroot_expl (cc:t) (n:node): unit =
n.n_expl <- E_none;
end
let raise_conflict (cc:t) (e:conflict): _ =
let (module A) = cc.acts in
let raise_conflict (cc:t) (acts:sat_actions) (e:conflict): _ =
(* clear tasks queue *)
Vec.iter (N.set_field N.field_is_pending false) cc.pending;
Vec.clear cc.pending;
Vec.clear cc.combine;
A.raise_conflict e
acts.Msat.acts_raise_conflict e Proof_default
let[@inline] all_classes cc : repr Sequence.t =
Term.Tbl.values cc.tbl
@ -240,11 +222,6 @@ let find_common_ancestor (a:node) (b:node) : node =
let[@inline] ps_add_obligation (cc:t) a b = Vec.push cc.ps_queue (a,b)
let[@inline] ps_add_lit ps l = ps.ps_lits <- Lit.Set.add l ps.ps_lits
and ps_add_obligation_t cc (t1:term) (t2:term) =
let n1 = get_ cc t1 in
let n2 = get_ cc t2 in
ps_add_obligation cc n1 n2
let ps_clear (cc:t) =
cc.ps_lits <- Lit.Set.empty;
Vec.clear cc.ps_queue;
@ -276,7 +253,7 @@ let rec explain_along_path ps (a:node) (parent_a:node) : unit =
(* find explanation *)
let explain_loop (cc : t) : Lit.Set.t =
while not (Vec.is_empty cc.ps_queue) do
let a, b = Vec.pop_last cc.ps_queue in
let a, b = Vec.pop cc.ps_queue in
Log.debugf 5
(fun k->k "(@[cc.explain_loop.at@ %a@ =?= %a@])" N.pp a N.pp b);
assert (N.equal (find cc a) (find cc b));
@ -292,12 +269,6 @@ let explain_eq_n ?(init=Lit.Set.empty) cc (n1:node) (n2:node) : Lit.Set.t =
ps_add_obligation cc n1 n2;
explain_loop cc
let explain_eq_t ?(init=Lit.Set.empty) cc (t1:term) (t2:term) : Lit.Set.t =
ps_clear cc;
cc.ps_lits <- init;
ps_add_obligation_t cc t1 t2;
explain_loop cc
let explain_unfold ?(init=Lit.Set.empty) cc (e:explanation) : Lit.Set.t =
ps_clear cc;
cc.ps_lits <- init;
@ -329,13 +300,22 @@ let relevant_subterms (t:Term.t) : Term.t Sequence.t =
yield b;
yield c
(* Checks if [ra] and [~into] have compatible normal forms and can
be merged w.r.t. the theories.
Side effect: also pushes sub-tasks *)
let notify_merge cc (ra:repr) ~into:(rb:repr) (e:explanation): unit =
assert (is_root_ rb);
match cc.on_merge with
| Some f -> f ra rb e
| None -> ()
(* main CC algo: add terms from [pending] to the signature table,
check for collisions *)
let rec update_tasks (cc:t): unit =
let rec update_tasks (cc:t) (acts:sat_actions) : unit =
while not (Vec.is_empty cc.pending && Vec.is_empty cc.combine) do
Vec.iter (task_pending_ cc) cc.pending;
Vec.clear cc.pending;
Vec.iter (task_combine_ cc) cc.combine;
Vec.iter (task_combine_ cc acts) cc.combine;
Vec.clear cc.combine;
done
@ -356,7 +336,7 @@ and task_pending_ cc n =
| App_cst (f1, a1), App_cst (f2, a2) ->
assert (Cst.equal f1 f2);
assert (IArray.length a1 = IArray.length a2);
Explanation.mk_merges @@ IArray.map2 (fun u1 u2 -> add_ cc u1, add_ cc u2) a1 a2
Explanation.mk_merges @@ IArray.map2 (fun u1 u2 -> add_term_rec_ cc u1, add_term_rec_ cc u2) a1 a2
| If _, _ | App_cst _, _ | Bool _, _
-> assert false
) in
@ -368,9 +348,13 @@ and task_pending_ cc n =
*)
()
and[@inline] task_combine_ cc acts = function
| CT_merge (a,b,e_ab) -> task_merge_ cc acts a b e_ab
| CT_distinct (l,tag,e) -> task_distinct_ cc acts l tag e
(* main CC algo: merge equivalence classes in [st.combine].
@raise Exn_unsat if merge fails *)
and task_combine_ cc (a,b,e_ab) : unit =
and task_merge_ cc acts a b e_ab : unit =
let ra = find cc a in
let rb = find cc b in
if not (N.equal ra rb) then (
@ -387,15 +371,15 @@ and task_combine_ cc (a,b,e_ab) : unit =
else ra, rb
in
(* check we're not merging [true] and [false] *)
if (N.equal ra cc.true_ && N.equal rb cc.false_) ||
(N.equal rb cc.true_ && N.equal ra cc.false_) then (
if (N.equal ra (true_ cc) && N.equal rb (false_ cc)) ||
(N.equal rb (true_ cc) && N.equal ra (false_ cc)) then (
Log.debugf 5
(fun k->k "(@[<hv>cc.merge.true_false_conflict@ @[:r1 %a@]@ @[:r2 %a@]@ :e_ab %a@])"
N.pp ra N.pp rb Explanation.pp e_ab);
let lits = explain_unfold cc e_ab in
let lits = explain_eq_n ~init:lits cc a ra in
let lits = explain_eq_n ~init:lits cc b rb in
raise_conflict cc @@ Lit.Set.elements lits
raise_conflict cc acts @@ Lit.Set.elements lits
);
(* update set of tags the new node cannot be equal to *)
let new_tags =
@ -413,13 +397,16 @@ and task_combine_ cc (a,b,e_ab) : unit =
let lits = explain_unfold ~init:lits cc e_ab in
let lits = explain_eq_n ~init:lits cc a n1 in
let lits = explain_eq_n ~init:lits cc b n2 in
raise_conflict cc @@ Lit.Set.elements lits)
raise_conflict cc acts @@ Lit.Set.elements lits)
ra.n_tags rb.n_tags
in
(* when merging terms with [true] or [false], possibly propagate them to SAT *)
let merge_bool r1 t1 r2 t2 =
if N.equal r1 cc.true_ then propagate_bools cc r2 t2 r1 t1 e_ab true
else if N.equal r1 cc.false_ then propagate_bools cc r2 t2 r1 t1 e_ab false
if N.equal r1 (true_ cc) then (
propagate_bools cc acts r2 t2 r1 t1 e_ab true
) else if N.equal r1 (false_ cc) then (
propagate_bools cc acts r2 t2 r1 t1 e_ab false
)
in
merge_bool ra a rb b;
merge_bool rb b ra a;
@ -471,12 +458,28 @@ and task_combine_ cc (a,b,e_ab) : unit =
notify_merge cc r_from ~into:r_into e_ab;
)
and task_distinct_ cc acts (l:node list) tag expl : unit =
let l = List.map (fun n -> n, find cc n) l in
let coll =
Sequence.diagonal_l l
|> Sequence.find_pred (fun ((_,r1),(_,r2)) -> N.equal r1 r2)
in
begin match coll with
| Some ((n1,_r1),(n2,_r2)) ->
(* two classes are already equal *)
Log.debugf 5 (fun k->k "(@[cc.distinct.conflict@ %a = %a@])" N.pp n1 N.pp n2);
let lits = explain_unfold cc expl in
acts.Msat.acts_raise_conflict (Lit.Set.to_list lits) Proof_default
| None ->
(* put a tag on all equivalence classes, that will make their merge fail *)
List.iter (fun (_,n) -> add_tag_n cc n tag expl) l
end
(* we are merging [r1] with [r2==Bool(sign)], so propagate each term [u1]
in the equiv class of [r1] that is a known literal back to the SAT solver
and which is not the one initially merged.
We can explain the propagation with [u1 = t1 =e= t2 = r2==bool] *)
and propagate_bools cc r1 t1 r2 t2 (e_12:explanation) sign : unit =
let (module A) = cc.acts in
and propagate_bools cc acts r1 t1 r2 t2 (e_12:explanation) sign : unit =
(* explanation for [t1 =e= t2 = r2] *)
let half_expl = lazy (
let expl = explain_unfold cc e_12 in
@ -494,17 +497,10 @@ and propagate_bools cc r1 t1 r2 t2 (e_12:explanation) sign : unit =
Log.debugf 5 (fun k->k "(@[cc.bool_propagate@ %a@])" Lit.pp lit);
(* complete explanation with the [u1=t1] chunk *)
let expl = explain_eq_n ~init:(Lazy.force half_expl) cc u1 t1 in
A.propagate lit (Lit.Set.to_list expl)
let reason = Msat.Consequence (Lit.Set.to_list expl, Proof_default) in
acts.Msat.acts_propagate lit reason
))
(* Checks if [ra] and [~into] have compatible normal forms and can
be merged w.r.t. the theories.
Side effect: also pushes sub-tasks *)
and notify_merge cc (ra:repr) ~into:(rb:repr) (e:explanation): unit =
assert (is_root_ rb);
let (module A) = cc.acts in
A.on_merge ra rb e
(* add [t] to [cc] when not present already *)
and add_new_term_ cc (t:term) : node =
assert (not @@ mem cc t);
@ -518,7 +514,7 @@ and add_new_term_ cc (t:term) : node =
in
(* add sub-term to [cc], and register [n] to its parents *)
let add_sub_t (u:term) : unit =
let n_u = add_ cc u in
let n_u = add_term_rec_ cc u in
add_to_parents_of_sub_node n_u
in
(* register sub-terms, add [t] to their parent list *)
@ -535,16 +531,16 @@ and add_new_term_ cc (t:term) : node =
n
(* add a term *)
and[@inline] add_ cc t : node =
and[@inline] add_term_rec_ cc t : node =
try Term.Tbl.find cc.tbl t
with Not_found -> add_new_term_ cc t
let check_invariants_ (cc:t) =
Log.debug 5 "(cc.check-invariants)";
Log.debugf 15 (fun k-> k "%a" pp_full cc);
assert (Term.equal (Term.true_ cc.tst) cc.true_.n_term);
assert (Term.equal (Term.false_ cc.tst) cc.false_.n_term);
assert (not @@ same_class cc cc.true_ cc.false_);
assert (Term.equal (Term.true_ cc.tst) (true_ cc).n_term);
assert (Term.equal (Term.false_ cc.tst) (false_ cc).n_term);
assert (not @@ same_class cc (true_ cc) (false_ cc));
assert (Vec.is_empty cc.combine);
assert (Vec.is_empty cc.pending);
(* check that subterms are internalized *)
@ -576,15 +572,23 @@ let check_invariants_ (cc:t) =
let[@inline] check_invariants (cc:t) : unit =
if Util._CHECK_INVARIANTS then check_invariants_ cc
let add cc t : node =
let n = add_ cc t in
update_tasks cc;
n
let[@inline] add cc t : node = add_term_rec_ cc t
let add_seq cc seq =
seq (fun t -> ignore @@ add_ cc t);
update_tasks cc
seq (fun t -> ignore @@ add_term_rec_ cc t);
()
let[@inline] push_level (self:t) : unit =
Backtrack_stack.push_level self.undo
let pop_levels (self:t) n : unit =
Backtrack_stack.pop_levels self.undo n ~f:(fun f -> f());
Vec.clear self.pending;
Vec.clear self.combine;
()
(* TODO: if a lit is [= a b], merge [a] and [b];
if it's [distinct a1an], make them distinct, etc. etc. *)
(* assert that this boolean literal holds *)
let assert_lit cc lit : unit =
let t = Lit.view lit in
@ -593,69 +597,59 @@ let assert_lit cc lit : unit =
let sign = Lit.sign lit in
(* equate t and true/false *)
let rhs = if sign then true_ cc else false_ cc in
let n = add_ cc t in
let n = add_term_rec_ cc t in
(* TODO: ensure that this is O(1).
basically, just have [n] point to true/false and thus acquire
the corresponding value, so its superterms (like [ite]) can evaluate
properly *)
push_combine cc n rhs (Lazy.from_val @@ E_lit lit);
update_tasks cc
push_combine cc n rhs (Lazy.from_val @@ E_lit lit)
let[@inline] assert_lits cc lits : unit =
Sequence.iter (assert_lit cc) lits
let assert_eq cc (t:term) (u:term) e : unit =
let n1 = add_ cc t in
let n2 = add_ cc u in
let n1 = add_term_rec_ cc t in
let n2 = add_term_rec_ cc u in
if not (same_class cc n1 n2) then (
let e = Lazy.from_val @@ Explanation.E_lits e in
push_combine cc n1 n2 e;
);
update_tasks cc
)
let assert_distinct cc (l:term list) ~neq (lit:Lit.t) : unit =
assert (match l with[] | [_] -> false | _ -> true);
let tag = Term.id neq in
Log.debugf 5
(fun k->k "(@[cc.assert_distinct@ (@[%a@])@ :tag %d@])" (Util.pp_list Term.pp) l tag);
let l = List.map (fun t -> t, add cc t |> find cc) l in
let coll =
Sequence.diagonal_l l
|> Sequence.find_pred (fun ((_,n1),(_,n2)) -> N.equal n1 n2)
in
begin match coll with
| Some ((t1,_n1),(t2,_n2)) ->
(* two classes are already equal *)
Log.debugf 5 (fun k->k "(@[cc.assert_distinct.conflict@ %a = %a@])" Term.pp t1 Term.pp t2);
let lits = Lit.Set.singleton lit in
let lits = explain_eq_t ~init:lits cc t1 t2 in
raise_conflict cc @@ Lit.Set.to_list lits
| None ->
(* put a tag on all equivalence classes, that will make their merge fail *)
List.iter (fun (_,n) -> add_tag_n cc n tag @@ Explanation.lit lit) l
end
let l = List.map (add cc) l in
Vec.push cc.combine @@ CT_distinct (l, tag, Explanation.lit lit)
let create ?(size=2048) ~actions (tst:Term.state) : t =
let nd = N.dummy in
let (module A : ACTIONS) = actions in
let cc = {
let create ?on_merge ?(size=`Big) (tst:Term.state) : t =
let size = match size with `Small -> 128 | `Big -> 2048 in
let rec cc = {
tst;
acts=actions;
tbl = Term.Tbl.create size;
signatures_tbl = Sig_tbl.create size;
pending=Vec.make_empty N.dummy;
combine=Vec.make_empty (N.dummy,N.dummy,Lazy.from_val Explanation.dummy);
on_merge;
pending=Vec.create();
combine=Vec.create();
ps_lits=Lit.Set.empty;
on_backtrack=A.on_backtrack;
ps_queue=Vec.make_empty (nd,nd);
true_ = N.dummy;
false_ = N.dummy;
} in
cc.true_ <- add_ cc (Term.true_ tst);
cc.false_ <- add_ cc (Term.false_ tst);
update_tasks cc;
undo=Backtrack_stack.create();
ps_queue=Vec.create();
true_;
false_;
} and true_ = lazy (
add_term_rec_ cc (Term.true_ tst)
) and false_ = lazy (
add_term_rec_ cc (Term.false_ tst)
)
in
ignore (Lazy.force true_ : node);
ignore (Lazy.force false_ : node);
cc
let final_check cc : unit =
Log.debug 5 "(CC.final_check)";
update_tasks cc
let[@inline] check cc acts : unit =
Log.debug 5 "(CC.check)";
update_tasks cc acts
(* model: map each uninterpreted equiv class to some ID *)
let mk_model (cc:t) (m:Model.t) : Model.t =
@ -671,8 +665,8 @@ let mk_model (cc:t) (m:Model.t) : Model.t =
let v = match Model.eval m t with
| Some v -> v
| None ->
if same_class cc r cc.true_ then Value.true_
else if same_class cc r cc.false_ then Value.false_
if same_class cc r (true_ cc) then Value.true_
else if same_class cc r (false_ cc) then Value.false_
else (
Value.mk_elt
(ID.makef "v_%d" @@ Term.id t)

View file

@ -13,25 +13,9 @@ type repr = Equiv_class.t
type conflict = Theory.conflict
module type ACTIONS = sig
val on_backtrack: (unit -> unit) -> unit
(** Register a callback to be invoked upon backtracking below the current level *)
val on_merge: repr -> repr -> explanation -> unit
(** Call this when two classes are merged *)
val raise_conflict: conflict -> 'a
(** Report a conflict *)
val propagate: Lit.t -> Lit.t list -> unit
(** Propagate a literal *)
end
type actions = (module ACTIONS)
val create :
?size:int ->
actions:actions ->
?on_merge:(repr -> repr -> explanation -> unit) ->
?size:[`Small | `Big] ->
Term.state ->
t
(** Create a new congruence closure.
@ -55,6 +39,8 @@ val assert_lit : t -> Lit.t -> unit
(** Given a literal, assume it in the congruence closure and propagate
its consequences. Will be backtracked. *)
val assert_lits : t -> Lit.t Sequence.t -> unit
val assert_eq : t -> term -> term -> Lit.t list -> unit
val assert_distinct : t -> term list -> neq:term -> Lit.t -> unit
@ -62,9 +48,13 @@ val assert_distinct : t -> term list -> neq:term -> Lit.t -> unit
with explanation [e]
precond: [u = distinct l] *)
val final_check : t -> unit
val check : t -> sat_actions -> unit
(** Perform all pending operations done via {!assert_eq}, {!assert_lit}, etc.
Will use the [sat_actions] to propagate literals, declare conflicts, etc. *)
val post_backtrack : t -> unit
val push_level : t -> unit
val pop_levels : t -> int -> unit
val mk_model : t -> Model.t -> Model.t
(** Enrich a model by mapping terms to their representative's value,

View file

@ -38,21 +38,14 @@ let set_payload ?(can_erase=fun _->false) n e =
n.n_payload <- aux n.n_payload
let payload_find ~f:p n =
begin match n.n_payload with
let[@unroll 2] rec aux = function
| [] -> None
| e1 :: tail ->
match p e1, tail with
| Some _ as res, _ -> res
| None, [] -> None
| None, e2 :: tail2 ->
match p e2, tail2 with
| Some _ as res, _ -> res
| None, [] -> None
| None, e3 :: tail3 ->
match p e3 with
| Some _ as res -> res
| None -> CCList.find_map p tail3
end
match p e1 with
| Some _ as res -> res
| None -> aux tail
in
aux n.n_payload
let payload_pred ~f:p n =
begin match n.n_payload with
@ -71,5 +64,3 @@ module Tbl = CCHashtbl.Make(struct
let equal = equal
let hash = hash
end)
let dummy = make Term.dummy

View file

@ -59,7 +59,3 @@ val get_field : Node_bits.field -> t -> bool
val set_field : Node_bits.field -> bool -> t -> unit
module Tbl : CCHashtbl.S with type key = t
(**/**)
val dummy : t
(**/**)

View file

@ -17,7 +17,6 @@ let mk_lit l : t = E_lit l
let mk_lits = function [x] -> mk_lit x | l -> E_lits l
let mk_reduction : t = E_reduction
let dummy = E_lit Lit.dummy
let[@inline] lit l : t = E_lit l
module Set = CCSet.Make(struct

View file

@ -1,191 +0,0 @@
(* This file is free software, part of containers. See file "license" for more details. *)
(** {1 Associative containers with Heterogenerous Values} *)
(*$R
let k1 : int Key.t = Key.create() in
let k2 : int Key.t = Key.create() in
let k3 : string Key.t = Key.create() in
let k4 : float Key.t = Key.create() in
let tbl = Tbl.create () in
Tbl.add tbl k1 1;
Tbl.add tbl k2 2;
Tbl.add tbl k3 "k3";
assert_equal (Some 1) (Tbl.find tbl k1);
assert_equal (Some 2) (Tbl.find tbl k2);
assert_equal (Some "k3") (Tbl.find tbl k3);
assert_equal None (Tbl.find tbl k4);
assert_equal 3 (Tbl.length tbl);
Tbl.add tbl k1 10;
assert_equal (Some 10) (Tbl.find tbl k1);
assert_equal 3 (Tbl.length tbl);
assert_equal None (Tbl.find tbl k4);
Tbl.add tbl k4 0.0;
assert_equal (Some 0.0) (Tbl.find tbl k4);
()
*)
type 'a sequence = ('a -> unit) -> unit
type 'a gen = unit -> 'a option
module type KEY_IMPL = sig
type t
exception Store of t
val id : int
end
module Key = struct
type 'a t = (module KEY_IMPL with type t = 'a)
let _n = ref 0
let create (type k) () =
incr _n;
let id = !_n in
let module K = struct
type t = k
let id = id
exception Store of k
end in
(module K : KEY_IMPL with type t = k)
let id (type k) (module K : KEY_IMPL with type t = k) = K.id
let equal
: type a b. a t -> b t -> bool
= fun (module K1) (module K2) -> K1.id = K2.id
end
type pair =
| Pair : 'a Key.t * 'a -> pair
type exn_pair =
| E_pair : 'a Key.t * exn -> exn_pair
let pair_of_e_pair (E_pair (k,e)) =
let module K = (val k) in
match e with
| K.Store v -> Pair (k,v)
| _ -> assert false
module Tbl = struct
module M = Hashtbl.Make(struct
type t = int
let equal (i:int) j = i=j
let hash (i:int) = Hashtbl.hash i
end)
type t = exn_pair M.t
let create ?(size=16) () = M.create size
let mem t k = M.mem t (Key.id k)
let find_exn (type a) t (k : a Key.t) : a =
let module K = (val k) in
let E_pair (_, v) = M.find t K.id in
match v with
| K.Store v -> v
| _ -> assert false
let find t k =
try Some (find_exn t k)
with Not_found -> None
let add_pair_ t p =
let Pair (k,v) = p in
let module K = (val k) in
let p = E_pair (k, K.Store v) in
M.replace t K.id p
let add t k v = add_pair_ t (Pair (k,v))
let length t = M.length t
let iter f t = M.iter (fun _ pair -> f (pair_of_e_pair pair)) t
let to_seq t yield = iter yield t
let to_list t = M.fold (fun _ p l -> pair_of_e_pair p::l) t []
let add_list t l = List.iter (add_pair_ t) l
let add_seq t seq = seq (add_pair_ t)
let of_list l =
let t = create() in
add_list t l;
t
let of_seq seq =
let t = create() in
add_seq t seq;
t
end
module Map = struct
module M = Map.Make(struct
type t = int
let compare (i:int) j = Pervasives.compare i j
end)
type t = exn_pair M.t
let empty = M.empty
let mem k t = M.mem (Key.id k) t
let find_exn (type a) (k : a Key.t) t : a =
let module K = (val k) in
let E_pair (_, e) = M.find K.id t in
match e with
| K.Store v -> v
| _ -> assert false
let find k t =
try Some (find_exn k t)
with Not_found -> None
let add_e_pair_ p t =
let E_pair ((module K),_) = p in
M.add K.id p t
let add_pair_ p t =
let Pair ((module K) as k,v) = p in
let p = E_pair (k, K.Store v) in
M.add K.id p t
let add (type a) (k : a Key.t) v t =
let module K = (val k) in
add_e_pair_ (E_pair (k, K.Store v)) t
let cardinal t = M.cardinal t
let length = cardinal
let iter f t = M.iter (fun _ p -> f (pair_of_e_pair p)) t
let to_seq t yield = iter yield t
let to_list t = M.fold (fun _ p l -> pair_of_e_pair p::l) t []
let add_list t l = List.fold_right add_pair_ l t
let add_seq t seq =
let t = ref t in
seq (fun pair -> t := add_pair_ pair !t);
!t
let of_list l = add_list empty l
let of_seq seq = add_seq empty seq
end

View file

@ -1,85 +0,0 @@
(* This file is free software, part of containers. See file "license" for more details. *)
(** {1 Associative containers with Heterogenerous Values} *)
type 'a sequence = ('a -> unit) -> unit
type 'a gen = unit -> 'a option
module Key : sig
type 'a t
val create : unit -> 'a t
val equal : 'a t -> 'a t -> bool
(** Compare two keys that have compatible types *)
end
type pair =
| Pair : 'a Key.t * 'a -> pair
(** {2 Imperative table indexed by {!Key}} *)
module Tbl : sig
type t
val create : ?size:int -> unit -> t
val mem : t -> _ Key.t -> bool
val add : t -> 'a Key.t -> 'a -> unit
val length : t -> int
val find : t -> 'a Key.t -> 'a option
val find_exn : t -> 'a Key.t -> 'a
(** @raise Not_found if the key is not in the table *)
val iter : (pair -> unit) -> t -> unit
val to_seq : t -> pair sequence
val of_seq : pair sequence -> t
val add_seq : t -> pair sequence -> unit
val add_list : t -> pair list -> unit
val of_list : pair list -> t
val to_list : t -> pair list
end
(** {2 Immutable map} *)
module Map : sig
type t
val empty : t
val mem : _ Key.t -> t -> bool
val add : 'a Key.t -> 'a -> t -> t
val length : t -> int
val cardinal : t -> int
val find : 'a Key.t -> t -> 'a option
val find_exn : 'a Key.t -> t -> 'a
(** @raise Not_found if the key is not in the table *)
val iter : (pair -> unit) -> t -> unit
val to_seq : t -> pair sequence
val of_seq : pair sequence -> t
val add_seq : t -> pair sequence -> t
val add_list : t -> pair list -> t
val of_list : pair list -> t
val to_list : t -> pair list
end

View file

@ -6,8 +6,7 @@ type t = lit = {
lit_sign : bool
}
let neg l = {l with lit_sign=not l.lit_sign}
let[@inline] neg l = {l with lit_sign=not l.lit_sign}
let[@inline] sign t = t.lit_sign
let[@inline] view (t:t): term = t.lit_term
@ -15,8 +14,6 @@ let[@inline] abs t: t = {t with lit_sign=true}
let make ~sign t = {lit_sign=sign; lit_term=t}
let dummy = make ~sign:true Term.dummy
let atom ?(sign=true) (t:term) : t =
let t, sign' = Term.abs t in
let sign = if not sign' then not sign else sign in
@ -31,7 +28,7 @@ let pp = pp_lit
let print = pp
let norm l =
if l.lit_sign then l, Sidekick_sat.Same_sign else neg l, Sidekick_sat.Negated
if l.lit_sign then l, Msat.Solver_intf.Same_sign else neg l, Msat.Solver_intf.Negated
module Set = CCSet.Make(struct type t = lit let compare=compare end)
module Tbl = CCHashtbl.Make(struct type t = lit let equal=equal let hash=hash end)

View file

@ -12,14 +12,13 @@ val abs : t -> t
val sign : t -> bool
val view : t -> term
val as_atom : t -> term * bool
val dummy : t
val atom : ?sign:bool -> term -> t
val hash : t -> int
val compare : t -> t -> int
val equal : t -> t -> bool
val print : t Fmt.printer
val pp : t Fmt.printer
val norm : t -> t * Sidekick_sat.negated
val norm : t -> t * Msat.Solver_intf.negated
module Set : CCSet.S with type elt = t
module Tbl : CCHashtbl.S with type key = t

View file

@ -11,10 +11,10 @@ let get_time : unit -> float = Sys.time
(** {2 The Main Solver} *)
module Sat_solver = Sidekick_sat.Make(Theory_combine)
module Sat_solver = Msat.Make_cdcl_t(Theory_combine)
let[@inline] clause_of_mclause (c:Sat_solver.clause): Lit.t IArray.t =
Sat_solver.Clause.forms c
Sat_solver.Clause.atoms c |> Array.map Sat_solver.Atom.formula |> IArray.of_array_unsafe
module Proof = struct
type t = Sat_solver.Proof.t
@ -59,13 +59,14 @@ let stats self = self.stat
let[@inline] tst self = Theory_combine.tst (th_combine self)
let create ?size ?(config=Config.empty) ~theories () : t =
let th_combine = Theory_combine.create() in
let self = {
solver=Sat_solver.create ?size ();
solver=Sat_solver.create ?size th_combine;
stat=Stat.create ();
config;
} in
(* now add the theories *)
Theory_combine.add_theory_l (th_combine self) theories;
Theory_combine.add_theory_l th_combine theories;
self
(** {2 Sat Solver} *)
@ -193,8 +194,8 @@ let do_on_exit ~on_exit =
let assume (self:t) (c:Lit.t IArray.t) : unit =
let sat = solver self in
let c = Sat_solver.Clause.make (IArray.to_array_map (Sat_solver.Atom.make sat) c) in
Sat_solver.add_clause ~permanent:true sat c
let c = IArray.to_array_map (Sat_solver.make_atom sat) c in
Sat_solver.add_clause_a sat c Proof_default
let[@inline] assume_eq self t u expl : unit =
Congruence_closure.assert_eq (cc self) t u [expl]
@ -202,18 +203,22 @@ let[@inline] assume_eq self t u expl : unit =
let[@inline] assume_distinct self l ~neq lit : unit =
Congruence_closure.assert_distinct (cc self) l lit ~neq
let check_model (s:t) : unit =
let check_model (_s:t) : unit =
Log.debug 1 "(smt.solver.check-model)";
(* TODO
Sat_solver.check_model s.solver
*)
()
(* TODO: main loop with iterative deepening of the unrolling limit
(not the value depth limit) *)
let solve ?restarts ?on_exit:(_=[]) ?check:(_=true) ~assumptions (self:t) : res =
let r = Sat_solver.solve ?restarts ~assumptions (solver self) () in
let solve ?on_exit:(_=[]) ?check:(_=true) ~assumptions (self:t) : res =
let r = Sat_solver.solve ~assumptions (solver self) in
match r with
| Sat_solver.Sat (Sidekick_sat.Sat_state st) ->
| Sat_solver.Sat st ->
Log.debugf 0 (fun k->k "SAT");
let m = Theory_combine.mk_model (th_combine self) st.iter_trail in
let lits f = st.iter_trail f (fun _ -> ()) in
let m = Theory_combine.mk_model (th_combine self) lits in
Sat m
(*
let env = Ast.env_empty in
@ -221,7 +226,7 @@ let solve ?restarts ?on_exit:(_=[]) ?check:(_=true) ~assumptions (self:t) : res
Unknown U_incomplete (* TODO *)
*)
| Sat_solver.Unsat (Sidekick_sat.Unsat_state us) ->
| Sat_solver.Unsat us ->
let pr = us.get_proof () in
Unsat pr

View file

@ -5,8 +5,8 @@
The solving algorithm, based on MCSat *)
module Sat_solver : Sidekick_sat.S
with type formula = Lit.t
module Sat_solver : Msat.S
with module Formula = Lit
and type theory = Theory_combine.t
and type lemma = Theory_combine.proof

View file

@ -1,4 +1,7 @@
module Vec = Msat.Vec
module Log = Msat.Log
module Fmt = CCFormat
module Node_bits = CCBitField.Make(struct end)
@ -135,6 +138,10 @@ and value =
and value_custom_view = ..
type proof = Proof_default
type sat_actions = (Msat.void, lit, value, proof) Msat.acts
let[@inline] term_equal_ (a:term) b = a==b
let[@inline] term_hash_ a = a.term_id
let[@inline] term_cmp_ a b = CCInt.compare a.term_id b.term_id

View file

@ -113,9 +113,3 @@ let as_cst_undef (t:term): (cst * Ty.Fun.t) option =
| _ -> None
let pp = Solver_types.pp_term
let dummy : t = {
term_id= -1;
term_ty=Ty.prop;
term_view=Term_cell.true_;
}

View file

@ -56,7 +56,3 @@ val as_cst_undef : t -> (cst * Ty.Fun.t) option
module Tbl : CCHashtbl.S with type key = t
module Map : CCMap.S with type key = t
(**/**)
val dummy : t
(**/**)

View file

@ -1,5 +1,5 @@
module Clause : sig
module Th_clause : sig
type t = Lit.t IArray.t
val pp : t CCFormat.printer
end = struct
@ -14,35 +14,6 @@ end = struct
)
end
module type STATE = sig
type t
val state : t
val on_merge: t -> Equiv_class.t -> Equiv_class.t -> Explanation.t -> unit
(** Called when two classes are merged *)
val on_assert: t -> Lit.t -> unit
(** Called when a literal becomes true *)
val final_check: t -> Lit.t Sequence.t -> unit
(** Final check, must be complete (i.e. must raise a conflict
if the set of literals is not satisfiable) *)
val mk_model : t -> Lit.t Sequence.t -> Model.t
(** Make a model for this theory's terms *)
val post_backtrack : t -> unit
(**/**)
val check_invariants : t -> unit
(**/**)
end
(** Runtime state of a theory, with all the operations it provides. *)
type state = (module STATE)
(** Unsatisfiable conjunction.
Its negation will become a conflict clause *)
type conflict = Lit.t list
@ -83,31 +54,63 @@ end
type actions = (module ACTIONS)
type t = {
name: string;
make: Term.state -> actions -> state;
}
module type S = sig
type t
let make ~name ~make () : t = {name;make}
val name : string
(** Name of the theory *)
let make_st
val create : Term.state -> t
(** Instantiate the theory's state *)
val on_merge: t -> actions -> Equiv_class.t -> Equiv_class.t -> Explanation.t -> unit
(** Called when two classes are merged *)
val partial_check : t -> actions -> Lit.t Sequence.t -> unit
(** Called when a literal becomes true *)
val final_check: t -> actions -> Lit.t Sequence.t -> unit
(** Final check, must be complete (i.e. must raise a conflict
if the set of literals is not satisfiable) *)
val mk_model : t -> Lit.t Sequence.t -> Model.t
(** Make a model for this theory's terms *)
val push_level : t -> unit
val pop_levels : t -> int -> unit
(**/**)
val check_invariants : t -> unit
(**/**)
end
type t = (module S)
type 'a t1 = (module S with type t = 'a)
let make
(type st)
?(check_invariants=fun _ -> ())
?(on_merge=fun _ _ _ _ -> ())
?(on_assert=fun _ _ -> ())
?(on_merge=fun _ _ _ _ _ -> ())
?(partial_check=fun _ _ _ -> ())
?(mk_model=fun _ _ -> Model.empty)
?(post_backtrack=fun _ -> ())
?(push_level=fun _ -> ())
?(pop_levels=fun _ _ -> ())
~name
~final_check
~st
() : state =
~create
() : t =
let module A = struct
type nonrec t = st
let state = st
let name = name
let create = create
let on_merge = on_merge
let on_assert = on_assert
let partial_check = partial_check
let final_check = final_check
let mk_model = mk_model
let post_backtrack = post_backtrack
let check_invariants = check_invariants
let push_level = push_level
let pop_levels = pop_levels
end in
(module A : STATE)
(module A : S)

View file

@ -3,13 +3,12 @@
(** Combine the congruence closure with a number of plugins *)
module Sat_solver = Sidekick_sat
module C_clos = Congruence_closure
open Solver_types
module Proof = struct
type t = Proof
let default = Proof
type t = Solver_types.proof
let default = Proof_default
end
module Form = Lit
@ -18,60 +17,64 @@ type formula = Lit.t
type proof = Proof.t
type conflict = Theory.conflict
(* raise upon conflict *)
exception Exn_conflict of conflict
type theory_state =
| Th_state : ('a Theory.t1 * 'a) -> theory_state
(* TODO: first-class module instead *)
type t = {
cdcl_acts: (formula,proof) Sat_solver.actions;
(** actions provided by the SAT solver *)
tst: Term.state;
(** state for managing terms *)
cc: C_clos.t lazy_t;
(** congruence closure *)
mutable theories : Theory.state list;
mutable theories : theory_state list;
(** Set of theories *)
}
let[@inline] cc t = Lazy.force t.cc
let[@inline] tst t = t.tst
let[@inline] theories (self:t) : Theory.state Sequence.t =
let[@inline] theories (self:t) : theory_state Sequence.t =
fun k -> List.iter k self.theories
(** {2 Interface with the SAT solver} *)
(* handle a literal assumed by the SAT solver *)
let assume_lit (self:t) (lit:Lit.t) : unit =
Sat_solver.Log.debugf 2
(fun k->k "(@[<1>@{<green>th_combine.assume_lit@}@ @[%a@]@])" Lit.pp lit);
(* check consistency first *)
begin match Lit.view lit with
| {term_view=Bool true; _} -> ()
| {term_view=Bool false; _} -> ()
| _ ->
(* transmit to theories. *)
C_clos.assert_lit (cc self) lit;
theories self (fun (module Th) -> Th.on_assert Th.state lit);
end
module Plugin = struct
let with_conflict_catch f =
(* handle a literal assumed by the SAT solver *)
let assert_lits (self:t) acts (lits:Lit.t Sequence.t) : unit =
Msat.Log.debugf 2
(fun k->k "(@[<1>@{<green>th_combine.assume_lits@}@ @[%a@]@])" (Fmt.seq Lit.pp) lits);
(* transmit to theories. *)
C_clos.assert_lits (cc self) lits;
C_clos.check (cc self) acts;
theories self (fun (Th_state ((module Th),st)) -> Th.partial_check st acts lits);
()
(* TODO: remove
let with_conflict_catch acts f =
try
f ();
Sat_solver.Sat
with Exn_conflict lit_set ->
let conflict_clause = IArray.of_list_map Lit.neg lit_set in
Sat_solver.Log.debugf 3
Msat.Log.debugf 3
(fun k->k "(@[<1>@{<yellow>th_combine.conflict@}@ :clause %a@])"
Theory.Clause.pp conflict_clause);
Sat_solver.Unsat (IArray.to_list conflict_clause, Proof.default)
acts.Msat.acts_raise_conflict (IArray.to_list conflict_clause) Proof.default
*)
let[@inline] iter_atoms_ acts : _ Sequence.t =
fun f ->
acts.Msat.acts_iter_assumptions
(function
| Msat.Lit a -> f a
| Msat.Assign _ -> assert false)
(* propagation from the bool solver *)
let assume_real (self:t) (slice:Lit.t Sat_solver.slice_actions) =
let check_ (self:t) (acts:_ Msat.acts) =
let iter = iter_atoms_ acts in
(* TODO if Config.progress then print_progress(); *)
let (module SA) = slice in
Log.debugf 5 (fun k->k "(th_combine.assume :len %d)" (Sequence.length @@ SA.slice_iter));
with_conflict_catch
(fun () -> SA.slice_iter (assume_lit self))
Msat.Log.debugf 5 (fun k->k "(th_combine.assume :len %d)" (Sequence.length iter));
iter (assume_lit self);
Congruence_closure.check (cc self) acts
let add_formula (self:t) (lit:Lit.t) =
let t = Lit.view lit in
@ -81,19 +84,17 @@ let add_formula (self:t) (lit:Lit.t) =
()
(* propagation from the bool solver *)
let[@inline] assume (self:t) (slice:_ Sat_solver.slice_actions) =
assume_real self slice
let[@inline] partial_check (self:t) (acts:_ Msat.acts) =
check_ self acts
(* perform final check of the model *)
let if_sat (self:t) (slice:Lit.t Sat_solver.slice_actions) : _ Sat_solver.res =
let final_check (self:t) (acts:_ Msat.acts) : unit =
(* all formulas in the SAT solver's trail *)
let (module SA) = slice in
with_conflict_catch
(fun () ->
(* final check for CC + each theory *)
C_clos.final_check (cc self);
theories self
(fun (module Th) -> Th.final_check Th.state SA.slice_iter))
let iter = iter_atoms_ acts in
(* final check for CC + each theory *)
Congruence_closure.check (cc self) acts;
theories self
(fun (module Th) -> Th.final_check Th.state iter)
let mk_model (self:t) lits : Model.t =
let m =
@ -107,44 +108,31 @@ let mk_model (self:t) lits : Model.t =
(** {2 Various helpers} *)
(* forward propagations from CC or theories directly to the SMT core *)
let act_propagate (self:t) f guard : unit =
let (module A) = self.cdcl_acts in
Sat_solver.Log.debugf 2
let act_propagate acts f guard : unit =
Msat.Log.debugf 2
(fun k->k "(@[@{<green>th.propagate@}@ %a@ :guard %a@])"
Lit.pp f (Util.pp_list Lit.pp) guard);
A.propagate f guard Proof.default
let reason = Msat.Consequence (guard,Proof.default) in
acts.Msat.acts_propagate f reason
(** {2 Interface to Congruence Closure} *)
let act_raise_conflict e = raise (Exn_conflict e)
(* when CC decided to merge [r1] and [r2], notify theories *)
let on_merge_from_cc (self:t) r1 r2 e : unit =
theories self
(fun (module Th) -> Th.on_merge Th.state r1 r2 e)
let mk_cc_actions (self:t) : C_clos.actions =
let (module A) = self.cdcl_acts in
let module R = struct
let on_backtrack = A.on_backtrack
let on_merge = on_merge_from_cc self
let raise_conflict = act_raise_conflict
let propagate = act_propagate self
end in
(module R)
(** {2 Main} *)
(* create a new theory combination *)
let create (cdcl_acts:_ Sat_solver.actions) : t =
Sat_solver.Log.debug 5 "th_combine.create";
let create () : t =
Log.debug 5 "th_combine.create";
let rec self = {
cdcl_acts;
tst=Term.create ~size:1024 ();
cc = lazy (
(* lazily tie the knot *)
let actions = mk_cc_actions self in
C_clos.create ~size:1024 ~actions self.tst;
let on_merge = on_merge_from_cc self in
C_clos.create ~on_merge ~size:`Big self.tst;
);
theories = [];
} in
@ -165,17 +153,18 @@ let act_find self t =
C_clos.add (cc self) t
|> C_clos.find (cc self)
(* TODO: remove
let act_add_local_axiom self c : unit =
Sat_solver.Log.debugf 5 (fun k->k "(@[<2>th_combine.push_local_lemma@ %a@])" Theory.Clause.pp c);
let (module A) = self.cdcl_acts in
Log.debugf 5 (fun k->k "(@[<2>th_combine.push_local_lemma@ %a@])" Theory.Clause.pp c);
A.push_local c Proof.default
(* push one clause into [M], in the current level (not a lemma but
an axiom) *)
let act_add_persistent_axiom self c : unit =
Sat_solver.Log.debugf 5 (fun k->k "(@[<2>th_combine.push_persistent_lemma@ %a@])" Theory.Clause.pp c);
Log.debugf 5 (fun k->k "(@[<2>th_combine.push_persistent_lemma@ %a@])" Theory.Clause.pp c);
let (module A) = self.cdcl_acts in
A.push_persistent c Proof.default
*)
let check_invariants (self:t) =
if Util._CHECK_INVARIANTS then (

View file

@ -4,16 +4,22 @@
(** Combine the congruence closure with a number of plugins *)
module Proof : sig
type t = Proof
type t = Solver_types.proof (* dummy proofs, for now *)
end
include Sidekick_sat.Theory_intf.S
with type formula = Lit.t
include Msat.Solver_intf.PLUGIN_CDCL_T
with module Formula = Lit
and type proof = Proof.t
val create : unit -> t
val cc : t -> Congruence_closure.t
val tst : t -> Term.state
val theories : t -> Theory.state Sequence.t
type theory_state =
| Th_state : ('a Theory.t1 * 'a) -> theory_state
val theories : t -> theory_state Sequence.t
val mk_model : t -> Lit.t Sequence.t -> Model.t

View file

@ -2,8 +2,8 @@
(library
(name Sidekick_smt)
(public_name sidekick.smt)
(libraries containers containers.data sequence sidekick.util sidekick.sat zarith)
(flags :standard -w +a-4-44-48-58-60@8
(libraries containers containers.data sequence sidekick.util msat zarith)
(flags :standard -warn-error -a+8
-color always -safe-string -short-paths -open Sidekick_util)
(ocamlopt_flags :standard -O3 -color always
-unbox-closures -unbox-closures-factor 20))

View file

@ -9,7 +9,7 @@ module E = CCResult
module A = Ast
module Form = Sidekick_th_bool
module Fmt = CCFormat
module Dot = Sidekick_backend.Dot.Simple(Solver.Sat_solver)
module Dot = Msat_backend.Dot.Simple(Solver.Sat_solver)
module Subst = struct
type 'a t = 'a ID.Map.t

View file

@ -3,8 +3,10 @@
(** {1 Preprocessing AST} *)
module ID = Sidekick_smt.ID
module Loc = Locations
module Fmt = CCFormat
module Log = Msat.Log
module A = Sidekick_smt.Ast
module PA = Parse_ast
@ -324,7 +326,7 @@ let rec conv_term ctx (t:PA.term) : A.term = match t with
errorf_ctx ctx "unsupported term %a" PA.pp_term t
let find_file_ name ~dir : string option =
Sidekick_sat.Log.debugf 2 (fun k->k "search %s in %s" name dir);
Log.debugf 2 (fun k->k "search %s in %s" name dir);
let abs_path = Filename.concat dir name in
if Sys.file_exists abs_path
then Some abs_path

View file

@ -2,8 +2,8 @@
(library
(name sidekick_smtlib)
(public_name sidekick.smtlib)
(libraries containers zarith sidekick.smt sidekick.util
sidekick.smt.th-bool sidekick.backend)
(libraries containers zarith msat sidekick.smt sidekick.util
sidekick.smt.th-bool msat.backend)
(flags :standard -w +a-4-42-44-48-50-58-32-60@8
-safe-string -color always -open Sidekick_util)
(ocamlopt_flags :standard -O3 -color always -bin-annot

View file

@ -4,8 +4,6 @@
open Sidekick_smt
open Solver_types
module Fmt = CCFormat
type term = Term.t
(* TODO (long term): relevancy propagation *)
@ -168,12 +166,11 @@ end
type t = {
tst: Term.state;
acts: Theory.actions;
}
let tseitin (self:t) (lit:Lit.t) (lit_t:term) (v:term view) : unit =
let tseitin (_self:t) (acts:Theory.actions) (lit:Lit.t) (lit_t:term) (v:term view) : unit =
let (module A) = acts in
Log.debugf 5 (fun k->k "(@[th_bool.tseitin@ %a@])" Lit.pp lit);
let (module A) = self.acts in
match v with
| B_atom _ -> ()
| B_not _ -> assert false (* normalized *)
@ -236,23 +233,21 @@ let tseitin (self:t) (lit:Lit.t) (lit_t:term) (v:term view) : unit =
guard
)
let on_assert (self:t) (lit:Lit.t) =
let t = Lit.view lit in
begin match view t with
| B_atom _ -> ()
| v -> tseitin self lit t v
end
let partial_check (self:t) acts (lits:Lit.t Sequence.t) =
lits
(fun lit ->
let t = Lit.view lit in
match view t with
| B_atom _ -> ()
| v -> tseitin self acts lit t v)
let final_check _ _ : unit = ()
let final_check _ _ _ : unit = ()
let th =
let make tst acts =
let st = {tst;acts} in
Theory.make_st
~on_assert
~final_check
?mk_model:None (* entirely interpreted *)
~st
()
in
Theory.make ~name:"boolean" ~make ()
Theory.make
~partial_check
~final_check
~name:"boolean"
~create:(fun tst -> {tst})
?mk_model:None (* entirely interpreted *)
()

View file

@ -0,0 +1,35 @@
module Vec = Msat.Vec
type 'a t = {
vec: 'a Vec.t;
lvls: int Vec.t;
}
let create() : _ t = {vec=Vec.create(); lvls=Vec.create()}
let[@inline] n_levels self : int = Vec.size self.vec
let[@inline] push self x : unit =
Vec.push self.vec x
let[@inline] push_if_nonzero_level self x : unit =
if n_levels self > 0 then (
Vec.push self.vec x;
)
let[@inline] push_level (self:_ t) : unit =
Vec.push self.lvls (Vec.size self.vec)
let pop_levels (self:_ t) (n:int) ~f : unit =
if n > n_levels self then invalid_arg "Backtrack_stack.pop_levels";
if n > 0 then (
let new_lvl = n_levels self - n in
let i = Vec.get self.lvls new_lvl in
while Vec.size self.vec > i do
let x = Vec.pop self.vec in
f x
done;
Vec.shrink self.lvls i
)

View file

@ -0,0 +1,21 @@
(** {1 A backtracking stack} *)
type 'a t
val create : unit -> 'a t
val push : 'a t -> 'a -> unit
(** Push an element onto the stack *)
val push_if_nonzero_level : 'a t -> 'a -> unit
(** Push an element onto the stack if level > 0 *)
val n_levels : _ t -> int
(** Number of levels *)
val push_level : _ t -> unit
(** Push a backtracking point *)
val pop_levels : 'a t -> int -> f:('a -> unit) -> unit
(** [pop_levels st n ~f] removes [n] levels, calling [f] on every removed item *)

View file

@ -1,135 +0,0 @@
(* This file is free software, part of containers. See file "license" for more details. *)
(** {1 Bit Field} *)
exception TooManyFields
exception Frozen
let max_width = Sys.word_size - 2
(*$R
let module B = CCBitField.Make(struct end) in
let x = B.mk_field () in
let y = B.mk_field () in
let z = B.mk_field () in
let f = B.empty |> B.set x true |> B.set y true in
assert_bool "z_false" (not (B.get z f)) ;
assert_bool "z_true" (f |> B.set z true |> B.get z);
*)
(*$R
let module B = CCBitField.Make(struct end) in
let _ = B.mk_field () in
B.freeze();
assert_bool "must raise"
(try ignore (B.mk_field()); false with Frozen -> true);
*)
(*$R
let module B = CCBitField.Make(struct end) in
let x = B.mk_field () in
let y = B.mk_field () in
let z = B.mk_field () in
let u = B.mk_field () in
B.freeze();
let f = B.empty
|> B.set y true
|> B.set z true
in
assert_equal ~printer:CCInt.to_string 6 (f :> int) ;
assert_equal false (B.get x f) ;
assert_equal true (B.get y f) ;
assert_equal true (B.get z f);
let f' = B.set u true f in
assert_equal false (B.get x f') ;
assert_equal true (B.get y f') ;
assert_equal true (B.get z f');
assert_equal true (B.get u f');
()
*)
module type S = sig
type t = private int
(** Generative type of bitfields. Each instantiation of the functor
should create a new, incompatible type *)
val empty : t
(** Empty bitfields (all bits 0) *)
type field
val get : field -> t -> bool
(** Get the value of this field *)
val set : field -> bool -> t -> t
(** Set the value of this field *)
val mk_field : unit -> field
(** Make a new field *)
val freeze : unit -> unit
(** Prevent new fields from being added. From now on, creating
a field will raise Frozen *)
val total_width : unit -> int
(** Current width of the bitfield *)
end
(* all bits from 0 to w-1 set to true *)
let rec all_bits_ acc w =
if w=0 then acc
else
let acc = acc lor (1 lsl w-1) in
all_bits_ acc (w-1)
(*$T
all_bits_ 0 1 = 1
all_bits_ 0 2 = 3
all_bits_ 0 3 = 7
all_bits_ 0 4 = 15
*)
(* increment and return previous value *)
let get_then_incr n =
let x = !n in
incr n;
x
module Make() : S = struct
type t = int
let empty = 0
let width_ = ref 0
let frozen_ = ref false
type field = int (* a mask *)
let[@inline] get field x = (x land field) <> 0
let[@inline] set field b x =
if b then x lor field else x land (lnot field)
let mk_field () =
if !frozen_ then raise Frozen;
let n = get_then_incr width_ in
if n > max_width then raise TooManyFields;
let mask = 1 lsl n in
mask
let freeze () = frozen_ := true
let total_width () = !width_
end

View file

@ -1,69 +0,0 @@
(* This file is free software, part of containers. See file "license" for more details. *)
(** {1 Bit Field}
This module defines efficient bitfields
up to 30 or 62 bits (depending on the architecture) in
a relatively type-safe way.
{[
module B = CCBitField.Make(struct end);;
#install_printer B.pp;;
let x = B.mk_field ()
let y = B.mk_field ()
let z = B.mk_field ()
let f = B.empty |> B.set x true |> B.set y true;;
assert (not (B.get z f)) ;;
assert (f |> B.set z true |> B.get z);;
]}
*)
exception TooManyFields
(** Raised when too many fields are packed into one bitfield *)
exception Frozen
(** Raised when a frozen bitfield is modified *)
val max_width : int
(** System-dependent maximum width for a bitfield, typically 30 or 62 *)
(** {2 Bitfield Signature} *)
module type S = sig
type t = private int
(** Generative type of bitfields. Each instantiation of the functor
should create a new, incompatible type *)
val empty : t
(** Empty bitfields (all bits 0) *)
type field
val get : field -> t -> bool
(** Get the value of this field *)
val set : field -> bool -> t -> t
(** Set the value of this field *)
val mk_field : unit -> field
(** Make a new field *)
val freeze : unit -> unit
(** Prevent new fields from being added. From now on, creating
a field will raise Frozen *)
val total_width : unit -> int
(** Current width of the bitfield *)
end
(** Create a new bitfield type *)
module Make() : S
(**/**)
val all_bits_ : int -> int -> int
(**/**)

View file

@ -1,149 +0,0 @@
module type RANKED = Heap_intf.RANKED
module type S = Heap_intf.S
module Make(Elt : RANKED) = struct
type elt = Elt.t
type t = {
heap : elt Vec.t;
}
let _absent_index = -1
let create () =
{ heap = Vec.make_empty Elt.dummy; }
let[@inline] left i = (i lsl 1) + 1 (* i*2 + 1 *)
let[@inline] right i = (i + 1) lsl 1 (* (i+1)*2 *)
let[@inline] parent i = (i - 1) asr 1 (* (i-1) / 2 *)
(*
let rec heap_property cmp ({heap=heap} as s) i =
i >= (Vec.size heap) ||
((i = 0 || not(cmp (Vec. get heap i) (Vec.get heap (parent i))))
&& heap_property cmp s (left i) && heap_property cmp s (right i))
let heap_property cmp s = heap_property cmp s 1
*)
(* [elt] is above or at its expected position. Move it up the heap
(towards the root) to restore the heap property *)
let percolate_up {heap} (elt:Elt.t) : unit =
let pi = ref (parent (Elt.idx elt)) in
let i = ref (Elt.idx elt) in
while !i <> 0 && Elt.cmp elt (Vec.get heap !pi) do
Vec.set heap !i (Vec.get heap !pi);
Elt.set_idx (Vec.get heap !i) !i;
i := !pi;
pi := parent !i
done;
Vec.set heap !i elt;
Elt.set_idx elt !i
(* move [elt] towards the leaves of the heap to restore the heap
property *)
let percolate_down {heap} (elt:Elt.t): unit =
let sz = Vec.size heap in
let li = ref (left (Elt.idx elt)) in
let ri = ref (right (Elt.idx elt)) in
let i = ref (Elt.idx elt) in
begin
try
while !li < sz do
let child =
if !ri < sz && Elt.cmp (Vec.get heap !ri) (Vec.get heap !li)
then !ri
else !li
in
if not (Elt.cmp (Vec.get heap child) elt) then raise Exit;
Vec.set heap !i (Vec.get heap child);
Elt.set_idx (Vec.get heap !i) !i;
i := child;
li := left !i;
ri := right !i
done;
with Exit -> ()
end;
Vec.set heap !i elt;
Elt.set_idx elt !i
let[@inline] in_heap x = Elt.idx x >= 0
let[@inline] decrease s x =
assert (in_heap x);
percolate_up s x
(*
let increase cmp s n =
assert (in_heap s n); percolate_down cmp s (V.get s.indices n)
*)
let filter s filt =
let j = ref 0 in
let lim = Vec.size s.heap in
for i = 0 to lim - 1 do
if filt (Vec.get s.heap i) then (
Vec.set s.heap !j (Vec.get s.heap i);
Elt.set_idx (Vec.get s.heap i) !j;
incr j;
) else (
Elt.set_idx (Vec.get s.heap i) _absent_index;
);
done;
Vec.shrink s.heap (lim - !j);
for i = (lim / 2) - 1 downto 0 do
percolate_down s (Vec.get s.heap i)
done
let size s = Vec.size s.heap
let is_empty s = Vec.is_empty s.heap
let clear {heap} =
Vec.iter (fun e -> Elt.set_idx e _absent_index) heap;
Vec.clear heap;
()
let insert s elt =
if not (in_heap elt) then (
Elt.set_idx elt (Vec.size s.heap);
Vec.push s.heap elt;
percolate_up s elt;
)
let[@inline] grow_to_at_least s sz =
Vec.grow_to_at_least s.heap sz
let remove_min ({heap} as s) =
if Vec.size heap=0 then raise Not_found;
let x = Vec.get heap 0 in
Elt.set_idx x _absent_index;
let new_hd = Vec.last heap in (* heap.last() *)
Vec.set heap 0 new_hd;
Elt.set_idx new_hd 0;
Vec.pop heap; (* remove last *)
(* enforce heap property again *)
if Vec.size heap > 1 then (
percolate_down s new_hd;
);
x
let remove ({heap} as s) (elt:elt) : unit =
if in_heap elt then (
let i = Elt.idx elt in
(* move last element in place of [i] *)
let elt' = Vec.last heap in
Vec.set heap i elt';
Elt.set_idx elt' i;
Elt.set_idx elt ~-1;
Vec.pop heap;
assert (not (in_heap elt));
(* element put in place of [elt] might be too high *)
if Vec.size heap > i then (
percolate_down s elt';
);
)
end

View file

@ -1,6 +0,0 @@
module type RANKED = Heap_intf.RANKED
module type S = Heap_intf.S
module Make(X : RANKED) : S with type elt = X.t

View file

@ -1,56 +0,0 @@
module type RANKED = sig
type t
val idx: t -> int (** Index in heap. return -1 if never set *)
val set_idx : t -> int -> unit (** Update index in heap *)
val dummy : t
val cmp : t -> t -> bool
end
module type S = sig
type elt
(** Type of elements *)
type t
(** Heap of {!elt}, whose priority is increased or decreased
incrementally (see {!decrease} for instance) *)
val create : unit -> t
(** Create a heap *)
val decrease : t -> elt -> unit
(** [decrease h x] decreases the value associated to [x] within [h],
making it closer to the root (so, more prioritary) *)
val in_heap : elt -> bool
(*val increase : (int -> int -> bool) -> t -> int -> unit*)
val size : t -> int
(** Number of integers within the heap *)
val is_empty : t -> bool
val clear : t -> unit
(** Clear the content of the heap *)
val insert : t -> elt -> unit
(** Insert a new element into the heap *)
val grow_to_at_least: t -> int -> unit
(** Hint: augment the internal capacity of the heap until it reaches at
least the given integer *)
(*val update : (int -> int -> bool) -> t -> int -> unit*)
val remove_min : t -> elt
(** Remove and return the integer that has the lowest value from the heap
@raise Not_found if the heap is empty *)
val remove : t -> elt -> unit
(** Remove element from the heap.
precond: [in_heap elt] *)
val filter : t -> (elt -> bool) -> unit
(** Filter out values that don't satisfy the predicate *)
end

View file

@ -1,33 +0,0 @@
(*
MSAT is free software, using the Apache license, see file LICENSE
Copyright 2014 Guillaume Bury
Copyright 2014 Simon Cruanes
*)
(** {1 Logging functions, real version} *)
let enabled = true (* NOTE: change here for 0-overhead *)
let debug_level_ = ref 0
let set_debug l = debug_level_ := l
let get_debug () = !debug_level_
let debug_fmt_ = ref Format.err_formatter
let set_debug_out f = debug_fmt_ := f
(* does the printing, inconditionally *)
let debug_real_ l k =
k (fun fmt ->
Format.fprintf !debug_fmt_ "@[<2>@{<Blue>[%d|%.3f]@}@ "
l (Sys.time());
Format.kfprintf
(fun fmt -> Format.fprintf fmt "@]@.")
!debug_fmt_ fmt)
let[@inline] debugf l k =
if enabled && l <= !debug_level_ then (
debug_real_ l k;
)
let[@inline] debug l msg = debugf l (fun k->k "%s" msg)

View file

@ -1,26 +0,0 @@
(*
MSAT is free software, using the Apache license, see file LICENSE
Copyright 2014 Guillaume Bury
Copyright 2014 Simon Cruanes
*)
(** {1 Logging function, for debugging} *)
val enabled : bool
val set_debug : int -> unit (** Set debug level *)
val get_debug : unit -> int (** Current debug level *)
val debugf :
int ->
((('a, Format.formatter, unit, unit) format4 -> 'a) -> unit) ->
unit
(** Emit a debug message at the given level. If the level is lower
than [get_debug ()], the message will indeed be emitted *)
val debug : int -> string -> unit
(** Simpler version of {!debug}, without formatting *)
val set_debug_out : Format.formatter -> unit
(** Change the output formatter. *)

View file

@ -1,208 +0,0 @@
(**************************************************************************)
(* *)
(* Cubicle *)
(* Combining model checking algorithms and SMT solvers *)
(* *)
(* Mohamed Iguernelala *)
(* Universite Paris-Sud 11 *)
(* *)
(* Copyright 2011. This file is distributed under the terms of the *)
(* Apache Software License version 2.0 *)
(* *)
(**************************************************************************)
type 'a t = {
mutable dummy: 'a;
mutable data : 'a array;
mutable sz : int;
}
let _size_too_big()=
failwith "Vec: capacity exceeds maximum array size"
let make capa d =
if capa > Sys.max_array_length then _size_too_big();
{data = Array.make capa d; sz = 0; dummy = d}
let[@inline] make_empty d = {data = [||]; sz=0; dummy=d }
let init capa f d =
if capa > Sys.max_array_length then _size_too_big();
{data = Array.init capa (fun i -> f i); sz = capa; dummy = d}
let from_array data sz d =
assert (sz <= Array.length data);
{data = data; sz = sz; dummy = d}
let from_list l d =
let a = Array.of_list l in
from_array a (Array.length a) d
let to_list s =
let l = ref [] in
for i = 0 to s.sz - 1 do
l := s.data.(i) :: !l
done;
List.rev !l
let[@inline] clear s = s.sz <- 0
let[@inline] shrink t i =
assert (i >= 0);
assert (i<=t.sz);
t.sz <- i
let[@inline] pop t =
if t.sz = 0 then invalid_arg "vec.pop";
t.sz <- t.sz - 1
let[@inline] size t = t.sz
let[@inline] is_empty t = t.sz = 0
let grow_to_exact t new_capa =
assert (new_capa > Array.length t.data);
let new_data = Array.make new_capa t.dummy in
assert (t.sz <= new_capa);
Array.blit t.data 0 new_data 0 t.sz;
t.data <- new_data
let grow_to_double_size t =
if Array.length t.data = Sys.max_array_length then _size_too_big();
let size = min Sys.max_array_length (2* Array.length t.data + 1) in
grow_to_exact t size
let grow_to_at_least t new_capa =
assert (new_capa >= 0);
if new_capa > Sys.max_array_length then _size_too_big ();
let data = t.data in
let capa = ref (max (Array.length data) 1) in
while !capa < new_capa do
capa := min (2 * !capa + 1) Sys.max_array_length;
done;
if !capa > Array.length data then (
grow_to_exact t !capa
)
let[@inline] is_full t = Array.length t.data = t.sz
let[@inline] push t e =
if is_full t then grow_to_double_size t;
Array.unsafe_set t.data (t.sz) e;
t.sz <- t.sz + 1
let[@inline] last t =
if t.sz = 0 then invalid_arg "vec.last";
Array.unsafe_get t.data (t.sz - 1)
let[@inline] pop_last t =
if t.sz = 0 then invalid_arg "vec.pop_last";
let x = Array.unsafe_get t.data (t.sz - 1) in
t.sz <- t.sz - 1;
x
let[@inline] get t i =
if i < 0 || i >= t.sz then invalid_arg "vec.get";
Array.unsafe_get t.data i
let[@inline] set t i v =
if i < 0 || i > t.sz then invalid_arg "vec.set";
if i = t.sz then (
push t v
) else (
Array.unsafe_set t.data i v
)
let[@inline] copy t =
let data = Array.copy t.data in
{data; sz=t.sz; dummy=t.dummy}
let[@inline] move_to t t' =
t'.data <- Array.copy t.data;
t'.sz <- t.sz
let[@inline] fast_remove t i =
assert (i < t.sz);
t.data.(i) <- t.data.(t.sz - 1);
t.sz <- t.sz - 1
let filter_in_place f vec =
let i = ref 0 in
while !i < size vec do
if f (get vec !i) then incr i else fast_remove vec !i
done
let sort t f =
let sub_arr = Array.sub t.data 0 t.sz in
Array.fast_sort f sub_arr;
t.data <- sub_arr
let[@inline] iter f t =
for i = 0 to size t - 1 do
f (Array.unsafe_get t.data i)
done
let append a b =
grow_to_at_least a (size a + size b);
iter (push a) b
let append_l v l = List.iter (push v) l
let fold f acc t =
let rec _fold f acc t i =
if i=t.sz
then acc
else (
let acc' = f acc (Array.unsafe_get t.data i) in
_fold f acc' t (i+1)
)
in _fold f acc t 0
exception ExitVec
let exists p t =
try
for i = 0 to t.sz - 1 do
if p (Array.unsafe_get t.data i) then raise ExitVec
done;
false
with ExitVec -> true
let for_all p t =
try
for i = 0 to t.sz - 1 do
if not (p (Array.unsafe_get t.data i)) then raise ExitVec
done;
true
with ExitVec -> false
let print ?(sep=", ") pp out v =
let first = ref true in
iter
(fun x ->
if !first then first := false else Format.fprintf out "%s@," sep;
pp out x)
v
(*
template<class V, class T>
static inline void remove(V& ts, const T& t)
{
int j = 0;
for (; j < ts.size() && ts[j] != t; j++);
assert(j < ts.size());
ts[j] = ts.last();
ts.pop();
}
#endif
template<class V, class T>
static inline bool find(V& ts, const T& t)
{
int j = 0;
for (; j < ts.size() && ts[j] != t; j++);
return j < ts.size();
}
#endif
*)

View file

@ -1,120 +0,0 @@
(**************************************************************************)
(* *)
(* Cubicle *)
(* Combining model checking algorithms and SMT solvers *)
(* *)
(* Mohamed Iguernelala *)
(* Universite Paris-Sud 11 *)
(* *)
(* Copyright 2011. This file is distributed under the terms of the *)
(* Apache Software License version 2.0 *)
(* *)
(**************************************************************************)
type 'a t
(** Abstract type of vectors of 'a *)
val make : int -> 'a -> 'a t
(** [make cap dummy] creates a new vector filled with [dummy]. The vector
is initially empty but its underlying array has capacity [cap].
[dummy] will stay alive as long as the vector *)
val make_empty : 'a -> 'a t
(** Vector with an empty capacity. The only argument is the dummy. *)
val init : int -> (int -> 'a) -> 'a -> 'a t
(** Same as {!Array.init}, but also with a dummy element *)
val from_array : 'a array -> int -> 'a -> 'a t
(** [from_array arr size dummy] takes ownership of [data] (no copy)
to create a vector. [size] is the length of the slice of [data] that is
used ([size <= Array.length data] must hold) *)
val from_list : 'a list -> 'a -> 'a t
val to_list : 'a t -> 'a list
(** Returns the list of elements of the vector *)
val clear : 'a t -> unit
(** Set size to 0, doesn't free elements *)
val shrink : 'a t -> int -> unit
(** [shrink vec sz] resets size of [vec] to [sz].
Assumes [sz >=0 && sz <= size vec] *)
val pop : 'a t -> unit
(** Pop last element
@raise Invalid_argument if the vector is empty *)
val size : 'a t -> int
val is_empty : 'a t -> bool
val grow_to_exact : 'a t -> int -> unit
val grow_to_double_size : 'a t -> unit
val grow_to_at_least : 'a t -> int -> unit
(** [grow_to_at_least vec n] ensures that [capacity vec >= n] in
the most efficient way *)
val is_full : 'a t -> bool
(** Is the capacity of the vector equal to the number of its elements? *)
val push : 'a t -> 'a -> unit
val append : 'a t -> 'a t -> unit
(** [append v1 v2] pushes all elements of [v2] into [v1] *)
val append_l : 'a t -> 'a list -> unit
val last : 'a t -> 'a
(** Last element, or
@raise Invalid_argument if the vector is empty *)
val pop_last : 'a t -> 'a
(** Combine {!last} and {!pop}: remove last element and return it
@raise Invalid_argument if empty *)
val get : 'a t -> int -> 'a
(** get the element at the given index, or
@raise Invalid_argument if the index is not valid *)
val set : 'a t -> int -> 'a -> unit
(** set the element at the given index, either already set or the first
free slot if [not (is_full vec)], or
@raise Invalid_argument if the index is not valid *)
val copy : 'a t -> 'a t
(** Fresh copy *)
val move_to : 'a t -> 'a t -> unit
(** [move_to a b] copies the content of [a] to [b], discarding [b]'s old content *)
val fast_remove : 'a t -> int -> unit
(** Remove element at index [i] without preserving order
(swap with last element) *)
val filter_in_place : ('a -> bool) -> 'a t -> unit
(** [filter_in_place f v] removes from [v] the elements that do
not satisfy [f] *)
val sort : 'a t -> ('a -> 'a -> int) -> unit
(** Sort in place the array *)
val iter : ('a -> unit) -> 'a t -> unit
(** Iterate on elements *)
val fold : ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b
(** Fold over elements *)
val exists : ('a -> bool) -> 'a t -> bool
(** Does there exist an element that satisfies the predicate? *)
val for_all : ('a -> bool) -> 'a t -> bool
(** Do all elements satisfy the predicate? *)
val print :
?sep:string ->
(Format.formatter -> 'a -> unit) ->
Format.formatter -> 'a t -> unit

View file

@ -1,7 +1,7 @@
(library
(name sidekick_util)
(public_name sidekick.util)
(libraries containers sequence)
(libraries containers sequence msat)
(flags :standard -w +a-4-42-44-48-50-58-32-60@8 -color always -safe-string)
(ocamlopt_flags :standard -O3 -bin-annot
-unbox-closures -unbox-closures-factor 20)