mirror of
https://github.com/c-cube/sidekick.git
synced 2025-12-06 03:05:31 -05:00
330 lines
10 KiB
Text
330 lines
10 KiB
Text
(* output proof *)
|
|
module P = Sidekick_quip.Proof
|
|
|
|
(* serialized proof trace *)
|
|
module PS = Sidekick_base_proof_trace.Proof_ser
|
|
|
|
type t = P.t
|
|
|
|
module type CONV_ARG = sig
|
|
val proof : Proof.t
|
|
val unsat : Proof.step_id
|
|
end
|
|
|
|
module Make_lazy_tbl (T : sig
|
|
type t
|
|
end)
|
|
() =
|
|
struct
|
|
let lazy_tbl_ : T.t lazy_t Util.Int_tbl.t = Util.Int_tbl.create 32
|
|
|
|
(** FInd step by its ID *)
|
|
let find (id : PS.ID.t) : T.t =
|
|
match Util.Int_tbl.get lazy_tbl_ (Int32.to_int id) with
|
|
| Some (lazy p) -> p
|
|
| None ->
|
|
Error.errorf "proof-quip: step `%a` was not reconstructed" PS.ID.pp id
|
|
|
|
(** Add entry *)
|
|
let add id (e : T.t lazy_t) =
|
|
assert (not (Util.Int_tbl.mem lazy_tbl_ (Int32.to_int id)));
|
|
Util.Int_tbl.add lazy_tbl_ (Int32.to_int id) e
|
|
end
|
|
|
|
module Conv (X : CONV_ARG) : sig
|
|
val reconstruct : unit -> t
|
|
end = struct
|
|
let ( !! ) = Lazy.force
|
|
|
|
(* Steps we need to decode.
|
|
Invariant: the IDs of these steps must be lower than the current processed
|
|
ID (we start from the end) *)
|
|
let needed_steps_ = Util.Int_tbl.create 128
|
|
|
|
let add_needed_step (id : PS.ID.t) : unit =
|
|
Util.Int_tbl.replace needed_steps_ (Int32.to_int id) ()
|
|
|
|
let unsat_id = Proof.Unsafe_.id_of_proof_step_ X.unsat
|
|
|
|
(* start with the unsat step *)
|
|
let () = add_needed_step unsat_id
|
|
|
|
(* store reconstructed proofs, but lazily because their dependencies
|
|
(sub-steps, terms, etc.) might not have come up in the reverse stream yet. *)
|
|
module L_proofs =
|
|
Make_lazy_tbl
|
|
(struct
|
|
type t = P.t
|
|
end)
|
|
()
|
|
|
|
(* store reconstructed terms *)
|
|
module L_terms =
|
|
Make_lazy_tbl
|
|
(struct
|
|
type t = P.term
|
|
end)
|
|
()
|
|
|
|
(* id -> function symbol *)
|
|
let funs : P.Fun.t Util.Int_tbl.t = Util.Int_tbl.create 32
|
|
|
|
let find_fun (f : PS.ID.t) : P.Fun.t =
|
|
try Util.Int_tbl.find funs (Int32.to_int f)
|
|
with Not_found -> Error.errorf "unknown function '%ld'" f
|
|
|
|
(* list of toplevel steps, in the final proof order *)
|
|
let top_steps_ : P.composite_step lazy_t list ref = ref []
|
|
let add_top_step s = top_steps_ := s :: !top_steps_
|
|
|
|
let conv_lit (lit : PS.Lit.t) : P.Lit.t lazy_t =
|
|
let v = Int32.abs lit in
|
|
add_needed_step v;
|
|
lazy
|
|
(let t = L_terms.find v in
|
|
let sign = lit > Int32.zero in
|
|
(* reconstruct literal *)
|
|
P.Lit.mk sign t)
|
|
|
|
let conv_lits (lits : PS.Lit.t array) : P.Lit.t list lazy_t =
|
|
let lits = lits |> Util.array_to_list_map conv_lit in
|
|
lazy (List.map Lazy.force lits)
|
|
|
|
let conv_clause (c : PS.Clause.t) : P.clause lazy_t = conv_lits c.lits
|
|
let name_clause (id : PS.ID.t) : string = Printf.sprintf "c%ld" id
|
|
let name_step (id : PS.ID.t) : string = Printf.sprintf "s%ld" id
|
|
let name_term (id : PS.ID.t) : string = Printf.sprintf "t%ld" id
|
|
|
|
(* TODO: see if we can allow `(stepc c5 (cl …) (… (@ c5) …))`.
|
|
Namely, the alias `c5 := (cl …)` could be available within its own proof
|
|
so we don't have to print it twice, which is useful for rules like `ccl`
|
|
where it's typically `(stepc c5 (cl …) (ccl (cl …)))` for twice the space.
|
|
*)
|
|
|
|
let is_def_ (step : PS.Step.t) =
|
|
match step.view with
|
|
| PS.Step_view.Expr_def _ -> true
|
|
| _ -> false
|
|
|
|
(* process a step of the trace *)
|
|
let process_step_ (step : PS.Step.t) : unit =
|
|
let lid = step.id in
|
|
let id = Int32.to_int lid in
|
|
if Util.Int_tbl.mem needed_steps_ id || is_def_ step then (
|
|
Log.debugf 20 (fun k ->
|
|
k "(@[proof-quip.process-needed-step@ %a@])" PS.Step.pp step);
|
|
Util.Int_tbl.remove needed_steps_ id;
|
|
|
|
(* now process the step *)
|
|
match step.view with
|
|
| PS.Step_view.Step_input i ->
|
|
let c = conv_clause i.PS.Step_input.c in
|
|
let name = name_clause lid in
|
|
let step =
|
|
lazy (P.S_step_c { name; res = !!c; proof = P.assertion_c_l !!c })
|
|
in
|
|
add_top_step step;
|
|
(* refer to the step by name now *)
|
|
L_proofs.add lid (lazy (P.ref_by_name name))
|
|
| PS.Step_view.Step_unsat { c = uclause } ->
|
|
let c = [] in
|
|
let name = "unsat" in
|
|
add_needed_step uclause;
|
|
let name_c = name_clause uclause in
|
|
add_top_step
|
|
(lazy (P.S_step_c { name; res = c; proof = P.ref_by_name name_c }))
|
|
| PS.Step_view.Step_cc { eqns } ->
|
|
let c = conv_lits eqns in
|
|
let name = name_clause lid in
|
|
let step =
|
|
lazy (P.S_step_c { name; res = !!c; proof = P.cc_lemma !!c })
|
|
in
|
|
add_top_step step;
|
|
L_proofs.add lid (lazy (P.ref_by_name name))
|
|
| PS.Step_view.Fun_decl { f } -> Util.Int_tbl.add funs id f
|
|
| PS.Step_view.Expr_eq { lhs; rhs } ->
|
|
add_needed_step lhs;
|
|
add_needed_step rhs;
|
|
let name = name_term lid in
|
|
let step =
|
|
lazy
|
|
(let lhs = L_terms.find lhs and rhs = L_terms.find rhs in
|
|
let t = P.T.eq lhs rhs in
|
|
P.S_define_t_name (name, t))
|
|
in
|
|
add_top_step step;
|
|
L_terms.add lid (lazy (P.T.ref name))
|
|
| PS.Step_view.Expr_bool { b } ->
|
|
let t = Lazy.from_val (P.T.bool b) in
|
|
L_terms.add lid t
|
|
| PS.Step_view.Expr_isa { c; arg } ->
|
|
add_needed_step c;
|
|
add_needed_step arg;
|
|
let t =
|
|
lazy
|
|
(let c = find_fun c in
|
|
let arg = L_terms.find arg in
|
|
P.T.is_a c arg)
|
|
in
|
|
L_terms.add lid t
|
|
| PS.Step_view.Expr_app { f; args } ->
|
|
add_needed_step f;
|
|
Array.iter add_needed_step args;
|
|
let t =
|
|
lazy
|
|
(let f = find_fun f in
|
|
let args = Array.map L_terms.find args in
|
|
P.T.app_fun f args)
|
|
in
|
|
|
|
if Array.length args > 0 then (
|
|
(* introduce a name *)
|
|
let name = name_term lid in
|
|
let step = lazy (P.S_define_t_name (name, !!t)) in
|
|
add_top_step step;
|
|
L_terms.add lid (lazy (P.T.ref name))
|
|
) else
|
|
L_terms.add lid t
|
|
| PS.Step_view.Expr_def { c; rhs } ->
|
|
add_needed_step c;
|
|
add_needed_step rhs;
|
|
let name = name_clause lid in
|
|
(* add [name := (|- c=rhs) by refl c].
|
|
Make sure we do it first, order in final proof will be reversed. *)
|
|
let step_refl =
|
|
lazy
|
|
(let c = L_terms.find c in
|
|
let rhs = L_terms.find rhs in
|
|
P.S_step_c { name; res = [ P.Lit.eq c rhs ]; proof = P.refl c })
|
|
in
|
|
add_top_step step_refl;
|
|
(* define [c:=rhs] *)
|
|
let step_def =
|
|
lazy
|
|
(let c = L_terms.find c in
|
|
let rhs = L_terms.find rhs in
|
|
P.S_define_t (c, rhs))
|
|
in
|
|
add_top_step step_def;
|
|
L_proofs.add lid (lazy (P.ref_by_name name))
|
|
| PS.Step_view.Expr_not { f } ->
|
|
add_needed_step f;
|
|
let t =
|
|
lazy
|
|
(let f = L_terms.find f in
|
|
P.T.not_ f)
|
|
in
|
|
L_terms.add lid t
|
|
| PS.Step_view.Expr_if _ -> () (* TODO *)
|
|
| PS.Step_view.Step_rup { res; hyps } ->
|
|
let res = conv_clause res in
|
|
Array.iter add_needed_step hyps;
|
|
let name = name_clause lid in
|
|
let step =
|
|
lazy
|
|
(let hyps = Util.array_to_list_map L_proofs.find hyps in
|
|
P.S_step_c { name; res = !!res; proof = P.rup !!res hyps })
|
|
in
|
|
add_top_step step;
|
|
L_proofs.add lid (lazy (P.ref_by_name name))
|
|
| PS.Step_view.Step_clause_rw { c; res; using } ->
|
|
let res = conv_clause res in
|
|
add_needed_step c;
|
|
Array.iter add_needed_step using;
|
|
let name = name_clause lid in
|
|
|
|
let step =
|
|
lazy
|
|
(let c = L_proofs.find c in
|
|
let using = Util.array_to_list_map L_proofs.find using in
|
|
let res = !!res in
|
|
P.S_step_c
|
|
{ name; res; proof = P.Clause_rw { res; c0 = c; using } })
|
|
in
|
|
add_top_step step;
|
|
L_proofs.add lid (lazy (P.ref_by_name name))
|
|
| PS.Step_view.Step_proof_p1 { rw_with; c } ->
|
|
add_needed_step c;
|
|
add_needed_step rw_with;
|
|
let p =
|
|
lazy
|
|
(let rw_with = L_proofs.find rw_with in
|
|
let c = L_proofs.find c in
|
|
P.paramod1 rw_with c)
|
|
in
|
|
L_proofs.add lid p
|
|
| PS.Step_view.Step_proof_r1 { unit; c } ->
|
|
add_needed_step c;
|
|
add_needed_step unit;
|
|
let p =
|
|
lazy
|
|
(let unit = L_proofs.find unit in
|
|
let c = L_proofs.find c in
|
|
P.res1 unit c)
|
|
in
|
|
L_proofs.add lid p
|
|
| PS.Step_view.Step_proof_res { pivot; c1; c2 } ->
|
|
add_needed_step c1;
|
|
add_needed_step c2;
|
|
add_needed_step pivot;
|
|
let p =
|
|
lazy
|
|
(let pivot = L_terms.find pivot in
|
|
let c1 = L_proofs.find c2 in
|
|
let c2 = L_proofs.find c2 in
|
|
P.res ~pivot c1 c2)
|
|
in
|
|
L_proofs.add lid p
|
|
| PS.Step_view.Step_bool_c { rule; exprs } ->
|
|
let name = name_step lid in
|
|
Array.iter add_needed_step exprs;
|
|
let step =
|
|
lazy
|
|
(let exprs = Util.array_to_list_map L_terms.find exprs in
|
|
P.step_anon ~name @@ P.bool_c rule exprs)
|
|
in
|
|
add_top_step step;
|
|
L_proofs.add lid (lazy (P.ref_by_name name))
|
|
| PS.Step_view.Step_preprocess { t; u; using } ->
|
|
let name = name_clause lid in
|
|
add_needed_step t;
|
|
add_needed_step u;
|
|
Array.iter add_needed_step using;
|
|
|
|
let step =
|
|
lazy
|
|
(let t = L_terms.find t and u = L_terms.find u in
|
|
let using = Util.array_to_list_map L_proofs.find using in
|
|
let res = [ P.Lit.eq t u ] in
|
|
P.S_step_c { res; name; proof = P.cc_imply_l using t u })
|
|
in
|
|
add_top_step step;
|
|
L_proofs.add lid (lazy (P.ref_by_name name))
|
|
| PS.Step_view.Step_bridge_lit_expr _ -> assert false
|
|
| PS.Step_view.Step_bool_tauto _ -> () (* TODO *)
|
|
| PS.Step_view.Step_true _ -> ()
|
|
(* TODO *)
|
|
)
|
|
|
|
let reconstruct_once_ =
|
|
lazy
|
|
( Profile.with_ "proof-quip.reconstruct" @@ fun () ->
|
|
Proof.iter_steps_backward X.proof process_step_;
|
|
() )
|
|
|
|
let reconstruct () : t =
|
|
Lazy.force reconstruct_once_;
|
|
let steps = Util.array_of_list_map Lazy.force !top_steps_ in
|
|
P.composite_a steps
|
|
end
|
|
|
|
let of_proof (self : Proof.t) ~(unsat : Proof.step_id) : P.t =
|
|
let module C = Conv (struct
|
|
let proof = self
|
|
let unsat = unsat
|
|
end) in
|
|
C.reconstruct ()
|
|
|
|
type out_format = Sidekick_quip.out_format = Sexp | CSexp
|
|
|
|
let output = Sidekick_quip.output
|