diff --git a/.gitignore b/.gitignore index e3c1c27ad3f4633a8da8e368f47c4236ee4be0d5..3b3577736b17a10ee4058ede4525cde7883f5fe7 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,5 @@ rdbg-session*.ml Makefile.local notes.org sasa-*.dot -*.html \ No newline at end of file +*.html +*.pyc \ No newline at end of file diff --git a/lib/sasacore/topology.ml b/lib/sasacore/topology.ml index 3b7f61d411f0a5dfa0637a7c7fa31fb5229fcf36..72bc0c4f3d65c2e959ba41bb51f1bacc78fa6055 100644 --- a/lib/sasacore/topology.ml +++ b/lib/sasacore/topology.ml @@ -127,6 +127,53 @@ let (read: string -> t) = fun f -> ) } + +let make_links_dot : (t -> string) = + fun t -> + let links = List.flatten ( + List.map (fun n -> + let l = t.succ n.id in + List.map (fun (w,neighbour) -> + ( + match w with + | None -> if n.id < neighbour then + Printf.sprintf (" %s -- %s") n.id neighbour + else + Printf.sprintf (" %s -- %s") neighbour n.id + | Some x -> + Printf.sprintf (" %s -- %s [weight=%d]") n.id neighbour x + ) + + ) l + ) t.nodes + ) in + String.concat "\n" (List.sort_uniq compare links) + +let rec make_nodes_dot : (node list -> string) = + (*Create a string in the dot syntax from a node list*) + function + | [] -> "" + | (node)::tail -> (Printf.sprintf " %s [algo=\"%s\"]\n" node.id node.file)^(make_nodes_dot tail) + +let make_dot : (t -> string -> unit) = + (*Create a dot file from a graph*) + fun t file_name -> + let name = ref "graph0" in + let f = (if file_name = "" then stdout else + ( + name := Filename.basename file_name; + (try ( (* remove all extensions. So if name = ref "tt.dot.dot" at the beginning, at the end name = ref "tt". *) + while true do + name := Filename.chop_extension !name; + done; + ) with Invalid_argument _ -> ()); + open_out file_name + ) + ) in + let dot = (Printf.sprintf "graph %s {\n\n" !name) ^ (make_nodes_dot t.nodes) ^ "\n" ^ (make_links_dot t) ^ "\n}\n" in + Printf.fprintf f "%s" dot + (*ignore (Sys.command (Printf.sprintf "echo \"%s\" > \"%s.dot\"" dot file_name)); ()*) + let (to_adjency: t -> bool array array) = fun t -> let n = List.length t.nodes in @@ -140,3 +187,4 @@ let (to_adjency: t -> bool array array) = ) t.nodes; m + diff --git a/lib/sasacore/topology.mli b/lib/sasacore/topology.mli index c04cb057cc2878653db72a0879a4ec67d1616e9d..bbea959405b2748c02527aebad34354cf4009810 100644 --- a/lib/sasacore/topology.mli +++ b/lib/sasacore/topology.mli @@ -18,5 +18,16 @@ type t = { val read: string -> t +(** Create a string containing the links of the graph given in argument in a DOT syntax *) +val make_links_dot : (t -> string) + +(** Create a string containing the nodes given in argument in a DOT syntax *) +val make_nodes_dot : (node list -> string) + +(** Create a DOT file from a graph *) +val make_dot : (t -> string -> unit) + + val to_adjency: t -> bool array array + diff --git a/tools/ggDeco/dune b/tools/ggDeco/dune new file mode 100644 index 0000000000000000000000000000000000000000..3a17c9261af8cf19bb6cf4ccb0aa202b6498b574 --- /dev/null +++ b/tools/ggDeco/dune @@ -0,0 +1,9 @@ +(executable + (name ggDeco) + (libraries sasacore) +) + +(install + (section bin) +(files (ggDeco.exe as gg-deco)) +) diff --git a/tools/ggDeco/ggDeco.ml b/tools/ggDeco/ggDeco.ml new file mode 100644 index 0000000000000000000000000000000000000000..7803c51822e7a94e7d841be449da12da33881782 --- /dev/null +++ b/tools/ggDeco/ggDeco.ml @@ -0,0 +1,59 @@ +open Sasacore +open Topology + +open GgDeco_Arg + +exception Crossover (* Two different intervals crossover each-other *) +exception No_file_for of int + +let compare_file_spec : (files_spec_t -> files_spec_t -> int) = + fun (a1,b1,_s1) (a2,b2,_s2) -> + if (b2 < a1 && b2 <> -1) then 1 else + if (b1 < a2 && b1 <> -1) then -1 + (*else if s1 = s2 then compare a1 a2 *) + else raise Crossover + +(* + * Parse strings into a file_spec list. + * Each file_spec element should be represented in the format "a-b:file", + * with "a" being the start, "b" being the end, and "file" being the file. + * To create multiple file_spec elements from one string, separate each element by a whitespace. + * If the syntax is not respected, an exception will be raised. + * Possible exceptions : End_of_file, Stdlib.Scanf.Scan_failure, int_of_string + * Caution : Crossover exceptions are not raised in this function + *) + +let pop : (files_spec_t list ref -> int -> string) = + fun fl i -> + match !fl with + | [] -> raise (No_file_for i) + | (a,b,s)::tl -> if i < a || (i > b && b <> -1) then raise (No_file_for i) else + if i = b then fl := tl; + s + + +let deco : (Topology.t -> files_spec_t list -> Topology.t) = + fun g fl -> + let newNodes = ref [] and fl = ref (List.sort compare_file_spec fl) in + List.iteri (fun i n -> + let s = (pop fl i) in + newNodes := ( + ({ + id=n.id; + file=(if s = "." then n.file else s); + init=n.init + })::!newNodes + ); + ) g.nodes; + { + nodes = List.rev !newNodes; + succ = g.succ; + of_id = g.of_id; + } + +let () = ( + let args = parse Sys.argv in + let g = read args.dot_file in + let new_g = deco g args.files_spec in + make_dot new_g args.output attr +) diff --git a/tools/ggDeco/ggDeco.mli b/tools/ggDeco/ggDeco.mli new file mode 100644 index 0000000000000000000000000000000000000000..dc71bddd1ef2edfdd386e71fda297aceede72f8e --- /dev/null +++ b/tools/ggDeco/ggDeco.mli @@ -0,0 +1,2 @@ +val deco : (Sasacore.Topology.t -> GgDeco_Arg.files_spec_t list -> Sasacore.Topology.t) + diff --git a/tools/ggDeco/ggDeco_Arg.ml b/tools/ggDeco/ggDeco_Arg.ml new file mode 100644 index 0000000000000000000000000000000000000000..a9ec8905c5773e85870f7d9b535326c986df333e --- /dev/null +++ b/tools/ggDeco/ggDeco_Arg.ml @@ -0,0 +1,204 @@ +let () = Random.self_init (); + +type file = string +type files_spec_t = (int * int * file) +exception Invalid_file_spec of string*string + +type t = { + mutable dot_file: string; + mutable output: string; + + mutable files_spec : files_spec_t list; + + mutable _args : (string * Arg.spec * string) list; + mutable _general_man : (string * string list) list; + + mutable _others : string list; + mutable _margin : int; +} + +let usage_msg tool = + ("usage: " ^ tool ^ " ... [-o ]\n" ) + +let print_usage tool = Printf.printf "\n%s use -h for additional information.\n\n" (usage_msg tool); flush stdout; exit 1 + + +let (make_args : unit -> t) = + fun () -> + { + dot_file = ""; + output = ""; + + files_spec = []; + + _args = []; + _general_man = []; + + _others = []; + _margin = 12; + } + +let first_line b = ( + try ( + let f = String.index b '\n' in + String.sub b 0 f + ) with Not_found -> b +) +let exist_file f = ( + if not (Sys.file_exists f) then ( + prerr_string ("File not found: \""^f^"\""); + prerr_newline (); + exit 1 + ) +) +let unexpected s = ( + prerr_string ("unexpected argument \""^s^"\""); + prerr_newline (); + exit 1 +) + + +let printSpec args outChannel (c, messageList) = ( + let (m1, oth) = match messageList with + | h::t -> (h,t) + | _ -> ("",[]) + in + let t2 = String.make args._margin ' ' in + let cl = String.length c in + let t1 = if (cl < args._margin ) then + String.make (args._margin - cl) ' ' + else + "\n"^t2 + in + Printf.fprintf outChannel "%s%s%s" c t1 m1; + List.iter (function x -> Printf.fprintf outChannel "\n%s%s" t2 x) oth ; + Printf.fprintf outChannel "\n" ; +) + +let help args tool = ( + Printf.printf "\n"; + Printf.printf "%s" (usage_msg tool); + Printf.printf "\n"; + Printf.printf "Add or replace the 'algo' label in the dot file, to change the algorithms attached to each node.\n"; + Printf.printf " is the dot file on which you wish to change the algo.\n"; + Printf.printf " describe which nodes will have which algo attached to them. The syntax is the following :\n"; + Printf.printf "\n"; + Printf.printf " - To assign a file (containing an algo) to one node, write 'i:file', 'i' being the index of the node\n"; + Printf.printf " and 'file' being the file of the algo. \n"; + Printf.printf "\n"; + Printf.printf " - To assign a file to multiple nodes, write 'i-j:file', with 'i' being the index of the first node,\n"; + Printf.printf " 'j' being the last node's index (included) and 'file' being the file of the algo. \n"; + Printf.printf " Note : write 'i-:file' to assign the file to all nodes from i.\n"; + Printf.printf "\n"; + Printf.printf " - Concatenate all the the descriptions, with a whitespace between each.\n"; + Printf.printf " Example : \"1:root.ml 2-:p.ml\" assign root.ml to the first node, and p.ml to the other ones.\n"; + (* maybe I should change it such that it accepts with or without the double quotes *) + Printf.printf "\n"; + Printf.printf "\n"; + Printf.printf "============ Available Options ============\n\n"; + ( + List.iter (printSpec args stdout) (List.rev args._general_man) + ); + Printf.printf "\n"; + exit 0 +) + + +let mkopt : t -> string list -> ?arg:string -> Arg.spec -> (string list -> unit) = + fun opt ol ?(arg="") se ml -> + let add_option o = opt._args <- (o, se, "")::opt._args in + List.iter add_option ol ; + let col1 = (String.concat ", " ol)^arg in + opt._general_man <- (col1, ml)::opt._general_man + +(*** User Options Tab **) +let (mkoptab : string array -> t -> unit) = + fun argv args -> ( + + mkopt args ["--output";"-o"] ~arg:" " + (Arg.String (fun s -> args.output <- s)) + ["Set the output file to the given file.\n"]; + + + mkopt args ["--help";"-h"] + (Arg.Unit (fun () -> help args argv.(0) )) + ["Prints this help\n"]; + ) + +(* all unrecognized options are accumulated *) +let (add_other : t -> string -> unit) = + fun opt s -> + opt._others <- s::opt._others + +let current = ref 0;; + +let parse_file_spec : (string list -> files_spec_t list) = + fun s -> + List.map (fun file -> + try ( + Scanf.sscanf file "%[-0-9]:%s" (fun range file -> + if range = "" then (raise (Invalid_file_spec (file, "The first and last node's indexes are missing")) + ) else + Scanf.sscanf range "%d%s" (fun a s -> + if (a < 0) then raise (Invalid_file_spec (file,"The first node's index have to be positive or null")) else + if (s = "") then (a,a,file) else + + if (s = "-") then (a,-1,file) else + Scanf.sscanf s "-%d" (fun b -> + if (b < a) then + raise (Invalid_file_spec (file,"The last node's index have to be higher than the first node's index")) + else + (a,b,file) + ) + ) + ) + ) + with + | Scanf.Scan_failure _ -> + raise (Invalid_file_spec (file, + "The boundaries (first and last node's indexes) should be integers, but an non-numerical character has been found")) + ) s + +let rec pop l = + match l with + | [] -> assert false + | [a] -> ([],a) + | b::tl -> let (l,a) = pop tl in (b::l,a) + +let parse argv = ( + let save_current = !current in + let args = make_args () in + mkoptab argv args; + try ( + Arg.parse_argv ~current:current argv args._args (add_other args) (usage_msg argv.(0)); + current := save_current; + + (* Same as List.rev, but also check if there's no option (starting by '-') in these arguments *) + let others = List.fold_left (fun l o -> if String.get o 0 = '-' then unexpected o else o::l) [] args._others in + + (match others with + | [] | [_] -> + (Printf.fprintf stderr "Error : you need 2 arguments to use %s\n" (argv.(0)); flush stderr; print_usage (argv.(0))) + | l -> ( + let (a,b) = pop l in + let a = try ( + parse_file_spec a + ) with Invalid_file_spec (fs,s) -> ( + Printf.fprintf stderr "Error while parsing the file specification \"%s\" :\n" fs; + Printf.fprintf stderr "%s\n"s; + flush stderr; print_usage (argv.(0)) + ) in + args.files_spec <- a; exist_file b; args.dot_file <- b + ) + ); + args + ) + with + | Arg.Bad msg -> ( + Printf.fprintf stderr "*** Error when calling '%s': %s\n%s\n" (argv.(0)) + (first_line msg) (usage_msg argv.(0)); exit 2 + ) + | Arg.Help _ -> ( + help args argv.(0) + ) +) diff --git a/tools/ggDeco/ggDeco_Arg.mli b/tools/ggDeco/ggDeco_Arg.mli new file mode 100644 index 0000000000000000000000000000000000000000..384f34d00f619d28157ef9b88c8bfb6b27adbd6e --- /dev/null +++ b/tools/ggDeco/ggDeco_Arg.mli @@ -0,0 +1,17 @@ +type file = string +type files_spec_t = (int * int * file) + +type t = { + mutable dot_file: string; + mutable output: string; + + mutable files_spec : files_spec_t list; + + mutable _args : (string * Arg.spec * string) list; + mutable _general_man : (string * string list) list; + + mutable _others : string list; + mutable _margin : int; +} + +val parse : string array -> t diff --git a/tools/graphgen/classicGraph.ml b/tools/graphgen/classicGraph.ml new file mode 100644 index 0000000000000000000000000000000000000000..55a336ac35ab856436826e872874fdecd62be5b3 --- /dev/null +++ b/tools/graphgen/classicGraph.ml @@ -0,0 +1,124 @@ +open Sasacore +open Topology +open Ggcore +open List + +type node_succ_t = (string, (int option * string) list) Hashtbl.t + +let nid_list_remove : (node_id list -> node_id -> (int option*node_id) list) = + fun l e -> rev (fold_left (fun acc elem -> if(elem <> e) then (None,elem)::acc else acc ) [] l) + + +let (gen_clique: int -> Topology.t) = + fun nb -> + let (node_succ:node_succ_t) = Hashtbl.create nb and nodes = create_nodes "p" (0,nb) in + List.iter (fun node_id -> Hashtbl.replace node_succ node_id (nid_list_remove nodes node_id)) nodes; + let nl = id_to_empty_nodes nodes in + { + nodes = nl; + succ = (fun n -> try Hashtbl.find node_succ n with Not_found -> []); + of_id = get_of_id nl + } + + +let (gen_star: int -> Topology.t) = + fun nb -> + let (node_succ:node_succ_t) = Hashtbl.create nb and nodes = "root"::(create_nodes "p" (1,nb)) in + let first = hd nodes in + List.iter (fun node -> Hashtbl.replace node_succ node (if node = first then nid_list_remove nodes node else [(None,first)])) nodes; + let nl = id_to_empty_nodes nodes in + { + nodes = nl; + succ = (fun n -> try Hashtbl.find node_succ n with Not_found -> []); + of_id = get_of_id nl + } + +let add_weight (li : node_id list) : (int option * node_id) list = map (fun elem -> (None,elem)) li + +let neighbours_ring : (node_id list -> (node_id -> (int option * node_id) list)) = + fun li -> + let node_succ:node_succ_t = Hashtbl.create (length li) in + let ((_,last),ret) = fold_left (fun ((first,prev),accu) elem -> + if first = "" then + ((elem,elem),[[""; ""]]) + else + ( + match accu with + | [x;_]::tl -> ((first,elem),[prev;first]::([x;elem]::tl)) + | _ -> assert false + ) + ) (("",""),[]) li in let ret = rev ret in + let ret = (match ret with + | ["";x]::tail -> [last;x]::tail + | _ -> assert false) in + iter2 (fun neighbours elem -> Hashtbl.replace node_succ elem (add_weight neighbours)) ret li ; + (fun n -> try Hashtbl.find node_succ n with Not_found -> []) + + let (gen_ring: int -> Topology.t) = + fun nb -> + let nodes = (create_nodes "p" (0,nb)) in + let nl = id_to_empty_nodes nodes in + { + nodes = nl; + succ = neighbours_ring nodes; + of_id = get_of_id nl + } + + +let (gen_grid: int -> int -> Topology.t) = + fun length width -> + let nb = length*width in + let nodes = (create_nodes "p" (0,nb)) and table = Hashtbl.create nb in + for i=0 to length-1 do + for j=0 to width-1 do + let n_id = (List.nth nodes (j*length + i)) in + let bl = if(i=0) then 0 else -1 in + let br = if(i=(length-1)) then 0 else 1 in + let bup = if(j=0) then 0 else -1 in + let bdown = if(j=(width-1)) then 0 else 1 in + for ip=bl to br do + for jp=bup to bdown do + if not ((ip=0 && jp=0) || (ip=jp) || (ip = -jp)) then + (Hashtbl.replace table n_id ((None,(List.nth nodes ((j+jp)*length + i+ip)))::(try Hashtbl.find table n_id with Not_found -> [])); ) else () + done; + done; + done; + done; + let nl = id_to_empty_nodes nodes in + { + nodes = nl; + succ = (fun nid -> (try Hashtbl.find table nid with Not_found -> [])); + of_id = get_of_id nl + } + +let rec link_hypercube_nodes : (node_id array -> node_succ_t -> unit) = + fun na n_s -> + let len = Array.length na in let mid = len / 2 in + if len > 1 then + let n1 = (Array.sub na 0 mid) and n2 = (Array.sub na mid mid) in + link_hypercube_nodes n1 n_s; + link_hypercube_nodes n2 n_s; + Array.iter2 (fun node1 node2 -> + Hashtbl.replace n_s node1 + ((None,node2)::(try Hashtbl.find n_s node1 with Not_found -> [])); + Hashtbl.replace n_s node2 + ((None,node1)::(try Hashtbl.find n_s node2 with Not_found -> [])) + ) n1 n2 + +let neighbours_hyper_cube : (node_id list -> (node_id -> (int option * node_id) list)) = + fun nl -> + let na = Array.of_list nl in + let (node_succ:node_succ_t) = Hashtbl.create (Array.length na) in + link_hypercube_nodes na node_succ; + (fun n -> try Hashtbl.find node_succ n with Not_found -> []) + +let gen_hyper_cube : (int -> Topology.t) = + fun dim -> + let nb = int_of_float (2. ** (float_of_int dim)) in + let nodes = (create_nodes "p" (0,nb)) in + let nl = id_to_empty_nodes nodes in + { + nodes = nl; + succ = neighbours_hyper_cube nodes; + of_id = get_of_id nl + } diff --git a/tools/graphgen/classicGraph.mli b/tools/graphgen/classicGraph.mli new file mode 100644 index 0000000000000000000000000000000000000000..bed633a6f357f5b5a85186fa2932a8a62bb4db8e --- /dev/null +++ b/tools/graphgen/classicGraph.mli @@ -0,0 +1,17 @@ +open Sasacore + +(** Generate a clique graph of n nodes *) +val gen_clique : (int -> Topology.t) + +(** Generate a star graph of n nodes *) +val gen_star : (int -> Topology.t) + +(** Generate a ring graph of n nodes *) +val gen_ring :(int -> Topology.t) + +(** take the two dimension i,j of the grid and return a grid graph whith these dimension *) +val gen_grid : (int -> int -> Topology.t) + +(** take a dimension and generate hyper cube graph of this dimension *) +val gen_hyper_cube : (int -> Topology.t) + diff --git a/tools/graphgen/dune b/tools/graphgen/dune new file mode 100644 index 0000000000000000000000000000000000000000..8dac014c623a4b2918e70e6b6381e24cb942b1c1 --- /dev/null +++ b/tools/graphgen/dune @@ -0,0 +1,9 @@ +(executable + (name graphGen) + (libraries sasacore) +) + +(install + (section bin) +(files (graphGen.exe as gg)) +) diff --git a/tools/graphgen/ggcore.ml b/tools/graphgen/ggcore.ml new file mode 100644 index 0000000000000000000000000000000000000000..ddcaef5b7dcf6ee906b297383e27d35154a628dd --- /dev/null +++ b/tools/graphgen/ggcore.ml @@ -0,0 +1,20 @@ +open Sasacore.Topology + +type node_ofId_t = (string, node) Hashtbl.t + +let rec create_nodes : (string -> int*int -> node_id list) = + (* Create names from a generic name *) + fun name (start,finish) -> + if start >= finish then [] else + let tmp : node_id = name ^ (string_of_int (start)) in + tmp::(create_nodes name (start+1, finish)) + +let id_to_empty_nodes : (node_id list -> node list) = + List.map (fun n_id -> {id = n_id; file = ""; init = ""}) + +let get_of_id : (node list->(node_id-> node)) = + fun nl -> + let (of_id_hash:node_ofId_t) = Hashtbl.create (List.length nl) in + List.iter (fun node -> Hashtbl.replace of_id_hash node.id node) nl; + (fun n -> try Hashtbl.find of_id_hash n with Not_found -> + failwith (n^ " unknown node id")) diff --git a/tools/graphgen/ggcore.mli b/tools/graphgen/ggcore.mli new file mode 100644 index 0000000000000000000000000000000000000000..f87e17374b028c689c7a988eb2095dfbd325630e --- /dev/null +++ b/tools/graphgen/ggcore.mli @@ -0,0 +1,10 @@ +open Sasacore.Topology + +(** Create a name (i.d. node ID) list from a generic name *) +val create_nodes : (string -> int*int -> node_id list) + +(** creates a list of nodes, each having an ID from the list given in argument, and no file or init value. *) +val id_to_empty_nodes : (node_id list -> node list) + +(** create a function to get a node from it's ID *) +val get_of_id : (node list -> (node_id-> node)) diff --git a/tools/graphgen/graphGen.ml b/tools/graphgen/graphGen.ml new file mode 100644 index 0000000000000000000000000000000000000000..ae66f1543baa3da7fef0abc16578874df4080761 --- /dev/null +++ b/tools/graphgen/graphGen.ml @@ -0,0 +1,97 @@ +open Sasacore +open Topology +open GraphProp + +open ClassicGraph +open RandomGraph +open GraphGen_arg +open UdgUtils + +exception Incorrect_attribute + +let min_max = ref None +let connected_cyclic = ref None +let height = ref None + +let generate_du_dur graph plan_udg t : unit = + + if (t.dotUDG <> "") then ( + make_dot_udg_qudg graph plan_udg (t.qudg.width,t.qudg.height) (t.dotUDGrad)); + if (t.dotUDGrad <> "") then ( + make_dot_udg_qudg graph plan_udg (t.qudg.width,t.qudg.height) ~r0:(t.qudg.radius) ~r1:(t.qudg.r1) (t.dotUDGrad); + Printf.printf "%f -- %f" t.qudg.radius t.qudg.r1 + ); + () + +let compute_attr : (Topology.t -> string list -> (string*string) list) = + fun g -> List.map (fun attr -> + attr,match attr with + | "min_deg" -> string_of_int (match !min_max with + | None -> (let x = get_degree g in min_max := Some x; fst x) + | Some x -> fst x) + | "mean_deg" -> string_of_float (get_mean_degree g) + | "max_deg" -> string_of_int (match !min_max with + | None -> (let x = get_degree g in min_max := Some x; snd x) + | Some x -> snd x) + | "is_connected" -> string_of_bool (match !connected_cyclic with + | None -> (let x = is_connected_and_cyclic g in connected_cyclic := Some x; fst x) + | Some x -> fst x ) + | "is_cyclic" -> string_of_bool (match !connected_cyclic with + | None -> (let x = is_connected_and_cyclic g in connected_cyclic := Some x; snd x) + | Some x -> snd x ) + | "is_tree" -> string_of_bool (match !connected_cyclic with + | None -> (let x = is_connected_and_cyclic g in connected_cyclic := Some x; (fst x) && (snd x)) + | Some x -> (fst x) && (snd x) ) + | "links_number" -> string_of_int (get_nb_link g false) + | "diameter" -> string_of_int (get_diameter g) + | s -> string_of_int (let s = String.split_on_char ' ' s in + if List.hd s = "height" && List.length s = 2 then + get_height g (List.hd (List.tl s)) + else raise Incorrect_attribute) + ) + + + +let () = ( + let t = parse Sys.argv in + + if (t.n < 0) then ( + let msg = match t.action with + | "void" | "grid" -> "" + | "HC" -> ( + t.n <- 3; + "=========================================================================\n"^ + "Caution : the dimension is not defined or negative. It has been set to 3.\n"^ + "=========================================================================\n" + ) + | _ -> ( + t.n <- 10; + "=============================================================================\n"^ + "Caution : the nodes number is not defined or negative. It has been set to 10.\n"^ + "=============================================================================\n" + ) in + if (not t.silent) then Printf.fprintf stderr "%s" msg + ); + if (t.outputFile <> "" && not t.silent) + then Printf.printf "Generating a %s graph...\n" t.action; + let g = ( match t.action with + | "void" -> exit 0 + | "clique" -> (gen_clique t.n) + | "star" -> (gen_star t.n) + | "ring" -> (gen_ring t.n) + | "grid" -> (gen_grid t.grid.height t.grid.width) + | "HC" -> (gen_hyper_cube t.n) + | "ER" -> (gen_ER t.n t.er) + | "BA" -> (gen_BA t.n t.ba) + | "tree" -> (rand_tree t.n) + | "UDG" -> (let (graph, plan) = gen_udg t.n t.qudg.width t.qudg.height t.qudg.radius in + generate_du_dur graph plan t; graph) + | "QUDG" -> (let (graph, plan) = gen_qudg t.n t.qudg.width t.qudg.height t.qudg.radius t.qudg.r1 t.qudg.p in + generate_du_dur graph plan t; graph) + | _ -> (Printf.fprintf stderr "Unexpected outcome. Command line : %s\n" + (String.concat " " (Array.to_list Sys.argv)); assert false) + ) in + make_dot g t.outputFile (compute_attr g t.attr); + if (t.outputFile <> "" && not t.silent) + then Printf.printf "Done.\nOutput file : '%s'\n" t.outputFile +) diff --git a/tools/graphgen/graphGen_arg.ml b/tools/graphgen/graphGen_arg.ml new file mode 100644 index 0000000000000000000000000000000000000000..215579a4ebc7065bfcad706e6347668d2f7b7477 --- /dev/null +++ b/tools/graphgen/graphGen_arg.ml @@ -0,0 +1,347 @@ +let () = Random.self_init (); + +type action = string + +type grid_arg = { + mutable width: int; + mutable height: int; +} + +type qudg_arg = { + mutable width: float; + mutable height: float; + mutable radius: float; + mutable r1: float; + mutable p: float; +} + +type er_prob = float (*between 0 and 1*) +type ba_m = int (*positive*) + +type t = { + mutable outputFile: string; + mutable dotUDG: string; + mutable dotUDGrad: string; + mutable action: action; + + mutable n : int; + mutable grid : grid_arg; + mutable er : er_prob; + mutable ba : ba_m; + mutable qudg : qudg_arg; + + mutable attr : string list; + + mutable silent : bool; + + mutable _args : (string * Arg.spec * string) list; + mutable _man : (string * (string list * action) list) list; + + mutable _others : string list; + mutable _margin : int; +} + +let usage_msg do_print_command tool = + if do_print_command then + ("usage: " ^ tool ^ + " [options]\n") + else + ("usage: "^tool^" [options]\n") + +let print_usage output do_print_command tool = + Printf.fprintf output "%s%s" (usage_msg do_print_command tool) ( + if (do_print_command) then + "use -h to see the available commands.\n\n" + else "use -h to see available options.\n" + ) + + +let (make_args : unit -> t) = + fun () -> + { + outputFile = ""; + dotUDG = ""; + dotUDGrad = ""; + action = "void"; + + n = -1; + grid = { + width = 0; + height = 0; + }; + er = 0.3; + ba = 2; + qudg = { + width = 10.; + height = 10.; + radius = 3.; + r1 = 2.; + p = 0.5; + }; + + attr = []; + + silent = false; + + _args = []; + _man = []; + + _others = []; + _margin = 12; + } + +let first_line b = ( + try ( + let f = String.index b '\n' in + String.sub b 0 f + ) with Not_found -> b +) +let unexpected s = ( + prerr_string ("unexpected argument \""^s^"\""); + prerr_newline (); + exit 2 (* unexpected argument *) +) + + +let printSpec args outChannel action (c, messageList) = ( + List.iter (fun (ml,action_type) -> + if (action = action_type) then + let (m1, oth) = match ml with + | h::t -> (h,t) + | _ -> ("",[]) + in + let t2 = String.make args._margin ' ' in + let cl = 1 + String.length c in + let t1 = if (cl < args._margin ) then + String.make (args._margin - cl) ' ' + else + "\n"^t2 + in + Printf.fprintf outChannel "%s%s%s" c t1 m1; + List.iter (function x -> Printf.fprintf outChannel "\n%s%s" t2 x) oth ; + Printf.fprintf outChannel "\n" ; + ) messageList +) + +let help args tool = ( + Printf.printf "\n"; + Printf.printf "%s" (usage_msg (args.action = "void") tool); + Printf.printf "\n"; + ( + if (args.action = "void") then ( + Printf.printf "======================\n"; + Printf.printf " Available commands :\n"; + Printf.printf "======================\n"; + List.iter (printSpec args stdout "") [ + ("clique",[(["Generate a clique graph"],"")]); + ("star",[(["Generate a star graph"],"")]); + ("ring",[(["Generate a ring graph"],"")]); + ("grid",[(["Generate a grid graph"],"")]); + ("HC",[(["Generate a hyper-cube graph"],"")]); + ("ER",[(["Generate a graph using the Erdos Renyi algo"],"")]); + ("BA",[(["Generate a graph using the Barabasi–Albert algo"],"")]); + ("tree",[(["Generate an acyclic graph (tree)"],"")]); + ("UDG",[(["Generate a graph using the Unit Disc Graph algo"],"")]); + ("QUDG",[(["Generate a graph using the Quasi UDG algo"],"")]); + ]; + Printf.printf "\n"; + Printf.printf "Use '%s -h' to see the command's options.\n" tool; + Printf.printf "\n"; + ); + Printf.printf "==================\n"; + Printf.printf " General options :\n"; + Printf.printf "==================\n"; + List.iter (printSpec args stdout "void") (List.rev args._man); + if (args.action <> "void") then ( + Printf.printf "\n"; + Printf.printf "===================================\n"; + Printf.printf " Specific options to the command :\n"; + Printf.printf "===================================\n"; + List.iter (printSpec args stdout args.action) (List.rev args._man) + ); + ); + Printf.printf "\n"; + exit 0 +) + + +let (mkopt : t -> string list -> ?arg:string -> Arg.spec -> + (string list * action) list -> unit) = + fun opt ol ?(arg="") se ml -> + let add_option o = opt._args <- (o, se, "")::opt._args in + List.iter add_option ol ; + let col1 = (String.concat ", " ol)^arg in + opt._man <- (col1, ml)::opt._man + +(*** User Options Tab **) +let (mkoptab : string array -> t -> unit) = + fun argv args -> ( + + mkopt args ["--standard-output";"-stdout"] + (Arg.Unit (fun () -> args.outputFile <- "")) + [(["Set the output channel for the generated graph to stdout."; + "This is the output by default"; + "The output will have a DOT file syntax.\n"],"void")]; + + mkopt args ["--DOT-output";"-o"] ~arg:" " + (Arg.String (fun s -> args.outputFile <- s)) + [(["Set the output file for the generated graph to the given file."; + "The output will have a DOT file syntax.\n"],"void")]; + + let msg = "Set the node number in the graph\n" in + mkopt args ["--nodes-number";"-n"] ~arg:" " + (Arg.Int (fun n -> match args.action with + | "grid" | "HC" | "void" -> unexpected "-n" + | _ -> args.n <- n )) + [([msg],"clique");([msg],"star");([msg],"ring"); + ([msg],"ER");([msg],"BA");([msg],"tree");([msg],"UDG");([msg],"QUDG")]; + + mkopt args ["--dimension";"-d"] ~arg:" " + (Arg.Int (fun n -> match args.action with + | "HC"-> args.n <- n + | _ -> unexpected "-d")) + [(["Set the hyper-cube dimension.\n"],"HC")]; + + mkopt args ["--width";"-w"] ~arg:" " + (Arg.Float (fun w -> match args.action with + | "grid" -> args.grid.width <- (int_of_float w) + | "UDG" | "QUDG" -> args.qudg.width <- w + | _ -> unexpected "-w" )) + [(["Set the grid's width to the value (integer)\n"],"grid"); + (["Set the UDG's terrain width to the value (float)";"10 by default.\n"],"UDG"); + (["Set the QUDG's terrain width to the value (float)";"10 by default.\n"],"QUDG")]; + + mkopt args ["--height";"-he"] ~arg:" " + (Arg.Float (fun h -> match args.action with + | "grid" -> args.grid.height <- (int_of_float h) + | "UDG" | "QUDG" -> args.qudg.height <- h + | _ -> unexpected "-he")) + [(["Set the grid's height to the value (integer)\n"],"grid"); + (["Set the UDG's terrain height to the value (float)";"10 by default.\n"],"UDG"); + (["Set the QUDG's terrain height to the value (float)";"10 by default.\n"],"QUDG")]; + + + mkopt args ["--edge-probability";"-p"]~arg:" " + (Arg.Float (fun p -> match args.action with + | "ER" -> args.er <- p + | _ -> unexpected "-p")) + [(["Set the edge appearing probability to the given value."; + "Must be between 0 and 1, and is set to 0.3 by default\n"],"ER")]; + + + mkopt args ["--";"-m"]~arg:" " + (Arg.Int (fun m -> match args.action with + | "BA" -> args.ba <- m + | _ -> unexpected "-m")) + [(["Set the number of edge generated per additional node to the given value"; + "(2 by default)\n"],"BA")]; + + mkopt args ["--radius";"-r"]~arg:" " + (Arg.Float (fun r -> match args.action with + | "UDG" -> args.qudg.radius <- r + | _ -> unexpected "-r")) + [(["Set the Unit Disc's radius around all nodes to the given value.";"3 by default.\n"],"UDG")]; + + mkopt args ["--first-radius";"-r0"]~arg:" " + (Arg.Float (fun r -> match args.action with + | "QUDG" -> args.qudg.radius <- r + | _ -> unexpected "-r0")) + [(["Set the first radius around all nodes to the given value.";"3 by default.\n"],"QUDG")]; + + mkopt args ["--second-radius";"-r1"]~arg:" " + (Arg.Float (fun r -> match args.action with + | "QUDG" -> args.qudg.r1 <- r + | _ -> unexpected "-r1")) + [(["Set the second radius around all nodes to the given value.";"2 by default.\n"],"QUDG")]; + + mkopt args ["--probability";"-p"]~arg:" " + (Arg.Float (fun p -> args.qudg.p <- p)) + [(["Sets the probability of the nodes being neighbors when they are inside the second"; + "radius, but not the first one.\n" + ], "QUDG")]; + + let msg = ["Create a DOT file to visualize the UDG plan."; + "When it transformed into a PDF that takes the positioning tags into account"; + "(like 'neato' command from GraphViz), each node is visible at the coordinates"; + "where they were placed during execution.\n"] in + mkopt args ["--dot_udg";"-du"]~arg:" " + (Arg.String (fun f -> match args.action with + | "UDG" | "QUDG" -> args.dotUDG <- f + | _ -> unexpected "-du")) + [(msg,"UDG");(msg,"QUDG")]; + + mkopt args ["--dot_udg_radius";"-dur"]~arg:" " + (Arg.String (fun f -> match args.action with + | "UDG" | "QUDG" -> args.dotUDGrad <- f + | _ -> unexpected "-dur")) + [(["Create a DOT file to visualize the UDG plan."; + "Same as the option '-du', but with the radius being also displayed.\n"],"UDG"); + (["Create a DOT file to visualize the UDG plan."; + "Same as the option '-du', but with the two radiuses being also displayed.\n"],"QUDG")]; + + mkopt args ["--silent";"-s"] + (Arg.Unit (fun () -> args.silent <- true)) + [(["Remove all outputs, except the dot output if it is on stdout,"; + "and the error if one occurred.\n"],"void")]; + + args._man <- ("--attributes, -atr ...", [([ + "Specify the given attributes of the graph to his DOT file."; + "the possible attributes are :"; + "min_deg, mean_deg, max_deg, is_connected, is_cyclic, is_tree, links_number, + diameter, height i (with i being the index of a node)" + ],"void")])::args._man; + + mkopt args ["--help";"-h"] + (Arg.Unit (fun () -> help args ((argv.(0))^(if args.action = "void" then "" + else " "^args.action)))) + [(["Prints the help of the command.\n"],"void")]; + ) + +(* all unrecognized options are accumulated *) +let (add_other : t -> string -> unit) = + fun opt s -> + opt._others <- s::opt._others + +let current = ref 1;; + +let parse argv = ( + let save_current = !current in + let args = make_args () in + mkoptab argv args; + try ( + (if (Array.length argv) = 1 then (print_usage stdout true argv.(0); exit 1 (* no command or invalid command *))); + let possible_actions = ["clique";"star";"ring";"grid";"HC";"ER";"BA";"tree";"UDG";"QUDG"] in + ( + if (List.mem argv.(1) possible_actions) then args.action <- argv.(1) + else + if (List.mem argv.(1) ["-h";"--help";"-help"]) then help args (argv.(0)) else + (Printf.fprintf stderr "*** Error when calling '%s %s': No such command\n\n" (argv.(0)) (argv.(1)); + (print_usage stderr true argv.(0)); exit 1 (* no command or invalid command *)) + ); + + Arg.parse_argv ~current:current argv args._args (add_other args) (usage_msg false (argv.(0)^argv.(1))); + current := save_current; + args._others <- List.rev args._others; + let have_atr = ref false in + (List.iter (fun o -> + if !have_atr then ( + if (String.sub o 0 1) = "-" then + unexpected o + else + args.attr <- o::args.attr + ) else ( + if List.mem o ["--attributes";"-atr"] then + have_atr := true + else + unexpected o + ) + ) args._others); + args + ) + with + | Arg.Bad msg -> + Printf.fprintf stderr "*** Error when calling '%s': %s\n" (argv.(0)) (first_line msg); + (print_usage stderr true argv.(0)); exit 3 (* bad argument *); + | Arg.Help _msg -> + help args argv.(0) +) diff --git a/tools/graphgen/graphGen_arg.mli b/tools/graphgen/graphGen_arg.mli new file mode 100644 index 0000000000000000000000000000000000000000..70eda6d08b2c1ac7142cf488f2038518bc71acf3 --- /dev/null +++ b/tools/graphgen/graphGen_arg.mli @@ -0,0 +1,42 @@ +type action = string + +type grid_arg = { + mutable width: int; + mutable height: int; +} + +type qudg_arg = { + mutable width: float; + mutable height: float; + mutable radius: float; + mutable r1: float; + mutable p: float; +} + +type er_prob = float (*between 0 and 1*) +type ba_m = int (*positive*) + +type t = { + mutable outputFile: string; + mutable dotUDG: string; + mutable dotUDGrad: string; + mutable action: action; + + mutable n : int; + mutable grid : grid_arg; + mutable er : er_prob; + mutable ba : ba_m; + mutable qudg : qudg_arg; + + mutable attr : string list; + + mutable silent : bool; + + mutable _args : (string * Arg.spec * string) list; + mutable _man : (string * (string list * action) list) list; + + mutable _others : string list; + mutable _margin : int; +} + +val parse : (string array -> t) diff --git a/tools/graphgen/randomGraph.ml b/tools/graphgen/randomGraph.ml new file mode 100644 index 0000000000000000000000000000000000000000..1fb625ad5c5a44214c64e623cc515b9dc14b8b14 --- /dev/null +++ b/tools/graphgen/randomGraph.ml @@ -0,0 +1,157 @@ +open Sasacore +open Topology +open Ggcore +open List + +type node_succ_t = (node_id, (int option * node_id) list) Hashtbl.t +type probability = float (*between 0 and 1*) + +let gen_ER : (int -> probability -> Topology.t) = + fun nb p -> + let (node_succ:node_succ_t) = Hashtbl.create nb and nodes = create_nodes "p" (0,nb) in + iteri (fun i n -> + iteri (fun j m -> + if (i < j) && (Random.float 1.) < p then + (Hashtbl.replace node_succ n + ((None,m)::(try Hashtbl.find node_succ n with Not_found -> [])); + Hashtbl.replace node_succ m + ((None,n)::(try Hashtbl.find node_succ m with Not_found -> []))) + ) nodes + ) nodes; + let nl = id_to_empty_nodes nodes in + { + nodes = nl; + succ = (fun n -> try Hashtbl.find node_succ n with Not_found -> []); + of_id = get_of_id nl + } + + +let rec init_m_nodes : (int -> node_succ_t -> node_id list -> node_id list) = + fun i node_succ -> + function + | (node::tail) -> + if i > 0 then + (Hashtbl.replace node_succ node []; + init_m_nodes (i-1) node_succ tail) + else node::tail + | _ -> assert false + +let neighbours_BA : (node_id list -> int -> node_succ_t -> (node_id -> (int option * node_id) list)) = + fun nodes m node_succ -> + let d_tot = 2 * m and nodes = init_m_nodes m node_succ nodes in + match nodes with + | [] -> assert false + | head::nodes -> Hashtbl.replace node_succ head ( + Hashtbl.fold (fun n _ succ -> Hashtbl.replace node_succ n [(None,head)]; (None,n)::succ) node_succ [] + ); + (*init terminée. On a un graph connexe pour les m+1 premiers points, nl ne contient que les points non ajoutés*) + ignore (fold_left (fun deg_tot node -> + + let deg_temp = deg_tot and succ = ref [] in + let deg_temp = ref deg_temp in + + for _ = 0 to m-1 do (*for each edge to create*) + let ran = Random.int !deg_temp in + ignore (Hashtbl.fold (fun n_id n_succ r -> + if r >= 0 && not (List.mem (None,n_id) !succ) then + let r = r - (length n_succ) in ( + if r < 0 then + (succ := (None,n_id)::!succ; + Hashtbl.replace node_succ n_id + ((None,node)::n_succ); + deg_temp := !deg_temp - length n_succ) + ); r + else r + ) node_succ ran); + + done; + + + Hashtbl.replace node_succ node !succ; + (deg_tot + (2 * m)) + + ) d_tot nodes); + (fun n -> try Hashtbl.find node_succ n with Not_found -> []) + + + + + +let gen_BA : (int -> int -> Topology.t) = + fun nb m -> + let (node_succ:node_succ_t) = Hashtbl.create nb and nodes = create_nodes "p" (0,nb) in + if nb < m + 1 then + failwith ( + Printf.sprintf "BA Error : with m = %d, nb needs to be at least %d. %d is lower than %d" m (m+1) nb (m+1)); + + let nl = id_to_empty_nodes nodes in + { + nodes = nl; + succ = neighbours_BA nodes m node_succ; + of_id = get_of_id nl + } + +let pre_rand_tree : (node_succ_t -> node_id list -> (node_id -> (int option * node_id) list)) = + fun node_succ -> + function + | [] -> failwith "Tree Error : You need at least one nodes in your tree" + | h::t -> + ignore (List.fold_left (fun acc elem -> + let no = (List.nth acc (Random.int (List.length acc))) in + (Hashtbl.replace node_succ no ((None,elem)::(try Hashtbl.find node_succ no with Not_found -> [])); + Hashtbl.replace node_succ elem ((None,no)::(try Hashtbl.find node_succ elem with Not_found -> [])) + ); + (elem::acc) + ) [h] (t)); + (fun n -> try Hashtbl.find node_succ n with Not_found -> []) + +let (rand_tree: int -> Topology.t) = + fun nb -> + let (node_succ:node_succ_t) = Hashtbl.create nb and nodes = create_nodes "p" (0,nb) in + let nl = id_to_empty_nodes nodes in + { + nodes = nl; + succ = (pre_rand_tree node_succ nodes); + of_id = get_of_id nl + } + + +type node_udg = node_id*float*float +type plan_udg = node_udg list + +let (make_plan_udg: node_id list -> float -> float -> plan_udg) = + fun nodes x y -> + List.map (fun elem -> (elem,(Random.float x),(Random.float y))) (nodes) + +let (dist_udg: node_udg -> node_udg -> float) = + fun n1 n2 -> + let (_,x1,y1) = n1 and (_,x2,y2) = n2 in + sqrt (((x1-.x2)**2.) +. ((y1 -. y2)**2.)) + +let gen_qudg : (int -> float -> float -> float -> float -> float -> (Topology.t * plan_udg)) = + fun nb x y r0 r1 p -> + let (node_succ:node_succ_t) = Hashtbl.create nb and nodes = create_nodes "p" (0,nb) in + let pl = (make_plan_udg nodes x y) in + List.iter (fun n_udg -> + let (node, _, _) = n_udg in + List.iter (fun elem -> + let (n,_,_) = elem and dist = dist_udg n_udg elem in + + if node <> n && (dist <= r0 || (dist <= r1 && Random.float 1. <= p)) + (* e.q. if the node is : (within the radius r0) + or : (within the radius r1, with a brobability of p) *) + then ( + Hashtbl.replace node_succ node + ((None,n)::(try Hashtbl.find node_succ node with Not_found -> [])) + ) + ) pl + ) pl; + let nl = id_to_empty_nodes nodes in + { + nodes = nl; + succ =(fun n -> (try Hashtbl.find node_succ n with Not_found -> [])); + of_id = get_of_id nl + },pl + +let gen_udg : (int -> float -> float -> float -> (Topology.t * plan_udg)) = + fun nb x y r -> (gen_qudg nb x y r 0. 0.) \ No newline at end of file diff --git a/tools/graphgen/randomGraph.mli b/tools/graphgen/randomGraph.mli new file mode 100644 index 0000000000000000000000000000000000000000..85d6476a854ceb14adb8ff234d028f68d629b35c --- /dev/null +++ b/tools/graphgen/randomGraph.mli @@ -0,0 +1,34 @@ +open Sasacore +open Topology +type probability = float (*between 0 and 1*) + +type node_udg = node_id*float*float +type plan_udg = node_udg list + +(** [gen_ER n p] generate a graph using Erdos Renyi model, + of n nodes and of probability p for each possible edge to appear. *) +val gen_ER : (int -> probability -> Topology.t) + +(** [gen_BA n m] generate a graph using Barabasi–Albert model, + of n nodes and with m edges added for each new node. + m has to be lower than n. + The initialization is a star of m+1 nodes, with the (m+1)th node being the root. + Barabasi–Albert model is used for the remaining nodes *) +val gen_BA : (int -> int -> Topology.t) + +(** [rand_tree n] generate a random tree of n nodes *) +val rand_tree: (int -> Topology.t) + +(** [gen_udg nb x y r] generate a graph using the Unit Disc Graph model, of n nodes. + w and h are the width and the height of the area in which the nodes are randomly disposed, + and r is the radius around each node, in which all the other nodes will be neighbors. + *) +val gen_udg : (int -> float -> float -> float -> (Topology.t * plan_udg)) + +(** [gen_qudg nb x y r0 r1 p] generate a graph using the Quasi Unit Disc Graph model, of n nodes. + w and h are the width and the height of the area in which the nodes are randomly disposed. + r0, r1 and p are three values to determine if two nodes, at a distance d of each other, + are neighbors. If d <= r0, they are neighbors. Otherwise, if d <= r1, + they have a probability of p of being neighbors. + *) +val gen_qudg: (int -> float -> float -> float -> float -> float -> (Topology.t * plan_udg)) diff --git a/tools/graphgen/udgUtils.ml b/tools/graphgen/udgUtils.ml new file mode 100644 index 0000000000000000000000000000000000000000..8dd52f8ad7764a963e2c5984f5843436f37612ae --- /dev/null +++ b/tools/graphgen/udgUtils.ml @@ -0,0 +1,74 @@ +open Sasacore +open Topology +type probability = float (*between 0 and 1*) +type prob_udg = (float -> probability) + +type node_udg = node_id*float*float +type plan_udg = node_udg list + +let prob_from_list : (float list -> float -> prob_udg) = + fun l r d -> (List.nth l (int_of_float (((float_of_int (List.length l))/.r)*.d))) + +let linear_prob : (float -> prob_udg) = + fun r -> (fun d -> d/.r) + +let prob_from_constant : (float -> prob_udg) = + fun x _ -> x + +let recommend_radius : (int -> float -> float -> float -> float) = + fun nb_node mean_deg h w -> + if(mean_deg > float_of_int nb_node) then + failwith "Error : the number of node should be greater or equal than mean degree" + else + sqrt ((h*.w)*.mean_deg/.(Float.pi*.(float_of_int nb_node))) + +let recommend_nb_node : (float -> float -> float -> float -> int) = + fun radius mean_deg h w -> + (int_of_float ((mean_deg*.h*.w)/.((radius**2.)*.Float.pi)))+1 + +let compute_mean_degree : (int -> float -> float -> float -> float) = + fun nb_node radius h w -> + ((radius**2.)*.Float.pi*.(float_of_int nb_node))/.(h*.w) + +(******************************************************************************) + +let rec make_nodes_dot_udg : (node_udg list -> float -> float -> string) = + (*Create a string in the dot syntax from a node list*) + fun nudg r0 r1 -> + match nudg with + | [] -> "" + | head::tail -> + let (node,x,y) = head in + (Printf.sprintf "%s [pos=\"%f,%f!\"]\n" node x y )^ + let draw_rad = (if (r0 > 0.) then + (Printf.sprintf "%srad [pos=\"%f,%f!\",width=%f, length=%f,shape = circle,label=\"\",color=\"red\"]\n" + node x y (2.*.r0) (2.*.r0) ) else "")^ + if(r1 > r0) then + (Printf.sprintf "%srad2 [pos=\"%f,%f!\",width=%f, length=%f,shape = circle,label=\"\",color=\"lightblue\"]\n" + node x y (2.*.r1) (2.*.r1) ) else "" in + draw_rad^(make_nodes_dot_udg tail r0 r1) + +let make_dot_udg_qudg : (Topology.t -> plan_udg -> (float*float) -> ?r0:float -> ?r1:float -> string -> unit) = + (*Create a dot file from a graph*) + fun t plan dim ?(r0 = 0.) ?(r1 = 0.) file_name -> + let name = ref "graph0" in (* default name *) + let f = (if file_name = "" then stdout else + ( + name := file_name; + (try ( (* remove all extensions. So if name = ref "tt.dot.dot" at the beginning, at the end name = ref "tt". *) + while true do + name := Filename.chop_extension !name; + done; + ) with Invalid_argument _ -> ()); + open_out file_name + ) + ) in + let (w,l) = dim in + let mpos = if(r0 > 0. || r1 > 0.) then + (Printf.sprintf "size = \"%f,%f!\"\ntopLeft [pos=\"%f,%f!\",style=invis]\nlowRight [pos=\"0,0!\",style = invis]\nnode [fixedsize=false,shape=circle]\n" w l w l) + else "" in + let dot = (Printf.sprintf "graph %s {\n\n"!name )^mpos + ^(make_nodes_dot_udg plan r0 r1) ^ "\n" ^ (make_links_dot t) ^ "\n}\n" in + Printf.fprintf f "%s" dot; + flush f; + close_out f diff --git a/tools/graphgen/udgUtils.mli b/tools/graphgen/udgUtils.mli new file mode 100644 index 0000000000000000000000000000000000000000..df43f39604295c605d5a3fa0e88ae0adef2b7045 --- /dev/null +++ b/tools/graphgen/udgUtils.mli @@ -0,0 +1,43 @@ +open Sasacore +open Topology +type probability = float (*between 0 and 1*) +type prob_udg = (float -> probability) + +type node_udg = node_id*float*float +type plan_udg = node_udg list + + +(** create a probability function for UDG that always return the same probability *) +val prob_from_constant: (float -> prob_udg) + +(** [proba_from_list fl r] create a probability function for UDG that changes the probability according to the distance. + It cuts r into (length fl), and attribute the first element of fl to the first slice (closest to the center), and so on. + For example, [proba_from_list [1.;0.5;0.3;0.1] 10 d] will return 1. if 0 <= d < 2.5, 0.5 if 2.5 <= d < 5, and so on. + Note that r must be equal to the radius of the Unit Disc *) +val prob_from_list: (float list -> float -> prob_udg) + +(** [linear_proba r] gives a function that, for an input d (distance) outputs d/r. + If r is the Unit Disc radius and d the distance between two points, + it outputs a probability that is higher for a low d and lower for a high d. *) +val linear_prob: (float -> prob_udg) + +(** [recommend_radius n mean_deg h w] returns the recommended radius to give to UDG + in order to get a mean degree approximately equal to mean_deg, + knowing there's n nodes and the UDG is applied in an area of height h and width w. *) +val recommend_radius : (int -> float -> float -> float -> float) + +(** [reccomend_nb_node r mean_deg h w] returns the recommended number of nodes + to give to UDG in order to get a mean degree approximately equal to mean_deg, + knowing the radius is r and the UDG is applied in an area of height h and width w. *) +val recommend_nb_node : (float -> float -> float -> float -> int) + +(** [compute_mean_degree n r h w] computes and return the approximative mean degree of + an UDG graph with the same arguments. *) +val compute_mean_degree : (int -> float -> float -> float -> float) + +(** [make_dot_udg g p (h,w) ~r=r f] Creates a DOT file to represent the UDG area, + r being the radius, g being the graph, p being the UDG area, + and (h,w) being the dimensions of the plan. + If no radius is given, they won't appear on the pdf. + If you have Grahviz, we advice using 'twopi -Tpdf f' to obtain a pdf. *) +val make_dot_udg_qudg : (Topology.t -> plan_udg -> (float*float) -> ?r0:float -> ?r1:float -> string -> unit) diff --git a/tools/scriptEnv.py b/tools/scriptEnv.py new file mode 100644 index 0000000000000000000000000000000000000000..c3028dbe26a7b69300a55caabb7dd4d5ef532c58 --- /dev/null +++ b/tools/scriptEnv.py @@ -0,0 +1,439 @@ +import subprocess, os, datetime, math + +def call(command, log = None, verb = 2, options = {}): + """Calls the shell command given in argument, and give its output in stdout as return value + + If the command fails (command return value other than 0), the command's stdout is printed, and + the error 'subprocess.CalledProcessError' is raised. + + Optional Args : + log (filename): a log file, in which the command's stdout will be appended + verb (int): the verbose level. It has three levels : 0,1 or 2. + On level 0, nothing will be printed excepted the raised error + On level 1, if there's an error, the command's stdout will be printed. + On level 2 (default), a message will tell when the command is called, + and when it has finished. + On level 3, the command's stdout will always be printed + options (dict): a way to add options in the command line. Each key of the dict will + be added to the command line with an additional "-" in front, followed by the + corresponding value in the dict. For example, {"n":3, "o":"toto.ml"} will add the + string '-n "3" -o "toto.ml"' to the command line. + + Return (str) : the output of the command + + Note : the module subprocess is used to call the command + + """ + + if verb > 1 : + print("Calling '"+command.split()[0]+"' ...") + + for k in options: + if options[k] == "" : + command += " -"+k + else : + command += " -"+k+' "'+str(options[k])+'"' + + try: + output = subprocess.check_output( + command, shell=True, stderr=subprocess.STDOUT, encoding = "utf-8") + except subprocess.CalledProcessError as exc: + if log != None: + f = open(log, "a+") + f.write(exc.output) + if verb > 2 : + print("------ output : ------") + if verb > 0 : + print(exc.output) + if verb > 2 : + print("----------------------") + raise + + if log != None: + f = open(log, "a+") + f.write(output) + if verb > 2 : + print("------ output : ------") + print(output) + print("----------------------") + if verb > 1 : + print("Done.") + return output + +def genGraph(graphType, outputFile, options, is_silent = False, log = None, verb = 2): + """Generate a graph, using the 'call' function on the command 'gg'. + + It also removes any '.lut' file at the name of the outputFile + + Args: + graphType (str): The first argument given to 'gg', corresponding to the command in 'gg '. + Use 'gg -h' in console to view the available commands (corresponding to available graphTypes). + outputFile (str): The output file given to 'gg' with the option '-o'. + We strongly advice to give it a '.dot' extension + graphProperties (dict): options given to 'call' function. + is_silent (bool, optional): Indicates whether the option '-s' will be added to the command line or not. + log (str) : the log file given to 'call' + verb (int) : the verbose level given to 'call' + + Return (str): the output of the 'gg' command + + """ + + args = "gg " + graphType + ' -o "' + outputFile + '"' + + if is_silent: + args += "-s" + + ret = call(args, log, verb, options) + + try : + os.remove(outputFile[0:-3]+"lut") + except FileNotFoundError as e: + pass + return ret + +def compileAlgos(fileList): + """Compile an iterable object (like lists or sets) of .ml files into .cmxs files, + or compile the .ml files of a string in the algo-files syntax supported by gg-deco.""" + if type(fileList) == str : + tmp = fileList + fileList = [] + for s in tmp.split(): + fileList.append(s.split(":")[1]) + ret = [] + for file in fileList: + ret.append(subprocess.call(["ocamlfind", "ocamlopt", "-shared", "-package", "algo", file, "-o", file[0:-3]+".cmxs"])) + return ret + +def decoGraph(inputFile: str, decoration, outputFile:str = "", comp = True): + """Decorate a graph, using the 'call' function on the command 'gg-deco' + + Args : + inputFile (str) : the DOT file to decorate + decoration : a string, list or a dictionary describing the files of each node. + If it is a string, it will be given as it is to gg-deco + outputFile (str, optional) : The name for the decorated DOT file. + Same as the inputFile by default. + comp (bool, optional) : If True, runs 'compileAlgo' on decoration + + Return : A list of the return values of each file + + """ + + if (outputFile == "") : + outputFile = inputFile + spec = [] + files = [] + + if type(decoration) == dict: + for key in decoration: + spec.append(str(key) + ":" + decoration[key]) + files.append(decoration[key]) + elif type(decoration) == str: + files = decoration + spec = decoration.split() + else : # type list + spec = decoration + for s in decoration: + s = s.split(":") + files.append([1]) + + ret = subprocess.call(["gg-deco"] + spec + [inputFile, "-o", outputFile]) + if comp : + ret = (ret, compileAlgos(files)) + return ret + +def callSasa(topologyFile, length = 200, seed = None, daemon = "dd", rif = True): + """Calls sasa on the specified topologyFile (a decorated DOT file) + + Needs to have the compiled versions of all .ml files used in the topology file + in the same directory. May create a .lut file if the daemon "custd" is used. + + Args : + topologyFile (string) : the DOT file on which sasa is executed + length (int, optional) : the maximum number of steps + seed (int, optional) : the pseudo-random number generator seed. Random by default + daemon (string, optional) : Indicates the daemon to use. List of possible daemons below + rif (bool, optional) : if True, indicates to sasa to output in RIF syntax + + Return : the output of sasa (string) + + Possible daemons : + "sd": Synchronous demon (selects as much actions as possible, up to one action per node) + "cd": Central demon (selects exactly one action) + "lcd" : Locally Central demon (never activates two neighbors' actions in the same step) + "dd" : Distributed demon (select at least one action, randomly) + + """ + + commandLine = ["sasa", "-l", str(length), topologyFile] + + if seed != None: + commandLine.append("-seed") + commandLine.append(str(seed)) + + if rif: commandLine.append("-rif") + + commandLine.append("-"+daemon) + + #if (daemon == "custd") and os.path.isfile(topologyFile[0:-3]+"lut") : + # subprocess.call(["sasa", "-gld", topologyFile]) + + return subprocess.check_output(commandLine, stderr=subprocess.STDOUT, encoding = "utf-8") + +def parseRif(rif): + """ Parse a rif document made by a sasa call (from sasa's stdout or output file) + + Return : a tuple of two elements : + - A list of the names of the variable + + - A list containing the state of each step. + (A state is the list of the values of the variables.) + """ + + """ + Example (not in the docstring) : + >>> rif = '\n#outputs "p0_x":int "p1_x":int "Enab_p0_action":bool "Enab_p1_action":bool ' + >>> rif += '"p0_action":bool "p1_action":bool\n\n#step 1\n #outs 4 2 t t t t\n\n#step 2\n ' + >>> rif += '#outs 0 0 f t f t\n\n#step 3\n #outs 0 1 f f f f\n\n#quit\n' + >>> (names, states) = parseRif(rif) + >>> names + ['p0_x', 'p1_x', 'Enab_p0_action', 'Enab_p1_action', 'p0_action', 'p1_action'] + >>> states + [[4, 2, True, True, True, True], [0, 0, False, True, False, True], [0, 1, False, False, False, False]] + + """ + + rif = list(filter(None, rif.split("\n"))) + names = [] + types = [] + states = [] + possibleTypes = {"int":int,"real":float,"bool":bool} + for line in rif: + newStateLine = [] + line = [x.split() for x in line.split("#", 1)] + + for v in line[0]: + newStateLine.append(v) + pragma = line[1][0] + if pragma in ["inputs", "outputs"]: + for n in line[1][1:]: + n = n.split(":") + names.append(n[0][1:-1]) + types.append(possibleTypes[n[1]]) + + if pragma == "outs": + for v in line[1][1:]: + newStateLine.append(v) + + tmp = [] + for v, t in zip(newStateLine, types): + if t != bool: + tmp.append(t(v)) + else : + tmp.append(v in ["t","T","1"]) + + if tmp != []: + states.append(tmp) + + return (names,states) + +def column(i, matrix, names = None): + """Outputs a list with the elements of a column in the matrix. + + If names is not specified, outputs the column i of the matrix + If names is specified, outputs the column at the index of i in names. + (useful to get the states of a variable from parseRif though all the steps) + + """ + + if names != None : i = names.index(i) + return [x[i] for x in matrix] + +class FileManager: + """Manage files of a project, to keep traces of a test battery. + Inside a project directory, there will be a directory for each version + (i.e. for each creation of a Project object). + + Proprieties : + path (str) : the path to the current version + """ + + def __init__(self, projectName = "sasaProject", version_name = "", date = True, time = True, add_index = True): + """Initialize a version of the project. + + Args : + projectName (str) : The name of the project, in which a new version will be created. + version_name (str, optional) : indicates the version's name base name + date (bool, optional) : indicates if the version's name will contain the date after the base name + time (bool, optional) : indicates if the version's name will contain the time after the date or base name + add_index (bool, optional) : indicates if the version's name will contain an index if a version of the same + name already exists (to ensure not to mix an old version with the new one). + + """ + + self.name = projectName # the name of the project + version = version_name + now = str(datetime.datetime.now()).split() + if version_name == "" and date: + version += "_" + if date: + version += now[0] + if date and time: + version += "_" + if time: + version += now[1].split(".")[0] + + try: + os.makedirs(projectName) + except FileExistsError: + pass + + if os.path.exists(projectName+"/"+version) and add_index : + i = 0 + while os.path.exists(projectName+"/"+version+"_"+str(i)):i += 1 + version += "_"+str(i) + + try : + os.makedirs(projectName+"/"+version) + except FileExistsError: + if add_index : raise + + self.version = version # the name of the version + self.path = projectName+"/"+version+"/" # the path to the version + + def createDir (self, dir): + """Creates the directory dir in the current version if it doesn't exist. + + Return value (bool) : True if the directory was created, False if it already existed + + Note : this method is principally created to be used by other methods in the object. + + """ + + try: + os.makedirs(self.path + dir) + return True + except FileExistsError: + return False + + def open(self, dir, name, ext, openType = 'w+', add_index = True): + """Use the built-in function open() to open a file in the directory 'dir' of the current version. + + The actual name of the file is 'name_i.ext', with name and ext being the arguments of the same name, + and i being the lower number such as no other file has the same name. + + Args : + dir (str) : the name of the directory in which the file will be created. Creates it if it doesn't exists. + name (str) : the name of the file, without extension. + ext (str) : the extension of the file. If it doesn't start by a point ('.'), it will be added. + openType (str, optional) : the second argument given to the built-in function open(). + Note that if add_index is set to False and the file already exists, + 'w'/'w+' will overwrite the file, whereas 'a'/'a+' and 'r' will not. + add_index (bool, optional) : indicates if an index will be given to the file's name. + Set it to True if you want to ensure that the opened file is a new file. + + + Return value : + The opened file (same as "open(path)") + + """ + + self.createDir(dir) + i = 0 + path = self.path + dir + "/" + name + if ext != "" and ext[0] != ".": ext = "."+ext + + while os.path.exists(path + "_" + str(i) + ext):i += 1 + path += "_"+str(i)+ ext + + f = open(path, 'w+') + + return f + + def add_file(self, dir, name, ext, content = "", add_index = True): + """Creates a new file in the directory dir of the current version. + + The actual name of the file is 'name_i.ext', with name and ext being the arguments of the same name, + and i being the lower number such as no other file has the same name. + + Args : + dir (str) : the name of the directory in which the file will be created. Creates it if it doesn't exists. + name (str) : the name of the file, without extension. + ext (str) : the extension of the file. + content (str, optional) : a string that will be added automatically to the file on creation. + add_index (bool, optional) : indicates if an index will be given to the file's name. + Set it to True if you want to ensure that no file of the same name + is overwritten on creation. + + Return value : + The path to the created file + + """ + self.createDir(dir) + i = 0 + path = self.path + dir + "/" + name + #if ext != "" and ext[0] != ".": ext = "."+ext + + while os.path.exists(path + "_" + str(i) + ext):i += 1 + path += "_"+str(i)+ ext + + f = open(path, 'w+') + + f.write(content) + f.close() + + return path + + def open_i(self, i, dir, name, ext, openType = 'a'): + """Opens a file in dir with the name 'name_i.ext'. + (The point between 'i' and 'ext' is for readability, and is not added by the method.) + + Note : Use it to modify a file created previously, not to create a file at index i + unless you are sure of what you are doing + + Args : + dir (str) : the name of the directory in which the file will be created. Creates it if it doesn't exists. + name (str) : the name of the file, without extension. + ext (str) : the extension of the file. If it doesn't start by a point ('.'), it will be added. + + """ + + self.createDir(dir) + return open(self.path + dir + "/" + name + "_" + str(i) + ext, openType) + + def read_i(self, i, dir, name, ext): + f = open_i(self, i, dir, name, ext, "r") + return f.read() + + def exist_i(self, i, dir, name, ext): + """Checks if the file 'name_i.ext' exists in dir. + (The point between 'i' and 'ext' is for readability, and is not added by the method.)""" + + return os.path.exists(self.path + dir + "/" + name + "_" + str(i) + ext) + + def get_last(self, dir, name, ext): + """Outputs the index just before the first free index. + + The purpose of this method is to give the last used index, but it might be wrong if + files are deleted or created without using this object, or if a file is created using open_i. + + Args : + dir (str) : the name of the directory in which the file will be created. Creates it if it doesn't exists. + name (str) : the name of the file, without extension. + ext (str) : the extension of the file. If it doesn't start by a point ('.'), it will be added. + + """ + + i = 0 + while os.path.exists(self.path + dir + "/" + name + "_" + str(i) + ext): i += 1 + return i - 1 + +class UDGtools(object): + """A set of tools to use UDG""" + @property # so that UDGtools() gives an error + def __init__(self):pass + + @staticmethod + def getRadius(n, h, w, md): + math.sqrt(h * w * md / (math.pi * n)) diff --git a/tools/scriptSkeleton.py b/tools/scriptSkeleton.py new file mode 100644 index 0000000000000000000000000000000000000000..34984d5e01c8c3e3325be7143a5db847d81f56ac --- /dev/null +++ b/tools/scriptSkeleton.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 + +import sys +sys.path.insert(0, //path_to_env//) +from scriptEnv import * + +genGraph("ring", "ring.dot", {"n":20}, True) +#call("gg ring -n 20 -o ring.dot") + +decoGraph("ring.dot", "0-:p.ml", comp = True) +#call('gg-deco 0-:p.ml ring.dot -o ring.dot', verb = 1) +#compileAlgos(["p.ml"]) + +(_,vals) = parseRif(callSasa("ring.dot")) +#(_,vals) = parseRif(call("sasa -rif ring.dot")) + +print(len(vals)) \ No newline at end of file diff --git a/tools/scriptSkeletonProject.py b/tools/scriptSkeletonProject.py new file mode 100644 index 0000000000000000000000000000000000000000..686502889606746384fd99dc982e1f7b8ad02b1e --- /dev/null +++ b/tools/scriptSkeletonProject.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 + +import sys +sys.path.insert(0, //path_to_env//) +from scriptEnv import * + +myProj = Project() +compileAlgos(["p.ml"]) +totSteps = 0 + +for _ in range(10): + path = myProj.add_file("dots", "ring", ".dot") + genGraph("ring", path, {"n":20}, True) + + decoGraph(path, "0-:p.ml", comp = False) + + (_,vals) = parseRif(callSasa(path)) + + totSteps += len(vals) + +print(totSteps, totSteps/10) \ No newline at end of file diff --git a/tools/test/skeleton/p.ml b/tools/test/skeleton/p.ml new file mode 100644 index 0000000000000000000000000000000000000000..0f117826054d0b534ff6ead051a86737c67e83ad --- /dev/null +++ b/tools/test/skeleton/p.ml @@ -0,0 +1,49 @@ +(* Time-stamp: *) + +(* a dumb algo *) + +open Algo + +type memory = int + +let (init_state: int -> memory) = + fun _i -> Random.int 10 + +(*let (state_to_string: memory -> string) = + fun m -> + string_of_int m*) + + +let (copy_state : memory -> memory) = + fun m -> m + +let (enable_f:'v neighbor list -> 'v -> action list) = + fun nl e -> + match (List.hd nl).state with + | 0 -> if e <> 1 then ["action2"] else [] + | 1 -> [] + | _ -> if e <> 0 then ["action1"] else [] + +let (step_f : 'v neighbor list -> 'v -> action -> 'v) = + fun nl e -> + function + | "action1" -> 0 + | "action2" -> 1 + | _ -> e + + +let () = + Algo.register { + algo = [ + { + algo_id = "p"; + init_state = init_state; + actions = Some ["action1"; "action2"]; + enab = enable_f; + step = step_f; + } + ]; + state_to_string = string_of_int; + state_of_string = Some int_of_string; + copy_state = copy_state; + } diff --git a/tools/test/skeleton/script0.py b/tools/test/skeleton/script0.py new file mode 100644 index 0000000000000000000000000000000000000000..847eff90858cb3e0442d2c0dabf5d3af0778434d --- /dev/null +++ b/tools/test/skeleton/script0.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 + +import sys +sys.path.insert(0, "../..") +from scriptEnv import * + +genGraph("ring", "ring.dot", {"n":20}, True) +#call("gg ring -n 20 -o ring.dot") + +decoGraph("ring.dot", "0-:p.ml", comp = True) +#call('gg-deco 0-:p.ml ring.dot -o ring.dot', verb = 1) +#compileAlgos(["p.ml"]) + +(_,vals) = parseRif(callSasa("ring.dot")) +#(_,vals) = parseRif(call("sasa -rif ring.dot")) + +print(len(vals)) \ No newline at end of file diff --git a/tools/test/skeleton/script1.py b/tools/test/skeleton/script1.py new file mode 100644 index 0000000000000000000000000000000000000000..543ab972a616fe08873a9e5c0081cad6561fb31c --- /dev/null +++ b/tools/test/skeleton/script1.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 + +import sys +sys.path.insert(0, "../..") +from scriptEnv import * + +myProj = Project() +compileAlgos(["p.ml"]) +totSteps = 0 + +for _ in range(10): + path = myProj.add_file("dots", "ring", ".dot") + genGraph("ring", path, {"n":20}, True) + + decoGraph(path, "0-:p.ml", comp = False) + + (_,vals) = parseRif(callSasa(path)) + + totSteps += len(vals) + +print(totSteps, totSteps/10) \ No newline at end of file diff --git a/tools/test/skeleton/script2_1.py b/tools/test/skeleton/script2_1.py new file mode 100755 index 0000000000000000000000000000000000000000..fc5ddeaf7e67bb668161b570d8660fdc189aea41 --- /dev/null +++ b/tools/test/skeleton/script2_1.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 + +import sys +sys.path.insert(0, '../..') +from scriptEnv import * + +from statistics import stdev + +proj = Project("proj_s1", True, False) + +f = proj.open("log", "log", "") +compileAlgos(["p.ml"]) + +step_nb = [] +for x in range(1000): + + path = proj.add_file("dots", "tree", ".dot") + genGraph("tree", path, {"n":20}, True) + decoGraph(path, "0-:p.ml", comp = False) + rif = callSasa(path) + + proj.add_file("rifs", "tree", ".rif", content = rif) + (_,vals) = parseRif(rif) + step_nb.append(len(vals)) + sd = None + if x > 1:sd = stdev(step_nb) + #print(x,"-",sd) + f.write(str(len(vals)) + " -- " + str(sd) + "\n") + +print(sum(step_nb)/1000) diff --git a/tools/test/skeleton/script2_2.py b/tools/test/skeleton/script2_2.py new file mode 100755 index 0000000000000000000000000000000000000000..36f2738bc98ebc865962073acce354fb521bc17f --- /dev/null +++ b/tools/test/skeleton/script2_2.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 + +import sys +sys.path.insert(0, '../..') +from scriptEnv import * +from statistics import stdev +proj = Project("proj_s2", True, False) + +f = proj.open("log", "log", "") +compileAlgos(["p.ml"]) + +step_nb = [] +for x in range(1000): + + path = proj.add_file("dots", "tree", ".dot") + call("gg tree -n 20 -o "+path, verb = 1) + call('gg-deco 0-:p.ml '+path+" -o "+path, verb = 1) + rif = call('sasa -rif '+path, verb = 1) + + proj.add_file("rifs", "tree", ".rif", content = rif) + (_,vals) = parseRif(rif) + step_nb.append(len(vals)) + sd = None + if x > 1:sd = stdev(step_nb) + print(x,"-",sd) + f.write(str(len(vals)) + " -- " + str(sd) + "\n") + +print(sum(step_nb)/1000) \ No newline at end of file diff --git a/tools/test/skeleton/tree.dot b/tools/test/skeleton/tree.dot new file mode 100644 index 0000000000000000000000000000000000000000..e9e92a30831caa9923637e7bd10c5c69aa6e15b3 --- /dev/null +++ b/tools/test/skeleton/tree.dot @@ -0,0 +1,43 @@ +graph tree { + + p0 [algo="p.ml"] + p1 [algo="p.ml"] + p2 [algo="p.ml"] + p3 [algo="p.ml"] + p4 [algo="p.ml"] + p5 [algo="p.ml"] + p6 [algo="p.ml"] + p7 [algo="p.ml"] + p8 [algo="p.ml"] + p9 [algo="p.ml"] + p10 [algo="p.ml"] + p11 [algo="p.ml"] + p12 [algo="p.ml"] + p13 [algo="p.ml"] + p14 [algo="p.ml"] + p15 [algo="p.ml"] + p16 [algo="p.ml"] + p17 [algo="p.ml"] + p18 [algo="p.ml"] + p19 [algo="p.ml"] + + p0 -- p1 + p1 -- p11 + p1 -- p12 + p1 -- p16 + p1 -- p2 + p1 -- p3 + p10 -- p19 + p10 -- p4 + p11 -- p15 + p13 -- p9 + p14 -- p5 + p17 -- p2 + p18 -- p2 + p2 -- p4 + p2 -- p6 + p3 -- p5 + p3 -- p9 + p5 -- p7 + p6 -- p8 +}