(***********************************************************************) (* *) (* TAToo *) (* *) (* Lucca Hirschi, LRI UMR8623 *) (* Université Paris-Sud & CNRS *) (* *) (* Copyright 2010-2012 Université Paris-Sud and Centre National de la *) (* Recherche Scientifique. All rights reserved. This file is *) (* distributed under the terms of the GNU Lesser General Public *) (* License, with the special exception on linking described in file *) (* ../LICENSE. *) (* *) (***********************************************************************) INCLUDE "utils.ml" module Node = struct type t = int let hash n = n let compare = (-) let equal = (=) end module NodeHash = Hashtbl.Make (Node) type t = (StateSet.t*StateSet.t) NodeHash.t (** Map from nodes to query and recognizing states *) (* Note that we do not consider nil nodes *) exception Oracle_fail exception Over_max_fail exception Max_fail (* Hash Consign modules *) open Hconsed_run module HashOracle = Hashtbl.Make(Oracle_fixpoint) module HashRun = Hashtbl.Make(Run_fixpoint) (* Mapped sets for leaves *) let map_leaf asta = (Asta.bot_states_s asta, StateSet.empty) (* Build the Oracle *) let rec bu_oracle asta run tree tnode hashOracle hashEval = let node = Tree.preorder tree tnode in if Tree.is_leaf tree tnode then if tnode == Tree.nil then () else NodeHash.add run node (map_leaf asta) else let tfnode = Tree.first_child_x tree tnode and tnnode = Tree.next_sibling tree tnode in let fnode,nnode = (* their preorders *) (Tree.preorder tree tfnode, Tree.preorder tree tnnode) in begin bu_oracle asta run tree tfnode hashOracle hashEval; bu_oracle asta run tree tnnode hashOracle hashEval; (* add states which satisfy a transition *) let rec result set qfr qnr flag = function | [] -> set,flag | (q,form) :: tl -> if Formula.eval_form (set,qfr,qnr) form hashEval then if StateSet.mem q set then result set qfr qnr 0 tl else result (StateSet.add q set) qfr qnr 1 tl else result set qfr qnr 0 tl in (* compute the fixed point of states of node *) let rec fix_point set_i qfr qnr list_tr t = try HashOracle.find hashOracle (set_i, qfr, qnr, list_tr, t) with _ -> let set,flag = result set_i qfr qnr 0 list_tr in HashOracle.add hashOracle (set_i,qfr,qnr,list_tr,t) (set); (* todo: Think about this position *) if flag = 0 then set else fix_point set qfr qnr list_tr t in let q_rec n = (* compute the set for child/sibling *) try NodeHash.find run n with Not_found -> map_leaf asta in let (_,qfr),(_,qnr) = q_rec fnode,q_rec nnode (* computed in rec call *) and lab = Tree.tag tree tnode in let _,list_tr = Asta.transitions_lab asta lab in (*only reco. tran.*) NodeHash.add run node (StateSet.empty, fix_point StateSet.empty qfr qnr list_tr lab) end (* Build the over-approx. of the maximal run *) let rec bu_over_max asta run tree tnode hashOver hashInfer = if (Tree.is_leaf tree tnode) (* BU_oracle has already created the map *) then () else let tfnode = Tree.first_child_x tree tnode and tnnode = Tree.next_sibling tree tnode in begin bu_over_max asta run tree tfnode hashOver hashInfer; bu_over_max asta run tree tnnode hashOver hashInfer; let (fnode,nnode) = (Tree.preorder tree tfnode, Tree.preorder tree tnnode) and node = Tree.preorder tree tnode in let q_rec n = try NodeHash.find run n with Not_found -> map_leaf asta in let qf,qn = q_rec fnode,q_rec nnode in let lab = Tree.tag tree tnode in let list_tr,_ = Asta.transitions_lab asta lab (* only take query st. *) and _,resultr = try NodeHash.find run node with _ -> raise Over_max_fail in let rec result set qf qn flag list_tr = function | [] -> if flag = 0 then set else result set qf qn 0 list_tr list_tr | (q,form) :: tl -> if StateSet.mem q set then result set qf qn 0 list_tr tl else if Formula.infer_form (set,resultr) qf qn form hashInfer then result (StateSet.add q set) qf qn 1 list_tr tl else result set qf qn 0 list_tr tl in let result_set () = try HashRun.find hashOver ((StateSet.empty,resultr),qf,qn,list_tr,lab) with _ -> let res = result StateSet.empty qf qn 0 list_tr list_tr in HashRun.add hashOver ((StateSet.empty,resultr), qf,qn,list_tr,lab) res; res in (* we keep the old recognizing states set *) NodeHash.replace run node (result_set(), resultr) end (* Build the maximal run *) let rec tp_max asta run tree tnode hashMax hashInfer = if (Tree.is_leaf tree tnode) (* BU_oracle has already created the map *) then () else let node = Tree.preorder tree tnode and tfnode = Tree.first_child_x tree tnode and tnnode = Tree.next_sibling tree tnode in let (fnode,nnode) = (Tree.preorder tree tfnode, Tree.preorder tree tnnode) in begin if tnode == Tree.root tree (* we must intersect with top states *) then let setq,_ = try NodeHash.find run node with _ -> raise Max_fail in NodeHash.replace run node ((StateSet.inter (Asta.top_states_s asta) setq),StateSet.empty) else (); let q_rec n = try NodeHash.find run n with Not_found -> map_leaf asta in let qf,qn = q_rec fnode,q_rec nnode in let lab = Tree.tag tree tnode in let list_tr,_ = Asta.transitions_lab asta lab in (* only take query. *) let (self_q,self_r) = try NodeHash.find run node with Not_found -> raise Max_fail in (* We must compute again accepting states from self transitions since previous calls of tp_max may remove them *) let rec result_q self_q queue = function (* for initializing the queue *) | [] -> self_q,queue | (q,form) :: tl -> if (StateSet.mem q self_q) then begin let q_cand,_,_ = Formula.st form in StateSet.iter (fun x -> Queue.push x queue) q_cand; result_q (StateSet.add q self_q) queue tl; end else result_q self_q queue tl and result_st_q self_q queue flag = function (*for computing the fixed p*) | [] -> flag,queue | form :: tl -> if Formula.infer_form (self_q,self_r) qf qn form hashInfer then begin let q_cand,_,_ = Formula.st form in StateSet.iter (fun x -> Queue.push x queue) q_cand; result_st_q self_q queue 1 tl; end else result_st_q self_q queue flag tl in let rec comp_acc_self self_q_i queue = (* compute the fixed point *) if Queue.is_empty queue (* todo: to be hconsigned? *) then self_q_i else let q = Queue.pop queue in let list_q,_ = Asta.transitions_st_lab asta q lab in let flag,queue = result_st_q self_q_i queue 0 list_q in let self_q = if flag = 1 then StateSet.add q self_q_i else self_q_i in comp_acc_self self_q queue in let self,queue_init = result_q self_q (Queue.create()) list_tr in let self_q = comp_acc_self self_q queue_init in NodeHash.replace run node (self_q,self_r); (* From now, the correct set of states is mapped to (self) node! *) let rec result self qf qn = function | [] -> [] | (q,form) :: tl -> if (StateSet.mem q (fst self)) && (* infers & trans. can start here *) (Formula.infer_form self qf qn form hashInfer) then form :: (result self qf qn tl) else result self qf qn tl in let list_form = try HashRun.find hashMax ((self_q,self_r),qf,qn,list_tr,lab) with _ -> let res = result (self_q,self_r) qf qn list_tr in HashRun.add hashMax ((self_q,self_r),qf,qn,list_tr,lab) res; res in (* compute states occuring in transition candidates *) let rec add_st (ql,qr) = function | [] -> ql,qr | f :: tl -> let sqs,sql,sqr = Formula.st f in let ql' = StateSet.union sql ql and qr' = StateSet.union sqr qr in add_st (ql',qr') tl in let ql,qr = add_st (StateSet.empty, StateSet.empty) list_form in let qfq,qfr = try NodeHash.find run fnode with | _ -> map_leaf asta and qnq,qnr = try NodeHash.find run nnode with | _ -> map_leaf asta in begin if tfnode == Tree.nil || Tree.is_attribute tree tnode then () else NodeHash.replace run fnode (StateSet.inter qfq ql,qfr); if tnnode == Tree.nil || Tree.is_attribute tree tnode then () else NodeHash.replace run nnode (StateSet.inter qnq qr,qnr); (* indeed we delete all states from self transitions! *) tp_max asta run tree tfnode hashMax hashInfer; tp_max asta run tree tnnode hashMax hashInfer; end; end let compute tree asta = let flag = 2 in (* debug *) let size_tree = 10000 in (* todo (Tree.size ?) *) let size_hcons_O = 1000 in (* todo size Hashtbl *) let size_hcons_M = 1000 in (* todo size Hashtbl *) let size_hcons_F = 1000 in (* todo size Hashtbl *) let map = NodeHash.create size_tree in let hashOracle = HashOracle.create(size_hcons_O) in let hashEval = Formula.HashEval.create(size_hcons_F) in let hashInfer = Formula.HashInfer.create(size_hcons_F) in bu_oracle asta map tree (Tree.root tree) hashOracle hashEval; HashOracle.clear hashOracle; Formula.HashEval.clear hashEval; if flag > 0 then begin let hashOver = HashRun.create(size_hcons_M) in let hashMax = HashRun.create(size_hcons_M) in bu_over_max asta map tree (Tree.root tree) hashOver hashInfer; if flag = 2 then tp_max asta map tree (Tree.root tree) hashMax hashInfer else (); HashRun.clear hashOver; HashRun.clear hashMax; end else (); map let selected_nodes tree asta = let run = compute tree asta in NodeHash.fold (fun key set acc -> if not(StateSet.is_empty (StateSet.inter (fst set) (Asta.selec_states asta))) then key :: acc else acc) run [] let print fmt run = let print_d_set fmt (s_1,s_2) = Format.fprintf fmt "(%a,%a)" StateSet.print s_1 StateSet.print s_2 in let print_map fmt run = let pp = Format.fprintf fmt in if NodeHash.length run = 0 then Format.fprintf fmt "ø" else NodeHash.iter (fun cle set -> pp "| %i->%a @ " cle print_d_set set) run in let print_box fmt run = let pp = Format.fprintf fmt in pp "@[@. # Mapping:@. @[%a@]@]" print_map run in Format.fprintf fmt "@[##### RUN #####@, %a@]@." print_box run