diff --git a/.gitignore b/.gitignore index a90f785ec69810fc99f57bf21c919787316c5c90..21848c58bb32dff0c4f0ec9d598af9a5a439feca 100644 --- a/.gitignore +++ b/.gitignore @@ -17,9 +17,14 @@ rdbg-session*.ml *.save *.lus *.lut -*.ml flymd.html log +*.sh +test/**/*.ml +results +Makefile.local +.ocamldebug +lib/sasacore/sasaVersion.ml # Created by https://www.gitignore.io/api/vim,code,ocaml,emacs # Edit at https://www.gitignore.io/?templates=vim,code,ocaml,emacs diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1cd3fd40c88847eb62b03a8627f75d97af06e8b2..5eb7f3ad2c7bf7ddbbe3f93e6bdc2fe4440ea3e5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -16,7 +16,7 @@ build: - make install - make test -test_opam_test: +simca: stage: release script: - opam repo add verimag-sync-repo "http://www-verimag.imag.fr/DIST-TOOLS/SYNCHRONE/test/opam-repository" @@ -24,8 +24,24 @@ test_opam_test: - opam update -y - opam depext -y sasa - opam install -y sasa + - cd tools/simca + - make + - make cmxs + - make log + - make pdf allow_failure: true + +test_opam_test: + stage: release + script: + - opam repo add verimag-sync-repo "http://www-verimag.imag.fr/DIST-TOOLS/SYNCHRONE/test/opam-repository" + - eval `opam config env` + - opam depext -y sasa + - opam install -y sasa + allow_failure: true + + # automating version numbering release: diff --git a/test/alea-coloring-alt/Makefile b/test/alea-coloring-alt/Makefile index f56815721356c84022581342e4d8a62975624f38..1620fe4b82397e2857d2a0dbc5b1a4aea16a6241 100644 --- a/test/alea-coloring-alt/Makefile +++ b/test/alea-coloring-alt/Makefile @@ -1,4 +1,4 @@ -# Time-stamp: <modified the 22/04/2020 (at 10:39) by Erwan Jahier> +# Time-stamp: <modified the 27/04/2020 (at 14:43) by Erwan Jahier> test: ring.cmxs @@ -22,17 +22,12 @@ grid44.dot: gg grid -w 4 -he 4 -o grid44.dot -clique10_%.dot:clique10.dot - gg-deco "0-:algo_$*.ml" $< -o $@ clique10.dot: gg clique -n 10 -o $@ # -grid44_%.dot: grid44.dot - gg-deco "0-:algo_$*.ml" $< -o $@ - rdbg321: grid44_331.dot grid44_331.ml diff --git a/tools/readme.org b/tools/readme.org index 4ce40b460f08e39146f5b3067d9b32bf4297552d..94c86a80521d07d973baca6ccf769b9ec131ae69 100644 --- a/tools/readme.org +++ b/tools/readme.org @@ -2,6 +2,7 @@ Various tools to use with =sasa= - =rdbgui= is a GUI dedicated to the use of =sasa= with =rdbg= +- =simca= a set of scripts to perform SIMulation CAmpaigns with =sasa= - =gg= is a dot graph generator (*). - =gg-deco= decorates graphs generated by =gg= (*) diff --git a/tools/simca/Makefile b/tools/simca/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..2a2810310b6fcc99765899c4e8f89b9eaeec0d25 --- /dev/null +++ b/tools/simca/Makefile @@ -0,0 +1,21 @@ +CAMPAIGN=spanning_tree_campaign.ml +CAMPAIGN=coloring_campaign.ml +CAMPAIGN=nonreg_test_campaign.ml + +Makefile.expe-rules: $(CAMPAIGN) + echo "#use \"$(CAMPAIGN)\";;\n gen_make_rules ();;" | ocaml + +-include Makefile.expe-rulesexpe-rules +# CMXS and LOG are defined in Makefile.expe-rules, which must have been generated! +cmxs: $(CMXS) +log: $(LOG) +pdf: + echo "#use \"$(CAMPAIGN)\";;\n gen_pdf ();;" | ocaml + + +-include Makefile.local + +clean: + rm -rf *.log *.data *.pdf Makefile.expe-rules *~ *.png cmxs_done + + diff --git a/tools/simca/coloring_campaign.ml b/tools/simca/coloring_campaign.ml new file mode 100644 index 0000000000000000000000000000000000000000..f475018495c06cf26ae6854b0c20517187496e46 --- /dev/null +++ b/tools/simca/coloring_campaign.ml @@ -0,0 +1,40 @@ +#use "genExpeMakefiles.ml";; +precision := 0.01;; + (* 0.01 means that we simulate until the Interval Confidence + size of the 3 complexity numbers under estimation is smaller + than 1% of their current estimation. + *) +let algos = ["../../test/alea-coloring-alt"; + "../../test/alea-coloring-unif"; + "../../test/alea-coloring"] +let daemons = ["-sd";"-lcd";"-dd"] +let cliques = List.init 25 (fun n -> Clique (20*(n+1))) (* [20; 40; ...; 500] *) +let rings = List.init 25 (fun n -> Ring (400*(n+1))) (* [100; 500; ...; 10000] *) +let er = List.init 25 (fun n -> ER (10*(n+1), 0.4)) (* [10; 20; ...; 2500] *) + +let gen_make_rules () = + let fn = "Makefile.expe-rules" in + let oc = open_out fn in + Printf.fprintf oc "# Generated by coloring_campaign.ml"; + let compare_neg x y = compare y x in + let l = List.sort_uniq compare_neg (cliques @ rings @ er) in + let targets,targets_cmxs = gen_makefile oc daemons algos l [] [] in + Printf.fprintf oc "\nCMXS=%s\n" (String.concat " " targets_cmxs); + Printf.fprintf oc "\nLOG=%s\n%!" (String.concat " " targets); + Printf.printf "%s has been generated\n%!" fn; + close_out oc +;; + + + +#use "parseLog.ml";; +let gen_pdf () = + let gl = ["clique"; "ring"; "er"] in + List.iter (fun n -> sh ("rm -f "^n^".data")) gl; + parse_log ["Uniform When Triggered","alea-coloring-unif"] gl daemons; + parse_log ["Smallest When Triggered","alea-coloring"] gl daemons; + parse_log ["Always the Biggest","alea-coloring-alt"] gl daemons; + List.iter (fun n -> sh ("./gen_pdf.r "^n^".data coloring")) gl; + () +;; + diff --git a/tools/simca/genExpeMakefiles.ml b/tools/simca/genExpeMakefiles.ml new file mode 100644 index 0000000000000000000000000000000000000000..85efab52baa7c7c8caa67d6077dd9249f1a23f33 --- /dev/null +++ b/tools/simca/genExpeMakefiles.ml @@ -0,0 +1,71 @@ +(* [algo_to_deco dir] associates a directory names to + - an argument to provide to gg-deco (to decorate gg generated graphs) + - a label that is used to create the dot file names + *) +let algo_to_deco dir = + match Filename.basename dir with + | "bfs-spanning-tree" + | "dijkstra-ring" + | "bfs-st-HC92" + | "st-CYH91" + | "st-KK06-algo1" + | "st-KK06-algo2" + | "dfs" + | "dfs-list" -> "1-:p.ml 0:root.ml", "p_root" + | "async-unison" + | "coloring" + | "alea-coloring" + | "alea-coloring-unif" -> "0-:p.ml","p" + | "alea-coloring-alt" -> "0-:algo_331.ml","algo_331" + | "unison" -> "0-:unison.ml","unison" + | s -> Printf.printf + "Error: %s is not handled by GenExpeMakefiles.algo_to_deco\n%!" s; + assert false + +type dir = string +type daemon = string +type graph = + | Udg of int (* size of the graph *) + | Qudg of int (* size of the graph *) + | ER of int * float (* size of the graph, probability to add an edge *) + | Grid of int * int (* width and height of the grid *) + | Clique of int (* size of the graph *) + | Ring of int (* size of the graph *) +;; + +(* The 4 following simulation parameters are put into references so + that they can be modified. They could have been arguments of + gen_makefile, but their values ought to be always the same (this is + arguable). *) +let precision = ref 0.01 + (* 0.01 here means that we simulate until the Interval Confidence + size of the 3 complexity numbers under estimation is smaller + than 1% of their current estimation. + *) +let max_simu_nb = ref 10000 (* no more simulations are done once reached *) +let timeout_in_sec = ref 10000 (* Ditto, once overtaken *) +let regen_dot = ref true;; (* regenerate the graph before every simulation *) +#use "genExpeMakefilesUtils.ml";; + +(* The Main function (or in other words, the entry point) + + [gen_makefile oc p dl dirl gl targets targets_cmxs] writes on [oc] + |dl| x |dirl| x |gl| Makefile rules that build .log files. It also + generates the dependencies to execute those log rules (to build + .dot and .cmxs files). + + It returns the list of log and cmxs targets that were written on [oc] + + More precisely, it adds to the input list [targets_log] + [targets_cmxs] the log and cmxs targets that were written on [oc]. + *) +let (gen_makefile : out_channel -> daemon list -> dir list + -> graph list -> string list -> string list -> string list * string list) = + fun oc dl dirl gl targets_log targets_cmxs -> + let targets_log, targets_cmxs = + List.fold_left (gen_makefile0 oc dl dirl) (targets_log, targets_cmxs) gl + in + Printf.fprintf oc "\n# ZZZ do not do 'make -j 20 $(CMXS)'! (p.cmxs is regenerated +# at each target, which causes failures)\n"; + Printf.fprintf oc "\n# But 'make -j 20 $(LOG)' is fine!\n%!"; + targets_log, targets_cmxs diff --git a/tools/simca/genExpeMakefilesUtils.ml b/tools/simca/genExpeMakefilesUtils.ml new file mode 100644 index 0000000000000000000000000000000000000000..1dccbbd95ca75940f54bcfede7bb2ab06bc2580f --- /dev/null +++ b/tools/simca/genExpeMakefilesUtils.ml @@ -0,0 +1,110 @@ +#use "topfind";; +#require "unix";; +#require "str";; + +(** Generate a dot file with gg *) +let gen_dot graph_kind dot dir = + let gg_cmd = Printf.sprintf "echo \"gg %s -o %s/%s.dot\" > %s/%s.dot.sh" + graph_kind dir dot dir dot in + gg_cmd + +(** Decorate gg generated dot files with gg-deco *) +let deco_dot dir dot = + let deco, _label = algo_to_deco dir in + let dot = dir^"/"^dot in + Printf.sprintf "echo \"gg-deco \\\"%s\\\" %s -o %s\" >> %s.sh " deco dot dot dot + +let grid w h label dir = + let name = Printf.sprintf "grid%dx%d_%s" w h label in + name, gen_dot (Printf.sprintf "grid -w %d -he %d" w h) name dir + +let erdos_renyi n p label dir = + let name = Printf.sprintf "er%i_%s" n label in + (* The graph is a.c. connected if p> log(n)/n *) + name, gen_dot (Printf.sprintf "ER -n %d -p %f" n p) name dir + +let clique n label dir = + let name = Printf.sprintf "clique%i_%s" n label in + name, gen_dot (Printf.sprintf "clique -n %d" n) name dir + +let ring n label dir = + let name = Printf.sprintf "ring%i_%s" n label in + name, gen_dot (Printf.sprintf "ring -n %d" n) name dir + +let udg n label dir = + let name = Printf.sprintf "udg%i_%s" n label in + name, gen_dot (Printf.sprintf "UDG -n %d" n) name dir + +let qudg n label dir = + let name = Printf.sprintf "qudg%i_%s" n label in + name, gen_dot (Printf.sprintf "QUDG -n %d" n) name dir + +let gen_graph label dir = function + | Udg i -> udg i label dir + | Qudg i -> qudg i label dir + | ER (i,p) -> erdos_renyi i p label dir + | Grid (i,j) -> grid i j label dir + | Clique i -> clique i label dir + | Ring i -> ring i label dir + +let (gen_dot_rule : out_channel -> graph -> daemon list -> dir list -> + string list * string list) = + fun oc g dl dirl -> + List.iter (fun dir -> + if not (Sys.file_exists dir) then failwith ("Error: "^dir^" does not exist\n");) + dirl; + let regen_dot = match g with + | Udg _ | Qudg _ | ER _ -> !regen_dot + | Grid (_,_) | Clique _ | Ring _ -> false (* those are deterministic *) + in + let targets = + (List.fold_left + (fun acc dir -> + let _, label = algo_to_deco dir in + let name, _ = gen_graph label dir g in + let dot = Printf.sprintf "%s.dot" name in + let bdir = Filename.basename dir in + let log = Printf.sprintf "%s%s-%s.log" name (List.hd dl) bdir in + let cwd = Sys.getcwd() in + Printf.fprintf oc "\n%s: \n\t[ -f %s/%s.cmxs ] || \\ +(echo \"\\n ===> do a 'make cmxs' before!\\n\\n\"; exit 1)\n\tcd %s \\\n" log dir name dir; + List.iter (fun d -> + let log = Printf.sprintf "%s%s-%s.log" name d bdir in + Printf.fprintf oc + "\t && echo \"#use \\\"%s/runSimus.ml\\\";;\\n \\ + run_simus %b %f %d %d.0 \\\"sasa -l 20000 %s %s -nd > /dev/null\\\";;\\n\" \\ + | ocaml > %s/%s \\ +" + cwd regen_dot !precision !max_simu_nb !timeout_in_sec dot d cwd log + ) + dl; + Printf.fprintf oc "\t && echo \"%s done\"\n\n" log; + log::acc + ) + [] dirl + ) + in + let targets_cmxs : string list = + List.fold_left + (fun acc dir -> + let _deco, label = algo_to_deco dir in + let name, gg_cmd = gen_graph label dir g in + let dot = Printf.sprintf "%s.dot" name in + let cmxs = Printf.sprintf "%s.cmxs" name in + Printf.fprintf oc "%s/%s:\n %s\n" dir dot gg_cmd; + Printf.fprintf oc " %s\n" (deco_dot dir dot); + Printf.fprintf oc " sh %s/%s.sh\n" dir dot; + Printf.fprintf oc "\n\n%s/%s: %s/%s\n" dir cmxs dir dot; + Printf.fprintf oc " cd %s; make %s\n" dir cmxs; + (dir^"/"^cmxs)::acc + ) + [] + dirl + in + targets,targets_cmxs + +let (gen_makefile0 : out_channel -> daemon list -> dir list -> + string list * string list -> graph -> string list * string list) = + fun oc dl dirl (targets_acc, targets_cmxs_acc) g -> + let targets, targets_cmxs = gen_dot_rule oc g dl dirl in + targets @ targets_acc, targets_cmxs @ targets_cmxs_acc diff --git a/tools/simca/gen_pdf.r b/tools/simca/gen_pdf.r new file mode 100755 index 0000000000000000000000000000000000000000..5057de93dd0d58b855a3e8efd4f4ea76115dc261 --- /dev/null +++ b/tools/simca/gen_pdf.r @@ -0,0 +1,84 @@ +#! /usr/bin/env Rscript +args <- commandArgs(TRUE) +if (length(args) == 0) { + stop("At least one argument is necessary +usage: + gen_pdf.r file.data [a_label [ordinate_label]] +where the optional arguments + a_label is used to build output file names (use current dir by default) + ordinate_label is used to label the ordinate axis (use 'Nodes' by default) + +example: + gen_pdf.r clique.data + gen_pdf.r clique.data coloring + gen_pdf.r clique.data coloring Diameter +", call.=FALSE) +} + +datafilename <- args[1] +campaign <- ifelse(is.na(args[2]), basename(getwd()), args[2]) +abscissa <- ifelse(is.na(args[3]),"Nodes",args[3]) +graphname <- tools::file_path_sans_ext(args[1]) + +# Read the data file +data <- data.frame(val=read.table(datafilename)) + +# Give to columns a name +names(data) <- c("n", "Daemons", "Algorithms", "complexity_kind", "min", "mean", "max") + +# Generate a pdf visualisation of the Data with ggplot2 +library(ggplot2) +library(dplyr) + +gen_pdf <- function(pdffilename, x1, x2){ + pdf(pdffilename,onefile=TRUE) + mplot <- ggplot(data, aes_string(x="n",y="mean",colour=x1))+ geom_line() + + facet_grid(c(paste("complexity_kind"), paste(x2)) , scales='free') + + ylab("Move/Step/Round Numbers")+xlab(paste(abscissa,"Number"))+ + ggtitle(paste("Compare", x1, "on various", x2, "Numbers on", + stringr::str_to_title(graphname), sep = " ")) + mplot2 <- mplot+geom_ribbon(aes_string(x="n", ymax="max", ymin="min"), alpha=0.2) + + ggtitle(paste("Compare", x1, "on various", x2, "Numbers on", + stringr::str_to_title(graphname), "(+ min/max ribbon)", sep = " ")) + print(mplot) + print(mplot2) + + for (cm in c("moves","steps","rounds")){ + datax <- filter(data, complexity_kind == cm) + + plot <- ggplot(datax,aes_string(x="n",y="mean",colour=x1))+ geom_line() + + facet_grid(c(paste(x2)), scales='free') + + ylab(paste(cm, "Number", sep=" ")) + xlab(paste(abscissa,"Number"))+ + ggtitle(paste(stringr::str_to_title(cm), "Numbers on", + stringr::str_to_title(graphname), sep = " ")) + plot1 <- plot + geom_point() + plot2 <- plot+geom_ribbon(aes_string(x="n", ymax="max", ymin="min"), alpha=0.2)+ + ggtitle(paste(stringr::str_to_title(cm), "Numbers on", + stringr::str_to_title(graphname), + "(+ min/max ribbon)", sep = " ")) + print(plot1) + print(plot2) + + plot <- ggplot(datax,aes_string(x="n",y="mean",colour=x1,shape=x2))+ geom_line() + + ylab(paste(cm, "Number", sep=" ")) + xlab(paste(abscissa,"Number"))+ + ggtitle(paste(stringr::str_to_title(cm), "Numbers on", + stringr::str_to_title(graphname), sep = " ")) + plot1 <- plot + geom_point() + plot2 <- plot+geom_ribbon(aes_string(x="n", ymax="max", ymin="min"), alpha=0.2)+ + ggtitle(paste(stringr::str_to_title(cm), "Numbers on", + stringr::str_to_title(graphname), + "(+ min/max ribbon)", sep = " ")) + print(plot1) + print(plot2) + } + pngfilename = paste(pdffilename,".png", sep ="") + png(pngfilename) + print(mplot2) + +} + +pdffilename1 = paste(campaign, datafilename,"algos.pdf", sep ="-") +pdffilename2 = paste(campaign, datafilename,"demons.pdf", sep ="-") +gen_pdf(pdffilename1, "Algorithms", "Daemons") +gen_pdf(pdffilename2, "Daemons", "Algorithms") + diff --git a/tools/simca/nonreg_test_campaign.ml b/tools/simca/nonreg_test_campaign.ml new file mode 100644 index 0000000000000000000000000000000000000000..36d0b592072e01ee4da7dd9c40d25355ebf2f5b0 --- /dev/null +++ b/tools/simca/nonreg_test_campaign.ml @@ -0,0 +1,43 @@ +#use "genExpeMakefiles.ml";; +precision := 0.1;; +(* 0.1 means that we simulate until the Interval Confidence size of + the 3 complexity numbers under estimation is smaller than 10% of + their current estimation. + + Starting with 10% to get a rough idea of the complexity in the + first place is probably a wise idea. *) +max_simu_nb := 1000;; (* by default, it's more *) +timeout_in_sec := 60;; (* ditto *) +let algos = ["../../test/alea-coloring-alt"; + "../../test/alea-coloring-unif"; + "../../test/alea-coloring"] +let daemons = ["-sd";"-lcd";"-dd"] +let cliques = List.init 5 (fun n -> Clique (10*(n+1))) (* [10; 20; ...; 50] *) +let rings = List.init 5 (fun n -> Ring (100*(n+1))) (* [100; 200; ...; 500] *) +let er = List.init 5 (fun n -> ER (20*(n+1), 0.4)) (* [20; 40; ...; 100] *) + +let gen_make_rules () = + let fn = "Makefile.expe-rules" in + let oc = open_out fn in + Printf.fprintf oc "# Generated by nonreg_test_campaign.ml"; + (* 5 Cliques and 5 rings (not too big for the CI scripts) *) + let compare_neg x y = compare y x in + let l = List.sort_uniq compare_neg (cliques @ rings @ er) in + let targets,targets_cmxs = gen_makefile oc daemons algos l [] [] in + Printf.fprintf oc "\nCMXS=%s\n" (String.concat " " targets_cmxs); + Printf.fprintf oc "\nLOG=%s\n%!" (String.concat " " targets); + Printf.printf "%s has been generated\n%!" fn; + close_out oc +;; + +#use "parseLog.ml";; +open Printf +let gen_pdf () = + let gl = ["clique"; "ring"; "er"] in + List.iter (fun n -> sh ("rm -f "^n^".data")) gl; (* because parse_log appends data *) + parse_log ["Uniform When Triggered", "alea-coloring-unif"] gl daemons; + parse_log ["Smallest When Triggered","alea-coloring"] gl daemons; + parse_log ["Always the Biggest", "alea-coloring-alt"] gl daemons; + List.iter (fun n -> sh ("./gen_pdf.r "^n^".data nonreg")) gl; + () +;; diff --git a/tools/simca/parseLog.ml b/tools/simca/parseLog.ml new file mode 100644 index 0000000000000000000000000000000000000000..e3375eceb3e6350ed4ed37a1cd62ee3d18cd4556 --- /dev/null +++ b/tools/simca/parseLog.ml @@ -0,0 +1,18 @@ +#use "parseLogUtils.ml";; +(* [parse_log algo_labels graphs daemons] generates as many .data + files as there are graphs i.e., if graphs=["g1"; "g2"; etc.], then + this function generates "g1.data", "g2.data", etc. If the .data + files do not exist, there are created. Otherwise, data are + appended at the end. *) +let parse_log + (algo_labels:(string * string) list) (* (algo label, algo directory) *) + (graphs: string list) (* graph label (clique, ring, udg) *) + (daemons:string list) = (* sasa daemon short option name *) + let f l a g d = + let log_mask = Printf.sprintf "%s*%s-*%s.log" g d a in + Printf.printf "parse_log \"%s\" \"%s\" \"%s\" \"%s\" mask='%s'\n%!" l a g d log_mask; + data_from_log log_mask d l (g^".data") + in + List.iter (fun (l, a) -> List.iter (fun g -> List.iter (fun d -> + f l a g d) daemons) graphs) algo_labels +;; diff --git a/tools/simca/parseLogUtils.ml b/tools/simca/parseLogUtils.ml new file mode 100644 index 0000000000000000000000000000000000000000..ad1ea71ea0c5146095f1e867f3b88e54e8b5f752 --- /dev/null +++ b/tools/simca/parseLogUtils.ml @@ -0,0 +1,85 @@ +#use "utils.ml";; + +(***********************************************************************) +(* Append to datafile information read in log files specified with log_mask. +log files are expected to contain something like +" + Min. : 906.0 + 1st Qu.: 973.5 + Median :1007.5 + Mean :1020.5 + 3rd Qu.:1053.5 + Max. :1164.0 +" +3 times (for moves, steps, and then rounds) +*) +exception Pb +let data_from_log log_mask demon algolbl datafile = + let demon = match demon with + | "-sd" -> "Synchronous" + | "-dd" -> "Distributed" + | "-lcd" -> "Locally Central" + | "-cd" -> "Central" + | s -> s + in + let oc = open_out_gen [Open_wronly; Open_creat; Open_append; Open_binary] 0o666 + datafile + in + let ocorg = open_out_gen [Open_wronly; Open_creat; Open_append; Open_binary] 0o666 + (datafile^".org") + in + let log_files = run ("ls -1 " ^ log_mask) in + List.iter + (fun log -> + try + let _first_int = Str.search_forward (Str.regexp "[0-9]+") log 0 in + let size = Str.matched_string log in + let min_moves, min_steps, min_rounds = + match run ("grep -n \"Min.\" "^ log) with + | str1::str2::str3::_ -> + List.hd (List.rev(Str.split (Str.regexp ":") str1)), + List.hd (List.rev(Str.split (Str.regexp ":") str2)), + List.hd (List.rev(Str.split (Str.regexp ":") str3)) + | _ -> + Printf.printf "W: no Min found in %s\n%!" log; + raise Pb + in + let max_moves, max_steps, max_rounds = + match run ("grep -n \"Max.\" "^ log) with + | str1::str2::str3::_ -> + List.hd (List.rev(Str.split (Str.regexp ":") str1)), + List.hd (List.rev(Str.split (Str.regexp ":") str2)), + List.hd (List.rev(Str.split (Str.regexp ":") str3)) + | _ -> + Printf.printf "W: no Max found in %s\n%!" log; + raise Pb + in + let mean_moves, mean_steps, mean_rounds = + match run ("grep -n \"Mean\" "^ log) with + | str1::str2::str3::_ -> + List.hd (List.rev(Str.split (Str.regexp ":") str1)), + List.hd (List.rev(Str.split (Str.regexp ":") str2)), + List.hd (List.rev(Str.split (Str.regexp ":") str3)) + | _ -> + Printf.printf "W: no Mean found in %s\n%!" log; + raise Pb + in + Printf.fprintf oc "%s \"%s\" \"%s\" moves %s %s %s\n%!" + size demon algolbl min_moves mean_moves max_moves ; + Printf.fprintf oc "%s \"%s\" \"%s\" steps %s %s %s\n%!" + size demon algolbl min_steps mean_steps max_steps ; + Printf.fprintf oc "%s \"%s\" \"%s\" rounds %s %s %s\n%!" + size demon algolbl min_rounds mean_rounds max_rounds; + Printf.fprintf ocorg "| %s | %s | %s | moves | %s | %s | %s| \n%!" + size demon algolbl min_moves mean_moves max_moves ; + Printf.fprintf ocorg "| %s | %s | %s | steps | %s | %s | %s| \n%!" + size demon algolbl min_steps mean_steps max_steps ; + Printf.fprintf ocorg "| %s | %s | %s | rounds | %s | %s | %s| \n%!" + size demon algolbl min_rounds mean_rounds max_rounds + with + Pb -> () + ) + log_files; + close_out oc; + close_out ocorg; + () diff --git a/tools/simca/plot.r b/tools/simca/plot.r new file mode 100755 index 0000000000000000000000000000000000000000..e2b342542e5963db39626d1215157fb1b79fbae9 --- /dev/null +++ b/tools/simca/plot.r @@ -0,0 +1,36 @@ +#! /usr/bin/env Rscript +# Let's first give to CLI arguments some meaningfull names +args <- commandArgs(TRUE) +datafilename <- args[1] +outfilename <- args[2] +title <- args[3] +pdffilename = paste(datafilename,outfilename,"rounds.pdf", sep ="-") +pdffilename2 = paste(datafilename,outfilename,"moves.pdf", sep ="-") +pdffilename3 = paste(datafilename,outfilename,"steps.pdf", sep ="-") + +# Read the data file +data <- data.frame(val=read.table(datafilename)) + +# Give to columns a name +names(data) <- c("n", "r", "Daemon", "Algorithm", "complexity_kind") + +# Generate a pdf visualisation of the Data with ggplot2 +library(ggplot2) +pdf(pdffilename) +ggplot(subset(data,complexity_kind=="rounds"),aes(x=n,y=r,colour=Daemon,shape=Algorithm))+ + geom_line() + geom_point() + +# facet_grid(complexity_kind ~ .) + ylab("Rounds Number")+xlab("Nodes Number")+ggtitle(paste(title,"Rounds Numbers"," ")) + +# Ditto for moves +pdf(pdffilename2) +ggplot(subset(data,complexity_kind=="moves"),aes(x=n,y=r,colour=Daemon,shape=Algorithm))+ + geom_line() + geom_point() + + ylab("Moves Number")+xlab ("Nodes Number")+ggtitle(paste(title,"Moves Numbers"," ")) + +# Ditto for steps +pdf(pdffilename3) +ggplot(subset(data,complexity_kind=="steps"),aes(x=n,y=r,colour=Daemon,shape=Algorithm))+ + geom_line() + geom_point() + + ylab("Steps Number")+xlab("Nodes Number")+ggtitle(paste(title,"Steps Numbers"," ")) + diff --git a/tools/simca/runSimus.ml b/tools/simca/runSimus.ml new file mode 100644 index 0000000000000000000000000000000000000000..1cfa8cf519d8e59b94abb46d37f4890a76cda405 --- /dev/null +++ b/tools/simca/runSimus.ml @@ -0,0 +1,185 @@ +#use "topfind";; +#require "unix";; +#require "str";; + +let (run0 : string -> (string -> string option) -> string list) = + fun cmd filter -> + let proc = Unix.open_process_in ("("^cmd^" ) 2>&1") in + let list = ref [] in + try + while true do + let line = input_line proc in + match filter line with + | None -> () + | Some str -> list := str::!list + done; + [] + with End_of_file -> + ignore (Unix.close_process_in proc); + List.rev !list + +(* Executes a system call and get the printed result in a list *) +let run str = run0 str (fun s -> Some s) + +let run_p str = print_string (String.concat " " (run str)) +let sh = run_p + +let time f x = + let t = Sys.time() in + let fx = f x in + Printf.printf "Execution time: %fs\n" (Sys.time() -. t); + fx + + +(**********************************************************************) +(* Parses a sasa call outputs to get the nb of moves, rounds, and steps. *) +let get_step str = + (* Printf.printf "get_step '%s'\n%!" str; *) + if + Str.string_match (Str.regexp "This algo is silent after \\(.+\\) moves, \\(.+\\) steps, \\(.+\\) rounds.$") str 0 + then + let moves = int_of_string (Str.matched_group 1 str) in + let step = int_of_string (Str.matched_group 2 str) in + let rounds = int_of_string (Str.matched_group 3 str) in + Some (moves, step, rounds) + else + ( + Printf.printf "runSimu.ml get_step: Fail to parse '%s'\n%!" str; + None + ) + +(**********************************************************************) +let tf = float_of_int +let stddev m xl = + let n = List.length xl in + let sum = + List.fold_left (fun acc xi -> acc +. (m -. (tf xi)) *. (m -. (tf xi))) 0.0 xl + in + sqrt (sum /. (tf n)) + +type t = { all: int list; sum: int; mini: int; maxi: int } +let init = { all = []; sum = 0; mini = max_int; maxi = 0 } + +let update v x = { + all = v::x.all; + sum = v+x.sum; + mini = min v x.mini; + maxi = max v x.maxi; +} + +let gen_data_file m il fn n label title = + let oc =open_out fn in + Printf.fprintf oc "# %s %s \n" label title; + List.iter (Printf.fprintf oc "%i\n") il; + flush oc; + close_out oc; + ignore(Sys.command (Printf.sprintf "../expe/distrib.r %s %f %s %s %i" + fn m label title n)); + () + +(* run_simus [cmd] several times [*], and generates 3 files: + - f.step.data + - f.move.data + - f.round.data + where f is a string made of out of the [cmd] string, and + where data files contain [n] step/move/round numbers (one per simu) + + Also call the R script distrib.r on each data file + + [*] at least 10, and at most 10000 times. New simulations are performed + as long as the size of the Confidence Interval with a confidence level of + 95% (CI_95) is higher than threshold x m, where is the estimation of one + on the complexity measure number. + + Take threshold= 0.01 for 1% of uncertainty +*) +let run_simus regen_dot threshold max_simu_nb timeout cmd = + let t = Unix.time() in + let g, d = + if Str.string_match (Str.regexp ".* \\(.*\\)\\.dot \\([^ ]*\\) ") cmd 0 + then Str.matched_group 1 cmd,Str.matched_group 2 cmd + else "g","" + in + let basefn = Printf.sprintf "%s%s" g d in + let step = ref init in + let move = ref init in + let round = ref init in + let continue = ref true in + let i = ref 0 in + while !continue do + if regen_dot then ( + let gen_dot_cmd = Printf.sprintf "sh ./%s.dot.sh 2>&1" g in + Printf.printf "Regenerating the dot graph: %s\n%!" gen_dot_cmd; + assert (0 = Sys.command gen_dot_cmd) + ); + let l = run cmd in + List.iter + (fun x -> + match get_step x with + | None -> () + (* can occur if the generated graph is not connected on some algos *) + | Some (m,s,r) -> + incr i; + step := update s !step; + move := update m !move; + round := update r !round; + Printf.printf + "\n%i: step = %i round = %i mean(step) = %d mean(round) = %d\n%!" + !i s r + (!step.sum / !i) + (!round.sum / !i) ; + ) + l; + let step_mean = (tf !step.sum /. (tf !i)) + and move_mean = (tf !move.sum /. (tf !i)) + and round_mean= (tf !round.sum /. (tf !i)) + in + let step_dev = stddev step_mean !step.all + and move_dev = stddev move_mean !move.all + and round_dev = stddev round_mean !round.all + in + let continue_f m rho = + let x = (1.96 *. rho /. (threshold *. m)) ** 2.0 in + let y = (1.96 *. rho *. 10.) ** 2.0 in + Printf.printf "m=%f rho=%f : %d > %f or %f ? \n%!" m rho !i x y; + !i < max_simu_nb && ( (* do at most max_simu_nb simulations *) + (Unix.time() -. t < timeout) && + (!i < 10 (* and at least 10 *) + || + (!i < (int_of_float x ) (* stop when |ci|< threshold x m *) + && !i < (int_of_float y )) (* or stop when |ci|< 0.1 *) + )) + in + continue := continue_f move_mean move_dev || + continue_f step_mean step_dev || + continue_f round_mean round_dev + done; + let step_mean = (tf !step.sum /. (tf !i)) + and move_mean = (tf !move.sum /. (tf !i)) + and round_mean= (tf !round.sum /. (tf !i)) + in + let step_dev = stddev step_mean !step.all + and move_dev = stddev move_mean !move.all + and round_dev = stddev round_mean !round.all + in + let step_delta_ci = 2.0 *. 1.96 *. step_dev /. (sqrt (tf !i)) + and move_delta_ci = 2.0 *. 1.96 *. move_dev /. (sqrt (tf !i)) + and round_delta_ci = 2.0 *. 1.96 *. round_dev /. (sqrt (tf !i)) + in + let step_str = Printf.sprintf "\"[%d ... %.2f~%.2f ... %d] |CI_95|=%.2f n=%d\"%!" + !step.mini step_mean step_dev !step.maxi step_delta_ci !i + and move_str= Printf.sprintf "\"[%d ... %.2f~%.2f ... %d] |CI_95|=%.2f n=%d\"%!" + !move.mini move_mean move_dev !move.maxi move_delta_ci !i + and round_str = Printf.sprintf "\"[%d ... %.2f~%.2f ... %d] |CI_95|=%.2f n=%d\"%!" + !round.mini round_mean round_dev !round.maxi round_delta_ci !i + in + gen_data_file move_mean !move.all (basefn^".move.data") !i "Move" move_str; + gen_data_file step_mean !step.all (basefn^".step.data") !i "Step" step_str; + gen_data_file round_mean !round.all (basefn^".round.data") !i "Round" round_str; + Printf.printf "Moves:%s\nSteps:%s\nRounds:%s\n" move_str step_str round_str ; + if !i = max_simu_nb then + Printf.printf "ZZZ Maximal number of simulations (%d) reached!\n" max_simu_nb; + if Unix.time() -. t > timeout then + Printf.printf "ZZZ Timeout! (%f > %f)\n" (Unix.time() -. t) timeout ; + Printf.printf "Wall-clock Execution time for this file: %fs\n" (Unix.time() -. t); + exit 0;; diff --git a/tools/simca/spanning_tree_campaign.ml b/tools/simca/spanning_tree_campaign.ml new file mode 100644 index 0000000000000000000000000000000000000000..a58268ba5c756a2a7a1f0e801bfe325a4a7cce91 --- /dev/null +++ b/tools/simca/spanning_tree_campaign.ml @@ -0,0 +1,38 @@ +#use "genExpeMakefiles.ml";; +let algos = ["../../test/bfs-spanning-tree"; + "../../test/dfs"; + "../../test/bfs-st-HC92"; + "../../test/st-CYH91"; + "../../test/st-KK06-algo1"; + "../../test/st-KK06-algo2" + ] +let daemons = ["-cd";"-dd"] +let cliques = List.init 10 (fun n -> Clique (20*(n+1))) (* [20; 40; ...; 200] *) +let er = List.init 10 (fun n -> ER (10*(n+1), 0.4)) (* [10; 20; ...; 100] *) + +let gen_make_rules () = + precision := 0.5; + let fn = "Makefile.expe-rules" in + let oc = open_out fn in + Printf.fprintf oc "# Generated by spanning_tree_campaign.ml / gen_make_rules"; + let compare_neg x y = compare y x in + let l = List.sort_uniq compare_neg (cliques @ er) in + let targets,targets_cmxs = gen_makefile oc daemons algos l [] [] in + Printf.fprintf oc "\nCMXS=%s\n" (String.concat " " targets_cmxs); + Printf.fprintf oc "\nLOG=%s\n%!" (String.concat " " targets); + Printf.printf "%s has been generated\n%!" fn; + close_out oc +;; +#use "parseLog.ml";; +let gen_pdf () = + let gl = ["clique"; "er"] in + List.iter (fun n -> sh ("rm -f "^n^".data")) gl; + parse_log ["BFS book", "bfs-spanning-tree"; + "DFS book", "dfs"; + "HC92", "bfs-st-HC92"; + "CYH91", "st-CYH91"; + "KK06-algo1", "st-KK06-algo1"; + "KK06-algo2", "st-KK06-algo2"] + gl daemons; + List.iter (fun n -> sh ("./gen_pdf.r "^n^".data Tree")) gl +;; diff --git a/tools/simca/utils.ml b/tools/simca/utils.ml new file mode 100644 index 0000000000000000000000000000000000000000..995eb858b1b54ea12a7fde4329005132264b44ae --- /dev/null +++ b/tools/simca/utils.ml @@ -0,0 +1,33 @@ +#use "topfind";; + +#require "str";; + +let (run0 : string -> (string -> string option) -> string list) = + fun cmd filter -> + let proc = Unix.open_process_in ("("^cmd^" | sed -e 's/^/stdout: /' ) 2>&1") in + let list = ref [] in + try + while true do + let line = input_line proc in + if String.length line >= 8 && String.sub line 0 8 = "stdout: " then + let str = String.sub line 8 (String.length line - 8) in + match filter str with + | None -> () + | Some str -> list := str::!list + done; + [] + with End_of_file -> + ignore (Unix.close_process_in proc); + List.rev !list + +(* Executes a system call and get the printed result in a list *) +let run str = run0 str (fun s -> Some s) + +let run_p str = print_string (String.concat " " (run str)) +let sh = run_p + +let time f x = + let t = Sys.time() in + let fx = f x in + Printf.printf "Execution time: %fs\n" (Sys.time() -. t); + fx