diff --git a/colibri2/theories/array/array.ml b/colibri2/theories/array/array.ml index 3b8b623792daba8df9756fe08b90969fb16cc0a5..652f0682a91d5ff1efea513e9603784e768aecd7 100644 --- a/colibri2/theories/array/array.ml +++ b/colibri2/theories/array/array.ml @@ -22,6 +22,8 @@ open Colibri2_core open Colibri2_popop_lib +let restrict_ext = ref false +let restrict_aup = ref false let db = Datastructure.Push.create Ground.pp "Array.db" let debug = Debug.register_info_flag ~desc:"For array theory" "Array" let stats = Debug.register_stats_int "Array.rule" @@ -33,6 +35,22 @@ let convert ~subst = let int_ty = Expr.Ty.int let array_ty = Expr.Ty.array int_ty int_ty +(* Helper functions *) +let is_array env n = + Ground.Ty.S.exists + (function { app = { builtin = Expr.Array; _ }; _ } -> true | _ -> false) + (Ground.tys env n) + +let is_foreign env n = + match Egraph.get_dom env Foreign_dom.key n with + | Some IsForeign -> true + | _ -> false + +let is_nonlinear env n = + match Egraph.get_dom env Linearity_dom.key n with + | Some NonLinear -> true + | _ -> false + (* Builtins *) module Builtin = struct type _ Expr.t += Arr_diff @@ -139,12 +157,78 @@ module Theory = struct in (aup_pattern, aup_run) + (* extᵣ (restricted extensionality): + - (a = b) ≡ false |> (a = b) ⋠(a[k] ≠b[k]) + - a, b, {a,b} ⊆ foreign |> (a = b) ⋠(a[k] ≠b[k]) *) + let rext_pattern, rext_run = + let rext_pattern = Pattern.of_term_exn ~subst:Ground.Subst.empty ta in + let rext_run env subst = + Debug.dprintf2 debug "Found rext1 with %a" Ground.Subst.pp subst; + let an = convert ~subst env ta in + Egraph.register env an; + let rext_pattern_bis = Pattern.of_term_exn ~subst tb in + let rext_run_bis env subst_bis = + Debug.dprintf2 debug "Found rext2 with %a" Ground.Subst.pp subst; + let subst = Ground.Subst.distinct_union subst_bis subst in + let bn = convert ~subst env tb in + Egraph.register env bn; + let eq = convert ~subst env (Expr.Term.eq ta tb) in + let is_registered = Egraph.is_registered env eq in + Egraph.register env eq; + (* it can't be false if it is not registered? *) + let is_false = Boolean.is_false env eq in + if + (is_registered && is_false) || (is_foreign env an && is_foreign env bn) + then ( + let v = convert ~subst env distinct_term in + Egraph.register env v; + Boolean.set_true env v) + in + InvertedPath.add_callback env rext_pattern_bis rext_run_bis + in + (rext_pattern, rext_run) + + (* ⇑ᵣ: a ≡ b[i <- v], b[j], b ∈ non-linear |> (i = j) \/ a[j] = b[j] *) + let raup_pattern, raup_run = + let term = Expr.Term.store tb ti tv in + let raup_pattern = Pattern.of_term_exn ~subst:Ground.Subst.empty term in + let raup_run env subst = + Debug.dprintf2 debug "Found raup1 with %a" Ground.Subst.pp subst; + let n = convert ~subst env term in + Egraph.register env n; + let term_bis = Expr.Term.select tb tj in + let raup_pattern_bis = Pattern.of_term_exn ~subst term_bis in + let raup_run_bis env subst_bis = + Debug.dprintf2 debug "Found raup2 with %a" Ground.Subst.pp subst; + let subst = Ground.Subst.distinct_union subst_bis subst in + let bn = convert ~subst env tb in + if is_nonlinear env bn then ( + let v = + convert ~subst env + (Expr.Term._or + [ + Expr.Term.eq ti tj; + Expr.Term.eq (Expr.Term.select term tj) + (Expr.Term.select tb tj); + ]) + in + Egraph.register env v; + Boolean.set_true env v) + in + InvertedPath.add_callback env raup_pattern_bis raup_run_bis + in + (raup_pattern, raup_run) + let init env = - List.iter - (fun (p, r) -> InvertedPath.add_callback env p r) - [ (adown_pattern, adown_run); (aup_pattern, aup_run) ] + let l = [ (adown_pattern, adown_run) ] in + let l = if !restrict_ext then (rext_pattern, rext_run) :: l else l in + let l = + if !restrict_aup then (raup_pattern, raup_run) :: l + else (aup_pattern, aup_run) :: l + in + List.iter (fun (p, r) -> InvertedPath.add_callback env p r) l - (* ext: a, b ⇒ (a = b) ⋠(a[k] ≠b[k]) *) + (* ext: a, b ⇒ (a = b) ⋠(a[k] ≠b[k]) *) let new_array env f = Datastructure.Push.iter db env ~f:(fun f2 -> let subst = @@ -166,18 +250,32 @@ end let converter env (f : Ground.t) = let s = Ground.sem f in (match s.ty with - | { app = { builtin = Expr.Array; _ }; _ } -> Theory.new_array env f + | { app = { builtin = Expr.Array; _ }; _ } when not !restrict_ext -> + (* Application of the extentionality rule *) + Theory.new_array env f | _ -> ()); match s with + | { app = { builtin = Expr.Base; _ }; args; _ } + when IArray.is_empty args && !restrict_aup -> + (* Update of the restrict_aup domain *) + Linearity_dom.upd_dom env (Ground.node f) Empty + | { app = { builtin = Expr.Base; _ }; args; _ } when !restrict_ext -> + IArray.iter + ~f:(fun n -> if is_array env n then Foreign_dom.set_dom env n IsForeign) + args | { app = { builtin = Expr.Select; _ }; args; _ } -> let a, k = IArray.extract2_exn args in Egraph.register env a; - Egraph.register env k + Egraph.register env k; + if !restrict_ext && is_array env k then + Foreign_dom.set_dom env k IsForeign | { app = { builtin = Expr.Store; _ }; args; _ } -> let a, k, v = IArray.extract3_exn args in Egraph.register env a; Egraph.register env k; Egraph.register env v; + (* Update of the restrict_aup/Linearity domain *) + if !restrict_aup then Linearity_dom.upd_dom env (Ground.node f) (Linear a); (* application of the `idx` rule *) let subst = Ground.Subst. diff --git a/colibri2/theories/array/foreign_dom.ml b/colibri2/theories/array/foreign_dom.ml new file mode 100644 index 0000000000000000000000000000000000000000..4864f8fabd2db2f6e42bb44fd794c1f67cff7e8a --- /dev/null +++ b/colibri2/theories/array/foreign_dom.ml @@ -0,0 +1,38 @@ +open Colibri2_core +open Colibri2_popop_lib +open Popop_stdlib + +module FVal = struct + module T = struct + type t = IsForeign [@@deriving eq, ord, hash] + + let pp fmt = function IsForeign -> Fmt.pf fmt "IsForeign" + end + + include T + include MkDatatype (T) + + let name = "Array.foreign.value" +end + +module D = struct + module Value = Value.Register (FVal) + + type t = FVal.t = IsForeign [@@deriving eq] + + let is_singleton (_ : Egraph.wt) _v = None (* ? *) + + let key = + Dom.Kind.create + (module struct + type nonrec t = t + + let name = "Array.foreign" + end) + + let inter (_ : Egraph.wt) IsForeign IsForeign = Some IsForeign + let pp = FVal.pp +end + +include D +include Dom.Lattice (D) diff --git a/colibri2/theories/array/foreign_dom.mli b/colibri2/theories/array/foreign_dom.mli new file mode 100644 index 0000000000000000000000000000000000000000..3c92f9cf512f5040e6fd4d9f2d6d46befb21f9f6 --- /dev/null +++ b/colibri2/theories/array/foreign_dom.mli @@ -0,0 +1,9 @@ +type t = IsForeign + +val equal : t -> t -> bool +val is_singleton : Egraph.wt -> t -> Colibri2_core.Value.t option +val key : t Dom.Kind.t +val inter : Egraph.wt -> t -> t -> t option +val pp : t Fmt.t +val set_dom : Egraph.wt -> Node.t -> t -> unit +val upd_dom : Egraph.wt -> Node.t -> t -> unit diff --git a/colibri2/theories/array/linearity_dom.ml b/colibri2/theories/array/linearity_dom.ml new file mode 100644 index 0000000000000000000000000000000000000000..e39dfb745c0b1d3905d06c7a7df95f3c65c24f11 --- /dev/null +++ b/colibri2/theories/array/linearity_dom.ml @@ -0,0 +1,61 @@ +open Colibri2_core +open Colibri2_popop_lib +open Popop_stdlib + +module LVal = struct + module T = struct + type t = NonLinear | Linear of Node.t | Empty [@@deriving eq, ord, hash] + + let pp fmt = function + | NonLinear -> Fmt.pf fmt "NonLinear" + | Linear n -> Fmt.pf fmt "Linear(%a)" Node.pp n + | Empty -> Fmt.pf fmt "Empty" + end + + include T + include MkDatatype (T) + + let name = "Array.linearity.value" +end + +module D = struct + module Value = Value.Register (LVal) + + type t = LVal.t = NonLinear | Linear of Node.t | Empty [@@deriving eq] + + let is_singleton _ _ = None + + let key = + Dom.Kind.create + (module struct + type nonrec t = t + + let name = "Array.linearity" + end) + + let inter d d1 d2 = + match (d1, d2) with + | NonLinear, NonLinear | NonLinear, Empty | Empty, NonLinear -> + Some NonLinear + | Linear n, Empty | Empty, Linear n -> Some (Linear n) + | Linear n1, Linear n2 -> + if Egraph.is_equal d n1 n2 then + (* does this ever happen? *) + Some (Linear n1) + else ( + Egraph.set_dom d key n1 NonLinear; + Egraph.set_dom d key n2 NonLinear; + Some NonLinear) + | NonLinear, Linear n | Linear n, NonLinear -> + Egraph.set_dom d key n NonLinear; + Some NonLinear + | Empty, Empty -> Some Empty + + let pp fmt = function + | NonLinear -> Fmt.pf fmt "NonLinear" + | Linear n -> Fmt.pf fmt "Linear(%a)" Node.pp n + | Empty -> Fmt.pf fmt "Empty" +end + +include D +include Dom.Lattice (D) diff --git a/colibri2/theories/array/linearity_dom.mli b/colibri2/theories/array/linearity_dom.mli new file mode 100644 index 0000000000000000000000000000000000000000..73de77b9963f68421bf05d1ce57e31adee291ffe --- /dev/null +++ b/colibri2/theories/array/linearity_dom.mli @@ -0,0 +1,9 @@ +type t = NonLinear | Linear of Node.t | Empty + +val equal : t -> t -> bool +val is_singleton : Egraph.wt -> t -> Colibri2_core.Value.t option +val key : t Dom.Kind.t +val inter : Egraph.wt -> t -> t -> t option +val pp : t Fmt.t +val set_dom : Egraph.wt -> Node.t -> t -> unit +val upd_dom : Egraph.wt -> Node.t -> t -> unit