1dfb6a8614327aac3f0b80ef37447d2529ece831
[tatoo.git] / src / run.ml
1 (***********************************************************************)
2 (*                                                                     *)
3 (*                               TAToo                                 *)
4 (*                                                                     *)
5 (*                   Lucca Hirschi, LRI UMR8623                        *)
6 (*                   Université Paris-Sud & CNRS                       *)
7 (*                                                                     *)
8 (*  Copyright 2010-2012 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
18 module Node  =
19 struct
20   type t = int
21   let hash n = n
22   let compare = (-)
23   let equal = (=)
24 end
25   
26 module NodeHash = Hashtbl.Make (Node)
27   
28 type t = (StateSet.t*StateSet.t) NodeHash.t
29 (** Map from nodes to query and recognizing states *)
30 (* Note that we do not consider nil nodes *)
31
32 exception Oracle_fail
33 exception Over_max_fail
34 exception Max_fail
35
36
37 (* Hash Consign modules *)
38
39 module type Oracle_fixpoint =
40 sig
41   type t = StateSet.t*StateSet.t*StateSet.t*((StateSet.elt*Formula.t) list)*QName.t
42   val equal : t -> t -> bool
43   val hash : t -> int
44 end
45
46 type dStateS = StateSet.t*StateSet.t
47 module type Run_fixpoint =
48 sig
49   type t = dStateS*dStateS*dStateS*(State.t*Formula.t) list*QName.t
50   val equal : t -> t -> bool
51   val hash : t -> int
52 end
53     
54 module Oracle_fixpoint : Oracle_fixpoint = struct
55   type t =
56       StateSet.t*StateSet.t*StateSet.t*((StateSet.elt*Formula.t) list)*QName.t
57   let equal (s,l,r,list,t) (s',l',r',list',t') = StateSet.equal s s' &&
58     StateSet.equal l l' && StateSet.equal r r' && QName.equal t t'
59   let hash (s,l,r,list,t) =
60     HASHINT4(StateSet.hash s, StateSet.hash l, StateSet.hash r, QName.hash t)
61 end
62   
63 let dequal (x,y) (x',y') = StateSet.equal x x' && StateSet.equal y y'
64 let dhash (x,y) = HASHINT2(StateSet.hash x, StateSet.hash y)
65 module Run_fixpoint : Run_fixpoint = struct
66   type t = dStateS*dStateS*dStateS*(State.t*Formula.t) list*QName.t
67   let equal (s,l,r,list,t) (s',l',r',list',t') = dequal s s' &&
68     dequal l l' && dequal r r' && QName.equal t t'
69   let hash (s,l,r,list,t) =
70     HASHINT4(dhash s, dhash l, dhash r, QName.hash t)
71 end
72
73 module HashOracle = Hashtbl.Make(Oracle_fixpoint)
74 module HashRun = Hashtbl.Make(Run_fixpoint)
75
76 (* Mapped sets for leaves *)
77 let map_leaf asta = (Asta.bot_states_s asta, StateSet.empty)
78
79 (* Build the Oracle *)
80 let rec bu_oracle asta run tree tnode hashOracle=
81   let node = Tree.preorder tree tnode in
82   if Tree.is_leaf tree tnode
83   then
84     if tnode == Tree.nil
85     then ()
86     else NodeHash.add run node (map_leaf asta)
87   else
88     let tfnode = Tree.first_child_x tree tnode
89     and tnnode = Tree.next_sibling tree tnode in
90     let fnode,nnode =                            (* their preorders *)
91       (Tree.preorder tree tfnode, Tree.preorder tree tnnode) in
92     begin
93       bu_oracle asta run tree tfnode hashOracle;
94       bu_oracle asta run tree tnnode hashOracle;
95       (* add states which satisfy a transition *)
96       let rec result set qfr qnr flag = function
97         | [] -> set,flag
98         | (q,form) :: tl ->
99           if Formula.eval_form (set,qfr,qnr) form (* evaluates the formula*)
100           then
101             if StateSet.mem q set
102             then result set qfr qnr 0 tl
103             else result (StateSet.add q set) qfr qnr 1 tl
104           else result set qfr qnr 0 tl in
105       (* compute the fixed point of states of node *)
106       let rec fix_point set_i qfr qnr list_tr t =
107         try HashOracle.find hashOracle (set_i, qfr, qnr, list_tr, t)
108         with _ -> 
109           let set,flag = result set_i qfr qnr 0 list_tr in
110           HashOracle.add hashOracle (set_i,qfr,qnr,list_tr,t) (set); (* todo: Think about this position *)
111           if flag = 0
112           then set
113           else fix_point set qfr qnr list_tr t in
114       let q_rec n =                     (* compute the set for child/sibling *)
115         try NodeHash.find run n
116         with Not_found -> map_leaf asta in
117       let (_,qfr),(_,qnr) = q_rec fnode,q_rec nnode (* computed in rec call *)
118       and lab = Tree.tag tree tnode in
119       let _,list_tr = Asta.transitions_lab asta lab in (*only reco. tran.*)     
120      NodeHash.add run node (StateSet.empty,
121                             fix_point StateSet.empty qfr qnr list_tr lab)
122     end
123
124 (* Build the over-approx. of the maximal run *)
125 let rec bu_over_max asta run tree tnode hashOver =
126   if (Tree.is_leaf tree tnode)      (* BU_oracle has already created the map *)
127   then
128     ()
129   else
130     let tfnode = Tree.first_child_x tree tnode
131     and tnnode = Tree.next_sibling tree tnode in
132     begin
133       bu_over_max asta run tree tfnode hashOver;
134       bu_over_max asta run tree tnnode hashOver;
135       let (fnode,nnode) =
136         (Tree.preorder tree tfnode, Tree.preorder tree tnnode)
137       and node = Tree.preorder tree tnode in          
138       let q_rec n =
139         try NodeHash.find run n
140         with Not_found -> map_leaf asta in
141       let qf,qn = q_rec fnode,q_rec nnode in
142       let lab = Tree.tag tree tnode in
143       let list_tr,_ = Asta.transitions_lab asta lab (* only take query st. *)
144       and _,resultr = try NodeHash.find run node
145         with _ -> raise Over_max_fail in      
146       let rec result set qf qn flag list_tr = function 
147         | [] -> if flag = 0 then set else result set qf qn 0 list_tr list_tr
148         | (q,form) :: tl ->
149           if StateSet.mem q set
150           then result set qf qn 0 list_tr tl
151           else if Formula.infer_form (set,resultr) qf qn form
152           then result (StateSet.add q set) qf qn 1 list_tr tl
153           else result set qf qn 0 list_tr tl in
154       let result_set () =
155         try HashRun.find hashOver ((StateSet.empty,resultr),qf,qn,list_tr,lab)
156         with _ -> let res = result StateSet.empty qf qn 0 list_tr list_tr in
157                   HashRun.add hashOver
158                     ((StateSet.empty,resultr), qf,qn,list_tr,lab) res;
159                   res in
160       (* we keep the old recognizing states set *)
161       NodeHash.replace run node (result_set(), resultr)
162     end
163
164
165 (* Build the maximal run *)
166 let rec tp_max asta run tree tnode hashMax =
167   if (Tree.is_leaf tree tnode)      (* BU_oracle has already created the map *)
168   then
169     ()
170   else
171     let node = Tree.preorder tree tnode
172     and tfnode = Tree.first_child_x tree tnode
173     and tnnode = Tree.next_sibling tree tnode in
174     let (fnode,nnode) =
175       (Tree.preorder tree tfnode, Tree.preorder tree tnnode) in
176     begin
177       if tnode == Tree.root tree        (* we must intersect with top states *)
178       then let setq,_  = try NodeHash.find run node
179         with _ -> raise Max_fail in
180            NodeHash.replace run node
181              ((StateSet.inter (Asta.top_states_s asta) setq),StateSet.empty)
182       else ();
183       let q_rec n =
184         try NodeHash.find run n
185         with Not_found -> map_leaf asta in
186       let qf,qn = q_rec fnode,q_rec nnode in
187       let lab = Tree.tag tree tnode in
188       let list_tr,_ = Asta.transitions_lab asta lab in (* only take query. *)
189       let (self_q,self_r) = try NodeHash.find run node
190         with Not_found -> raise Max_fail in
191
192       (* We must compute again accepting states from self transitions since
193          previous calls of tp_max may remove them *)
194       let rec result_q self_q queue = function (* for initializing the queue *)
195         | [] -> self_q,queue
196         | (q,form) :: tl ->
197           if (StateSet.mem q self_q)
198           then begin 
199             let q_cand,_,_ = Formula.st form in
200             StateSet.iter (fun x -> Queue.push x queue) q_cand;
201             result_q (StateSet.add q self_q) queue tl;
202           end
203           else result_q self_q queue tl
204       and result_st_q self_q queue flag = function (*for computing the fixed p*)
205         | [] -> flag,queue
206         | form :: tl ->
207           if Formula.infer_form (self_q,self_r) qf qn form
208           then begin 
209             let q_cand,_,_ = Formula.st form in
210             StateSet.iter (fun x -> Queue.push x queue) q_cand;
211             result_st_q self_q queue 1 tl;
212           end
213           else result_st_q self_q queue flag tl in
214       let rec comp_acc_self self_q_i queue = (* compute the fixed point *)
215         if Queue.is_empty queue              (* todo: to be hconsigned? *)
216         then self_q_i
217         else
218           let q = Queue.pop queue in
219           let list_q,_ = Asta.transitions_st_lab asta q lab in
220           let flag,queue = result_st_q self_q_i queue 0 list_q in
221           let self_q = if flag = 1 then StateSet.add q self_q_i else self_q_i in
222           comp_acc_self self_q queue in
223       
224       let self,queue_init = result_q self_q (Queue.create()) list_tr in
225       let self_q = comp_acc_self self_q queue_init in
226       NodeHash.replace run node (self_q,self_r);
227       (* From now, the correct set of states is mapped to (self) node! *)
228       let rec result self qf qn = function
229         | [] -> []
230         | (q,form) :: tl ->
231           if (StateSet.mem q (fst self)) && (* infers & trans. can start here *)
232             (Formula.infer_form self qf qn form)
233           then form :: (result self qf qn tl)
234           else result self qf qn tl in      
235       let list_form =
236         try HashRun.find hashMax ((self_q,self_r),qf,qn,list_tr,lab) 
237         with _ -> let res = result (self_q,self_r) qf qn list_tr in
238                   HashRun.add hashMax ((self_q,self_r),qf,qn,list_tr,lab) res;
239                   res in
240       (* compute states occuring in transition candidates *)
241       let rec add_st (ql,qr) = function
242         | [] -> ql,qr
243         | f :: tl -> let sqs,sql,sqr = Formula.st f in
244                      let ql' = StateSet.union sql ql
245                      and qr' = StateSet.union sqr qr in
246                      add_st (ql',qr') tl in
247       let ql,qr = add_st (StateSet.empty, StateSet.empty) list_form in
248       let qfq,qfr = try NodeHash.find run fnode
249         with | _ -> map_leaf asta
250       and qnq,qnr = try NodeHash.find run nnode
251         with | _ -> map_leaf asta in
252       begin
253         if tfnode == Tree.nil || Tree.is_attribute tree tnode
254         then ()
255         else NodeHash.replace run fnode (StateSet.inter qfq ql,qfr);
256         if tnnode == Tree.nil || Tree.is_attribute tree tnode
257         then ()
258         else NodeHash.replace run nnode (StateSet.inter qnq qr,qnr);
259         (* indeed we delete all states from self transitions!  *)
260         tp_max asta run tree tfnode hashMax;
261         tp_max asta run tree tnnode hashMax;
262       end;
263     end
264         
265 let compute tree asta =
266   let flag = 2 in                       (* debug  *)
267   let size_tree = 10000 in              (* todo (Tree.size ?) *)
268   let size_hcons_O = 1000 in            (* todo size Hashtbl *)
269   let size_hcons_M = 1000 in            (* todo size Hashtbl *)
270   let map = NodeHash.create size_tree in
271   let hashOracle = HashOracle.create(size_hcons_O) in
272   bu_oracle asta map tree (Tree.root tree) hashOracle;
273   HashOracle.clear hashOracle;
274   if flag > 0 then begin
275     let hashOver = HashRun.create(size_hcons_M) in
276     let hashMax = HashRun.create(size_hcons_M) in
277     bu_over_max asta map tree (Tree.root tree) hashOver;
278     if flag = 2
279     then
280       tp_max asta map tree (Tree.root tree) hashMax
281     else ();
282     HashRun.clear hashOver;
283     HashRun.clear hashMax;
284   end
285   else ();
286   map
287
288 let selected_nodes tree asta =
289   let run = compute tree asta in
290   NodeHash.fold
291     (fun key set acc ->
292       if not(StateSet.is_empty
293                (StateSet.inter (fst set) (Asta.selec_states asta)))
294       then key :: acc
295       else acc)
296     run []
297
298 let print fmt run =
299   let print_d_set fmt (s_1,s_2) =
300     Format.fprintf fmt "(%a,%a)"
301       StateSet.print s_1 StateSet.print s_2 in
302   let print_map fmt run =
303     let pp = Format.fprintf fmt in
304     if NodeHash.length run = 0
305     then Format.fprintf fmt "ø"
306     else
307       NodeHash.iter (fun cle set -> pp "|  %i->%a  @ " cle print_d_set set)
308         run in
309   let print_box fmt run =
310     let pp = Format.fprintf fmt in
311     pp "@[<hov 0>@.  # Mapping:@.   @[<hov 0>%a@]@]"
312       print_map run
313   in
314   Format.fprintf fmt "@[<hov 0>##### RUN #####@, %a@]@." print_box run