Revision 9c4b41905123055be4c5334c08744e27de6828b8 authored by Pierre Boutillier on 18 September 2015, 08:05:02 UTC, committed by Pierre Boutillier on 18 September 2015, 10:36:00 UTC
1 parent 7b3fbc4
Raw File
nonLocal.ml
(**Module that manages connectedness of injection images --for dot and plus operators*)
open Mods
open Graph
open State
open ExceptionDefn

(**updating non local mixtures after application of a linking rule using embedding [embedding_info]*)
let update_intra_in_components r embedding_info state counter env =
  let () =
    Debug.tag_if_debug "Looking for side effect update of non local rules..." in
	let components = 
		match embedding_info.Embedding.components with
			| Some map -> map
			| None -> invalid_arg "NonLocal.update_intra_in_components: component not computed"
	in
	
	(*case one tries to gather intra redexes in each components -- nb this may lead to false intras but that will be rejected upon rule application*)
	let tmp_extend part_inj inj =
		let _,cc_id = Injection.get_coordinate inj in
		if IntMap.mem cc_id part_inj then failwith "Invariant violation"
		else
			IntMap.add cc_id inj part_inj
	in
	
	let search_elements graph component extensions env =
	  let ext,modified =
	    IntSet.fold
	      (fun u_id (extensions,modified) ->
	       Debug.tag_if_debug "looking for a piece of intra on lifts of node %d" u_id ;
	    let u = try SiteGraph.node_of_id graph u_id
		    with Not_found -> invalid_arg "NonLocal.search_elements" in
	    let _,lifts = Node.get_lifts u 0 in (*BUG here site 0 is not always "_"*)
	    LiftSet.fold
	      (fun inj (extensions,modified) ->
	       if Injection.is_trashed inj
	       then (extensions,modified) (*injection should not be already invalid...*)
	       else
		 let (mix_id,cc_id) = Injection.get_coordinate inj in
		 if not (Environment.is_nl_rule mix_id env) then
		   begin
		     Debug.tag_if_debug "a lift points to rule %d but it is a local one" mix_id ;
		     (extensions,modified)
		   end
		 else
		   let inj_map = try IntMap.find mix_id extensions
				 with Not_found -> IntMap.empty in
		   let inj_list = try IntMap.find cc_id inj_map
				  with Not_found -> [] in
		   let inj_map' = IntMap.add cc_id (inj::inj_list) inj_map in
		   (IntMap.add mix_id inj_map' extensions,true)
	      ) lifts (extensions,modified)
	      ) component (extensions,false)
	  in
	  if modified then (Some ext)
	  else None
	in
	
	(*reusing components that were computed to check that the rule was indeed binary*)
	let con_map = match r.Primitives.cc_impact with None -> invalid_arg "State.nl_pos_upd: cc_impact is not initialized"  | Some (map,_,_) -> map in
	
	let extensions,found = (*r_id -> cc_id -> part_inj_list*)
		IntMap.fold 
		(fun eq cc_set (extensions,found) ->
		 Debug.tag_if_debug "CCs {%a} are merged by the rule and CC[%d] is the representative"
				    (Pp.set IntSet.elements Pp.comma
					    Format.pp_print_int) cc_set
				    eq;
			IntSet.fold 
			(fun cc_i (extensions,found) ->
				let root = 
					match Mixture.root_of_cc r.Primitives.lhs cc_i with Some r -> IntMap.find r embedding_info.Embedding.map | None -> invalid_arg "State.nl_positive_update" 
				in
				let () =
				  Debug.tag_if_debug
				    "Exploring into image of CC[%d] computed during rule %d application"
				    cc_i r.Primitives.r_id
				in
				let component_i =
				  try IntMap.find root components
				  with Not_found ->
				    (Debug.tag_if_debug "root %d (= phi(%d)) not found in %a" root (match Mixture.root_of_cc r.Primitives.lhs cc_i with Some r -> r | None -> -1)
							(Pp.set IntMap.bindings Pp.comma
								(fun f (i,s) ->
								 Format.fprintf f "%i -> {%a}" i (Pp.set IntSet.elements Pp.comma Format.pp_print_int) s))
							components;
				     invalid_arg "nl_pos_upd")
				in
				Debug.tag_if_debug "{%a}" (Pp.set IntSet.elements Pp.comma Format.pp_print_int) component_i ;
				let opt = search_elements (get_graph state) component_i extensions env
				in
				match opt with
					| Some ext -> (ext,found+1) 
					| None -> (extensions,found) (*no partial intra was found in cc_i *)
			) cc_set (extensions,found) 
		) con_map (IntMap.empty,0)
	in
	if found < 2 then
	  let () =
	    Debug.tag_if_debug
	      "Potential new intras are not shared between merged cc and cannot be new, skipping" in
	  state
	else
		IntMap.fold 
		(fun r_id cc_map state ->
		 Debug.tag_if_debug "Trying to find intra(s) for rule [%d]" r_id;
			
			let lhs = kappa_of_id r_id state in
			try
				let intras_for_r_id,cc = 
					IntMap.fold
					(fun cc_id injs_list (new_intras,cpt) ->
						if cpt <> cc_id then raise (Break cc_id)
						else
							let new_intras = 
								List.fold_left
								(fun cont part_prod_inj ->
									let l = 
										List.fold_left 
										(fun cont inj ->
											(tmp_extend part_prod_inj inj)::cont
										) cont injs_list
									in
									l (*@cont*)
								) [] new_intras
							in
							(new_intras,cpt+1)
					) cc_map ([IntMap.empty],0)
				in
				
				if cc <> Mixture.arity lhs then
				  let () =
				    Debug.tag_if_debug
				      "One CC of rule [%d] has no candidate for intra, aborting"
				      r_id in state
				else
					List.fold_left  (*nl_injections : (InjProdHeap.t option) array*)
					(fun state injprod_map -> 
						let injprod_hp = match (get_nl_injections state).(r_id) with
							| Some hp -> hp
							| None -> InjProdHeap.create !Parameter.defaultHeapSize 
						in
						let ip = match InjProdHeap.next_alloc injprod_hp with None -> InjProduct.create (Mixture.arity lhs) r_id | Some ip -> ip
						in
						let ip = IntMap.fold (fun cc_id inj injprod -> InjProduct.add cc_id inj injprod ; injprod) injprod_map ip
						in
						try
							let injprod_hp = InjProdHeap.alloc ~check:true ip injprod_hp in
							(get_nl_injections state).(r_id) <- Some injprod_hp ;
							update_activity state ~cause:r.Primitives.r_id r_id counter env ;
							state
						with
							| InjProdHeap.Is_present -> state
					) state intras_for_r_id
		with 
		| Break cc_id ->
		   Debug.tag_if_debug
		     "CC[%d] of rule [%d] has no candidate for intra, aborting" cc_id r_id;
		   state
		) extensions state
		
(**[update_rooted_intras injection_list state counter env] tries to form new intras using injections in [injection_list] as one component*)
let rec update_rooted_intras new_injs state counter env = 
	match new_injs with 
		| [] -> state
		| injection::tl ->
			let (mix_id,cc_id) = Injection.get_coordinate injection in (*mix_id is the id of a non local lhs by invariant*)
			
			(*one should look into cc(root_injection) whether some nodes have a lift to (mix_id,cc_id') with cc_id <> cc_id'*)
			let u_0 = match Injection.root_image injection with None -> invalid_arg "NonLocal.complete_injections" | Some (_,p) -> p
			in
			(*if activity of mix_id is null then there can be no intra*)
			if not (is_complete mix_id state) then update_rooted_intras tl state counter env
			else
				
				(***NOT EFFICIENT BECAUSE LIFTSET WILL BE TRAVERSED ONCE MORE, neighborhood FUNCTION SHOULD DO THIS***)
				let predicate = 
					fun node -> 
						let _,liftset = Node.get_lifts node 0 in
						LiftSet.exists (fun inj -> let (mix_id',cc_id') = Injection.get_coordinate inj in (mix_id' = mix_id) && (cc_id <> cc_id') ) liftset
				in   
				let (_,_,components,_) = SiteGraph.neighborhood ~filter_elements:predicate (get_graph state) u_0 (-1) in
				
				(*components contains nodes whose name is the root of at least one complemetary injection*)
				let candidate_map = 
					IntSet.fold 
					(fun u_i map -> 
						let node = SiteGraph.node_of_id (get_graph state) u_i in
						let _,lifts = Node.get_lifts node 0 in
						LiftSet.fold 
						(fun inj map -> 
							let mix_id',cc_id' = Injection.get_coordinate inj in
							if (mix_id' = mix_id) && (cc_id' <> cc_id) then
								let l_part_inj = try IntMap.find cc_id' map with Not_found -> [] in
								IntMap.add cc_id' (inj::l_part_inj) map
							else map
						) lifts map
					) components IntMap.empty
				in 
				Debug.tag_if_debug
				  "@[<v>@[Trying to extend (%d,%d) : %a with:@]@,@[%a@]@]@."
				  mix_id cc_id Injection.print injection
				  (Pp.set IntMap.bindings Pp.empty
					  (fun f (cc_id',inj_list) ->
					   Format.fprintf
					     f "(%d,%d):[%a]\n" mix_id cc_id'
					     (Pp.list Pp.colon Injection.print_coord) inj_list))
				  candidate_map;
				if IntMap.size candidate_map < (Mixture.arity (kappa_of_id mix_id state)) - 1 then
				  update_rooted_intras tl state counter env
				else
					let new_intras = 
						IntMap.fold (*folding on candidate map*)
						(fun cc_id ext_injs new_intras ->
							
							List.fold_left 
							(fun cont inj_map ->
								let ext_cont = 
									List.fold_left 
									(fun cont inj_cc ->
										(IntMap.add cc_id inj_cc inj_map)::cont
									) (*[]*) cont ext_injs
								in
								ext_cont (*@ cont*)
							) [] new_intras
							
						) candidate_map [IntMap.add cc_id injection IntMap.empty]
					in
										
					Debug.tag_if_debug
					  "%a" (Pp.list Pp.empty
							(fun f injmap ->
							 Format.fprintf f "new_intras: %a"
									(Pp.set IntMap.bindings Pp.comma
										(fun f (i,c) -> Format.fprintf
												  f "%i->%a" i
												  Injection.print_coord c))
									injmap))
					  new_intras ;

					let injprod_hp = match (get_nl_injections state).(mix_id) with None -> InjProdHeap.create !Parameter.defaultHeapSize | Some hp -> hp in
					let mix = kappa_of_id mix_id state in
					let injprod_hp = 
						List.fold_left
						(fun injprod_hp intra_map ->
							if IntMap.size intra_map < (Mixture.arity mix) then injprod_hp (*intra is not complete*)
							else
								let injprod = match InjProdHeap.next_alloc injprod_hp with None -> InjProduct.create (Mixture.arity mix) mix_id | Some ip -> ip 
								in
								let injprod,_new_roots = 
									IntMap.fold 
									(fun cc_id inj (injprod,new_roots) ->
										let root = (function Some (_,j) -> j | None -> invalid_arg "") (Injection.root_image inj) in
										InjProduct.add cc_id inj injprod ;
										(injprod, IntMap.add cc_id root new_roots)
									) intra_map (injprod,IntMap.empty)
								in
								let opt = 
									try 
										Some (
											InjProduct.fold_left 
											(fun cod inj -> 
												let _,cod' = Injection.codomain inj (IntMap.empty,cod) in cod'
											) IntSet.empty injprod
											) 
									with Injection.Clashing -> None
								in
								if opt = None then injprod_hp 
								else
								try
								let injprod_hp = InjProdHeap.alloc ~check:true injprod injprod_hp in
									injprod_hp
								with InjProdHeap.Is_present -> 
									Debug.tag_if_debug "Intra already added, skipping" ;
									injprod_hp 
						) injprod_hp new_intras
					in
					(get_nl_injections state).(mix_id) <- Some injprod_hp ;
					update_activity state mix_id counter env ;
					update_rooted_intras tl state counter env

let initialize_embeddings state counter env = 
	SiteGraph.fold 
	(fun _id u state ->
		let name = Node.name u in
		if Environment.is_nl_root name env then (*node is potentially the root of an intra rule*) 
		let _,lifts = Node.get_lifts u 0 in
		LiftSet.fold 
		(fun inj state -> 
			let (mix_id,_) = Injection.get_coordinate inj in
			if Environment.is_nl_rule mix_id env then
				update_rooted_intras [inj] state counter env
			else
				state
		) lifts state 
		else 
			state
	) (get_graph state) state
	


let positive_update r embedding_t new_injs state counter env = 
	(*Step 1 : For each new embedding phi, adding all new intras of the form <phi,psi> *)
	let state = update_rooted_intras new_injs state counter env in
	
	(*Step 2 : Non local positive update *)
	(*If rule is potentially breaking up some connected component this should wake up silenced rules*)
	begin
		match r.Primitives.cc_impact with
			| None -> Debug.tag_if_debug "Rule cannot decrease connectedness no need to update silenced rules"
			| Some _ -> (*should be more precise here*) State.unsilence_rule state r counter env
	end ;
		
	(*If rule is potentially merging two connected components this should trigger a positive update of non local rules*)
	begin
	  match r.Primitives.cc_impact with
	  | None ->
	     let () = Debug.tag_if_debug
			"No possible side effect update of unary rules because applied rule cannot increase connectedness" in
	     state
	  | Some (con_map,_,_) ->
	     if IntMap.is_empty con_map then state
	     else
	       begin
		 match embedding_t with
		 | Embedding.CONNEX _ ->
		    let () = Debug.tag_if_debug
			       "No possible side effect update of unary rules because a unary instance was applied" in
		    state
		 | Embedding.DISJOINT e | Embedding.AMBIGUOUS e -> (*one may need to compute connected components if they are not present in e, as in the AMBIGUOUS case*)
					   update_intra_in_components r e state counter env
	       end
	end
back to top