From a02515827a0ef2242acf0adc8928994989e173fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois=20Bobot?= <francois.bobot@cea.fr>
Date: Mon, 4 Jan 2021 14:18:51 +0100
Subject: [PATCH] [TimeWheel] use a time wheel

            in order to more easily prioritize without creating famine
---
 src_colibri2/core/demon.ml                    |  34 ++--
 src_colibri2/core/demon.mli                   |  16 +-
 src_colibri2/core/events.ml                   |  14 +-
 src_colibri2/core/events.mli                  |   4 +-
 src_colibri2/popop_lib/TimeWheel.ml           | 176 ++++++++++++++++++
 src_colibri2/popop_lib/TimeWheel.mli          |  46 +++++
 src_colibri2/popop_lib/intmap.mli             |   3 +
 src_colibri2/solver/scheduler.ml              |  63 +++----
 src_colibri2/stdlib/context.ml                |  11 ++
 src_colibri2/stdlib/context.mli               |  24 +++
 src_colibri2/theories/bool/boolean.ml         |   6 +-
 src_colibri2/theories/bool/equality.ml        |  10 +-
 src_colibri2/theories/bool/uninterp.ml        |   4 +-
 .../theories/quantifier/quantifier.ml         |  10 +-
 14 files changed, 345 insertions(+), 76 deletions(-)
 create mode 100644 src_colibri2/popop_lib/TimeWheel.ml
 create mode 100644 src_colibri2/popop_lib/TimeWheel.mli

diff --git a/src_colibri2/core/demon.ml b/src_colibri2/core/demon.ml
index 985218ad6..110ade1ed 100644
--- a/src_colibri2/core/demon.ml
+++ b/src_colibri2/core/demon.ml
@@ -115,7 +115,7 @@ module Key = struct
 
     val key: (Key.t, Data.t, info) t
 
-    val immediate: bool
+    val delayed: int option
     val wakeup:
       Egraph.t -> Key.t -> Data.t Events.Fired.t ->
       info -> Key.t alive
@@ -257,7 +257,7 @@ module Key = struct
       let enqueue = enqueue
 
       let key = D.key.dk_id
-      let immediate = D.immediate
+      let delayed = D.delayed
     end in
     Egraph.Wait.register_dem (module Dem)
 
@@ -384,7 +384,7 @@ module Fast = struct
     val key: Data.t t
 
     (** never killed *)
-    val immediate: bool
+    val delayed: int option
     val throttle: int (** todo int ref? *)
     (** number of time run in a row *)
     val wakeup: Egraph.t -> Data.t Events.Fired.event -> unit
@@ -421,7 +421,7 @@ module Fast = struct
             D.Data.pp (Events.Fired.get_data event);
           D.wakeup d event;
           Debug.dprintf0 debug "[Demon] @[Done@]";
-          if not D.immediate then Egraph.flush_internal d;
+          if Option.is_some D.delayed then Egraph.flush_internal d;
           run_one () in
       try
         run_one ();
@@ -469,7 +469,7 @@ module Fast = struct
       let enqueue = enqueue
 
       let key = D.key.dk_id
-      let immediate = D.immediate
+      let delayed = D.delayed
     end in
     Egraph.Wait.register_dem (module Dem)
 
@@ -505,7 +505,7 @@ module Fast = struct
   let register_init_daemon
     (type a)
     ~name
-    ?(immediate=false)
+    ?(delayed=Some 1)
     ?(throttle=100)
     (thterm: (module Nodes.RegisteredThTerm with type t = a) )
     (f:Egraph.t -> a -> unit)
@@ -515,7 +515,7 @@ module Fast = struct
     let module DaemonInit = struct
       let key = create name
       module Data = Popop_stdlib.DUnit
-      let immediate = immediate
+      let delayed = delayed
       let throttle = throttle
       let wakeup d = function
         | Events.Fired.EventRegSem(thterm,()) ->
@@ -530,7 +530,7 @@ module Fast = struct
   let register_init_daemon_value
     (type a)
     ~name
-    ?(immediate=false)
+    ?(delayed=Some 1)
     ?(throttle=100)
     (value: (module Nodes.RegisteredValue with type t = a) )
     (f:Egraph.t -> a -> unit)
@@ -540,7 +540,7 @@ module Fast = struct
     let module DaemonInit = struct
       let key = create name
       module Data = Popop_stdlib.DUnit
-      let immediate = immediate
+      let delayed = delayed
       let throttle = throttle
       let wakeup d = function
         | Events.Fired.EventRegValue(value,()) ->
