sidekick/src/base/Proof.ml
Simon Cruanes 8410a57f1a
wip: feat(LIA): LIA solver, will rely on LRA solver
we want to reuse the simplex, but do branch and bound + cutting planes
2022-01-11 14:00:04 -05:00

291 lines
8.1 KiB
OCaml

open Base_types
(* we store steps as binary chunks *)
module CS = Chunk_stack
module PS = Proof_ser
module Config = struct
type storage =
| No_store
| In_memory
| On_disk_at of string
let pp_storage out = function
| No_store -> Fmt.string out "no-store"
| In_memory -> Fmt.string out "in-memory"
| On_disk_at file -> Fmt.fprintf out "(on-file :at %S)" file
type t = {
enabled: bool;
storage: storage;
}
let default = { enabled=true; storage=In_memory }
let empty = { enabled=false; storage=No_store }
let pp out (self:t) =
let { enabled; storage } = self in
Fmt.fprintf out
"(@[config@ :enabled %B@ :storage %a@])"
enabled pp_storage storage
let enable b self = {self with enabled=b}
let store_in_memory self = {self with storage=In_memory}
let store_on_disk_at file self = {self with storage=On_disk_at file}
let no_store self = {self with storage=No_store}
end
(* a step is just a unique integer ID.
The actual step is stored in the chunk_stack. *)
type proof_step = Proof_ser.ID.t
type term_id = Proof_ser.ID.t
type lit = Lit.t
type term = Term.t
type t = {
mutable enabled : bool;
config: Config.t;
buf: Buffer.t;
mutable storage: Storage.t;
mutable dispose: unit -> unit;
mutable steps_writer: CS.Writer.t;
mutable next_id: int;
map_term: term_id Term.Tbl.t; (* term -> proof ID *)
map_fun: term_id Fun.Tbl.t;
}
type proof_rule = t -> proof_step
module Step_vec = struct
type elt=proof_step
include VecI32
end
let disable (self:t) : unit =
self.enabled <- false;
self.storage <- Storage.No_store;
self.dispose();
self.steps_writer <- CS.Writer.dummy;
()
let nop_ _ = ()
let create ?(config=Config.default) () : t =
(* acquire resources for logging *)
let storage, steps_writer, dispose =
match config.Config.storage with
| Config.No_store ->
Storage.No_store, CS.Writer.dummy, nop_
| Config.In_memory ->
let buf = CS.Buf.create ~cap:256 () in
Storage.In_memory buf, CS.Writer.into_buf buf, nop_
| Config.On_disk_at file ->
let oc =
open_out_gen [Open_creat; Open_wronly; Open_trunc; Open_binary] 0o644 file
in
let w = CS.Writer.into_channel oc in
let dispose () = close_out oc in
Storage.On_disk (file, oc), w, dispose
in
{ enabled=config.Config.enabled;
config;
next_id=1;
buf=Buffer.create 1_024;
map_term=Term.Tbl.create 32;
map_fun=Fun.Tbl.create 32;
steps_writer; storage; dispose;
}
let empty = create ~config:Config.empty ()
let iter_steps_backward (self:t) =
Storage.iter_steps_backward self.storage
let dummy_step : proof_step = Int32.min_int
let[@inline] enabled (self:t) = self.enabled
(* allocate a unique ID to refer to an event in the trace *)
let[@inline] alloc_id (self:t) : Proof_ser.ID.t =
let n = self.next_id in
self.next_id <- 1 + self.next_id;
Int32.of_int n
(* emit a proof step *)
let emit_step_ (self:t) (step:Proof_ser.Step.t) : unit =
if enabled self then (
Buffer.clear self.buf;
Proof_ser.Step.encode self.buf step;
Chunk_stack.Writer.add_buffer self.steps_writer self.buf;
)
let emit_fun_ (self:t) (f:Fun.t) : term_id =
try Fun.Tbl.find self.map_fun f
with Not_found ->
let id = alloc_id self in
Fun.Tbl.add self.map_fun f id;
let f_name = ID.to_string (Fun.id f) in
emit_step_ self
Proof_ser.({ Step.id; view=Fun_decl {Fun_decl.f=f_name}});
id
let rec emit_term_ (self:t) (t:Term.t) : term_id =
try Term.Tbl.find self.map_term t
with Not_found ->
let view = match Term_cell.map (emit_term_ self) @@ Term.view t with
| Term_cell.Bool b ->
PS.Step_view.Expr_bool {PS.Expr_bool.b}
| Term_cell.Ite (a,b,c) ->
PS.Step_view.Expr_if {PS.Expr_if.cond=a; then_=b; else_=c}
| Term_cell.Not a ->
PS.Step_view.Expr_not {PS.Expr_not.f=a}
| Term_cell.App_fun ({fun_view=Fun.Fun_is_a c;_}, args) ->
assert (IArray.length args=1);
let c = emit_fun_ self (Fun.cstor c) in
let arg = IArray.get args 0 in
PS.Step_view.Expr_isa {PS.Expr_isa.c; arg}
| Term_cell.App_fun (f, arr) ->
let f = emit_fun_ self f in
PS.Step_view.Expr_app {PS.Expr_app.f; args=(arr:_ IArray.t:> _ array)}
| Term_cell.Eq (a, b) ->
PS.Step_view.Expr_eq {PS.Expr_eq.lhs=a; rhs=b}
| LRA _ | LIA _ -> assert false (* TODO *)
in
let id = alloc_id self in
Term.Tbl.add self.map_term t id;
emit_step_ self {id; view};
id
let emit_lit_ (self:t) (lit:Lit.t) : term_id =
let sign = Lit.sign lit in
let t = emit_term_ self (Lit.term lit) in
if sign then t else Int32.neg t
let emit_ (self:t) f : proof_step =
if enabled self then (
let view = f () in
let id = alloc_id self in
emit_step_ self {PS.Step.id; view};
id
) else dummy_step
let emit_no_return_ (self:t) f : unit =
if enabled self then (
let view = f () in
emit_step_ self {PS.Step.id=(-1l); view}
)
let[@inline] emit_redundant_clause lits ~hyps (self:t) =
emit_ self @@ fun() ->
let lits = Iter.map (emit_lit_ self) lits |> Iter.to_array in
let clause = Proof_ser.{Clause.lits} in
let hyps = Iter.to_array hyps in
PS.Step_view.Step_rup {res=clause; hyps}
let emit_input_clause (lits:Lit.t Iter.t) (self:t) =
emit_ self @@ fun () ->
let lits = Iter.map (emit_lit_ self) lits |> Iter.to_array in
PS.(Step_view.Step_input {Step_input.c={Clause.lits}})
let define_term t u (self:t) =
emit_ self @@ fun () ->
let t = emit_term_ self t and u = emit_term_ self u in
PS.(Step_view.Expr_def {Expr_def.c=t; rhs=u})
let proof_p1 rw_with c (self:t) =
emit_ self @@ fun() ->
PS.(Step_view.Step_proof_p1 {Step_proof_p1.c; rw_with})
let proof_r1 unit c (self:t) =
emit_ self @@ fun() ->
PS.(Step_view.Step_proof_r1 {Step_proof_r1.c; unit})
let proof_res ~pivot c1 c2 (self:t) =
emit_ self @@ fun() ->
let pivot = emit_term_ self pivot in
PS.(Step_view.Step_proof_res {Step_proof_res.c1; c2; pivot})
let lemma_preprocess t u ~using (self:t) =
emit_ self @@ fun () ->
let t = emit_term_ self t and u = emit_term_ self u in
let using = using |> Iter.to_array in
PS.(Step_view.Step_preprocess {Step_preprocess.t; u; using})
let lemma_true t (self:t) =
emit_ self @@ fun () ->
let t = emit_term_ self t in
PS.(Step_view.Step_true {Step_true.true_=t})
let lemma_cc lits (self:t) =
emit_ self @@ fun () ->
let lits = Iter.map (emit_lit_ self) lits |> Iter.to_array in
PS.(Step_view.Step_cc {Step_cc.eqns=lits})
let lemma_rw_clause c ~res ~using (self:t) =
if enabled self then (
let using = Iter.to_array using in
if Array.length using=0 then c (* useless step *)
else (
emit_ self @@ fun() ->
let lits = Iter.map (emit_lit_ self) res |> Iter.to_array in
let res = Proof_ser.{Clause.lits} in
PS.(Step_view.Step_clause_rw {Step_clause_rw.c; res; using})
)
) else dummy_step
(* TODO *)
let with_defs _ _ (_pr:t) = dummy_step
(* not useful *)
let del_clause _ _ (_pr:t) = ()
(* TODO *)
let emit_unsat_core _ (_pr:t) = dummy_step
let emit_unsat c (self:t) : unit =
emit_no_return_ self @@ fun() ->
PS.(Step_view.Step_unsat {Step_unsat.c})
let lemma_bool_tauto lits (self:t) =
emit_ self @@ fun() ->
let lits = Iter.map (emit_lit_ self) lits |> Iter.to_array in
PS.(Step_view.Step_bool_tauto {Step_bool_tauto.lits})
let lemma_bool_c rule (ts:Term.t list) (self:t) =
emit_ self @@ fun() ->
let exprs = ts |> Util.array_of_list_map (emit_term_ self) in
PS.(Step_view.Step_bool_c {Step_bool_c.exprs; rule})
(* TODO *)
let lemma_lra _ _ = dummy_step
let lemma_lia _ _ = dummy_step
let lemma_bool_equiv _ _ _ = dummy_step
let lemma_ite_true ~ite:_ _ = dummy_step
let lemma_ite_false ~ite:_ _ = dummy_step
let lemma_isa_cstor ~cstor_t:_ _ (_pr:t) = dummy_step
let lemma_select_cstor ~cstor_t:_ _ (_pr:t) = dummy_step
let lemma_isa_split _ _ (_pr:t) = dummy_step
let lemma_isa_sel _ (_pr:t) = dummy_step
let lemma_isa_disj _ _ (_pr:t) = dummy_step
let lemma_cstor_inj _ _ _ (_pr:t) = dummy_step
let lemma_cstor_distinct _ _ (_pr:t) = dummy_step
let lemma_acyclicity _ (_pr:t) = dummy_step
module Unsafe_ = struct
let[@inline] id_of_proof_step_ (p:proof_step) : proof_step = p
end