Fst fixpoint is now hconsed.
[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);
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 hashRun =
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 hashRun;
134       bu_over_max asta run tree tnnode hashRun;
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 flag = function
147         | [] -> if flag = 0 then set else result set 0 list_tr
148         | (q,form) :: tl ->
149           if StateSet.mem q set
150           then result set 0 tl
151           else if Formula.infer_form (set,resultr) qf qn form
152                then result (StateSet.add q set) 1 tl
153                else result set 0 tl in
154       let result_set = result StateSet.empty 0 list_tr in
155       (* we keep the old recognizing states set *)
156       NodeHash.replace run node (result_set, resultr)
157     end
158
159
160 (* Build the maximal run *)
161 let rec tp_max asta run tree tnode hashRun =
162   if (Tree.is_leaf tree tnode)      (* BU_oracle has already created the map *)
163   then
164     ()
165   else
166     let node = Tree.preorder tree tnode
167     and tfnode = Tree.first_child_x tree tnode
168     and tnnode = Tree.next_sibling tree tnode in
169     let (fnode,nnode) =
170       (Tree.preorder tree tfnode, Tree.preorder tree tnnode) in
171     begin
172       if tnode == Tree.root tree        (* we must intersect with top states *)
173       then let setq,_  = try NodeHash.find run node
174         with _ -> raise Max_fail in
175            NodeHash.replace run node
176              ((StateSet.inter (Asta.top_states_s asta) setq),StateSet.empty)
177       else ();
178       let q_rec n =
179         try NodeHash.find run n
180         with Not_found -> map_leaf asta in
181       let qf,qn = q_rec fnode,q_rec nnode in
182       let lab = Tree.tag tree tnode in
183       let list_tr,_ = Asta.transitions_lab asta lab in (* only take query. *)
184       let (self_q,self_r) = try NodeHash.find run node
185         with Not_found -> raise Max_fail in
186
187       (* We must compute again accepting states from self transitions since
188          previous calls of tp_max may remove them *)
189       let rec result_q self_q queue = function (* for initializing the queue *)
190         | [] -> self_q,queue
191         | (q,form) :: tl ->
192           if (StateSet.mem q self_q)
193           then begin 
194             let q_cand,_,_ = Formula.st form in
195             StateSet.iter (fun x -> Queue.push x queue) q_cand;
196             result_q (StateSet.add q self_q) queue tl;
197           end
198           else result_q self_q queue tl
199       and result_st_q self_q queue flag = function (*for computing the fixed p*)
200         | [] -> flag,queue
201         | form :: tl ->
202           if Formula.infer_form (self_q,self_r) qf qn form
203           then begin 
204             let q_cand,_,_ = Formula.st form in
205             StateSet.iter (fun x -> Queue.push x queue) q_cand;
206             result_st_q self_q queue 1 tl;
207           end
208           else result_st_q self_q queue flag tl in
209       let rec comp_acc_self self_q_i queue = (* compute the fixed point *)
210         if Queue.is_empty queue
211         then self_q_i
212         else
213           let q = Queue.pop queue in
214           let list_q,_ = Asta.transitions_st_lab asta q lab in
215           let flag,queue = result_st_q self_q_i queue 0 list_q in
216           let self_q = if flag = 1 then StateSet.add q self_q_i else self_q_i in
217           comp_acc_self self_q queue in
218       
219       let self,queue_init = result_q self_q (Queue.create()) list_tr in
220       let self_q = comp_acc_self self_q queue_init in
221       NodeHash.replace run node (self_q,self_r);
222       (* From now, the correct set of states is mapped to node! *)
223       let rec result = function
224         | [] -> []
225         | (q,form) :: tl ->
226           if (StateSet.mem q self) &&  (* infers & trans. can start here *)
227             (Formula.infer_form (self_q,self_r) qf qn form)
228           then form :: (result tl)
229           else result tl in      
230       let list_form = result list_tr in (* tran. candidates *)
231       (* compute states occuring in transition candidates *)
232       let rec add_st (ql,qr) = function
233         | [] -> ql,qr
234         | f :: tl -> let sqs,sql,sqr = Formula.st f in
235                      let ql' = StateSet.union sql ql
236                      and qr' = StateSet.union sqr qr in
237                      add_st (ql',qr') tl in
238       let ql,qr = add_st (StateSet.empty, StateSet.empty) list_form in
239       let qfq,qfr = try NodeHash.find run fnode
240         with | _ -> map_leaf asta
241       and qnq,qnr = try NodeHash.find run nnode
242         with | _ -> map_leaf asta in
243       begin
244         if tfnode == Tree.nil || Tree.is_attribute tree tnode
245         then ()
246         else NodeHash.replace run fnode (StateSet.inter qfq ql,qfr);
247         if tnnode == Tree.nil || Tree.is_attribute tree tnode
248         then ()
249         else NodeHash.replace run nnode (StateSet.inter qnq qr,qnr);
250         (* indeed we delete all states from self transitions!  *)
251         tp_max asta run tree tfnode hashRun;
252         tp_max asta run tree tnnode hashRun;
253       end;
254     end
255         
256 let compute tree asta =
257   let flag = 2 in                       (* debug  *)
258   let size_tree = 10000 in              (* todo (Tree.size ?) *)
259   let size_hcons_O = 1000 in            (* todo size Hashtbl *)
260   let size_hcons_M = 1000 in            (* todo size Hashtbl *)
261   let map = NodeHash.create size_tree in
262   let hashOracle = HashOracle.create(size_hcons_O) in
263   bu_oracle asta map tree (Tree.root tree) hashOracle;
264   HashOracle.clear hashOracle;
265   if flag > 0 then begin
266     let hashRun = HashRun.create(size_hcons_M) in
267     bu_over_max asta map tree (Tree.root tree) hashRun;
268     if flag = 2
269     then
270       tp_max asta map tree (Tree.root tree) hashRun
271     else ();
272     HashRun.clear hashRun;
273   end
274   else ();
275   map
276
277 let selected_nodes tree asta =
278   let run = compute tree asta in
279   NodeHash.fold
280     (fun key set acc ->
281       if not(StateSet.is_empty
282                (StateSet.inter (fst set) (Asta.selec_states asta)))
283       then key :: acc
284       else acc)
285     run []
286
287 let print fmt run =
288   let print_d_set fmt (s_1,s_2) =
289     Format.fprintf fmt "(%a,%a)"
290       StateSet.print s_1 StateSet.print s_2 in
291   let print_map fmt run =
292     let pp = Format.fprintf fmt in
293     if NodeHash.length run = 0
294     then Format.fprintf fmt "ø"
295     else
296       NodeHash.iter (fun cle set -> pp "|  %i->%a  @ " cle print_d_set set)
297         run in
298   let print_box fmt run =
299     let pp = Format.fprintf fmt in
300     pp "@[<hov 0>@.  # Mapping:@.   @[<hov 0>%a@]@]"
301       print_map run
302   in
303   Format.fprintf fmt "@[<hov 0>##### RUN #####@, %a@]@." print_box run