@@ -554,7 +554,7 @@ module Fast = struct
 
   let register_change_repr_daemon
       ~name
-      ?(immediate=false)
+      ?(delayed=Some 1)
       ?(throttle=100)
       (f:Egraph.t -> Node.t -> unit)
       (init_d:Egraph.t)
@@ -562,7 +562,7 @@ module Fast = struct
     let module DaemonInit = struct
       let key = create name
       module Data = Popop_stdlib.DUnit
-      let immediate = immediate
+      let delayed = delayed
       let throttle = throttle
       let wakeup d = function
         | Events.Fired.EventChange(node,()) ->
@@ -576,7 +576,7 @@ module Fast = struct
   let register_change_domain_daemon
     (type a) (type b)
     ~name
-    ?(immediate=false)
+    ?(delayed=Some 1)
     ?(throttle=100)
     ?(pp=Fmt.nop)
     (dom:a DomKind.t)
@@ -588,7 +588,7 @@ module Fast = struct
         type t = b
         let pp = pp
       end
-      let immediate = immediate
+      let delayed = delayed
       let throttle = throttle
       let wakeup d = function
         | Events.Fired.EventDom(node,dom',data) ->
@@ -608,7 +608,7 @@ module Fast = struct
   let register_get_value_daemon
     (type a) (type b)
     ~name
-    ?(immediate=false)
+    ?(delayed=Some 1)
     ?(throttle=100)
     ?(pp=Fmt.nop)
     (value: (module Nodes.RegisteredValue with type s = a) )
@@ -621,7 +621,7 @@ module Fast = struct
         type t = b
         let pp = pp
       end
-      let immediate = immediate
+      let delayed = delayed
       let throttle = throttle
       let wakeup d = function
         | Events.Fired.EventValue(node,value,v,data) ->
@@ -643,7 +643,7 @@ module Fast = struct
   }
 
     let register_simply
-    ?(immediate=false)
+    ?(delayed=Some 1)
     ?(throttle=100)
     name
     =
@@ -653,7 +653,7 @@ module Fast = struct
         type t = Egraph.t -> Node.t -> unit
         let pp = Fmt.nop
       end
-      let immediate = immediate
+      let delayed = delayed
       let throttle = throttle
       let wakeup d = function
         | Events.Fired.EventDom(node,_,f) ->
diff --git a/src_colibri2/core/demon.mli b/src_colibri2/core/demon.mli
index 771f662fe..e04f49348 100644
--- a/src_colibri2/core/demon.mli
+++ b/src_colibri2/core/demon.mli
@@ -66,7 +66,7 @@ module Key: sig
 
     val key: (Key.t, Data.t, info) t
 
-    val immediate: bool
+    val delayed: int option
     val wakeup:
       Egraph.t -> Key.t -> Data.t Events.Fired.t ->
       info -> Key.t alive
@@ -113,7 +113,7 @@ module Fast: sig
     val key: Data.t t
 
     (** never killed *)
-    val immediate: bool
+    val delayed: int option
     val throttle: int (** todo int ref? *)
     (** number of time run in a row *)
     val wakeup: Egraph.t -> Data.t Events.Fired.event -> unit
@@ -130,7 +130,7 @@ module Fast: sig
   (** helper *)
   val register_init_daemon:
     name:string ->
