feat(simplify): add sidekick_simplify library

This commit is contained in:
Simon Cruanes 2022-07-30 21:17:55 -04:00
parent 83e456ef8a
commit 1e1b0f352d
No known key found for this signature in database
GPG key ID: EBFFF6F283F3A2B4
3 changed files with 125 additions and 0 deletions

6
src/simplify/dune Normal file
View file

@ -0,0 +1,6 @@
(library
(name Sidekick_simplify)
(public_name sidekick.simplify)
(synopsis "Simplifier")
(libraries containers iter sidekick.core sidekick.util)
(flags :standard -w +32 -open Sidekick_util))

View file

@ -0,0 +1,80 @@
open Sidekick_core
open struct
module P = Proof_trace
module Rule_ = Proof_core
end
type t = {
tst: Term.store;
proof: Proof_trace.t;
mutable hooks: hook list;
(* store [t --> u by step_ids] in the cache.
We use a bag for the proof steps because it gives us structural
sharing of subproofs. *)
cache: (Term.t * Proof_step.id Bag.t) Term.Tbl.t;
}
and hook = t -> Term.t -> (Term.t * Proof_step.id Iter.t) option
let create tst ~proof : t =
{ tst; proof; hooks = []; cache = Term.Tbl.create 32 }
let[@inline] tst self = self.tst
let[@inline] proof self = self.proof
let add_hook self f = self.hooks <- f :: self.hooks
let clear self = Term.Tbl.clear self.cache
let normalize (self : t) (t : Term.t) : (Term.t * Proof_step.id) option =
(* compute and cache normal form of [t] *)
let rec loop t : Term.t * _ Bag.t =
match Term.Tbl.find self.cache t with
| res -> res
| exception Not_found ->
let steps_u = ref Bag.empty in
let u = aux_rec ~steps:steps_u t self.hooks in
Term.Tbl.add self.cache t (u, !steps_u);
u, !steps_u
and loop_add ~steps t =
let u, pr_u = loop t in
steps := Bag.append !steps pr_u;
u
(* try each function in [hooks] successively, and rewrite subterms *)
and aux_rec ~steps t hooks : Term.t =
match hooks with
| [] ->
let u =
Term.map_shallow self.tst ~f:(fun _inb u -> loop_add ~steps u) t
in
if Term.equal t u then
t
else
loop_add ~steps u
| h :: hooks_tl ->
(match h self t with
| None -> aux_rec ~steps t hooks_tl
| Some (u, _) when Term.equal t u -> aux_rec ~steps t hooks_tl
| Some (u, pr_u) ->
let bag_u = Bag.of_iter pr_u in
steps := Bag.append !steps bag_u;
let v, pr_v = loop u in
(* fixpoint *)
steps := Bag.append !steps pr_v;
v)
in
let u, pr_u = loop t in
if Term.equal t u then
None
else (
(* proof: [sub_proofs |- t=u] by CC + subproof *)
let step =
P.add_step self.proof
@@ Rule_.lemma_preprocess t u ~using:(Bag.to_iter pr_u)
in
Some (u, step)
)
let normalize_t self t =
match normalize self t with
| Some (u, pr_u) -> u, Some pr_u
| None -> t, None

View file

@ -0,0 +1,39 @@
(** Term simplifier *)
open Sidekick_core
type t
val tst : t -> Term.store
val create : Term.store -> proof:Proof_trace.t -> t
(** Create a simplifier *)
val clear : t -> unit
(** Reset internal cache, etc. *)
val proof : t -> Proof_trace.t
(** Access proof *)
type hook = t -> Term.t -> (Term.t * Proof_step.id Iter.t) option
(** Given a Term.t, try to simplify it. Return [None] if it didn't change.
A simple example could be a hook that takes a Term.t [t],
and if [t] is [app "+" (const x) (const y)] where [x] and [y] are number,
returns [Some (const (x+y))], and [None] otherwise.
The simplifier will take care of simplifying the resulting Term.t further,
caching (so that work is not duplicated in subterms), etc.
*)
val add_hook : t -> hook -> unit
val normalize : t -> Term.t -> (Term.t * Proof_step.id) option
(** Normalize a Term.t using all the hooks. This performs
a fixpoint, i.e. it only stops when no hook applies anywhere inside
the Term.t. *)
val normalize_t : t -> Term.t -> Term.t * Proof_step.id option
(** Normalize a Term.t using all the hooks, along with a proof that the
simplification is correct.
returns [t, ø] if no simplification occurred. *)