Cosmetic changes (truncate long lines, remove trailing spaces…)
[tatoo.git] / src / run.ml
1 (***********************************************************************)
2 (*                                                                     *)
3 (*                               TAToo                                 *)
4 (*                                                                     *)
5 (*                     Kim Nguyen, LRI UMR8623                         *)
6 (*                   UniversitĂ© Paris-Sud & CNRS                       *)
7 (*                                                                     *)
8 (*  Copyright 2010-2013 UniversitĂ© Paris-Sud and Centre National de la *)
9 (*  Recherche Scientifique. All rights reserved.  This file is         *)
10 (*  distributed under the terms of the GNU Lesser General Public       *)
11 (*  License, with the special exception on linking described in file   *)
12 (*  ../LICENSE.                                                        *)
13 (*                                                                     *)
14 (***********************************************************************)
15
16 INCLUDE "utils.ml"
17 INCLUDE "debug.ml"
18
19 open Format
20 open Misc
21
22 type stats = { run : int;
23                tree_size : int;
24                cache2_access : int;
25                cache2_hit : int;
26                cache5_access : int;
27                cache5_hit : int;
28              }
29
30 let cache2_hit = ref 0
31 let cache2_access = ref 0
32 let cache5_hit = ref 0
33 let cache5_access = ref 0
34 let reset_stat_counters () =
35   cache2_hit := 0;
36   cache2_access := 0;
37   cache5_hit := 0;
38   cache5_access := 0
39
40
41 module Make (T : Tree.S) =
42  struct
43
44    module NodeSummary =
45    struct
46      (* Pack into an integer the result of the is_* and has_ predicates
47         for a given node *)
48      type t = int
49      let dummy = -1
50     (*
51       4444444444443210
52       4 -> kind
53       3 -> is_left
54       2 -> is_right
55       1 -> has_left
56       0 -> has_right
57     *)
58
59      let has_right (s : t) : bool =
60        Obj.magic (s land 1)
61
62      let has_left (s : t) : bool =
63        Obj.magic ((s lsr 1) land 1)
64
65      let is_right (s : t) : bool =
66        Obj.magic ((s lsr 2) land 1)
67
68      let is_left (s : t) : bool =
69        Obj.magic ((s lsr 3) land 1)
70
71      let kind (s : t) : Tree.NodeKind.t =
72        Obj.magic (s lsr 4)
73
74      let make is_left is_right has_left has_right kind =
75        ((Obj.magic kind) lsl 4) lor
76          ((int_of_bool is_left) lsl 3) lor
77          ((int_of_bool is_right) lsl 2) lor
78          ((int_of_bool has_left) lsl 1) lor
79          (int_of_bool has_right)
80
81    end
82
83    type node_status = {
84      rank : int;
85      sat : StateSet.t;  (* States that are satisfied at the current node *)
86      todo : StateSet.t; (* States that remain to be proven *)
87         (* For every node_status and automaton a,
88            a.states - (sat U todo) = unsat *)
89      summary : NodeSummary.t; (* Summary of the shape of the node *)
90    }
91 (* Describe what is kept at each node for a run *)
92
93    module NodeStatus =
94      struct
95        include Hcons.Make(struct
96          type t = node_status
97          let equal c d =
98            c == d ||
99              c.rank == d.rank &&
100              c.sat == d.sat &&
101              c.todo == d.todo &&
102              c.summary == d.summary
103
104          let hash c =
105            HASHINT4(c.rank,
106                     (c.sat.StateSet.id :> int),
107                     (c.todo.StateSet.id :> int),
108                     c.summary)
109        end
110        )
111        let print ppf s =
112          fprintf ppf
113            "{ rank: %i; sat: %a; todo: %a; summary: _ }"
114            s.node.rank
115            StateSet.print s.node.sat
116            StateSet.print s.node.todo
117      end
118
119    let dummy_status =
120      NodeStatus.make {
121        rank = -1;
122        sat = StateSet.empty;
123        todo = StateSet.empty;
124        summary = NodeSummary.dummy;
125      }
126
127
128    type run = {
129      tree : T.t ;
130      (* The argument of the run *)
131      auto : Ata.t;
132      (* The automaton to be run *)
133      status : NodeStatus.t array;
134      (* A mapping from node preorders to NodeStatus *)
135      unstable : Bitvector.t;
136      (* A bitvector remembering whether a subtree is stable *)
137      mutable redo : bool;
138      (* A boolean indicating whether the run is incomplete *)
139      mutable pass : int;
140      (* The number of times this run was updated *)
141      mutable cache2 : Ata.Formula.t Cache.N2.t;
142      (* A cache from states * label to list of transitions *)
143      mutable cache5 : NodeStatus.t Cache.N5.t;
144    }
145
146    let pass r = r.pass
147    let stable r = not r.redo
148    let auto r = r.auto
149    let tree r = r.tree
150
151
152    let dummy_form = Ata.Formula.stay State.dummy
153
154    let make auto tree =
155      let len = T.size tree in
156      {
157        tree = tree;
158        auto = auto;
159        status = Array.create len dummy_status;
160        unstable = Bitvector.create ~init:true len;
161        redo = true;
162        pass = 0;
163        cache2 = Cache.N2.create dummy_form;
164        cache5 = Cache.N5.create dummy_status;
165      }
166
167    let get_status a i =
168      if i < 0 then dummy_status else Array.get a i
169
170    let unsafe_get_status a i =
171      if i < 0 then dummy_status else Array.unsafe_get a i
172
173 IFDEF HTMLTRACE
174   THEN
175 DEFINE IFTRACE(e) = (e)
176   ELSE
177 DEFINE IFTRACE(e) = ()
178 END
179
180    let html tree node i config msg =
181      let config = config.NodeStatus.node in
182      Html.trace ~msg:msg
183        (T.preorder tree node) i
184        config.todo
185        config.sat
186
187
188
189    let debug msg tree node i config =
190      let config = config.NodeStatus.node in
191      eprintf
192        "DEBUG:%s node: %i\nsat: %a\ntodo: %a\nround: %i\n"
193        msg
194        (T.preorder tree node)
195        StateSet.print config.sat
196        StateSet.print config.todo
197        i
198
199    let get_form cache2 auto tag q =
200      let phi =
201        incr cache2_access;
202        Cache.N2.find cache2 (tag.QName.id :> int) (q :> int)
203      in
204      if phi == dummy_form then
205        let phi = Ata.get_form auto tag q in
206        let () =
207          Cache.N2.add
208            cache2
209            (tag.QName.id :> int)
210            (q :> int) phi
211        in phi
212      else begin
213        incr cache2_hit;
214        phi
215      end
216
217    type trivalent = False | True | Unknown
218    let of_bool = function false -> False | true -> True
219    let or_ t1 t2 =
220      match t1 with
221        False -> t2
222      | True -> True
223      | Unknown -> if t2 == True then True else Unknown
224
225    let and_ t1 t2 =
226      match t1 with
227        False -> False
228      | True -> t2
229      | Unknown -> if t2 == False then False else Unknown
230
231  (* Define as macros to get lazyness *)
232 DEFINE OR_(t1,t2) =
233      match t1 with
234        False -> (t2)
235      | True -> True
236      | Unknown -> if (t2) == True then True else Unknown
237
238 DEFINE AND_(t1,t2) =
239      match t1 with
240        False -> False
241      | True -> (t2)
242      | Unknown -> if (t2) == False then False else Unknown
243
244
245    let eval_form phi fcs nss ps ss summary =
246      let open Ata in
247          let rec loop phi =
248            begin match Formula.expr phi with
249            | Boolean.False -> False
250            | Boolean.True -> True
251            | Boolean.Atom (a, b) ->
252                begin
253                  let open NodeSummary in
254                      match a.Atom.node with
255                      | Move (m, q) ->
256                          let { NodeStatus.node = n_sum; _ } as sum =
257                            match m with
258                              `First_child -> fcs
259                            | `Next_sibling -> nss
260                            | `Parent | `Previous_sibling -> ps
261                            | `Stay -> ss
262                          in
263                          if sum == dummy_status
264                            || n_sum.rank < ss.NodeStatus.node.rank
265                            || StateSet.mem q n_sum.todo then
266                            Unknown
267                          else
268                            of_bool (b == StateSet.mem q n_sum.sat)
269                      | Is_first_child -> of_bool (b == is_left summary)
270                      | Is_next_sibling -> of_bool (b == is_right summary)
271                      | Is k -> of_bool (b == (k == kind summary))
272                      | Has_first_child -> of_bool (b == has_left summary)
273                      | Has_next_sibling -> of_bool (b == has_right summary)
274                end
275            | Boolean.And(phi1, phi2) -> AND_ (loop phi1, loop phi2)
276            | Boolean.Or (phi1, phi2) -> OR_ (loop phi1, loop phi2)
277            end
278          in
279          loop phi
280
281
282    let eval_trans_aux auto cache2 tag fcs nss ps old_status =
283      let { sat = old_sat;
284            todo = old_todo;
285            summary = old_summary } as os_node = old_status.NodeStatus.node
286      in
287      let sat, todo =
288        StateSet.fold (fun q ((a_sat, a_todo) as acc) ->
289          let phi =
290            get_form cache2 auto tag q
291          in
292
293          let v = eval_form phi fcs nss ps old_status old_summary in
294          match v with
295            True -> StateSet.add q a_sat, a_todo
296          | False -> acc
297          | Unknown -> a_sat, StateSet.add q a_todo
298        ) old_todo (old_sat, StateSet.empty)
299      in
300      if old_sat != sat || old_todo != todo then
301        NodeStatus.make { os_node with sat; todo }
302      else old_status
303
304
305    let eval_trans auto cache2 cache5 tag fcs nss ps ss =
306      let rec loop old_status =
307        let new_status =
308          eval_trans_aux auto cache2 tag fcs nss ps old_status
309        in
310        if new_status == old_status then old_status else loop new_status
311      in
312      let fcsid = (fcs.NodeStatus.id :> int) in
313      let nssid = (nss.NodeStatus.id :> int) in
314      let psid = (ps.NodeStatus.id :> int) in
315      let ssid = (ss.NodeStatus.id :> int) in
316      let tagid = (tag.QName.id :> int) in
317      let res = Cache.N5.find cache5 tagid ssid fcsid nssid psid in
318      incr cache5_access;
319      if res != dummy_status then begin incr cache5_hit; res end
320      else let new_status = loop ss in
321           Cache.N5.add cache5 tagid ssid fcsid nssid psid new_status;
322           new_status
323
324
325
326   let top_down run =
327     let i = run.pass in
328     let tree = run.tree in
329     let auto = run.auto in
330     let status = run.status in
331     let cache2 = run.cache2 in
332     let cache5 = run.cache5 in
333     let unstable = run.unstable in
334     let states_by_rank = Ata.get_states_by_rank auto in
335     let init_todo = states_by_rank.(i) in
336     let rec loop node =
337       let node_id = T.preorder tree node in
338       if node == T.nil (*|| not (Bitvector.get unstable node_id)*) then false
339       else begin
340         let parent = T.parent tree node in
341         let fc = T.first_child tree node in
342         let fc_id = T.preorder tree fc in
343         let ns = T.next_sibling tree node in
344         let ns_id = T.preorder tree ns in
345         let tag = T.tag tree node in
346         (* We enter the node from its parent *)
347
348         let status0 =
349           let c = unsafe_get_status status node_id in
350           if c.NodeStatus.node.rank < i then
351             (* first time we visit the node during this run *)
352             NodeStatus.make
353               { rank = i;
354                 sat = c.NodeStatus.node.sat;
355                 todo = init_todo;
356                 summary = let summary = c.NodeStatus.node.summary
357                           in
358                           if summary != NodeSummary.dummy then summary
359                           else
360                             NodeSummary.make
361                               (node == T.first_child tree parent) (* is_left *)
362                               (node == T.next_sibling tree parent) (* is_right *)
363                               (fc != T.nil) (* has_left *)
364                               (ns != T.nil) (* has_right *)
365                               (T.kind tree node) (* kind *)
366               }
367           else c
368         in
369         IFTRACE(html tree node _i status0 "Entering node");
370         (* get the node_statuses for the first child, next sibling and parent *)
371         let ps = unsafe_get_status status (T.preorder tree parent) in
372         let fcs = unsafe_get_status status fc_id in
373         let nss = unsafe_get_status status ns_id in
374         (* evaluate the transitions with all this statuses *)
375         let status1 =
376           if status0.NodeStatus.node.todo == StateSet.empty then status0
377           else begin
378             let status1 = eval_trans auto cache2 cache5 tag fcs nss ps status0 in
379             IFTRACE(html tree node _i status1 "Updating transitions");
380           (* update the cache if the status of the node changed *)
381             if status1 != status0 then status.(node_id) <- status1;
382             status1
383           end
384         in
385         (* recursively traverse the first child *)
386         let unstable_left = loop fc in
387         (* here we re-enter the node from its first child,
388            get the new status of the first child *)
389         let fcs1 = unsafe_get_status status fc_id in
390         (* update the status *)
391         let status2 =
392           if status1.NodeStatus.node.todo == StateSet.empty then status1
393           else begin
394             let status2 = eval_trans auto cache2 cache5 tag fcs1 nss ps status1 in
395             IFTRACE(html tree node _i status2
396                       "Updating transitions (after first-child)");
397             if status2 != status1 then status.(node_id) <- status2;
398             status2
399           end
400         in
401         let unstable_right = loop ns in
402         let nss1 = unsafe_get_status status ns_id in
403         let status3 =
404           if status2.NodeStatus.node.todo == StateSet.empty then status2
405           else begin
406             let status3 = eval_trans auto cache2 cache5 tag fcs1 nss1 ps status2 in
407             IFTRACE(html tree node _i status3
408                       "Updating transitions (after next-sibling)");
409           if status3 != status2 then status.(node_id) <- status3;
410           status3
411         end
412         in
413         let unstable_self =
414           (* if either our left or right child is unstable or if we
415              still have transitions pending, the current node is
416              unstable *)
417           unstable_left
418           || unstable_right
419           || StateSet.empty != status3.NodeStatus.node.todo
420         in
421         Bitvector.unsafe_set unstable node_id unstable_self;
422         IFTRACE((if not unstable_self then
423             Html.finalize_node
424               node_id
425               _i
426               Ata.(StateSet.intersect status3.NodeStatus.node.sat
427                      (get_selecting_states auto))));
428         unstable_self
429       end
430     in
431     run.redo <- loop (T.root tree);
432     run.pass <- run.pass + 1
433
434
435   let get_results run =
436     let cache = run.status in
437     let auto = run.auto in
438     let tree = run.tree in
439     let rec loop node acc =
440       if node == T.nil then acc
441       else
442         let acc0 = loop (T.next_sibling tree node) acc in
443         let acc1 = loop (T.first_child tree node) acc0 in
444
445         if Ata.(
446           StateSet.intersect
447             cache.(T.preorder tree node).NodeStatus.node.sat
448             (get_selecting_states auto)) then node::acc1
449         else acc1
450     in
451     loop (T.root tree) []
452
453
454   let get_full_results run =
455     let cache = run.status in
456     let auto = run.auto in
457     let tree = run.tree in
458     let res_mapper = Hashtbl.create MED_H_SIZE in
459     let () =
460       StateSet.iter
461         (fun q -> Hashtbl.add res_mapper q [])
462         (Ata.get_selecting_states auto)
463     in
464     let dummy = [ T.nil ] in
465     let res_mapper = Cache.N1.create dummy in
466     let () =
467       StateSet.iter
468         (fun q -> Cache.N1.add res_mapper (q :> int) [])
469         (Ata.get_selecting_states auto)
470     in
471     let rec loop node =
472       if node != T.nil then
473         let () = loop (T.next_sibling tree node) in
474         let () = loop (T.first_child tree node) in
475         StateSet.iter
476           (fun q ->
477             let res = Cache.N1.find res_mapper (q :> int) in
478             if res != dummy then
479               Cache.N1.add res_mapper (q :> int) (node::res)
480           )
481           cache.(T.preorder tree node).NodeStatus.node.sat
482     in
483     loop (T.root tree);
484     (StateSet.fold_right
485        (fun q acc -> (q, Cache.N1.find res_mapper (q :> int))::acc)
486        (Ata.get_selecting_states auto) [])
487
488
489   let prepare_run run list =
490     let tree = run.tree in
491     let auto = run.auto in
492     let status = run.status in
493     List.iter (fun node ->
494       let parent = T.parent tree node in
495       let fc = T.first_child tree node in
496       let ns = T.next_sibling tree node in
497       let status0 =
498         NodeStatus.make
499           { rank = 0;
500             sat = Ata.get_starting_states auto;
501             todo =
502               StateSet.diff (Ata.get_states auto) (Ata.get_starting_states auto);
503             summary = NodeSummary.make
504               (node == T.first_child tree parent) (* is_left *)
505               (node == T.next_sibling tree parent) (* is_right *)
506               (fc != T.nil) (* has_left *)
507               (ns != T.nil) (* has_right *)
508               (T.kind tree node) (* kind *)
509           }
510       in
511       let node_id = T.preorder tree node in
512       status.(node_id) <- status0) list
513
514   let tree_size = ref 0
515   let pass = ref 0
516   let compute_run auto tree nodes =
517     pass := 0;
518     tree_size := T.size tree;
519     let run = make auto tree in
520     prepare_run run nodes;
521     for i = 0 to Ata.get_max_rank auto do
522       top_down run
523     done;
524     pass := Ata.get_max_rank auto + 1;
525     IFTRACE(Html.gen_trace auto (module T : Tree.S with type t = T.t) tree);
526
527     run
528
529   let full_eval auto tree nodes =
530     let r = compute_run auto tree nodes in
531     get_full_results r
532
533   let eval auto tree nodes =
534     let r = compute_run auto tree nodes in
535     get_results r
536
537   let stats () = {
538     tree_size = !tree_size;
539     run = !pass;
540     cache2_access = !cache2_access;
541     cache2_hit = !cache2_hit;
542     cache5_access = !cache5_access;
543     cache5_hit = !cache5_hit;
544   }
545
546 end