-    ?immediate:bool ->
+    ?delayed:int option ->
     ?throttle:int ->
     (module RegisteredThTerm with type t = 'a) ->
     (Egraph.t -> 'a -> unit) ->
@@ -141,7 +141,7 @@ module Fast: sig
   (** helper *)
   val register_init_daemon_value:
     name:string ->
-    ?immediate:bool ->
+    ?delayed:int option ->
     ?throttle:int ->
     (module RegisteredValue with type t = 'a) ->
     (Egraph.t -> 'a -> unit) ->
@@ -152,7 +152,7 @@ module Fast: sig
   (** helper *)
   val register_change_repr_daemon:
     name:string ->
-    ?immediate:bool ->
+    ?delayed:int option ->
     ?throttle:int ->
     (Egraph.t -> Node.t -> unit) ->
     Egraph.t ->
@@ -162,7 +162,7 @@ module Fast: sig
   (** helper *)
   val register_change_domain_daemon:
     name:string ->
-    ?immediate:bool ->
+    ?delayed:int option ->
     ?throttle:int ->
     ?pp:('b Fmt.t) ->
     'a DomKind.t ->
@@ -173,7 +173,7 @@ module Fast: sig
   (** helper *)
   val register_get_value_daemon:
     name:string ->
-    ?immediate:bool ->
+    ?delayed:int option ->
     ?throttle:int ->
     ?pp:('b Fmt.t) ->
     (module RegisteredValue with type s = 'a) ->
@@ -188,7 +188,7 @@ module Fast: sig
 
   (** helper *)
   val register_simply:
-    ?immediate:bool ->
+    ?delayed:int option ->
     ?throttle:int ->
     string ->
     (Egraph.t -> unit) * waiter
diff --git a/src_colibri2/core/events.ml b/src_colibri2/core/events.ml
index 4bf8c04b3..7f4bc3718 100644
--- a/src_colibri2/core/events.ml
+++ b/src_colibri2/core/events.ml
@@ -163,13 +163,15 @@ module Wait = struct
       val print_event : event Format.printer
       val enqueue : delayed_ro -> event Fired.event -> runable enqueue
       val key : (event, runable) Dem.t
-      val immediate : bool
+      val delayed : int option
     end
 
     val register_dem : (module Dem with type event = 'k and type runable = 'd) -> unit
 
     val get_dem : ('k, 'd) Dem.t -> (module Dem with type event = 'k and type runable = 'd)
 
+    val get_priority: daemon_key -> int
+
     val print_dem_event : ('a, 'b) Dem.t -> 'a Format.printer
 
     val print_dem_runable : ('a, 'b) Dem.t -> 'b Format.printer
@@ -210,8 +212,7 @@ module Wait = struct
       val enqueue: delayed_ro -> event Fired.event -> runable enqueue
 
       val key: (event,runable) Dem.t
-      val immediate: bool
-
+      val delayed: int option
     end
 
     module Registry = Dem.Make_Registry(struct
@@ -231,6 +232,11 @@ module Wait = struct
     let register_dem = Registry.register
     let get_dem = Registry.get
 
+    let get_priority = function
+      | DaemonKey(dem,_) ->
+        let module Dem = (val get_dem dem) in
+        Option.value ~default:1 Dem.delayed
+
     let print_dem_event = Registry.printk
     let () = Print.pdem_event.Print.pdem_event <- print_dem_event
 
@@ -242,7 +248,7 @@ module Wait = struct
     let new_pending_daemon (type k) (type d) t (dem:(k,d) Dem.t) runable =
       let module Dem = (val get_dem dem) in
       let daemonkey = DaemonKey(dem, runable) in
-      if Dem.immediate
+      if Option.is_none Dem.delayed
       then S.schedule_immediate t daemonkey
       else S.schedule t daemonkey
 
diff --git a/src_colibri2/core/events.mli b/src_colibri2/core/events.mli
index 94fac946f..22567d837 100644
--- a/src_colibri2/core/events.mli
+++ b/src_colibri2/core/events.mli
@@ -88,13 +88,15 @@ module Wait : sig
       val print_event : event Format.printer
       val enqueue : delayed_ro -> event Fired.event -> runable enqueue
       val key : (event, runable) Dem.t
-      val immediate : bool
+      val delayed : int option
     end
 
     val register_dem : (module Dem with type event = 'k and type runable = 'd) -> unit
 
     val get_dem : ('k, 'd) Dem.t -> (module Dem with type event = 'k and type runable = 'd)
 
+    val get_priority: daemon_key -> int
+
     val print_dem_event : ('a, 'b) Dem.t -> 'a Format.printer
 
     val print_dem_runable : ('a, 'b) Dem.t -> 'b Format.printer
diff --git a/src_colibri2/popop_lib/TimeWheel.ml b/src_colibri2/popop_lib/TimeWheel.ml
new file mode 100644
index 000000000..c4c300150
--- /dev/null
+++ b/src_colibri2/popop_lib/TimeWheel.ml
@@ -0,0 +1,176 @@
+open Base
+
+(** TimeWheel *)
+
+module type S = sig
+
+  type 'a t
+  type context
+
+  val create: context -> 'a t
+
+
+  val add: 'a t -> 'a -> int -> unit
+  (** [add t v offset] add the event v at the given offset in the futur *)
+
+  val next: 'a t -> 'a option
+
+
+  val current_time: 'a t -> int
+
+  val size: 'a t -> int
+end
+
+module Make
+    (Context: sig type t end)
+    (Array:
+     sig
+       type 'a t
+       val create: Context.t -> int -> 'a -> 'a t
+       val get: 'a t -> int -> 'a
+       val set: 'a t -> int -> 'a -> unit
+     end)
+    (Ref:sig
+       type 'a t
+       val create: Context.t -> 'a -> 'a t
+       val get: 'a t -> 'a
+       val set: 'a t -> 'a -> unit
+     end) : S with type context := Context.t = struct
+
+  type 'a cell =
+    | Nil
+    | Cons of { v: 'a; time : int; next: 'a cell }
+
+  type 'a t = {
+    current: int Ref.t;
+    futur: 'a cell Array.t;
+    size: int Ref.t;
+  }
+
+  let new_bits o n = (lnot o) land n
+
+  (* lowest first *)
+  let rec iter_bits f i =
+    if i <> 0 then
+      let j = Int.ctz i in
+      f j;
+      iter_bits f (i land (lnot (1 lsl j)))
+
+
+  let create ctx = {
+    current = Ref.create ctx 0;
+    futur = Array.create ctx Int.num_bits Nil;
+    size = Ref.create ctx 0;
+  }
+
+  let current_time t = Ref.get t.current
+  let size t = Ref.get t.size
+
+  let bucket t time =
+    assert (Ref.get t.current <= time);
+    let i = Int.num_bits - Int.clz (time - (Ref.get t.current)) in
+    (* Caml.Format.eprintf "-> t.current = %i; time = %i; i = %i@." (Ref.get t.current) time i; *)
+    i
+
+  let add_in_bucket t v time =
+    let b = bucket t time in
+    Array.set t.futur b (Cons { v; time; next = Array.get t.futur b })
+
+  let add t v offset =
+    assert (1 <= offset);
+    let time = (Ref.get t.current) + offset in
+    Ref.set t.size ((Ref.get t.size) + 1);
+    add_in_bucket t v time
+
+
+  (* optim *)
+
+  (* let rec find_smallest i =
+   *   if Int.num_bits <= i then None
+   *   else
+   *     match t.futur.(i) with
+   *     | Nil -> find_smallest (i+1)
+   *     | Cons _ as b ->
+   *       let rec find_smallest acc = function
+   *         | Nil -> Some acc
+   *         | Cons { time; next } ->
+   *           find_smallest (min time acc) next
+   *       in
+   *       find_smallest Int.max_value b
+   * in
+   * match find_smallest 0 with
+   * | None -> ()
+   * | Some next ->
+   *   assert ( t.current < next );
+   *   (\* We compute all the bit that would have been the at 1 from 0, if we would
+   *      go from t.current to next by 1 increment.Arith_status
+   *      All the bits are in different categories :
+   *       - The leading unchanged bits
+   *       - The biggest set bit
+   *       - The bits that went from 1 to 0, before the biggest bit is set
+   *       - The bits that stayed at 0, after the biggest bit is set
+   *       - The other below those that changed multiple times.
+   *   *\)
+   *   let biggest = Int.clz (next land (lnot t.current)) in
+   *   let mask = -1 lsl biggest in
+   *   let before_1_0 = Int.clz (lnot (t.current lor mask)) in
+   *   let before_stay_0 = Int.clz (t.next land (lnot mask)) in *)
+
+
+  let incr_current t =
+    let bitset = Int.ctz (lnot (Ref.get t.current)) in
+    let bitset = bitset + 2 in
+    Ref.set t.current ((Ref.get t.current) + 1);
+    let empty i =
+      (* Caml.Format.eprintf "empty t.current = %i; i = %i@." (Ref.get t.current) i; *)
+      let b = Array.get t.futur i in
+      Array.set t.futur i Nil;
+      let rec aux = function
+        | Nil -> ()
+        | Cons { v; time; next } ->
+          (* Caml.Format.eprintf "t.current = %i; time = %i; i = %i@." (Ref.get t.current) time i; *)
+          assert ( (Ref.get t.current) <= time );
+          add_in_bucket t v time;
+          aux next
+      in
+      aux b
+    in
+    empty 1; (** always emptied because next but could be just copied *)
+    empty bitset
+
+  let find_next t =
+    if 0 < (Ref.get t.size) then
+      let rec aux t =
+        match Array.get t.futur 0 with
+        | Nil ->
+          incr_current t;
+          aux t
+        | Cons _ -> ()
+      in
+      aux t
+
+  let next t =
+    find_next t;
+    match Array.get t.futur 0 with
+    | Nil -> None
+    | Cons { v; time; next } ->
+      assert ( Int.equal time (Ref.get t.current) );
+      Ref.set t.size (Ref.get t.size - 1);
+      Array.set t.futur 0 next;
+      Some v
+end
+
+include (Make
+           (struct type t = unit end)
+           (struct
+             type 'a t = 'a array
+             let create () i v = Array.create ~len:i v
+             let get = Array.get
+             let set = Array.set
+           end)
+           (struct
+             type 'a t = 'a ref
+             let create () v = ref v
+             let get = Ref.(!)
+             let set = Ref.(:=)
+           end))
diff --git a/src_colibri2/popop_lib/TimeWheel.mli b/src_colibri2/popop_lib/TimeWheel.mli
new file mode 100644
index 000000000..9ea639639
--- /dev/null
+++ b/src_colibri2/popop_lib/TimeWheel.mli
@@ -0,0 +1,46 @@
+(** Time Wheel *)
+
+(** Allows to add timestamp in the futur and get the next timestamp.
+
+    It should be (but not proved) amortized O(log(offset)) for adding an event to
+    an offset in the futur and O(1) amortized to get the next elements
+*)
+
+module type S = sig
+
+  type 'a t
+  type context
+
+  val create: context -> 'a t
+
+
+  val add: 'a t -> 'a -> int -> unit
+  (** [add t v offset] add the event v at the given offset in the futur *)
+
+  val next: 'a t -> 'a option
+
+
+  val current_time: 'a t -> int
+  val size: 'a t -> int
+
+end
+
+
+include (S with type context := unit)
+
+
+module Make
+    (Context: sig type t end)
+    (Array:
+     sig
+       type 'a t
+       val create: Context.t -> int -> 'a -> 'a t
+       val get: 'a t -> int -> 'a
+       val set: 'a t -> int -> 'a -> unit
+     end)
+    (Ref:sig
+       type 'a t
+       val create: Context.t -> 'a -> 'a t
+       val get: 'a t -> 'a
+       val set: 'a t -> 'a -> unit
+     end) : S with type context := Context.t
diff --git a/src_colibri2/popop_lib/intmap.mli b/src_colibri2/popop_lib/intmap.mli
index 1ab848491..ff0c8fd6d 100644
--- a/src_colibri2/popop_lib/intmap.mli
+++ b/src_colibri2/popop_lib/intmap.mli
@@ -34,3 +34,6 @@ module Make(K:Map_intf.TaggedEqualType) :
 
 (** Remarks: *)
 (** translate is currently implemented using a complete reconstruction *)
+
+val hsb: int -> int
+(** highest bit set *)
diff --git a/src_colibri2/solver/scheduler.ml b/src_colibri2/solver/scheduler.ml
index a7ed6043d..2795ae7aa 100644
--- a/src_colibri2/solver/scheduler.ml
+++ b/src_colibri2/solver/scheduler.ml
@@ -45,7 +45,6 @@ exception Contradiction
 
 module Att = struct
   type t =
-    | Daemon   of int * Events.Wait.daemon_key
     | Decision of int * Egraph.choice
   type prio = float
   type db = float Node.H.t
@@ -55,13 +54,9 @@ module Att = struct
 
   let le (x:t) (xp:float) (y:t) (yp:float) =
     match x, y with
-    | Daemon (x,_)  , Daemon (y,_)   -> x <= y
     | Decision (x,_), Decision (y,_) ->
       if xp = yp then x <= y else xp >= yp (** min *)
-    | Daemon _  , Decision _ -> true
-    | Decision _, Daemon _   -> false
   let reprio _ = function
-    | Daemon _ -> 0.
     | Decision (_,choice) -> float_of_int choice.prio
 end
 
@@ -72,7 +67,7 @@ type bp_kind =
   | External
 
 type bp =
-  { pre_wakeup_daemons    : Prio.t;
+  { pre_choices    : Prio.t;
     pre_prev_scheduler_state : bp option;
     pre_backtrack_point      : Context.bp;
     kind                     : bp_kind;
@@ -80,7 +75,8 @@ type bp =
   }
 
 type t =
-  { mutable wakeup_daemons    : Prio.t;
+  { mutable choices    : Prio.t;
+    daemons : Events.Wait.daemon_key Context.TimeWheel.t;
     mutable prev_scheduler_state : bp option;
             solver_state      : Egraph.Backtrackable.t;
     mutable delayed           : Egraph.t option;
@@ -96,8 +92,8 @@ let get_t t = t.solver_state
 
 let print_level fmt t =
   let nb_dec =
-    Prio.fold (fun acc x _ -> match x with Att.Decision _ -> acc + 1 | _ -> acc)
-      0 t.wakeup_daemons in
+    Prio.fold (fun acc x _ -> match x with Att.Decision _ -> acc + 1)
+      0 t.choices in
   Format.fprintf fmt "%i (dec waiting:%i)" t.level nb_dec
 
 (* let new_handler t =
@@ -113,7 +109,8 @@ let print_level fmt t =
 
 let new_solver () =
   let context = Context.create () in
-  { wakeup_daemons = Prio.empty;
+  { choices = Prio.empty;
+    daemons = Context.TimeWheel.create (Context.creator context);
     prev_scheduler_state = None;
     solver_state = Egraph.Backtrackable.new_t (Context.creator context);
     context;
@@ -128,7 +125,7 @@ let push kind t =
     Egraph.Backtrackable.draw_graph ~force:true t.solver_state;
   Debug.dprintf2 debug_pushpop "[Scheduler] push from %a" print_level t;
   let prev =
-    { pre_wakeup_daemons    = t.wakeup_daemons;
+    { pre_choices    = t.choices;
       pre_prev_scheduler_state = t.prev_scheduler_state;
       pre_backtrack_point      = Context.bp t.context;
       kind;
@@ -142,7 +139,7 @@ let push kind t =
 let pop_to t prev =
   Debug.dprintf2 debug_pushpop "[Scheduler] pop from %a"
     print_level t;
-  t.wakeup_daemons <- prev.pre_wakeup_daemons;
+  t.choices <- prev.pre_choices;
   t.prev_scheduler_state <- prev.pre_prev_scheduler_state;
   t.level <- prev.pre_level;
   Context.pop prev.pre_backtrack_point;
@@ -163,15 +160,14 @@ let new_delayed =
       incr daemon_count;
       Debug.dprintf1 debug "[Scheduler] New possible daemon:%i"
         !daemon_count;
-      t.wakeup_daemons <-
-        Prio.insert t.decprio (Att.Daemon (!daemon_count,att))
-          t.wakeup_daemons in
+      Context.TimeWheel.add t.daemons att (Egraph.Wait.get_priority att)
+    in
     let sched_decision dec =
       incr dec_count;
       Debug.dprintf1 debug "[Scheduler] New possible decisions prio:%i"
         !dec_count;
-      t.wakeup_daemons <- Prio.insert t.decprio (Att.Decision (!dec_count,dec))
-          t.wakeup_daemons in
+      t.choices <- Prio.insert t.decprio (Att.Decision (!dec_count,dec))
+          t.choices in
     Egraph.Backtrackable.new_delayed ~sched_daemon ~sched_decision t.solver_state
 
 (*
@@ -185,7 +181,7 @@ let new_delayed =
         S.flush d;
         List.iter (fun f -> ignore (f d); S.flush d) l;
         run_until_dec t d;
-        run_dec t d t.wakeup_daemons
+        run_dec t d t.choices
           (fun d dec -> Conflict.make_decision d dec cho k)
       with S.Contradiction pexp ->
         Debug.dprintf0 debug "[Scheduler] Contradiction during apply learnt";
@@ -230,7 +226,7 @@ let rec conflict_analysis t =
       raise Contradiction
     | Some prev ->
       pop_to t prev;
-      t.wakeup_daemons <- Prio.reprio t.decprio t.wakeup_daemons;
+      t.choices <- Prio.reprio t.decprio t.choices;
       match prev.kind with
       | Choices [] -> rewind ()
       | Choices (choice::choices) ->
@@ -244,13 +240,13 @@ and try_run_dec:
   (** First we verify its the decision is at this point needed *)
   match choice.choice d with
   | Egraph.DecNo | Egraph.DecTodo [] ->
-    t.wakeup_daemons <- prio;
+    t.choices <- prio;
     d (** d can be precised by choose_decision *)
   | Egraph.DecTodo (todo::todos) ->
     (** We remove the decision it is replaced by the todos,
         we can change the interface of choice and in that case
         we want to keep the decision in the current branch *)
-    t.wakeup_daemons <- prio;
+    t.choices <- prio;
     Debug.incr stats_dec;
     Egraph.Backtrackable.delayed_stop d;
     make_choice t todo todos
@@ -266,12 +262,12 @@ and make_choice t todo todos =
 
 
 (* let rec run_until_dec t d =
- *   let act = Prio.min t.wakeup_daemons in
+ *   let act = Prio.min t.choices in
  *   match act with
  *   | Some (Att.Daemon (_,att)) -> begin
- *       let _, prio = Prio.extract_min t.wakeup_daemons in
+ *       let _, prio = Prio.extract_min t.choices in
  *       Debug.incr stats_propa;
- *       t.wakeup_daemons <- prio;
+ *       t.choices <- prio;
  *       Egraph.Backtrackable.run_daemon d att;
  *       Egraph.Backtrackable.flush d;
  *       run_until_dec t d
@@ -280,20 +276,21 @@ and make_choice t todo todos =
 
 
 let run_one_step t d =
-  let act, prio = Prio.extract_min t.wakeup_daemons in
-  match act with
-  | Att.Daemon (_,att) -> begin
+  match Context.TimeWheel.next t.daemons with
+  | Some att -> begin
       Debug.incr stats_propa;
-      t.wakeup_daemons <- prio;
       try
         Egraph.Backtrackable.run_daemon d att; d
       with Egraph.Contradiction ->
         Debug.dprintf0 debug "[Scheduler] Contradiction";
         conflict_analysis t
     end
-  | Att.Decision (_,chogen) ->
-    Debug.dprintf0 debug "[Scheduler] decision";
-    try_run_dec t d prio chogen
+  | None ->
+    let act, prio = Prio.extract_min t.choices in
+    match act with
+    | Att.Decision (_,chogen) ->
+      Debug.dprintf0 debug "[Scheduler] decision";
+      try_run_dec t d prio chogen
 
 let rec flush t d =
   try
@@ -309,9 +306,9 @@ 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 =
-    match Prio.min t.wakeup_daemons with
+    0 < Context.TimeWheel.size t.daemons ||
+    match Prio.min t.choices with
     | Some (Att.Decision _) -> not nodec
-    | Some (Att.Daemon _) -> true
     | None -> false
   in
   if run
diff --git a/src_colibri2/stdlib/context.ml b/src_colibri2/stdlib/context.ml
index 6d1ae3f24..c93ee2f71 100644
--- a/src_colibri2/stdlib/context.ml
+++ b/src_colibri2/stdlib/context.ml
@@ -406,3 +406,14 @@ module Hashtbl = struct
 
   end
 end
+
+module Array = struct
+  type 'a t = 'a Ref.t array
+  let create ctx i v = Array.init i (fun _ -> Ref.create ctx v)
+  let set t i v = Ref.set (Array.get t i) v
+  let get t i = Ref.get (Array.get t i)
+end
+
+module TimeWheel = Colibri2_popop_lib.TimeWheel.Make
+      (struct type t = creator end)
+      (Array)(Ref)
diff --git a/src_colibri2/stdlib/context.mli b/src_colibri2/stdlib/context.mli
index 5d8059fd3..02dbc240e 100644
--- a/src_colibri2/stdlib/context.mli
+++ b/src_colibri2/stdlib/context.mli
@@ -179,3 +179,27 @@ module Queue: sig
 
   val pp: ?sep:unit Fmt.t -> 'a Fmt.t -> 'a t Fmt.t
 end
+
+
+module Array: sig
+  type 'a t
+  val create: creator -> int -> 'a -> 'a t
+  val set: 'a t -> int -> 'a -> unit
+  val get: 'a t -> int -> 'a
+end
+module TimeWheel: sig
+  type 'a t
+
+  val create: creator -> 'a t
+
+
+  val add: 'a t -> 'a -> int -> unit
+  (** [add t v offset] add the event v at the given offset in the futur *)
+
+  val next: 'a t -> 'a option
+
+
+  val current_time: 'a t -> int
+
+  val size: 'a t -> int
+end
diff --git a/src_colibri2/theories/bool/boolean.ml b/src_colibri2/theories/bool/boolean.ml
index dfdcb7371..727488e80 100644
--- a/src_colibri2/theories/bool/boolean.ml
+++ b/src_colibri2/theories/bool/boolean.ml
@@ -155,7 +155,7 @@ module DaemonPropaNot = struct
       Format.fprintf fmt "%a,%a -> %a" Th.pp v Node.pp cl1 Node.pp cl2
   end
 
-  let immediate = false
+  let delayed = Some 1
   let key = Demon.Fast.create "Bool.DaemonPropaNot"
   let throttle = 100
   let wakeup d =
@@ -207,7 +207,7 @@ module DaemonPropa = struct
       | All thterm -> Format.fprintf fmt "All(%a)" ThE.pp thterm
   end
 
-  let immediate = false
+  let delayed = Some 1
   let throttle = 100
 
   let wakeup_lit d thterm watched watcher =
@@ -314,7 +314,7 @@ module DaemonInit = struct
 
   module Data = DUnit
 
-  let immediate = false
+  let delayed = Some 1
   let throttle = 100
   let wakeup d = function
     | Events.Fired.EventRegSem(thterm,()) ->
diff --git a/src_colibri2/theories/bool/equality.ml b/src_colibri2/theories/bool/equality.ml
index b6eb82684..44a554029 100644
--- a/src_colibri2/theories/bool/equality.ml
+++ b/src_colibri2/theories/bool/equality.ml
@@ -332,7 +332,7 @@ module DaemonPropa = struct
   module Data = DUnit
   type info = unit let default = ()
 
-  let immediate = false
+  let delayed = Some 1
   let wakeup d v _ev () =
     norm_dom d (ThE.index v)
 
@@ -347,7 +347,7 @@ module DaemonInit = struct
   module Data = DUnit
   type info = unit let default = ()
 
-  let immediate = true
+  let delayed = None
   let wakeup d () ev () =
     List.iter
       (function Events.Fired.EventRegSem(clsem,()) ->
@@ -422,7 +422,7 @@ module DaemonPropaITE = struct
     Egraph.register d branch;
     Egraph.merge d own branch
 
-  let immediate = false
+  let delayed = Some 1
   let throttle = 100
   let wakeup d = function
     | Events.Fired.EventValue(cond,dom,_,clsem) ->
@@ -445,7 +445,7 @@ module DaemonInitITE = struct
   module Key = DUnit
   module Data = DUnit
 
-  let immediate = false
+  let delayed = Some 1
   let throttle = 100
   let wakeup d = function
     | Events.Fired.EventRegSem(clsem,()) ->
@@ -521,7 +521,7 @@ let init_diff_to_value (type a) (type b)
       module Data = Val
       let key = key
       let throttle = 100
-      let immediate = false
+      let delayed = Some 1
 
       let wakeup d = function
         | Events.Fired.EventDom(_,_,v) ->
diff --git a/src_colibri2/theories/bool/uninterp.ml b/src_colibri2/theories/bool/uninterp.ml
index 39d803681..15569c642 100644
--- a/src_colibri2/theories/bool/uninterp.ml
+++ b/src_colibri2/theories/bool/uninterp.ml
@@ -125,7 +125,7 @@ module DaemonPropa = struct
     Demon.Key.attach d key v
       [EventChange(f,()); EventChange(g,())]
 
-  let immediate = true (** can be false *)
+  let delayed = None (** can be not None *)
   let wakeup d (nodesem:k) _ev () =
     match ThE.sem nodesem with
     | Th.Fun _ -> Demon.AliveStopped
@@ -148,7 +148,7 @@ module DaemonInit = struct
   module Data = DUnit
   type info = unit let default = ()
 
-  let immediate = false
+  let delayed = Some 1
 
   let wakeup d () ev () =
     List.iter
diff --git a/src_colibri2/theories/quantifier/quantifier.ml b/src_colibri2/theories/quantifier/quantifier.ml
index c52ad2c61..7b5fdfbe0 100644
--- a/src_colibri2/theories/quantifier/quantifier.ml
+++ b/src_colibri2/theories/quantifier/quantifier.ml
@@ -796,7 +796,8 @@ let init, attach =
             Ground.ThClosedQuantifier.pp th
             Fmt.(list ~sep:semi (Fmt.using (fun t -> t.Trigger.pat) Pattern.pp))
             triggers;
-          List.iter (add_trigger d) triggers)
+          List.iter (add_trigger d) triggers
+      (* Todo match with current terms *))
 
 let quantifier_registered d th = attach d (Ground.ThClosedQuantifier.node th) th
 
@@ -833,8 +834,11 @@ let add_info_on_ground_terms d thg =
   Context.Queue.iter (fun trg -> Trigger.match_ d trg res) trgs
 
 let init d =
-  Demon.Fast.register_change_repr_daemon ~name:"Quantifier.merge" merge d;
-  Demon.Fast.register_init_daemon ~name:"Quantifier.new_quantifier"
+  let delayed = Some 10 in
+  Demon.Fast.register_change_repr_daemon ~delayed ~name:"Quantifier.merge" merge
+    d;
+  Demon.Fast.register_init_daemon ~delayed:(Some 1)
+    ~name:"Quantifier.new_quantifier"
     (module Ground.ThClosedQuantifier)
     quantifier_registered d;
   Ground.register_converter d add_info_on_ground_terms;
-- 
GitLab