diff --git a/src_colibri2/core/datastructure.ml b/src_colibri2/core/datastructure.ml index 9731f24949d9b2484c79d871bd35a2f508b261e9..ae03ab0d6d061327768b3a0713f2e3b0f9fc300c 100644 --- a/src_colibri2/core/datastructure.ml +++ b/src_colibri2/core/datastructure.ml @@ -167,26 +167,26 @@ module Hashtbl2(S:Colibri2_popop_lib.Popop_stdlib.Datatype) : Sig2 with type key end -module type Sig3 = sig +module type Memo = sig type 'a t type key - val create: 'a Format.printer -> string -> (Context.creator -> 'a) -> 'a t + val create: 'a Format.printer -> string -> (Context.creator -> key -> 'a) -> 'a t val find : 'a t -> Egraph.t -> key -> 'a val iter : (key -> 'a -> unit) -> 'a t -> Egraph.t -> unit val fold : (key -> 'a -> 'acc -> 'acc) -> 'a t -> Egraph.t -> 'acc -> 'acc end -module Memo(S:Colibri2_popop_lib.Popop_stdlib.Datatype) : Sig3 with type key := S.t = struct +module Memo(S:Colibri2_popop_lib.Popop_stdlib.Datatype) : Memo with type key := S.t = struct type 'a t' = { h: 'a S.H.t; - def: (Context.creator -> 'a); + def: (Context.creator -> S.t -> 'a); } type 'a t = 'a t' Env.Unsaved.t - let create : type a. a Colibri2_popop_lib.Pp.pp -> _ -> (Context.creator -> a) -> a t = fun pp name def -> + let create : type a. a Colibri2_popop_lib.Pp.pp -> _ -> (Context.creator -> S.t -> a) -> a t = fun pp name def -> let module M = struct type t = a t' let name = name end @@ -203,7 +203,7 @@ module Memo(S:Colibri2_popop_lib.Popop_stdlib.Datatype) : Sig3 with type key := match S.H.find_opt h.h k with | Some r -> r | None -> - let r = h.def (Egraph.context d) in + let r = h.def (Egraph.context d) k in S.H.add h.h k r; r diff --git a/src_colibri2/core/datastructure.mli b/src_colibri2/core/datastructure.mli index be6adf80359792f91b3ea7cdac2e44cf6e8229d0..07a79d56793e03f5f31097f500dda4baca301145 100644 --- a/src_colibri2/core/datastructure.mli +++ b/src_colibri2/core/datastructure.mli @@ -18,6 +18,8 @@ (* for more details (enclosed in the file licenses/LGPLv2.1). *) (*************************************************************************) +(** {2 An hashtable context aware } *) + module type Sig = sig type 'a t type key @@ -39,6 +41,8 @@ end module Hashtbl (S:Colibri2_popop_lib.Popop_stdlib.Datatype) : Sig with type key := S.t +(** {2 An hashtable context aware, where data are automatically initialized } *) + module type Sig2 = sig type 'a t type key @@ -52,17 +56,20 @@ end module Hashtbl2 (S:Colibri2_popop_lib.Popop_stdlib.Datatype) : Sig2 with type key := S.t -module type Sig3 = sig +(** {2 A table where data are initialized on the fly, and can't be modified. It + is used with other context aware mutable data-structure as data } *) + +module type Memo = sig type 'a t type key - val create: 'a Format.printer -> string -> (Context.creator -> 'a) -> 'a t + val create: 'a Format.printer -> string -> (Context.creator -> key -> 'a) -> 'a t val find : 'a t -> Egraph.t -> key -> 'a val iter : (key -> 'a -> unit) -> 'a t -> Egraph.t -> unit val fold : (key -> 'a -> 'acc -> 'acc) -> 'a t -> Egraph.t -> 'acc -> 'acc end -module Memo (S:Colibri2_popop_lib.Popop_stdlib.Datatype) : Sig3 with type key := S.t +module Memo (S:Colibri2_popop_lib.Popop_stdlib.Datatype) : Memo with type key := S.t module HNode : Sig with type key := Nodes.Node.t diff --git a/src_colibri2/core/demon.ml b/src_colibri2/core/demon.ml index b2358dda69df4fda35d9d38af2338a36251b8730..11bd1dacdd668af2d78044b339b374b82eccb0a2 100644 --- a/src_colibri2/core/demon.ml +++ b/src_colibri2/core/demon.ml @@ -705,6 +705,7 @@ module Fast = struct type waiter = { for_dom: 'a. Egraph.t -> 'a DomKind.t -> Node.t -> (Egraph.t -> Node.t -> unit) -> unit; for_value: 'a. Egraph.t -> 'a ValueKind.t -> Node.t -> (Egraph.t -> Node.t -> unit) -> unit; + for_any_value: 'a. Egraph.t -> Node.t -> (Egraph.t -> Node.t -> unit) -> unit; } let register_simply @@ -740,6 +741,12 @@ module Fast = struct | None -> () | Some _ -> f d n in - RDaemonInit.init, {for_dom; for_value} + let for_any_value d n f = + attach d DaemonInit.key [Create.EventAnyValue(n,f)]; + match Egraph.get_value d n with + | None -> () + | Some _ -> f d n + in + RDaemonInit.init, {for_dom; for_value; for_any_value} end diff --git a/src_colibri2/core/demon.mli b/src_colibri2/core/demon.mli index 7c4dbde8bb070c994cf649f0ec07a30ff21405e1..d44791dd1b499c5bee5c3aba399abad6abff323e 100644 --- a/src_colibri2/core/demon.mli +++ b/src_colibri2/core/demon.mli @@ -208,6 +208,7 @@ module Fast: sig type waiter = { for_dom: 'a. Egraph.t -> 'a DomKind.t -> Node.t -> (Egraph.t -> Node.t -> unit) -> unit; for_value: 'a. Egraph.t -> 'a ValueKind.t -> Node.t -> (Egraph.t -> Node.t -> unit) -> unit; + for_any_value: 'a. Egraph.t -> Node.t -> (Egraph.t -> Node.t -> unit) -> unit; } (** helper *) diff --git a/src_colibri2/core/egraph.ml b/src_colibri2/core/egraph.ml index 505ac731444db8951288d19285e1b94fe5fd8f5f..4f12899b8b09135617048e381fb45d7550752db2 100644 --- a/src_colibri2/core/egraph.ml +++ b/src_colibri2/core/egraph.ml @@ -718,7 +718,7 @@ module Delayed = struct in VDomTable.iter_initializedi {VDomTable.iteri} t.env.dom; - (** move value events *) + (** move value events by valuekind *) let mapi _ events = match Node.M.find_opt other_node events with | None -> events @@ -729,6 +729,19 @@ module Delayed = struct t.env.value <- { t.env.value with events = ValueKind.M.mapi {mapi} t.env.value.events }; + begin + let event, other_event = Node.M.find_remove other_node t.env.event_value in + + (** move value events *) + begin match other_event with + | None -> () + | Some other_event -> + t.env.event_value <- + Node.M.add_change (fun x -> x) Bag.concat repr_node other_event + event + end; + end; + (** wakeup the daemons *) Wait.wakeup_events_bag diff --git a/src_colibri2/core/events.mli b/src_colibri2/core/events.mli index 5bd66017a7339dcc92240665fe65ff15f6a78aee..df4af5f05529b9d70502abb90b9c665a1cad9c6e 100644 --- a/src_colibri2/core/events.mli +++ b/src_colibri2/core/events.mli @@ -92,6 +92,9 @@ module Wait : sig type runable val print_runable : runable Format.printer val run : delayed -> runable -> runable option + (** The function run after the delay, + a new run can be scheduled using the result *) + type event val print_event : event Format.printer val enqueue : delayed_ro -> event Fired.event -> runable enqueue diff --git a/src_colibri2/core/ground.ml b/src_colibri2/core/ground.ml index 74aced0be13fce86277ca86449180c8bcecbb9ba..8183c53b1322c5eff080707616781ce534674f3f 100644 --- a/src_colibri2/core/ground.ml +++ b/src_colibri2/core/ground.ml @@ -9,7 +9,10 @@ module All = struct [@@deriving eq, ord, hash] let rec pp_ty fmt ty = - Fmt.pf fmt "%a%a" Expr.Ty.Const.pp ty.app Fmt.(list ~sep:comma pp_ty) ty.args + match ty.args with + | [] -> Fmt.pf fmt "%a" Expr.Ty.Const.pp ty.app + | _ -> + Fmt.pf fmt "%a(%a)" Expr.Ty.Const.pp ty.app Fmt.(list ~sep:comma pp_ty) ty.args type term = { app: Expr.Term.Const.t; @@ -74,6 +77,10 @@ module Ty = struct let real = convert Expr.Ty.Var.M.empty Expr.Ty.real let string = convert Expr.Ty.Var.M.empty Expr.Ty.string let string_reg_lang = convert Expr.Ty.Var.M.empty Expr.Ty.string_reg_lang + let definition sym = + match Expr.Ty.definition sym with + | None -> Expr.Ty.Abstract + | Some x -> x end module Term = struct @@ -222,12 +229,46 @@ let registered_converter = Datastructure.Queue.create Fmt.nop "Ground.converters let register_converter d f = Datastructure.Queue.push registered_converter d f +let dom_tys = + DomKind.create_key + (module struct + type nonrec t = Ty.S.t + + let name = "Ground.tys" + end) + +let () = Egraph.register_dom (module struct + include Ty.S + let key = dom_tys + let merged tys0 tys1 = + Option.equal equal tys0 tys1 + + let merge d (tys0,n0) (tys1,n1) _ = + let s = (Ty.S.union + (Base.Option.value ~default:Ty.S.empty tys0) + (Base.Option.value ~default:Ty.S.empty tys1) + ) in + Egraph.set_dom d key n0 s; + Egraph.set_dom d key n1 s + end) + +let tys d n = (Base.Option.value ~default:Ty.S.empty (Egraph.get_dom d dom_tys n)) + let init d = Demon.Fast.register_init_daemon ~name:"Ground.convert" (module ThTerm) - (fun d s -> - Defs.converter d s; - Datastructure.Queue.iter registered_converter d ~f:(fun f -> f d s)) + (fun d g -> + let n = ThTerm.node g in + let s = ThTerm.sem g in + (match Egraph.get_dom d dom_tys n with + | None -> Egraph.set_dom d dom_tys n (Ty.S.singleton s.ty) + | Some tys -> + match Ty.S.add_new Exit s.ty tys with + | exception Exit -> () + | tys -> Egraph.set_dom d dom_tys n tys + ); + Defs.converter d g; + Datastructure.Queue.iter registered_converter d ~f:(fun f -> f d g)) d include ThTerm diff --git a/src_colibri2/core/ground.mli b/src_colibri2/core/ground.mli index 4f9d94f0d173736994d0803c6495f925a4558fa4..8321dac26b9c3a9418b23fefebb3fd229544e480 100644 --- a/src_colibri2/core/ground.mli +++ b/src_colibri2/core/ground.mli @@ -67,6 +67,7 @@ module Ty: sig val string_reg_lang : t (** The type of regular language over strings. *) + val definition: Expr.Ty.Const.t -> Expr.Ty.def end module Term: sig @@ -94,6 +95,8 @@ val register_converter: Egraph.t -> (Egraph.t -> t -> unit) -> unit (** register callback called for each new ground term registered *) +val tys: Egraph.t -> Node.t -> Ty.S.t + module Defs : sig val add: Egraph.t -> diff --git a/src_colibri2/core/interp.ml b/src_colibri2/core/interp.ml index 7b0fd07648ef7cd5ce318bfd72e5778da2a23707..dde472fc3d78ae22b67795a1dbc33a09b70cba32 100644 --- a/src_colibri2/core/interp.ml +++ b/src_colibri2/core/interp.ml @@ -20,15 +20,22 @@ open Base open Colibri2_popop_lib +open Nodes -type env = - | Before (** fixing model *) - | During of { limitreached : bool ref; limit : int; nextnode : int } +let debug = + Debug.register_info_flag ~desc:"for the scheduler in the simple version" + "interp" + +type during = { + limitreached : bool ref; (** it is non backtrable variable *) + limit : int; + nextnode : Node.t Sequence.t; [@opaque] +} [@@deriving show] -module Data = struct - let ground = Datastructure.Queue.create Fmt.nop "Interp.ground" +type env = Before (** fixing model *) | During of during [@@deriving show] +module Data = struct let check = Datastructure.Queue.create Fmt.nop "Interp.check" let node = Datastructure.Queue.create Fmt.nop "Interp.node" @@ -37,14 +44,14 @@ module Data = struct let reg_ground = Datastructure.Queue.create Fmt.nop "Interp.reg_ground" - let reg_node = Datastructure.Queue.create Fmt.nop "Interp.reg_node" + (* let reg_node = Datastructure.Queue.create Fmt.nop "Interp.reg_node" *) let env = Env.Saved.create_key (module struct type t = env - let name = "Interp.name" + let name = "Interp.env" end) let () = Env.Saved.register pp_env env @@ -63,6 +70,10 @@ module SeqLim = struct d.limitreached := true; Sequence.Step.Done)) + let map = Sequence.map + + let unfold_with = Sequence.unfold_with + let ( let+ ) = Sequence.( >>| ) let ( let* ) t f = Sequence.bind t ~f @@ -95,83 +106,128 @@ exception CantInterpretTy of Ground.Ty.t let ty d ty = get_registered (fun ty -> raise (CantInterpretTy ty)) Data.ty d ty -exception CantInterpretNode of Nodes.Node.t +exception CantInterpretNode of Node.t let node d n = get_registered (fun n -> raise (CantInterpretNode n)) Data.node d n exception CantCheckGround of Ground.t -let ground d n = - get_registered (fun n -> raise (CantCheckGround n)) Data.ground d n +let check d n = + get_registered (fun n -> raise (CantCheckGround n)) Data.check d n -let check_ground_terms d = - Datastructure.Queue.iter Data.reg_ground d ~f:(fun g -> - if not (ground d g) then Egraph.contradiction d) - -(* module WTO = Colibri2_stdlib.Wto.Make (Ground) *) +exception GroundTermWithoutValueAfterModelFixing of Ground.t -(* let compute_wto d = - * let g = Nodes.Node.H.create 100 in *) +exception ArgOfGroundTermWithoutValueAfterModelFixing of Ground.t * Node.t -let init d = - Egraph.set_env d Data.env Before; - Demon.Fast.register_any_new_node_daemon ~name:"Interp.new_node" - (fun d g -> Datastructure.Queue.push Data.reg_node d g) - d; - Ground.register_converter d (fun d g -> - Datastructure.Queue.push Data.reg_ground d g) +let check_ground_terms d = + Datastructure.Queue.iter Data.reg_ground d ~f:(fun g -> + if Option.is_none (Egraph.get_value d (Ground.node g)) then + raise (GroundTermWithoutValueAfterModelFixing g); + List.iter (Ground.sem g).args ~f:(fun n -> + if Option.is_none (Egraph.get_value d n) then + raise (ArgOfGroundTermWithoutValueAfterModelFixing (g, n))); + if not (check d g) then ( + Debug.dprintf2 debug "Interp: checking of %a failed" Ground.Term.pp + (Ground.sem g); + Egraph.contradiction d)) + +module WTO = Colibri2_stdlib.Wto.Make (Node) + +let rec component_to_seq p = + let open Sequence.Generator in + match p with + | Wto.Component (n, part) -> yield n >>= fun () -> partition_to_seq part + | Wto.Node n -> yield n + +and partition_to_seq' l = + let open Sequence.Generator in + match l with + | [] -> return () + | a :: l -> component_to_seq a >>= fun () -> partition_to_seq' l + +and partition_to_seq l = partition_to_seq' (List.rev l) + +let sequence_of_ground d = + let open Base in + let n2g = Hashtbl.create (module Node) in + let inits = Queue.create () in + let push n = + if not (Hashtbl.mem n2g n) then ( + if Option.is_none (Egraph.get_value d n) then Queue.enqueue inits n; + Hashtbl.set n2g ~key:n ~data:[]) + in + Datastructure.Queue.iter Data.reg_ground d ~f:(fun g -> + let n = Egraph.find d (Ground.node g) in + push n; + List.iter ~f:push (Ground.sem g).args; + Hashtbl.add_multi n2g ~key:n ~data:g); + let partition = + WTO.partition ?pref:None ~inits:(Queue.to_list inits) ~succs:(fun n -> + if Option.is_some (Egraph.get_value d n) then [] + else + let gs = Hashtbl.find_multi n2g n in + List.concat_map gs ~f:(fun g -> (Ground.sem g).args)) + in + Debug.dprintf2 debug "FixModel wto:%a" (Wto.pp_partition Node.pp) partition; + Sequence.Generator.run (partition_to_seq partition) module Fix_model = struct let init d = let env = - During - { - limitreached = - (* true because we want to continue (i.e start) *) - ref true; - nextnode = Datastructure.Queue.length Data.reg_node d - 1; - limit = 1; - } + { + limitreached = + (* true because we want to continue (i.e start) *) + ref true; + nextnode = sequence_of_ground d; + limit = 1; + } in - Egraph.set_env d Data.env env + Debug.dprintf0 debug "FixModel with ground terms"; + Egraph.set_env d Data.env (During env); + env let next_dec d = match Egraph.get_env d Data.env with | Before -> (* The first branching is about the limit *) - init d; - Sequence.unfold ~init:(1, d) ~f:(fun (i, d) -> - match Egraph.get_env d Data.env with - | Before -> assert false - | During r -> - if !(r.limitreached) then - let limit = i * 2 in - let dec d = - match Egraph.get_env d Data.env with - | Before -> assert false - | During r -> - Egraph.set_env d Data.env (During { r with limit }) - in - Some (dec, (limit, d)) - else None) + let r = init d in + Sequence.unfold ~init:1 ~f:(fun i -> + if !(r.limitreached) then ( + r.limitreached := false; + if i > Base.Int.shift_left 1 25 then raise Impossible; + let limit = i * 2 in + Debug.dprintf1 debug "FixModel with limit at %i" limit; + let dec d = Egraph.set_env d Data.env (During { r with limit }) in + Some (dec, limit)) + else None) | During r -> let rec aux nextnode = - if nextnode < 0 then ( - check_ground_terms d; - Egraph.set_env d Data.env Before; - Sequence.empty) - else - (* The others are about the values *) - let n = Datastructure.Queue.get Data.reg_node d r.nextnode in - match Egraph.get_value d n with - | Some _ -> aux (nextnode - 1) - | None -> - let seq = node d n in - Sequence.map seq ~f:(fun v d -> - Egraph.set_env d Data.env - (During { r with nextnode = nextnode - 1 }); - Egraph.set_value d n v) + match Sequence.next nextnode with + | None -> + Debug.dprintf0 debug "FixModel check_ground terms"; + check_ground_terms d; + Egraph.set_env d Data.env Before; + Sequence.empty + | Some (n, nextnode) -> ( + (* The others are about the values *) + Debug.dprintf2 debug "FixModel look at %a" Node.pp n; + match Egraph.get_value d n with + | Some _ -> aux nextnode + | None -> + Debug.dprintf2 debug "FixModel %a has no value" Node.pp n; + let seq = + let tys = Ground.tys d n in + try node d n + with + | CantInterpretNode _ when not (Ground.Ty.S.is_empty tys) -> + ty d (Ground.Ty.S.choose tys) + in + Sequence.map seq ~f:(fun v d -> + Egraph.set_env d Data.env (During { r with nextnode }); + Debug.dprintf4 debug "FixModel by setting %a to %a" + Node.pp n Value.pp v; + Egraph.set_value d n v)) in aux r.nextnode end @@ -180,9 +236,129 @@ let () = Exn_printer.register (fun fmt exn -> match exn with | CantInterpretNode t -> - Fmt.pf fmt "Can't interpret the node %a." Nodes.Node.pp t - | CantCheckGround t -> - Fmt.pf fmt "Can't check ground term %a." Ground.pp t + Fmt.pf fmt "Can't interpret the node %a." Node.pp t + | CantCheckGround g -> + Fmt.pf fmt "Can't check ground term %a of type %a." Ground.Term.pp + (Ground.sem g) Ground.Ty.pp (Ground.sem g).ty | CantInterpretTy t -> Fmt.pf fmt "Can't interpret the type %a." Ground.Ty.pp t + | GroundTermWithoutValueAfterModelFixing g -> + Fmt.pf fmt "After model fixing %a of type %a has no value" + Ground.Term.pp (Ground.sem g) Ground.Ty.pp (Ground.sem g).ty + | ArgOfGroundTermWithoutValueAfterModelFixing (g, n) -> + Fmt.pf fmt + "After model fixing arguments %a of %a of type %a has no value" + Node.pp n Ground.Term.pp (Ground.sem g) Ground.Ty.pp + (Ground.sem g).ty | exn -> raise exn) + +(** Helpers *) +module TwoWatchLiteral = struct + (** todo move the check to enqueue; but it requires to be able to attach event + in delayed_ro *) + + module Daemon = struct + type t = { + callback : Egraph.t -> Ground.t -> unit; + ground : Ground.t; (** perhaps ground should have an array for args *) + args : Node.t array; + from_start : int Context.Ref.t; (** already set before *) + from_end : int Context.Ref.t; (** already set after *) + } + + type runable = t + + let print_runable = Fmt.nop + + type event = t + + let print_event = Fmt.nop + + let enqueue _ event = + match event with + | Events.Fired.EventValue (_, _, d) -> Events.Wait.EnqRun d + | _ -> raise Impossible + + let key = + Events.Dem.create_key + (module struct + type d = runable + + type t = event + + let name = "Interp.TwoWatchLiteral" + end) + + let delay = Events.FixingModel + + let rec check_if_set d args incr stop i = + if Option.is_none (Egraph.get_value d args.(i)) then i + else + let i = incr + i in + if i = stop then stop else check_if_set d args incr stop i + + let attach d r i = Egraph.attach_any_value d r.args.(i) key r + + let run d r = + let o_from_start = Context.Ref.get r.from_start in + let o_from_end = Context.Ref.get r.from_end in + (if o_from_end < o_from_start then () + else + let from_start = + check_if_set d r.args 1 (o_from_end + 1) o_from_start + in + if o_from_end < from_start then ( + Context.Ref.set r.from_start from_start; + r.callback d r.ground) + else ( + if from_start <> o_from_start then ( + Context.Ref.set r.from_start from_start; + attach d r from_start); + if o_from_end = from_start then () + else + let from_end = check_if_set d r.args (-1) from_start o_from_end in + Context.Ref.set r.from_end from_end)); + None + + let create d callback ground = + match (Ground.sem ground).args with + | [] -> callback d ground + | _ -> + let args = Array.of_list (Ground.sem ground).args in + let r from_start from_end = + assert (from_start <= from_end); + Debug.dprintf4 debug "Interp create %a (%i-%i)" Ground.pp ground + from_start from_end; + { + callback; + ground; + args; + from_start = Context.Ref.create (Egraph.context d) from_start; + from_end = Context.Ref.create (Egraph.context d) from_end; + } + in + let from_start = check_if_set d args 1 (Array.length args) 0 in + if from_start = Array.length args then callback d ground + else if from_start = Array.length args - 1 then + attach d (r from_start from_start) from_start + else + let from_end = + check_if_set d args (-1) from_start (Array.length args - 1) + in + let r = r from_start from_end in + attach d r from_start; + attach d r from_end + end + + let () = Egraph.Wait.register_dem (module Daemon) + + let create = Daemon.create +end + +let init d = + Egraph.set_env d Data.env Before; + (* Demon.Fast.register_any_new_node_daemon ~name:"Interp.new_node" + * (fun d g -> Datastructure.Queue.push Data.reg_node d g) + * d; *) + Ground.register_converter d (fun d g -> + Datastructure.Queue.push Data.reg_ground d g) diff --git a/src_colibri2/core/interp.mli b/src_colibri2/core/interp.mli index e2df2c8e162b7b63f0082d7fe8e7fd099f14fdc2..fcd757cb0dbf2597193a1056a43887574872bf0a 100644 --- a/src_colibri2/core/interp.mli +++ b/src_colibri2/core/interp.mli @@ -37,6 +37,11 @@ module SeqLim : sig (** cartesian product {!Base.Sequence.cartesian_product} *) val ( let* ) : 'a t -> ('a -> 'b t) -> 'b t + + val map : 'a t -> f:('a -> 'b) -> 'b t + + val unfold_with : + 'a t -> init:'b -> f:('b -> 'a -> ('c, 'b) Sequence.Step.t) -> 'c t end module Register : sig @@ -73,3 +78,8 @@ module Fix_model : sig (** Add a level of decision for fixing the model, another level could be needed. Could raise unsatisfiable if all the model have been tried *) end + +module TwoWatchLiteral : sig + val create : Egraph.t -> (Egraph.t -> Ground.t -> unit) -> Ground.t -> unit + (** call the given function when all the arguments of the ground term have a value *) +end diff --git a/src_colibri2/popop_lib/popop_stdlib.ml b/src_colibri2/popop_lib/popop_stdlib.ml index 78fc690efe31a1a10df85ed6b71be9548431d9be..8d981e9e37e57eb8a300796bbbaf5ec133c3a2f8 100644 --- a/src_colibri2/popop_lib/popop_stdlib.ml +++ b/src_colibri2/popop_lib/popop_stdlib.ml @@ -23,6 +23,9 @@ module XHashtbl = Exthtbl.Hashtbl (* Set, Map, Hashtbl on structures with a unique tag *) +let sexp_of_t_of_pp pp x = + Base.Sexp.Atom (Fmt.str "%a" pp x) + module type TaggedType = sig type t @@ -50,24 +53,13 @@ struct let hash_fold_t s x = Base.Hash.fold_int s (X.tag x) end -module OrderedHashedList (X : TaggedType) = -struct - type t = X.t list - let hash = Lists.hash X.tag 3 - let equ_ts ts1 ts2 = X.tag ts1 == X.tag ts2 - let equal = Lists.equal equ_ts - let cmp_ts ts1 ts2 = Stdlib.compare (X.tag ts1) (X.tag ts2) - let compare = Lists.compare cmp_ts - let pp = Pp.list Pp.comma X.pp - let hash_fold_t s x = - Base.hash_fold_list (fun s x -> Base.Hash.fold_int s (X.tag x)) s x -end - module MakeMSH (X : TaggedType) = struct module T = OrderedHashed(X) include T let hash_fold_t state t = Base.Hash.fold_int state (X.tag t) + let sexp_of_t = sexp_of_t_of_pp pp + let tag = X.tag module MGen = Intmap.Make(struct include X let equal ts1 ts2 = X.tag ts1 == X.tag ts2 @@ -100,6 +92,7 @@ module type Datatype = sig module S : Map_intf.Set with type 'a M.t = 'a M.t and type M.key = M.key module H : Exthtbl.Hashtbl.S with type key = t + include Base.Hashtbl.Key.S with type t := t end module type Printable = sig @@ -109,6 +102,7 @@ end module MkDatatype(T : OrderedHashedType) = struct let hash_fold_t state t = Base.Hash.fold_int state (T.hash t) + let sexp_of_t = sexp_of_t_of_pp T.pp module M = Map.Make(T) module S = Extset.MakeOfMap(M) module H = XHashtbl.Make(T) @@ -127,7 +121,7 @@ module Int = struct module DInt = struct - include Int + include Base.Int let pp fmt x = Format.pp_print_int fmt x let hash_fold_t = Base.Int.hash_fold_t module GM = Intmap.Make(Int) @@ -138,46 +132,32 @@ end module DIntOrd = DInt -module DUnit = Unit - -module Bool = struct - open! Base - type t = bool - [@@deriving eq, ord, hash, show] +module DUnit = struct + include Unit + let sexp_of_t = Base.Unit.sexp_of_t end module DBool = struct - include Bool - let hash_fold_t = Base.Bool.hash_fold_t - module M = Map.Make(Bool) + include Base.Bool + module M = Map.Make(Base.Bool) module S = Extset.MakeOfMap(M) - module H = XHashtbl.Make(Bool) + module H = XHashtbl.Make(Base.Bool) end module DStr = struct - module Str = struct - open! Base - type t = String.t - [@@deriving show,eq,ord,hash] - end - include Str + include Base.String let hash_fold_t = Base.String.hash_fold_t - module M = Map.Make(Str) + module M = Map.Make(Base.String) module S = Extset.MakeOfMap(M) - module H = XHashtbl.Make(Str) + module H = XHashtbl.Make(Base.String) end module DFloat = struct - module Float = struct - open! Base - type t = float - [@@deriving show,eq,ord,hash] - end - include Float + include Base.Float let hash_fold_t = Base.Float.hash_fold_t - module M = Map.Make(Float) + module M = Map.Make(Base.Float) module S = Extset.MakeOfMap(M) - module H = XHashtbl.Make(Float) + module H = XHashtbl.Make(Base.Float) end diff --git a/src_colibri2/popop_lib/popop_stdlib.mli b/src_colibri2/popop_lib/popop_stdlib.mli index a27d4c17b77d56464d878d6e0592e91ca172fb7a..5223769cc60fb9c7299a55933030908697546439 100644 --- a/src_colibri2/popop_lib/popop_stdlib.mli +++ b/src_colibri2/popop_lib/popop_stdlib.mli @@ -23,6 +23,8 @@ module XHashtbl : Exthtbl.Hashtbl (* Set, Map, Hashtbl on structures with a unique tag *) +val sexp_of_t_of_pp: 'a Fmt.t -> 'a -> Base.Sexp.t + module type TaggedType = sig type t @@ -48,6 +50,7 @@ module type Datatype = sig module S : Map_intf.Set with type 'a M.t = 'a M.t and type M.key = M.key module H : Exthtbl.Hashtbl.S with type key = t + include Base.Hashtbl.Key.S with type t := t end module type Printable = sig @@ -61,14 +64,12 @@ module MkDatatype(T : OrderedHashedType) : sig module S : Map_intf.Set with type 'a M.t = 'a M.t and type M.key = M.key module H : Exthtbl.Hashtbl.S with type key = T.t + val sexp_of_t: T.t -> Base.Sexp.t end module OrderedHashed (X : TaggedType) : OrderedHashedType with type t = X.t -module OrderedHashedList (X : TaggedType) : - OrderedHashedType with type t = X.t list - module MakeMSH (X : TaggedType) : Datatype with type t = X.t module MakeMSHW (X : Weakhtbl.Weakey) : diff --git a/src_colibri2/solver/scheduler.ml b/src_colibri2/solver/scheduler.ml index 572aadd85efc16c56558664bdfbfef0f88aea2bb..d418ba6989fd067da354adc1c0ba427a492340e8 100644 --- a/src_colibri2/solver/scheduler.ml +++ b/src_colibri2/solver/scheduler.ml @@ -41,6 +41,7 @@ let stats_lasteffort = Debug.register_stats_int ~name:"Scheduler.lasteffort" ~in let stats_dec = Debug.register_stats_int ~name:"Scheduler.decision" ~init:0 let stats_con = Debug.register_stats_int ~name:"Scheduler.conflict" ~init:0 let stats_step = Debug.register_stats_int ~name:"Scheduler.step" ~init:0 +let stats_fix_model = Debug.register_stats_int ~name:"Scheduler.fix_model" ~init:0 exception NeedStopDelayed exception Contradiction @@ -65,7 +66,7 @@ end module Prio = Leftistheap.Make(Att) type bp_kind = - | Choices of (Egraph.t -> unit) list + | Choices of (Egraph.t -> unit) Base.Sequence.t | External type bp = @@ -76,11 +77,15 @@ type bp = pre_level : int; } +type solve_step = + | Propagate + | FixModel + type t = { mutable choices : Prio.t; daemons : Events.Wait.daemon_key Context.TimeWheel.t; lasteffort : Events.Wait.daemon_key Context.TimeWheel.t; - mutable fixing_model : Events.Wait.daemon_key list; + fixing_model : Events.Wait.daemon_key list Context.Ref.t; mutable prev_scheduler_state : bp option; solver_state : Egraph.Backtrackable.t; mutable delayed : Egraph.t option; @@ -89,6 +94,7 @@ type t = var_inc : float ref; context : Context.context; mutable level: int; + solve_step: solve_step Context.Ref.t; } (** To treat in the reverse order *) @@ -116,7 +122,7 @@ let new_solver () = { choices = Prio.empty; daemons = Context.TimeWheel.create (Context.creator context); lasteffort = Context.TimeWheel.create (Context.creator context); - fixing_model = []; + fixing_model = Context.Ref.create (Context.creator context) []; prev_scheduler_state = None; solver_state = Egraph.Backtrackable.new_t (Context.creator context); context; @@ -124,6 +130,7 @@ let new_solver () = decprio = Node.H.create 100; var_inc = ref 1.; level = 0; + solve_step = Context.Ref.create (Context.creator context) (Propagate); } let push kind t = @@ -172,7 +179,7 @@ let new_delayed = | LastEffort offset -> Context.TimeWheel.add t.lasteffort att offset | FixingModel -> - t.fixing_model <- att :: t.fixing_model + Context.Ref.set t.fixing_model (att :: (Context.Ref.get t.fixing_model)) in let sched_decision dec = incr dec_count; @@ -240,9 +247,12 @@ let rec conflict_analysis t = pop_to t prev; t.choices <- Prio.reprio t.decprio t.choices; match prev.kind with - | Choices [] -> rewind () - | Choices (choice::choices) -> - make_choice t choice choices + | Choices seq -> begin + match Base.Sequence.next seq with + | None -> rewind () + | Some (choice,choices) -> + make_choice t choice choices + end | External -> raise Contradiction in rewind () @@ -261,7 +271,7 @@ and try_run_dec: t.choices <- prio; Debug.incr stats_dec; Egraph.Backtrackable.delayed_stop d; - make_choice t todo todos + make_choice t todo (Base.Sequence.of_list todos) and make_choice t todo todos = ignore (push (Choices todos) t); @@ -286,48 +296,81 @@ and make_choice t todo todos = * end * | Some (Att.Decision (_,_)) | None -> () *) +let [@inline always] protect_against_contradiction t d f g = + match f d with + | exception Egraph.Contradiction -> + Debug.dprintf0 debug "[Scheduler] Contradiction"; + Some (conflict_analysis t) + | r -> g d r -let run_one_step t d = +let run_one_step_propagation ~nodec t d = Debug.incr stats_step; match Context.TimeWheel.next t.daemons with | Some att -> begin Debug.incr stats_propa; - try - Egraph.Backtrackable.run_daemon d att; d - with Egraph.Contradiction -> - Debug.dprintf0 debug "[Scheduler] Contradiction"; - conflict_analysis t + protect_against_contradiction t d (fun d -> + Egraph.Backtrackable.run_daemon d att + ) (fun d () -> Some d) end + | None when nodec -> None | None -> match Context.TimeWheel.next_at_same_time t.lasteffort with | Some att -> begin Debug.dprintf1 debug "[Scheduler] LastEffort mode remaining: %t" (fun fmt -> Fmt.int fmt (Context.TimeWheel.size_at_current_time t.lasteffort)); Debug.incr stats_lasteffort; - try - Egraph.Backtrackable.run_daemon d att; d - with Egraph.Contradiction -> - Debug.dprintf0 debug "[Scheduler] Contradiction"; - conflict_analysis t + protect_against_contradiction t d (fun d -> + Egraph.Backtrackable.run_daemon d att + ) (fun d () -> Some d) end | None -> match Prio.extract_min t.choices with | Att.Decision (_,chogen), prio -> Debug.dprintf0 debug "[Scheduler] decision"; - try_run_dec t d prio chogen + Some (try_run_dec t d prio chogen) | exception Leftistheap.Empty -> Debug.dprintf1 debug "[Scheduler] LastEffort: %i" (Context.TimeWheel.size t.lasteffort); match Context.TimeWheel.next t.lasteffort with | Some att -> begin Debug.incr stats_lasteffort; - try - Egraph.Backtrackable.run_daemon d att; d - with Egraph.Contradiction -> - Debug.dprintf0 debug "[Scheduler] Contradiction"; - conflict_analysis t + protect_against_contradiction t d (fun d -> + Egraph.Backtrackable.run_daemon d att + ) (fun d () -> Some d) end | None -> - d + None + +let run_one_step_fix_model t d = + Debug.incr stats_fix_model; + match Context.Ref.get t.fixing_model with + | att::l -> begin + Context.Ref.set t.fixing_model l; + protect_against_contradiction t d (fun d -> + Egraph.Backtrackable.run_daemon d att + ) (fun d () -> Some d) + end + | [] -> + protect_against_contradiction t d (fun d -> + Interp.Fix_model.next_dec d + ) (fun d seq -> + match Base.Sequence.next seq with + | None -> None + | Some (todo,todos) -> + Egraph.Backtrackable.delayed_stop d; + Some (make_choice t todo todos) + ) + +let run_one_step ~nodec t d = + match Context.Ref.get t.solve_step with + | Propagate -> begin + match run_one_step_propagation ~nodec t d with + | Some _ as s -> s + | None when nodec -> None + | None -> + Context.Ref.set t.solve_step FixModel; + Some d + end + | FixModel -> run_one_step_fix_model t d let rec flush t d = try @@ -342,19 +385,11 @@ exception ReachStepLimit let rec run_inf_step ?limit ~nodec t d = (match limit with | Some n when n <= 0 -> raise ReachStepLimit | _ -> ()); let d = flush t d in - let run = - 0 < Context.TimeWheel.size t.daemons || - match Prio.min t.choices with - | Some (Att.Decision _) -> not nodec - | None -> 0 < Context.TimeWheel.size t.lasteffort - in - if run - then - let d = run_one_step t d in + match run_one_step ~nodec t d with + | Some d -> run_inf_step ?limit:(Opt.map pred limit) ~nodec t d - else begin + | None -> Egraph.Backtrackable.delayed_stop d - end let run_inf_step ?limit ?(nodec=false) t = if t.delayed <> None then raise NeedStopDelayed; @@ -395,7 +430,7 @@ let stop_delayed t = let init_theories ~theories t = try let d = get_delayed t in - List.iter (fun f -> f d) (Ground.init::theories); + List.iter (fun f -> f d) (Ground.init::Interp.init::theories); Egraph.Backtrackable.flush d; let d = flush t d in stop t d diff --git a/src_colibri2/stdlib/context.ml b/src_colibri2/stdlib/context.ml index 8e2bfdf0ce33a725f181e4d391e5d221bf012e41..d890f6f21414b7b64b1f25a403b5d8ce5b103233 100644 --- a/src_colibri2/stdlib/context.ml +++ b/src_colibri2/stdlib/context.ml @@ -326,6 +326,12 @@ module Queue = struct let fold f acc t = refresh t; Vector.fold f acc t.v let length t = refresh t; Vector.length t.v let get t i = refresh t; Vector.get t.v i + let of_seq t = refresh t; + Base.Sequence.unfold + ~init:(Vector.length t.v) + ~f:(fun i -> if i = 0 then None else + let i = i - 1 in + Some(Vector.get t.v i,i)) let pp ?sep pp fmt v = Vector.pp ?pp_sep:sep pp fmt v.v end @@ -342,71 +348,71 @@ module RefOpt = struct end -module Hashtbl = struct - module type Sig = sig - type 'a t - type key +module type Hashtbl = sig + type 'a t + type key - val create: creator -> 'a t + val create: creator -> 'a t - val remove : 'a t -> key -> unit - val set : 'a t -> key -> 'a -> unit - val find: 'a t -> key -> 'a - val find_opt: 'a t -> key -> 'a option - val change : ('a option -> 'a option) -> 'a t -> key -> unit - end - - module Make(S:Colibri2_popop_lib.Popop_stdlib.Datatype) : Sig with type key := S.t = struct - - - type 'a t = { - h: 'a RefOpt.t S.H.t; - context: context; - } - - let create context = { h = S.H.create 10; context } + val remove : 'a t -> key -> unit + val set : 'a t -> key -> 'a -> unit + val find: 'a t -> key -> 'a + val find_opt: 'a t -> key -> 'a option + val mem: 'a t -> key -> bool + val change : ('a option -> 'a option) -> 'a t -> key -> unit +end - let find t k = - let r = S.H.find t.h k in - match RefOpt.get r with - | Some v -> v - | None -> raise Not_found +module Hashtbl(S:Colibri2_popop_lib.Popop_stdlib.Datatype) : Hashtbl with type key := S.t = struct - let find_opt t k = - let open Option in - let* r = S.H.find_opt t.h k in - RefOpt.get r + type 'a t = { + h: 'a RefOpt.t S.H.t; + context: context; + } - let set t k v = - let r = match S.H.find_opt t.h k with - | Some r -> r - | None -> - let r = RefOpt.create t.context in - S.H.add t.h k r; - r - in - RefOpt.set r v - - let remove t k = - match S.H.find_opt t.h k with - | Some r -> RefOpt.unset r - | None -> () - - let change f (t:'a t) k = - let change = function - | Some r as o -> - let v = f (RefOpt.get r) in - Ref.set r v; - o - | None -> - let r = RefOpt.create t.context in - let v = f None in - Ref.set r v; - (Some r) - in - S.H.change change t.h k + let create context = { h = S.H.create 10; context } + + let find t k = + let r = S.H.find t.h k in + match RefOpt.get r with + | Some v -> v + | None -> raise Not_found + + let find_opt t k = + let open Option in + let* r = S.H.find_opt t.h k in + RefOpt.get r + + let mem t k = Option.is_some (find_opt t k) + + let set t k v = + let r = match S.H.find_opt t.h k with + | Some r -> r + | None -> + let r = RefOpt.create t.context in + S.H.add t.h k r; + r + in + RefOpt.set r v + + let remove t k = + match S.H.find_opt t.h k with + | Some r -> RefOpt.unset r + | None -> () + + let change f (t:'a t) k = + let change = function + | Some r as o -> + let v = f (RefOpt.get r) in + Ref.set r v; + o + | None -> + let r = RefOpt.create t.context in + let v = f None in + Ref.set r v; + (Some r) + in + S.H.change change t.h k - end end module Array = struct @@ -419,3 +425,83 @@ end module TimeWheel = Colibri2_popop_lib.TimeWheel.Make (struct type t = creator end) (Array)(Ref) + +module type HashtblWithDefault = sig + type 'a t + type key + + val create: creator -> (creator -> 'a) -> 'a t + + val set : 'a t -> key -> 'a -> unit + val find : 'a t -> key -> 'a + val change : ('a -> 'a) -> 'a t -> key -> unit +end + +module HashtblWithDefault(S:Colibri2_popop_lib.Popop_stdlib.Datatype) : HashtblWithDefault with type key := S.t = struct + + type 'a t = { + h: 'a Ref.t S.H.t; + def: (creator -> 'a); + creator: creator; + } + + let create creator def = + { h = S.H.create 5; def; creator } + + let find_aux t k = + match S.H.find_opt t.h k with + | Some r -> r + | None -> + let r = Ref.create t.creator (t.def t.creator) in + S.H.add t.h k r; + r + + let find t k = + Ref.get (find_aux t k) + + let set t k v = + let r = find_aux t k in + Ref.set r v + + let change f t k = + let r = find_aux t k in + let v = f (Ref.get r) in + Ref.set r v + +end + +module type Memo = sig + type 'a t + type key + + val create: creator -> (creator -> key -> 'a) -> 'a t + val find : 'a t -> key -> 'a + val iter : (key -> 'a -> unit) -> 'a t -> unit + val fold : (key -> 'a -> 'acc -> 'acc) -> 'a t -> 'acc -> 'acc +end + +module Memo(S:Colibri2_popop_lib.Popop_stdlib.Datatype) : Memo with type key := S.t = struct + + type 'a t = { + h: 'a S.H.t; + def: (creator -> S.t -> 'a); + creator: creator; + } + + let create creator def = + { h = S.H.create 5; def; creator } + + let find t k = + match S.H.find_opt t.h k with + | Some r -> r + | None -> + let r = t.def t.creator k in + S.H.add t.h k r; + r + + let iter f t = + S.H.iter f t.h + + let fold f t = + S.H.fold f t.h +end diff --git a/src_colibri2/stdlib/context.mli b/src_colibri2/stdlib/context.mli index 17b6ca326ea45202de5ddf7b6e81c1b84a48d476..8fb3f8b6567efde54a749127313f50bc12f28a7c 100644 --- a/src_colibri2/stdlib/context.mli +++ b/src_colibri2/stdlib/context.mli @@ -178,6 +178,8 @@ module Queue: sig val fold: ('acc -> 'a -> 'acc) -> 'acc -> 'a t -> 'acc val length: 'a t -> int val get: 'a t -> int -> 'a + val of_seq: 'a t -> 'a Base.Sequence.t + (** From last current element to first *) val pp: ?sep:unit Fmt.t -> 'a Fmt.t -> 'a t Fmt.t end @@ -191,3 +193,44 @@ module Array: sig end module TimeWheel: Colibri2_popop_lib.TimeWheel.S with type context := creator + +module type Hashtbl = sig + type 'a t + type key + + val create: creator -> 'a t + + val remove : 'a t -> key -> unit + val set : 'a t -> key -> 'a -> unit + val find: 'a t -> key -> 'a + val find_opt: 'a t -> key -> 'a option + val mem: 'a t -> key -> bool + val change : ('a option -> 'a option) -> 'a t -> key -> unit +end + +module Hashtbl (S:Colibri2_popop_lib.Popop_stdlib.Datatype) : Hashtbl with type key := S.t + +module type HashtblWithDefault = sig + type 'a t + type key + + val create: creator -> (creator -> 'a) -> 'a t + + val set : 'a t -> key -> 'a -> unit + val find : 'a t -> key -> 'a + val change : ('a -> 'a) -> 'a t -> key -> unit +end + +module HashtblWithDefault (S:Colibri2_popop_lib.Popop_stdlib.Datatype) : HashtblWithDefault with type key := S.t + +module type Memo = sig + type 'a t + type key + + val create: creator -> (creator -> key -> 'a) -> 'a t + val find : 'a t -> key -> 'a + val iter : (key -> 'a -> unit) -> 'a t -> unit + val fold : (key -> 'a -> 'acc -> 'acc) -> 'a t -> 'acc -> 'acc +end + +module Memo (S:Colibri2_popop_lib.Popop_stdlib.Datatype) : Memo with type key := S.t diff --git a/src_colibri2/stdlib/wto.ml b/src_colibri2/stdlib/wto.ml index defb3ba9937ebb2b485161f08012d65c60265ebd..a53a7bd6cecd10bf4aacb4d88ad2a19d549de02d 100644 --- a/src_colibri2/stdlib/wto.ml +++ b/src_colibri2/stdlib/wto.ml @@ -226,11 +226,15 @@ module Make(N:sig type pref = N.t -> N.t -> int - let partition ?pref ~init ~succs = + let partition ?pref ~inits ~succs = let state = {dfn = DFN.create 17; num = 0; succs; stack = Stack.create () } in - let loop,component = visit ~pref state init [] in - assert (is_no_loop loop); - component + let partition = List.fold_left (fun partition succ -> + let (loop',partition) = visit ~pref state succ partition in + assert (is_no_loop loop'); + partition + ) [] inits + in + partition end diff --git a/src_colibri2/stdlib/wto.mli b/src_colibri2/stdlib/wto.mli index 82d6f5b1921f4469616a4cd87a428db9563f1f4d..d09bd5a537687ced45cdfda8d37a5e7463eb0f05 100644 --- a/src_colibri2/stdlib/wto.mli +++ b/src_colibri2/stdlib/wto.mli @@ -40,6 +40,9 @@ type 'n component = (** A list of strongly connected components, sorted topologically *) and 'n partition = 'n component list +val pp_partition: 'n Fmt.t -> 'n partition Fmt.t +val pp_component: 'n Fmt.t -> 'n component Fmt.t + val flatten: 'n partition -> 'n list val fold_heads: ('a -> 'n -> 'a) -> 'a -> 'n partition -> 'a @@ -57,7 +60,7 @@ module Make(Node:sig (** Implements Bourdoncle "Efficient chaotic iteration strategies with widenings" algorithm to compute a WTO. *) - val partition: ?pref:pref -> init:Node.t -> succs:(Node.t -> Node.t list) -> Node.t partition + val partition: ?pref:pref -> inits:Node.t list -> succs:(Node.t -> Node.t list) -> Node.t partition val equal_component: Node.t component -> Node.t component -> bool val equal_partition: Node.t partition -> Node.t partition -> bool diff --git a/src_colibri2/tests/solve/smt_lra/sat/arith_mult_not_linear_in_conflict.smt2 b/src_colibri2/tests/solve/smt_lra/sat/arith_mult_not_linear_in_conflict.smt2 index 1feebcb70c1c8178096c4dd20d08028d6e52baef..52ed73ff6d5efd60c883e29d67e490d2a3e0290a 100644 --- a/src_colibri2/tests/solve/smt_lra/sat/arith_mult_not_linear_in_conflict.smt2 +++ b/src_colibri2/tests/solve/smt_lra/sat/arith_mult_not_linear_in_conflict.smt2 @@ -1,7 +1,7 @@ (set-logic QF_LRA) -(declare-fun v2 () Real) -(declare-fun v1 () Real) -(declare-fun v0 () Real) +(declare-fun v2 () Real) ; 1.0 +(declare-fun v1 () Real) ; 0.5 +(declare-fun v0 () Real) ; 0.0 (assert (let ((?n2 (= 0 v1))) (let ((?n3 (ite ?n2 v2 v0))) diff --git a/src_colibri2/tests/solve/smt_uf/sat/boolexpup.smt2 b/src_colibri2/tests/solve/smt_uf/sat/boolexpup.smt2 index ff84773fb83b09f905064fd7c74b81ede125070f..8cf5f1cee289822d5acd4bc66f4b223dd9bc79d6 100644 --- a/src_colibri2/tests/solve/smt_uf/sat/boolexpup.smt2 +++ b/src_colibri2/tests/solve/smt_uf/sat/boolexpup.smt2 @@ -1,8 +1,8 @@ (set-logic QF_UF) (declare-sort S0 0) -(declare-fun p3 ( S0) Bool) -(declare-fun v0 () S0) -(declare-fun v1 () S0) +(declare-fun p3 ( S0) Bool); false +(declare-fun v0 () S0) ; @us_S0_0 +(declare-fun v1 () S0) ; @us_S0_1 (assert (let ((?n1 true)) (let ((?n2 (distinct v0 v1))) diff --git a/src_colibri2/tests/tests_LRA.ml b/src_colibri2/tests/tests_LRA.ml index 7c6aac47964728e7ecaae55f070f81e970d0faef..0f2074808912beced31eca8cd6e8f7cc56bcf5fd 100644 --- a/src_colibri2/tests/tests_LRA.ml +++ b/src_colibri2/tests/tests_LRA.ml @@ -27,7 +27,8 @@ open Colibri2_core open Colibri2_stdlib.Std open Colibri2_stdlib -let theories = [Boolean.th_register; Equality.th_register; Uninterp.th_register; LRA.th_register] +let theories = [Boolean.th_register; Equality.th_register; + Colibri2_theories_quantifiers.Uninterp.th_register; LRA.th_register] let ($$) f x = f x diff --git a/src_colibri2/tests/tests_bool.ml b/src_colibri2/tests/tests_bool.ml index b10915ee59409dbac7aba707f7e562bab3e81af4..07cdfb4e1e70421b09e12b9bc6345250adc6814f 100644 --- a/src_colibri2/tests/tests_bool.ml +++ b/src_colibri2/tests/tests_bool.ml @@ -24,7 +24,7 @@ open Colibri2_core open Tests_lib open Colibri2_theories_bool -let theories = [(* Uninterp.th_register; *) Boolean.th_register] +let theories = [Colibri2_theories_quantifiers.Uninterp.th_register; Boolean.th_register] let ($$) f x = f x diff --git a/src_colibri2/tests/tests_uf.ml b/src_colibri2/tests/tests_uf.ml index 52ba777e06fd616c0b64cc09e6cbc9e457106210..af42f372e511e40c7ac8716dd78379b1a75ef12e 100644 --- a/src_colibri2/tests/tests_uf.ml +++ b/src_colibri2/tests/tests_uf.ml @@ -24,7 +24,8 @@ open Colibri2_core open Tests_lib open Colibri2_theories_bool -let theories = [Boolean.th_register; Equality.th_register; Uninterp.th_register; +let theories = [Boolean.th_register; Equality.th_register; + Colibri2_theories_quantifiers.Uninterp.th_register; Colibri2_theories_quantifiers.Quantifier.th_register ] let run = Tests_lib.run_exn ~theories ~nodec:() let ($$) f x = f x diff --git a/src_colibri2/theories/ADT/adt.ml b/src_colibri2/theories/ADT/adt.ml index f77b2dcfb1b652abbe783a966e8d35ae8cd6f395..626da1e95ad39a901cecf26569a0092f5bef4938 100644 --- a/src_colibri2/theories/ADT/adt.ml +++ b/src_colibri2/theories/ADT/adt.ml @@ -63,9 +63,9 @@ let upd_dom d n d2 = Egraph.set_dom d D.key n s let case_of_adt ty = - match Expr.Ty.definition ty with - | None | Some Expr.Ty.Abstract -> assert false (* absurd: must be an adt *) - | Some (Expr.Ty.Adt { cases; _ }) -> + match Ground.Ty.definition ty with + | Expr.Ty.Abstract -> assert false (* absurd: must be an adt *) + | Expr.Ty.Adt { cases; _ } -> Case.S.of_list (List.init (Array.length cases) (fun i -> i)) (** Decide for destructors *) @@ -168,14 +168,16 @@ let converter d (f : Ground.t) = (let+ ve = get e in match ve with | Unk s -> if Case.S.mem case s then None else Some false - | One { case = case'; _ } -> Some (Case.equal case case'))) + | One { case = case'; _ } -> Some (Case.equal case case'))); + Adt_value.propagate_value d f | { app = { builtin = Expr.Constructor { case; _ }; _ }; args; _ } -> List.iter reg args; let fields = Base.List.foldi ~init:Field.M.empty args ~f:(fun field acc a -> Field.M.add field a acc) in - upd_dom d r (One { case; fields }) + upd_dom d r (One { case; fields }); + Adt_value.propagate_value d f | { app = { builtin = Expr.Destructor { case; field; adt; _ }; _ }; args = [ e ]; @@ -195,10 +197,12 @@ let converter d (f : Ground.t) = if Case.S.mem case s then upd else nothing | Unk _ -> nothing | One { case = case'; _ } -> - if Case.equal case case' then upd else nothing)) + if Case.equal case case' then upd else nothing)); + Adt_value.propagate_value d f | _ -> () let init env : unit = + Adt_value.th_register env; Ground.register_converter env converter; init_got_value_bool env diff --git a/src_colibri2/theories/ADT/adt_value.ml b/src_colibri2/theories/ADT/adt_value.ml new file mode 100644 index 0000000000000000000000000000000000000000..0dad23a60ab11ff26ca508b3afaef038c930700f --- /dev/null +++ b/src_colibri2/theories/ADT/adt_value.ml @@ -0,0 +1,130 @@ +open Base +open Colibri2_core +open Colibri2_popop_lib.Popop_stdlib +open Colibri2_popop_lib +module Case = DInt +module Field = DInt + +module T' = struct + module T = struct + type t = { + adt : Expr.Ty.Const.t; + tyargs : Ground.Ty.t list; + case : Case.t; + fields : Value.t Field.M.t; + } + [@@deriving eq, ord, show, hash] + end + + include T + include MkDatatype (T) + + let key = + ValueKind.create_key + (module struct + include T + + let name = "ADT.value" + end) +end + +module V = ValueKind.Register (T') +include T' + +let interp d n = Opt.get_exn Impossible (Egraph.get_value d n) + +let compute d g = + match Ground.sem g with + | { app = { builtin = Expr.Tester { case; _ }; _ }; args = [ e ]; _ } -> + let v = V.coerce_nodevalue (interp d e) in + let v = V.value v in + `Some + (Colibri2_theories_bool.Boolean.values_of_bool (Case.equal case v.case)) + | { + app = { builtin = Expr.Constructor { adt; case; _ }; _ }; + args; + tyargs; + _; + } -> + let fields = + Base.List.foldi ~init:Field.M.empty args ~f:(fun field acc a -> + Field.M.add field (interp d a) acc) + in + let v = { tyargs; adt; case; fields } in + `Some (V.nodevalue (V.index v)) + | { + app = { builtin = Expr.Destructor { case; field; _ }; _ }; + args = [ e ]; + _; + } -> + let v = V.coerce_nodevalue (interp d e) in + let v = V.value v in + if Case.equal case v.case then + let v = Field.M.find_opt field v.fields in + match v with None -> raise Impossible | Some v -> `Some v + else `Uninterpreted + | _ -> `None + +let init_check d = + Interp.Register.check d (fun d t -> + match compute d t with + | `None -> None + | `Some r -> Some (Value.equal r (interp d (Ground.node t))) + | `Uninterpreted -> + Some + (Colibri2_theories_quantifiers.Uninterp.On_uninterpreted_domain + .check d t)) + +let propagate_value d g = + let f d g = + match compute d g with + | `None -> raise Impossible + | `Some v -> Egraph.set_value d (Ground.node g) v + | `Uninterpreted -> + Colibri2_theories_quantifiers.Uninterp.On_uninterpreted_domain.propagate + d g + in + Interp.TwoWatchLiteral.create d f g + +let init_ty d = + Interp.Register.ty d (fun _ ty -> + match ty with + | { app = { builtin = Dolmen_std.Expr.Base; _ } as sym; args } -> ( + match Ground.Ty.definition sym with + | Abstract -> None + | Adt { ty; record = _; cases } -> + Some + (let open Interp.SeqLim in + let cases = + Sequence.unfold ~init:0 ~f:(fun i -> + if i < Array.length cases then Some ((i, cases.(i)), i + 1) + else None) + in + let* case, { cstr; _ } = of_seq d cases in + let subst = + List.fold2_exn + ~f:(fun acc v ty -> Expr.Ty.Var.M.add v ty acc) + ~init:Expr.Ty.Var.M.empty cstr.ty.fun_vars args + in + let args = + List.map ~f:(Ground.Ty.convert subst) cstr.ty.fun_args + in + let rec aux seq i = function + | [] -> seq + | ty :: args -> + let seq = + let+ l = seq and* a = Interp.ty d ty in + Field.M.add i a l + in + aux seq (i + 1) args + in + let+ fields = + aux (of_seq d (Sequence.singleton Field.M.empty)) 0 args + in + V.nodevalue (V.index { tyargs = args; adt = ty; case; fields })) + ) + | _ -> None) + +let th_register d = + init_check d; + init_ty d diff --git a/src_colibri2/theories/ADT/adt_value.mli b/src_colibri2/theories/ADT/adt_value.mli new file mode 100644 index 0000000000000000000000000000000000000000..3f2f3f0ce9ab6429787cebf0a302b1cd807726ce --- /dev/null +++ b/src_colibri2/theories/ADT/adt_value.mli @@ -0,0 +1,3 @@ +val th_register : Egraph.t -> unit + +val propagate_value : Egraph.t -> Ground.t -> unit diff --git a/src_colibri2/theories/ADT/dune b/src_colibri2/theories/ADT/dune index d18eac71dce3d34ac50b768e3f98bb517b8b0335..ecb846e0b02f0e2faf08febe7b38c88d9492c46a 100644 --- a/src_colibri2/theories/ADT/dune +++ b/src_colibri2/theories/ADT/dune @@ -3,7 +3,9 @@ (public_name colibri2.theories.adt) (synopsis "theory of algebraic datatype for colibri2") (libraries containers ocamlgraph colibri2.stdlib colibri2.popop_lib - colibri2.core.structures colibri2.core colibri2.theories.bool) + colibri2.core.structures colibri2.core colibri2.theories.bool + colibri2.theories.quantifiers + ) (preprocess (pps ppx_deriving.std ppx_hash)) (flags :standard -open Containers -open Colibri2_stdlib -open Std -open diff --git a/src_colibri2/theories/LRA/LRA.ml b/src_colibri2/theories/LRA/LRA.ml index 4c2480f446fd43c48ac469902a74078591ba022f..3065286b53b42ae216d10d6958d30d31935cba1f 100644 --- a/src_colibri2/theories/LRA/LRA.ml +++ b/src_colibri2/theories/LRA/LRA.ml @@ -19,52 +19,10 @@ (*************************************************************************) open Colibri2_core -open Colibri2_popop_lib open Colibri2_stdlib.Std let default_value = Q.zero -let init_check d = - Interp.Register.check d (fun d t -> - let exception False in - try - let interp n = Opt.get_exn False (Egraph.get_value d n) in - let (!>) t = RealValue.value (RealValue.coerce_nodevalue (interp t)) in - let check r = - Some (Value.equal r (interp (Ground.node t))) - in - let (!<) v = check (RealValue.nodevalue (RealValue.index v)) in - let (!<<) b = - let r = if b then Boolean.values_true else Boolean.values_false in - check r - in - match Ground.sem t with - | { app = {builtin = Expr.Integer s}; tyargs = []; args = []; ty = _ } -> - !< (Q.of_string s) - | { app = {builtin = Expr.Decimal s}; tyargs = []; args = []; ty = _ } -> - !< (Q.of_string_decimal s) - | { app = {builtin = Expr.Rational s}; tyargs = []; args = []; ty = _ } -> - !< (Q.of_string_decimal s) - | { app = {builtin = Expr.Add}; tyargs = []; args = [a;b]; ty = _ } -> - !< (Q.add !>a !>b) - | { app = {builtin = Expr.Sub}; tyargs = []; args = [a;b]; ty = _ } -> - !< (Q.sub !>a !>b) - | { app = {builtin = Expr.Minus}; tyargs = []; args = [a]; ty = _ } -> - !< (Q.neg !>a) - | { app = {builtin = Expr.Mul}; tyargs = []; args = [a;b]; ty = _ } -> - !< (Q.mul !>a !>b) - | { app = {builtin = Expr.Lt}; tyargs = []; args = [arg1;arg2]; ty = _ } -> - !<< (Q.lt !>arg1 !>arg2) - | { app = {builtin = Expr.Leq}; tyargs = []; args = [arg1;arg2]; ty = _ } -> - !<< (Q.leq !>arg1 !>arg2) - | { app = {builtin = Expr.Gt}; tyargs = []; args = [arg1;arg2]; ty = _ } -> - !<< (Q.gt !>arg1 !>arg2) - | { app = {builtin = Expr.Geq}; tyargs = []; args = [arg1;arg2]; ty = _ } -> - !<< (Q.geq !>arg1 !>arg2) - | _ -> None - with False -> Some false - ) - module RealValue = RealValue let th_register env = @@ -74,7 +32,6 @@ let th_register env = Fourier.init env; Mul.init env; Dom_product.init env; - init_check env; () let () = Egraph.add_default_theory th_register diff --git a/src_colibri2/theories/LRA/realValue.ml b/src_colibri2/theories/LRA/realValue.ml index c6a2e8f33bf465b00acb9292a69cfb4e6ac8877d..70d14e86d5d655ef0c0073398f2a579b46a553bd 100644 --- a/src_colibri2/theories/LRA/realValue.ml +++ b/src_colibri2/theories/LRA/realValue.ml @@ -19,6 +19,7 @@ (*************************************************************************) open Colibri2_core +open Colibri2_popop_lib open Colibri2_stdlib.Std module RealValue = ValueKind.Register(struct @@ -29,7 +30,6 @@ module RealValue = ValueKind.Register(struct include RealValue let cst' c = index ~basename:(Format.asprintf "%aR" Q.pp c) c - let cst c = node (cst' c) let zero = cst Q.zero @@ -56,6 +56,7 @@ module Monad : sig val getb: Node.t -> (bool,bool option) monad val setb: Node.t -> ('a,bool option) monad -> ('a,unit) monad val (let+): ('a,'b option) monad -> ('b -> 'c) -> ('a,'c option) monad + val (let*): ('a,'b option) monad -> ('b -> 'c option) -> ('a,'c option) monad val (and+): ('a,'b option) monad -> ('a,'c option) monad -> ('a,('b * 'c) option) monad val (&&): ('a,unit) monad -> ('a,unit) monad -> ('a,unit) monad end = struct @@ -79,13 +80,112 @@ end = struct let getb a = get' Boolean.BoolValue.key a let setb a = set' (module Boolean.BoolValue) a let [@ocaml.always inline] (let+) a (f:'b -> 'a) = fun d n v -> Option.map f (a d n v) + let [@ocaml.always inline] (let*) a (f:'b -> 'a option) = fun d n v -> Option.bind (a d n v) f let [@ocaml.always inline] (and+) a b = fun d n v -> match a d n v, b d n v with | Some a, Some b -> Some (a,b) | _ -> None let [@ocaml.always inline] (&&) a b = fun d n v -> a d n v; b d n v end -(** {2 Initialization} *) + +let int_sequence_without_zero = + let open Base.Sequence.Generator in + let rec loop i = + yield i >>= fun () -> yield (Z.neg i) >>= (fun () -> loop (Z.succ i)) + in + run (loop Z.one) + +let int_sequence = + Base.Sequence.shift_right int_sequence_without_zero Z.zero + +let init_ty d = + Interp.Register.ty d (fun d ty -> + match ty with + | {app={builtin = Expr.Int;_};_} -> + Some (Interp.SeqLim.map (Interp.SeqLim.of_seq d int_sequence) + ~f:(fun i -> nodevalue (cst' (Q.of_bigint i))) + ) + | {app={builtin = Expr.Real;_};_} -> + let seq = + let open Interp.SeqLim in + let+ num = of_seq d int_sequence + and* den = of_seq d int_sequence_without_zero in + (Q.make num den) + in + let seq = Interp.SeqLim.unfold_with seq ~init:Q.S.empty + ~f:(fun s v -> if Q.S.mem v s then Skip s + else Yield(nodevalue (cst' v),Q.S.add v s)) in + Some seq + | _ -> None) + +module Check = struct + let interp d n = Opt.get_exn Impossible (Egraph.get_value d n) + let compute_ground d t = + let (!>) t = RealValue.value (RealValue.coerce_nodevalue (interp d t)) in + let (!<) v = `Some (RealValue.nodevalue (RealValue.index v)) in + let (!<<) b = + let r = if b then Boolean.values_true else Boolean.values_false in + `Some r + in + match Ground.sem t with + | { app = {builtin = Expr.Coercion}; + tyargs = + ( [{app={builtin = (Expr.Int|Expr.Rat);_};_};{app={builtin = Expr.Real;_};_}] + | [{app={builtin = Expr.Int;_};_};{app={builtin = Expr.Rat;_};_}] + ); + args = [a] } -> + !< (!> a) + | { app = {builtin = Expr.Integer s}; tyargs = []; args = []; ty = _ } -> + !< (Q.of_string s) + | { app = {builtin = Expr.Decimal s}; tyargs = []; args = []; ty = _ } -> + !< (Q.of_string_decimal s) + | { app = {builtin = Expr.Rational s}; tyargs = []; args = []; ty = _ } -> + !< (Q.of_string_decimal s) + | { app = {builtin = Expr.Add}; tyargs = []; args = [a;b]; ty = _ } -> + !< (Q.add !>a !>b) + | { app = {builtin = Expr.Sub}; tyargs = []; args = [a;b]; ty = _ } -> + !< (Q.sub !>a !>b) + | { app = {builtin = Expr.Minus}; tyargs = []; args = [a]; ty = _ } -> + !< (Q.neg !>a) + | { app = {builtin = Expr.Mul}; tyargs = []; args = [a;b]; ty = _ } -> + !< (Q.mul !>a !>b) + | { app = {builtin = Expr.Div}; tyargs = []; args = [a;b]; ty = _ } -> + if Q.is_zero !>b then `Uninterp + else !< (Q.div !>a !>b) + | { app = {builtin = Expr.Lt}; tyargs = []; args = [arg1;arg2]; ty = _ } -> + !<< (Q.lt !>arg1 !>arg2) + | { app = {builtin = Expr.Leq}; tyargs = []; args = [arg1;arg2]; ty = _ } -> + !<< (Q.leq !>arg1 !>arg2) + | { app = {builtin = Expr.Gt}; tyargs = []; args = [arg1;arg2]; ty = _ } -> + !<< (Q.gt !>arg1 !>arg2) + | { app = {builtin = Expr.Geq}; tyargs = []; args = [arg1;arg2]; ty = _ } -> + !<< (Q.geq !>arg1 !>arg2) + | { app = {builtin = Expr.Floor}; tyargs = []; args = [a]; _ } -> + !< (Q.floor (!> a)) + | _ -> `None + + let init d = + Interp.Register.check d (fun d t -> + let check r = + Value.equal r (interp d (Ground.node t)) + in + match compute_ground d t with + | `None -> None + | `Some v -> Some (check v) + | `Uninterp -> Some (Colibri2_theories_quantifiers.Uninterp.On_uninterpreted_domain.check d t) + ) + + let attach d g = + let f d g = + match compute_ground d g with + | `None -> raise Impossible + | `Some v -> Egraph.set_value d (Ground.node g) v + | `Uninterp -> Colibri2_theories_quantifiers.Uninterp.On_uninterpreted_domain.propagate d g + in + Interp.TwoWatchLiteral.create d f g +end + + (** {2 Initialization} *) let converter d (f:Ground.t) = let r = Ground.node f in let reg n = Egraph.register d n in @@ -105,7 +205,7 @@ let converter d (f:Ground.t) = | [{app={builtin = Expr.Int;_};_};{app={builtin = Expr.Rat;_};_}] ); args = [a] } -> - merge a + merge a; Check.attach d f | { app = {builtin = Expr.Integer s}; tyargs = []; args = []; _ } -> merge (cst (Q.of_string s)) | { app = {builtin = Expr.Decimal s}; tyargs = []; args = []; _ } -> @@ -113,7 +213,7 @@ let converter d (f:Ground.t) = | { app = {builtin = Expr.Rational s}; tyargs = []; args = []; _ } -> merge (cst (Q.of_string_decimal s)) | { app = {builtin = Expr.Add}; tyargs = []; args = [a;b]; _ } -> - reg a; reg b; + reg a; reg b; Check.attach d f; let wait = set r (let+ va = get a and+ vb = get b in Q.add va vb) && set a (let+ vb = get b and+ vr = get r in Q.sub vr vb) && @@ -121,33 +221,44 @@ let converter d (f:Ground.t) = in List.iter (fun n -> wait_for_this_node_get_a_value d n wait) [a;b;r] | { app = {builtin = Expr.Sub}; tyargs = []; args = [a;b]; _ } -> - reg a; reg b; + reg a; reg b; Check.attach d f; let wait = set r (let+ va = get a and+ vb = get b in Q.sub va vb) && set a (let+ vb = get b and+ vr = get r in Q.add vr vb) && set b (let+ va = get a and+ vr = get r in Q.sub va vr) in List.iter (fun n -> wait_for_this_node_get_a_value d n wait) [a;b;r] + | { app = {builtin = Expr.Mul}; tyargs = []; args = [a;b]; _ } -> + reg a; reg b; Check.attach d f; + let wait = + set r (let+ va = get a and+ vb = get b in Q.mul va vb) && + set a (let* vb = get b and+ vr = get r in if Q.is_zero vb then None else Some (Q.div vr vb)) && + set b (let* va = get a and+ vr = get r in if Q.is_zero va then None else Some (Q.div vr va)) + in + List.iter (fun n -> wait_for_this_node_get_a_value d n wait) [a;b;r] | { app = {builtin = Expr.Minus}; tyargs = []; args = [a]; _ } -> - reg a; + reg a; Check.attach d f; let wait = set r (let+ va = get a in Q.neg va) && set a (let+ vr = get r in Q.neg vr) in List.iter (fun n -> wait_for_this_node_get_a_value d n wait) [a;r] | { app = {builtin = Expr.Lt}; tyargs = []; args = [a;b]; _ } -> - cmp Q.lt a b + cmp Q.lt a b; Check.attach d f; | { app = {builtin = Expr.Leq}; tyargs = []; args = [a;b]; _ } -> - cmp Q.le a b + cmp Q.le a b; Check.attach d f; | { app = {builtin = Expr.Gt}; tyargs = []; args = [a;b]; _ } -> - cmp Q.gt a b + cmp Q.gt a b; Check.attach d f; | { app = {builtin = Expr.Geq}; tyargs = []; args = [a;b]; _ } -> - cmp Q.ge a b + cmp Q.ge a b; Check.attach d f; | { app = {builtin = Expr.Floor}; tyargs = []; args = [a]; _ } -> - reg a + reg a; Check.attach d f; | _ -> () + let init env = Ground.register_converter env converter; init_got_value_bool env; - init_got_value env + init_got_value env; + init_ty env; + Check.init env diff --git a/src_colibri2/theories/bool/.ocamlformat b/src_colibri2/theories/bool/.ocamlformat new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src_colibri2/theories/bool/.ocamlformat-ignore b/src_colibri2/theories/bool/.ocamlformat-ignore new file mode 100644 index 0000000000000000000000000000000000000000..79df3ce7b5a14dceda726de31dd2728842dc0de5 --- /dev/null +++ b/src_colibri2/theories/bool/.ocamlformat-ignore @@ -0,0 +1,4 @@ +equality.ml +boolean.ml +boolean.mli +equality.mli diff --git a/src_colibri2/theories/bool/boolean.ml b/src_colibri2/theories/bool/boolean.ml index 4492b29a5a300904ed27a30b7f11d62a0be9f915..89fd485417fd2fb4bea3c7b2276af896d7c40ba8 100644 --- a/src_colibri2/theories/bool/boolean.ml +++ b/src_colibri2/theories/bool/boolean.ml @@ -44,6 +44,9 @@ let value_false = BoolValue.index ~basename:"⊥" false let values_false = BoolValue.nodevalue value_false let node_false = BoolValue.node value_false +let value_of_bool b = if b then value_true else value_false +let values_of_bool b = if b then values_true else values_false + (* Function is not used. Don't know what the types of b1 and b2 are. *) (* let union_disjoint m1 m2 = * Node.M.union (fun _ b1 b2 -> assert (Equal.physical b1 b2); Some b1) m1 m2 *) @@ -446,32 +449,31 @@ let converter d (f:Ground.t) = let init_check d = Interp.Register.check d (fun d t -> - let exception False in - try - let interp n = Opt.get_exn False (Egraph.get_value d n) in - let (!>) t = BoolValue.value (BoolValue.coerce_nodevalue (interp t)) in - let check r = - Some (Value.equal r (interp (Ground.node t))) - in - let (!<) b = - let r = if b then values_true else values_false in - check r - in - match Ground.sem t with - | { app = {builtin = Expr.Or}; tyargs = []; args = args; ty = _ } -> - !< (List.fold_left (||) false (List.map (!>) args)) - | { app = {builtin = Expr.And}; tyargs = []; args = args; ty = _ } -> - !< (List.fold_left (&&) true (List.map (!>) args)) - | { app = {builtin = Expr.Imply}; tyargs = []; args = [arg1;arg2]; ty = _ } -> - !< ( not (!> arg1) || !> arg2 ) - | { app = {builtin = Expr.Neg}; tyargs = []; args = [arg]; ty = _ } -> - !< (not (!> arg)) - | { app = {builtin = Expr.True}; tyargs = []; args = []; ty = _ } -> - check values_true - | { app = {builtin = Expr.False}; tyargs = []; args = []; ty = _ } -> - check values_false - | _ -> None - with False -> Some false + let interp n = Opt.get_exn Impossible (Egraph.get_value d n) in + let (!>) t = BoolValue.value (BoolValue.coerce_nodevalue (interp t)) in + let check r = + Some (Value.equal r (interp (Ground.node t))) + in + let (!<) b = + let r = if b then values_true else values_false in + check r + in + match Ground.sem t with + | { app = {builtin = Expr.Or}; tyargs = []; args = args; ty = _ } -> + !< (List.fold_left (||) false (List.map (!>) args)) + | { app = {builtin = Expr.And}; tyargs = []; args = args; ty = _ } -> + !< (List.fold_left (&&) true (List.map (!>) args)) + | { app = {builtin = Expr.Imply}; tyargs = []; args = [arg1;arg2]; ty = _ } -> + !< ( not (!> arg1) || !> arg2 ) + | { app = {builtin = Expr.Xor}; tyargs = []; args = [arg1;arg2]; _ } -> + !< ( not (Bool.equal (!> arg1) (!> arg2) )) + | { app = {builtin = Expr.Neg}; tyargs = []; args = [arg]; ty = _ } -> + !< (not (!> arg)) + | { app = {builtin = Expr.True}; tyargs = []; args = []; ty = _ } -> + check values_true + | { app = {builtin = Expr.False}; tyargs = []; args = []; ty = _ } -> + check values_false + | _ -> None ) let th_register env = diff --git a/src_colibri2/theories/bool/boolean.mli b/src_colibri2/theories/bool/boolean.mli index 3bd258bfbe0968715515cafe347bdb46defe9cc5..e605b717a5154b9024ad85aecb1785bc8aeabef0 100644 --- a/src_colibri2/theories/bool/boolean.mli +++ b/src_colibri2/theories/bool/boolean.mli @@ -56,3 +56,5 @@ val value_true : BoolValue.t val value_false: BoolValue.t val values_true : Value.t val values_false: Value.t +val value_of_bool: bool -> BoolValue.t +val values_of_bool: bool -> Value.t diff --git a/src_colibri2/theories/bool/dune b/src_colibri2/theories/bool/dune index 4d9ae28830166d50a51643cd6953a4043027943a..1b294acbe56d529984963c6719b2625bf67f227d 100644 --- a/src_colibri2/theories/bool/dune +++ b/src_colibri2/theories/bool/dune @@ -2,7 +2,7 @@ (name colibri2_theories_bool) (public_name colibri2.theories.bool) (synopsis "theories for colibri2") - (modules Boolean Equality Uninterp) + (modules Boolean Equality) (libraries containers ocamlgraph colibri2.stdlib colibri2.popop_lib colibri2.core.structures colibri2.core dolmen.std) (preprocess diff --git a/src_colibri2/theories/bool/equality.ml b/src_colibri2/theories/bool/equality.ml index 3a26d463557ffaaf7ce767451fd31792fc9209b0..29bef0af8486fe492dc1207670cffa304078b935 100644 --- a/src_colibri2/theories/bool/equality.ml +++ b/src_colibri2/theories/bool/equality.ml @@ -565,39 +565,36 @@ let bool_init_diff_to_value d = let init_check d = Interp.Register.check d (fun d t -> - let exception False in - try - let interp n = Opt.get_exn False (Egraph.get_value d n) in - let (!>) n = - Boolean.BoolValue.value - (Boolean.BoolValue.coerce_nodevalue (interp n)) - in - let check r = - Some (Value.equal r (interp (Ground.node t))) - in - let (!<) b = - let r = if b then Boolean.values_true else Boolean.values_false in - check r - in - match Ground.sem t with - | { app = {builtin = Expr.Equal}; tyargs = [_]; args = a::l; ty = _ } -> - let a = interp a in - !< (List.for_all (fun t -> Value.equal a (interp t)) l) - | { app = {builtin = Expr.Equiv}; tyargs = [_]; args = [a;b]; ty = _ } -> - !< (Value.equal (interp a) (interp b)) - | { app = {builtin = Expr.Distinct}; tyargs = [_]; args = args; ty = _ } -> - begin - try - let fold acc v = Value.S.add_new Exit (interp v) acc in - let _ = List.fold_left fold Value.S.empty args in - check Boolean.values_false - with Exit -> - check Boolean.values_true - end - | { app = {builtin = Expr.Ite}; tyargs = [_]; args = [c;a;b]; ty = _ } -> - check (if (!> c) then interp a else interp b) - | _ -> None - with False -> Some false + let interp n = Opt.get_exn Impossible (Egraph.get_value d n) in + let (!>) n = + Boolean.BoolValue.value + (Boolean.BoolValue.coerce_nodevalue (interp n)) + in + let check r = + Some (Value.equal r (interp (Ground.node t))) + in + let (!<) b = + let r = if b then Boolean.values_true else Boolean.values_false in + check r + in + match Ground.sem t with + | { app = {builtin = Expr.Equal}; tyargs = [_]; args = a::l; ty = _ } -> + let a = interp a in + !< (List.for_all (fun t -> Value.equal a (interp t)) l) + | { app = {builtin = Expr.Equiv}; tyargs = [_]; args = [a;b]; ty = _ } -> + !< (Value.equal (interp a) (interp b)) + | { app = {builtin = Expr.Distinct}; tyargs = [_]; args = args; ty = _ } -> + begin + try + let fold acc v = Value.S.add_new Exit (interp v) acc in + let _ = List.fold_left fold Value.S.empty args in + check Boolean.values_true + with Exit -> + check Boolean.values_false + end + | { app = {builtin = Expr.Ite}; tyargs = [_]; args = [c;a;b]; ty = _ } -> + check (if (!> c) then interp a else interp b) + | _ -> None ) diff --git a/src_colibri2/theories/quantifier/FA.ml b/src_colibri2/theories/quantifier/FA.ml new file mode 100644 index 0000000000000000000000000000000000000000..3e0c8f7612b4da815f36cdea2974d1144c1e322d --- /dev/null +++ b/src_colibri2/theories/quantifier/FA.ml @@ -0,0 +1,12 @@ +(** instantiated function symbol *) +module T = struct + let hash_fold_list = Base.hash_fold_list + + type t = { tc : F.t; ta : Expr.Ty.t list } [@@deriving eq, ord, hash] + + let pp fmt { tc; ta } = + Format.fprintf fmt "%a[%a]" F.pp tc Fmt.(list ~sep:comma Expr.Ty.pp) ta +end + +include T +include Colibri2_popop_lib.Popop_stdlib.MkDatatype (T) diff --git a/src_colibri2/theories/quantifier/InvertedPath.ml b/src_colibri2/theories/quantifier/InvertedPath.ml index a485a4cf27a1198f4b42207846d9a8b37f953db1..daa271b5373d057cc9502f1814e53612d3909c75 100644 --- a/src_colibri2/theories/quantifier/InvertedPath.ml +++ b/src_colibri2/theories/quantifier/InvertedPath.ml @@ -12,7 +12,10 @@ type t' = { matches : t Pattern.M.t; triggers : Trigger.t list; ups : - (Expr.Ty.t list * Pattern.t list * int (* from which argument we come *) * t) + (Expr.Ty.t list + * Pattern.t list + * int (* from which argument we come *) + * t) list F_Pos.M.t; } @@ -83,16 +86,17 @@ module HPT = Datastructure.Memo (F_Pos) module HPN = Datastructure.Memo (PN) (** parent-child, wait for a subterm with the right application *) -let pc_ips = HPC.create Fmt.nop "Quantifier.pc" create +let pc_ips = HPC.create Fmt.nop "Quantifier.pc" (fun c _ -> create c) (** parent-parent, a variable appears two times *) -let pp_ips = HPP.create Fmt.nop "Quantifier.pp" create +let pp_ips = HPP.create Fmt.nop "Quantifier.pp" (fun c _ -> create c) (** parent-type, wait for a type to match a variable *) -let pt_ips = HPT.create Fmt.nop "Quantifier.pt" Context.Queue.create +let pt_ips = + HPT.create Fmt.nop "Quantifier.pt" (fun c _ -> Context.Queue.create c) (** parent-node, wait for a subterm with the right class *) -let pn_ips = HPN.create Fmt.nop "Quantifier.pn" create +let pn_ips = HPN.create Fmt.nop "Quantifier.pn" (fun c _ -> create c) let add_pattern pat ip = Context.Ref.set ip diff --git a/src_colibri2/theories/quantifier/InvertedPath.mli b/src_colibri2/theories/quantifier/InvertedPath.mli index a4b64a06d13b6c9bd6e3587363b918ecc1a6020e..c2551744b710523b0c167020b69b9811f1c9994f 100644 --- a/src_colibri2/theories/quantifier/InvertedPath.mli +++ b/src_colibri2/theories/quantifier/InvertedPath.mli @@ -19,23 +19,23 @@ val insert_pattern : Egraph.t -> Trigger.t -> unit (** [insert_pattern d tri] insert the pattern in the database of inverted path for all its subterms *) -module HPP : Datastructure.Sig3 with type key := PP.t +module HPP : Datastructure.Memo with type key := PP.t val pp_ips : t HPP.t (** The database of inverted path for each parent-parent pairs *) -module HPC : Datastructure.Sig3 with type key := PC.t +module HPC : Datastructure.Memo with type key := PC.t val pc_ips : t HPC.t (** The database of inverted path for each parent-child pairs *) -module HPT : Datastructure.Sig3 with type key := F_Pos.t +module HPT : Datastructure.Memo with type key := F_Pos.t val pt_ips : (Ty.t * t) Context.Queue.t HPT.t (** The database of inverted path for each parent-type, needed for polymorphic variables *) -module HPN : Datastructure.Sig3 with type key := PN.t +module HPN : Datastructure.Memo with type key := PN.t val pn_ips : t HPN.t (** The database of inverted path for each parent-node pairs *) diff --git a/src_colibri2/theories/quantifier/dune b/src_colibri2/theories/quantifier/dune index dbf85cf3e97adb77fa34de14c856ad6861b51827..addf72911482e3977c2c5cbea6d1037f06c33abe 100644 --- a/src_colibri2/theories/quantifier/dune +++ b/src_colibri2/theories/quantifier/dune @@ -2,7 +2,7 @@ (name colibri2_theories_quantifiers) (public_name colibri2.theories.quantifiers) (synopsis "theories for colibri2") - (libraries containers ocamlgraph colibri2.stdlib colibri2.popop_lib + (libraries containers base ocamlgraph colibri2.stdlib colibri2.popop_lib colibri2.core.structures colibri2.core colibri2.theories.bool) (preprocess (pps ppx_deriving.std ppx_hash)) diff --git a/src_colibri2/theories/quantifier/info.ml b/src_colibri2/theories/quantifier/info.ml index a9a0703227dacef78a1f403cb15de254cda7aec9..2f566d44bbb701123bbea8b52a32efcb55bb912d 100644 --- a/src_colibri2/theories/quantifier/info.ml +++ b/src_colibri2/theories/quantifier/info.ml @@ -3,7 +3,6 @@ type t = { parents : Ground.S.t F_Pos.M.t; (** parents *) apps : Ground.S.t F.M.t; (** parent parent *) - tys : Ground.Ty.S.t; } [@@deriving show] @@ -54,16 +53,14 @@ let merge d ~other ~repr info info' = parents = congruence_closure d ~other ~repr info.parents info'.parents; apps = F.M.union (fun _ a b -> Some (Ground.S.union a b)) info.apps info'.apps; - tys = Ground.Ty.S.union info.tys info'.tys; } -let empty = - { parents = F_Pos.M.empty; apps = F.M.empty; tys = Ground.Ty.S.empty } +let empty = { parents = F_Pos.M.empty; apps = F.M.empty } let dom = DomKind.create_key - ( module struct + (module struct type nonrec t = t let name = "Quantifier.info" - end ) + end) diff --git a/src_colibri2/theories/quantifier/info.mli b/src_colibri2/theories/quantifier/info.mli index 82834017100df6eac512fc0560857f767d4ed3b5..2b8dc01718aa3bd8b69be51c34b18e880cce7614 100644 --- a/src_colibri2/theories/quantifier/info.mli +++ b/src_colibri2/theories/quantifier/info.mli @@ -1,7 +1,6 @@ type t = { parents : Ground.S.t F_Pos.M.t; (** parents *) apps : Ground.S.t F.M.t; (** parent parent *) - tys : Ground.Ty.S.t; } [@@deriving show] diff --git a/src_colibri2/theories/quantifier/pattern.ml b/src_colibri2/theories/quantifier/pattern.ml index 5cd7975ca0ce5063ca8d4e884aff7763cc93f9f6..79dc1057cc60618e1099562390f700648ccf1ec9 100644 --- a/src_colibri2/theories/quantifier/pattern.ml +++ b/src_colibri2/theories/quantifier/pattern.ml @@ -31,7 +31,7 @@ let rec of_term ~subst (t : Expr.Term.t) = | Var v -> ( match Expr.Term.Var.M.find v subst.Ground.Subst.term with | exception Not_found -> Var v (* todo type substitution *) - | n -> Node n ) + | n -> Node n) | App ({ builtin = Expr.Coercion; _ }, _, [ a ]) -> of_term ~subst a | App (f, tys, tl) -> (* todo type substitution *) @@ -72,10 +72,7 @@ let rec match_term d (substs : Ground.Subst.S.t) (n : Node.t) (p : t) : Ground.Subst.S.union (match_ty substs ty v.ty) acc in let substs = - match Egraph.get_dom d Info.dom n with - | None -> Ground.Subst.S.empty - | Some info -> - Ground.Ty.S.fold_left match_ty Ground.Subst.S.empty info.tys + Ground.Ty.S.fold_left match_ty Ground.Subst.S.empty (Ground.tys d n) in Ground.Subst.S.fold_left (fun acc subst -> @@ -115,7 +112,7 @@ let rec check_ty subst (ty : Ground.Ty.t) (p : Expr.Ty.t) : bool = | Var v -> ( match Expr.Ty.Var.M.find v subst.Ground.Subst.ty with | exception Not_found -> false - | ty' -> Ground.Ty.equal ty ty' ) + | ty' -> Ground.Ty.equal ty ty') | App (cst', tyl') -> Expr.Ty.Const.equal ty.app cst' && List.for_all2 (check_ty subst) ty.args tyl' @@ -124,14 +121,11 @@ let rec check_term d (subst : Ground.Subst.t) (n : Node.t) (p : t) : bool = Debug.dprintf4 debug "[Quant] check %a %a" pp p Node.pp n; match p with | Var v -> ( - match Egraph.get_dom d Info.dom n with - | None -> false - | Some info -> ( - Ground.Ty.S.exists (fun ty -> check_ty subst ty v.ty) info.tys - && - match Expr.Term.Var.M.find v subst.term with - | exception Not_found -> false - | n' -> Egraph.is_equal d n n' ) ) + Ground.Ty.S.exists (fun ty -> check_ty subst ty v.ty) (Ground.tys d n) + && + match Expr.Term.Var.M.find v subst.term with + | exception Not_found -> false + | n' -> Egraph.is_equal d n n') | App (pf, ptyl, pargs) -> let s = Opt.get_def Ground.S.empty @@ -158,14 +152,12 @@ let rec check_term_exists_exn d (subst : Ground.Subst.t) (p : t) : Node.S.t = | Var v -> ( match Expr.Term.Var.M.find v subst.term with | exception Not_found -> raise IncompleteSubstitution - | n -> ( - match Egraph.get_dom d Info.dom n with - | None -> raise Not_found - | Some info -> - if - (Ground.Ty.S.exists (fun ty -> check_ty subst ty v.ty)) info.tys - then Node.S.singleton (Egraph.find_def d n) - else raise Not_found ) ) + | n -> + if + (Ground.Ty.S.exists (fun ty -> check_ty subst ty v.ty)) + (Ground.tys d n) + then Node.S.singleton (Egraph.find_def d n) + else raise Not_found) | App (pf, ptyl, []) -> let g = Ground.apply pf (List.map (Ground.Ty.convert subst.ty) ptyl) [] @@ -234,16 +226,14 @@ let get_pps pattern = m let env_ground_by_apps = - F.EnvApps.create - (Context.Queue.pp ~sep:Fmt.semi Ground.pp) - "Quantifier.Trigger.ground_by_apps" Context.Queue.create + F.EnvApps.create (Context.Queue.pp ~sep:Fmt.semi Ground.pp) + "Quantifier.Trigger.ground_by_apps" (fun c _ -> Context.Queue.create c) module EnvTy = Datastructure.Memo (Ground.Ty) let env_node_by_ty = - EnvTy.create - (Context.Queue.pp ~sep:Fmt.semi Node.pp) - "Quantifier.Trigger.node_by_ty" Context.Queue.create + EnvTy.create (Context.Queue.pp ~sep:Fmt.semi Node.pp) + "Quantifier.Trigger.node_by_ty" (fun c _ -> Context.Queue.create c) let match_any_term d (p : t) : Ground.Subst.S.t = Debug.dprintf2 debug "[Quant] matching any for %a" pp p; diff --git a/src_colibri2/theories/quantifier/pattern.mli b/src_colibri2/theories/quantifier/pattern.mli index e68c6ef09566ef2727fae4d4ccd67a02c32ba0b9..c8e528bfb03dc584542b9c4b59f1c95bc6f16049 100644 --- a/src_colibri2/theories/quantifier/pattern.mli +++ b/src_colibri2/theories/quantifier/pattern.mli @@ -43,7 +43,7 @@ val get_pps : t -> PP.t list Term.Var.M.t val env_ground_by_apps : Ground.t Context.Queue.t F.EnvApps.t (** The set of ground terms sorted by their top function *) -module EnvTy : Datastructure.Sig3 with type key := Ground.Ty.t +module EnvTy : Datastructure.Memo with type key := Ground.Ty.t val env_node_by_ty : Node.t Context.Queue.t EnvTy.t (** The set of nodes sorted by their type. A node can have multiple types *) diff --git a/src_colibri2/theories/quantifier/quantifier.ml b/src_colibri2/theories/quantifier/quantifier.ml index f0750206a5e545c09fac03a3a850c693e6cd98ce..15da41554e9a460f278dd02bcbc56a621fc2b22e 100644 --- a/src_colibri2/theories/quantifier/quantifier.ml +++ b/src_colibri2/theories/quantifier/quantifier.ml @@ -69,12 +69,12 @@ let find_new_event d n (info : Info.t) n' (info' : Info.t) = symmetric aux acc n info n' info' in let do_pt f_pos q acc = - let aux acc _ info1 _ info2 = + let aux acc n info1 n' _ = match F_Pos.M.find_opt f_pos info1.Info.parents with | Some _ -> Context.Queue.fold (fun acc (vty, ip) -> - let substs_by info = + let substs_by n0 = let match_ty (acc : Ground.Subst.S.t) ty : Ground.Subst.S.t = Ground.Subst.S.union (Pattern.match_ty Pattern.init ty vty) @@ -82,20 +82,19 @@ let find_new_event d n (info : Info.t) n' (info' : Info.t) = in let substs = Ground.Ty.S.fold_left match_ty Ground.Subst.S.empty - info.Info.tys + (Ground.tys d n0) in substs in - let substs2 = substs_by info2 in + let substs2 = substs_by n in let substs1 = - if Equal.physical info1 info2 then Ground.Subst.S.empty - else substs_by info1 + if Node.equal n n' then Ground.Subst.S.empty else substs_by n' in if Ground.Subst.S.subset substs2 substs1 then acc else ( Debug.dprintf6 debug_full "[Quant] PT %a %a found for %a" F_Pos.pp f_pos Expr.Ty.pp vty Node.pp n; - ip :: acc )) + ip :: acc)) acc q | None -> acc in @@ -127,13 +126,13 @@ module Delayed_find_new_event = struct let key = Events.Dem.create_key - ( module struct + (module struct type d = Node.t * InvertedPath.t list type t = unit let name = "Quantifier.new_event" - end ) + end) let delay = Events.Delayed_by 10 @@ -150,7 +149,7 @@ let () = Egraph.Wait.register_dem (module Delayed_find_new_event) let () = DomKind.register - ( module struct + (module struct type t = Info.t [@@deriving show] let key = Info.dom @@ -178,7 +177,7 @@ let () = if not (List.is_empty ips) then Egraph.register_delayed_event d Delayed_find_new_event.key (repr, ips) - end ) + end) let skolemize d (e : Ground.ClosedQuantifier.t) = if not (Base.List.is_empty e.ty_vars) then @@ -268,11 +267,7 @@ let add_info_on_ground_terms d thg = in List.iteri add_pc g.args; merge_info res - { - Info.empty with - apps = F.M.singleton g.app (Ground.S.singleton thg); - tys = Ground.Ty.S.singleton g.ty; - }; + { Info.empty with apps = F.M.singleton g.app (Ground.S.singleton thg) }; Context.Queue.push (F.EnvApps.find Pattern.env_ground_by_apps d g.app) thg; Context.Queue.push (Pattern.EnvTy.find Pattern.env_node_by_ty d g.ty) res; (* Try pattern from the start *) diff --git a/src_colibri2/theories/quantifier/trigger.ml b/src_colibri2/theories/quantifier/trigger.ml index 1b74443536a74452d896a2472eada6e659409fc9..c2b9728ecdcc49691e121d162ac3867ec212a817 100644 --- a/src_colibri2/theories/quantifier/trigger.ml +++ b/src_colibri2/theories/quantifier/trigger.ml @@ -61,13 +61,13 @@ let compute_top_triggers (cq : Ground.ThClosedQuantifier.t) = | [] -> acc | a :: l -> aux - ( { - pat = a; - checks = other @ l @ pats_partial; - form = cq; - eager = true; - } - :: acc ) + ({ + pat = a; + checks = other @ l @ pats_partial; + form = cq; + eager = true; + } + :: acc) (a :: other) l in aux [] [] pats_full @@ -109,7 +109,7 @@ let compute_all_triggers (cq : Ground.ThClosedQuantifier.t) = | None -> (pats, None) | Some (sty, st) -> let fv = (List.fold_left Expr.Ty.free_vars sty tyl, st) in - (add t fv pats, union fv acc_fv) ) + (add t fv pats, union fv acc_fv)) | Expr.Binder (_, _) -> (pats, None) (* todo *) | Expr.Match (_, _) -> (pats, None) (* todo *) @@ -136,9 +136,8 @@ let compute_all_triggers (cq : Ground.ThClosedQuantifier.t) = let env_vars = Datastructure.Queue.create pp "Quantifier.Trigger.vars" let env_tri_by_apps = - F.EnvApps.create - (Context.Queue.pp ~sep:Fmt.semi pp) - "Quantifier.Trigger.tri_by_apps" Context.Queue.create + F.EnvApps.create (Context.Queue.pp ~sep:Fmt.semi pp) + "Quantifier.Trigger.tri_by_apps" (fun c _ -> Context.Queue.create c) let add_trigger d t = match t.pat with @@ -174,13 +173,13 @@ module Delayed_instantiation = struct let key = Events.Dem.create_key - ( module struct + (module struct type d = t * Ground.Subst.t type t = unit let name = "delayed_instantiation" - end ) + end) let delay = Events.LastEffort 1 @@ -222,7 +221,7 @@ let instantiate d tri subst = then instantiate_aux d tri subst else ( Debug.dprintf0 debug "[Quant] Delayed"; - Egraph.register_delayed_event d Delayed_instantiation.key (tri, subst) ) + Egraph.register_delayed_event d Delayed_instantiation.key (tri, subst)) let match_ d tri n = Debug.dprintf4 debug "[Quant] match %a %a" pp tri Node.pp n; diff --git a/src_colibri2/theories/bool/uninterp.ml b/src_colibri2/theories/quantifier/uninterp.ml similarity index 62% rename from src_colibri2/theories/bool/uninterp.ml rename to src_colibri2/theories/quantifier/uninterp.ml index ae18b0b4b5c91c699f7c5359413ab2fbe179957c..81c29343f3b6fdc636ec7a3b614c4d91fe3c5979 100644 --- a/src_colibri2/theories/bool/uninterp.ml +++ b/src_colibri2/theories/quantifier/uninterp.ml @@ -19,12 +19,12 @@ (*************************************************************************) open Colibri2_popop_lib -(* open Popop_stdlib *) +open Popop_stdlib open Colibri2_core -let debug = Debug.register_info_flag - ~desc:"for the uninterpreted function theory" - "uninterp" +(* let debug = + * Debug.register_info_flag ~desc:"for the uninterpreted function theory" + * "uninterp" *) (* module Th = struct * module T = struct @@ -202,14 +202,158 @@ let debug = Debug.register_info_flag * * let () = Egraph.add_default_theory th_register *) -let converter d (t:Ground.t) = +(** uninterpreted sorts *) +module USModel = struct + type usv = { id : DInt.t; ty : Ground.Ty.t } + + module US = ValueKind.Register (struct + module T = struct + type t = usv = { id : DInt.t; ty : Ground.Ty.t } + [@@deriving eq, ord, hash] + + let pp fmt v = Fmt.pf fmt "@us_%a_%i" Ground.Ty.pp v.ty v.id + end + + include T + include MkDatatype (T) + + let key = + ValueKind.create_key + (module struct + type nonrec t = t + + let name = "US" + end) + end) + + module H = Datastructure.Memo (Ground.Ty) + (** The value created for uninterpreted sorts *) + + let create_value q ty = + US.nodevalue @@ US.index { id = Context.Queue.length q; ty } + + (** The values are generated so we could just store the current id, + and pay the small cost of indexing *) + let env = + H.create (Context.Queue.pp Value.pp) "Uninterp.Model.env" (fun creator _ -> + Context.Queue.create creator) + + let new_value d ty = + let q = H.find env d ty in + let v = create_value q ty in + Context.Queue.push q v; + (v, q) +end + +let init_ty d = + Interp.Register.ty d (fun d ty -> + match ty with + | { app = { builtin = Dolmen_std.Expr.Base; _ } as sym; _ } -> ( + match Ground.Ty.definition sym with + | Abstract -> + let _, q = USModel.new_value d ty in + Some (Interp.SeqLim.of_seq d (Context.Queue.of_seq q)) + | _ -> None) + | _ -> None) + +module UFModel = struct + module FA = struct + module T = struct + let hash_fold_list = Base.hash_fold_list + + type t = { tc : F.t; ta : Ground.Ty.t list } [@@deriving eq, ord, hash] + + let pp fmt { tc; ta } = + Format.fprintf fmt "%a[%a]" F.pp tc + Fmt.(list ~sep:comma Ground.Ty.pp) + ta + end + + include T + include Colibri2_popop_lib.Popop_stdlib.MkDatatype (T) + end + + module HConst = Datastructure.Memo (FA) + + module Values = struct + (** instantiated function symbol *) + module T = struct + let hash_fold_list = Base.hash_fold_list + + type t = Value.t list [@@deriving eq, ord, hash] + + let pp fmt l = Fmt.(list ~sep:comma Value.pp) fmt l + end + + include T + include Colibri2_popop_lib.Popop_stdlib.MkDatatype (T) + end + + module Hargs = Context.Hashtbl (Values) + + let env = + HConst.create Fmt.nop "Uninterp.UFModel.env" (fun c _ -> Hargs.create c) + + let find d (g : Ground.Term.t) lv = + let h = HConst.find env d { tc = g.app; ta = g.tyargs } in + Hargs.find_opt h lv + + let set d (g : Ground.Term.t) lv v = + let h = HConst.find env d { tc = g.app; ta = g.tyargs } in + Hargs.set h lv v + + let init, waiter = + Demon.Fast.register_simply ~delay:FixingModel + "Uninterp.UFModel.propagate_value" + + let on_uninterpreted_domain d g = + let check_or_update g d _ = + let interp n = Option.get_exn (Egraph.get_value d n) in + let s = Ground.sem g in + let lv = List.map interp s.args in + let v = interp (Ground.node g) in + match find d s lv with + | Some v' -> if not (Value.equal v v') then Egraph.contradiction d + | None -> set d s lv v + in + let f d g = waiter.for_any_value d (Ground.node g) (check_or_update g) in + f d g + + let init_attach_value d g = + Interp.TwoWatchLiteral.create d on_uninterpreted_domain g +end + +module On_uninterpreted_domain = struct + (** For model fixing *) + + let propagate = UFModel.on_uninterpreted_domain + + let check d t = + let interp n = Option.get_exn (Egraph.get_value d n) in + let s = Ground.sem t in + let lv = List.map interp s.args in + let v = interp (Ground.node t) in + match UFModel.find d s lv with Some v' -> Value.equal v v' | None -> false +end + +let init_check d = + Interp.Register.check d (fun d t -> + match Ground.sem t with + | { app = { builtin = Dolmen_std.Expr.Base; _ }; _ } -> + Some (On_uninterpreted_domain.check d t) + | _ -> None) + +let converter d (t : Ground.t) = match Ground.sem t with - | { app = {builtin = Dolmen_std.Expr.Base; _ }; args; _ } -> - List.iter (Egraph.register d) args + | { app = { builtin = Dolmen_std.Expr.Base; _ }; args; _ } -> + List.iter (Egraph.register d) args; + UFModel.init_attach_value d t | _ -> () let th_register env = Ground.register_converter env converter; - () + init_check env; + init_ty env; + UFModel.init env let () = Egraph.add_default_theory th_register diff --git a/src_colibri2/theories/bool/uninterp.mli b/src_colibri2/theories/quantifier/uninterp.mli similarity index 93% rename from src_colibri2/theories/bool/uninterp.mli rename to src_colibri2/theories/quantifier/uninterp.mli index 9e80e4ffb453b83b1119c34bb1443b44f112f7f6..952f5027f455f345b23dc94a86afd6618e392910 100644 --- a/src_colibri2/theories/bool/uninterp.mli +++ b/src_colibri2/theories/quantifier/uninterp.mli @@ -50,3 +50,11 @@ open Colibri2_core (* val app_fun: Node.t -> Node.t list -> Node.t *) val th_register : Egraph.t -> unit + +module On_uninterpreted_domain : sig + (** For model fixing *) + + val propagate : Egraph.t -> Ground.t -> unit + + val check : Egraph.t -> Ground.t -> bool +end