;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This file is part of "NOVA": NOVA = search + COCOMO tools
; Copyright, 2008, Tim Menzies tim@menzies.us
;
; NOVA is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
;
; NOVA is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
; You should have received a copy of the GNU General Public License
; a long with NOVA. If not, see .
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defvar *engines* '((isamp "isamp" "ISAMP")
(maxwalksat "maxwalksat" "MaxWalkSat")
(sa "sa" "SA")
(a-star "a-star" "A*")
(beam "beam" "Beam")
(keys "keys" "KEYS")
(seesaw "seesaw" "SEESAW-R")
(seesaw2 "seesaw2" "SEESAW-D")))
(defvar *case-studies* '((default "default" "Default" default-case-study)
(flight "flight" "Flight" flight-case-study)
(ground "ground" "Ground" ground-case-study)
(osp "osp" "OSP" osp-case-study)
(osp2 "osp2" "OSP2" osp2-case-study)
(tps10 "tps10" "TPS10" tps10-case-study)
(tps100 "tps100" "TPS100" tps100-case-study)
(tps1000 "tps1000" "TPS1000" tps1000-case-study)))
(defvar *policies* '((strategic "strategic" "Strategic" (strategic))
(tactical "tactical" "Tactical" (tactical))
(nil "all" "All" nil)))
(defun write-paths (engine case-study-name policy-name paths &optional (calculate-p t))
"Writes paths to the results folder."
(when calculate-p
(dolist (path paths)
(dolist (state (states? path))
(effort? state)
(months? state)
(defects? state)
(threat? state)
(energy? state))))
(let ((e (assoc engine *engines*))
(cs (assoc case-study-name *case-studies*))
(p (assoc policy-name *policies*)))
(with-open-file (str (concatenate 'string "../results/" (second cs) "/" (second p) "/" (second e) ".out")
:direction :output :if-exists :supersede)
(format str "~a~%" paths))))
(defun read-paths (engine case-study-name policy-name)
"Reads paths from the results folder."
(let ((e (assoc engine *engines*))
(cs (assoc case-study-name *case-studies*))
(p (assoc policy-name *policies*)))
(with-open-file (str (concatenate 'string "../results/" (second cs) "/" (second p) "/" (second e) ".out"))
(read str))))
(defun search-and-write-paths (engine)
"Searches all combinations of *case-studies* and *policies* and writes the paths."
(dolist (cs *case-studies*)
(dolist (p *policies*)
(format t "~a/~a~%" (third cs) (third p))
(write-paths engine (first cs) (first p)
(funcall engine :case-study (funcall (fourth cs)) :policies (fourth p))))))
(defun search-rig ()
"Searches everything."
(dolist (e *engines*)
(format t "~a~%" (third e))
(search-and-write-paths (first e))))
(defun score-engine (engine score-fn &key (case-study-name 'default) (policy-name nil))
"Scores the given engine."
(let ((e (assoc engine *engines*))
(paths (read-paths engine case-study-name policy-name)))
(mapcar #'(lambda (path) (funcall score-fn (min-energy-ttest? path))) paths)))
(defun score-baseline (engine score-fn &key (case-study-name 'default) (policy-name nil))
"Generates a baseline score."
(let ((e (assoc engine *engines*))
(paths (read-paths engine case-study-name policy-name)))
(mapcar #'(lambda (path) (funcall score-fn (first (path-states path)))) paths)))
(defun score-engines (score-fn &key (case-study-name 'default) (policy-name nil) (baseline nil))
"Scores all engines for the given case study and policy."
(let (output)
(push (list "Baseline" (score-baseline (first (first *engines*))
score-fn
:case-study-name case-study-name
:policy-name policy-name))
output)
(dolist (engine *engines* (nreverse output))
(push (list (third engine) (score-engine (first engine)
score-fn
:case-study-name case-study-name
:policy-name policy-name))
output))))
(defun write-lc-data (state file &optional (num-simulations 1000))
"Writes local calibration data for the given state into a CSV file."
(init-db)
(kloc! (state-kloc-min state) (state-kloc-max state))
(apply-treatments (append (state-ignore state)
(state-open state)
(state-closed state)))
(apply-treatments (state-open state))
(let ((sfs '(prec flex resl team pmat))
(ems '(time stor data pvol ruse rely docu acap pcap pcon aexp plex ltex tool sced cplx site)))
(with-open-file (str file :direction :output :if-exists :supersede)
(format str "~{~a,~}" sfs)
(format str "~{~a,~}" ems)
(format str "A,B,KSLOC,EFFORT~%")
(dotimes (i num-simulations)
(zaps)
(format str "~{~,12f,~}" (mapcar #'sf2effort sfs))
(format str "~{~,12f,~}" (mapcar #'em2effort ems))
(format str "~,12f,~,12f,~,12f,~,12f~%" (! 'a) (! 'b) (! 'kloc) (effort))))))
(defun search-and-write-lc-data (engine)
"Searches all combinations of *case-studies* and *policies* and writes LC data."
(let ((e (assoc engine *engines*)))
(dolist (cs *case-studies*)
(dolist (p *policies*)
(format t "~a ~a~%" (third cs) (third p))
(write-lc-data (min-energy-ttest? (funcall engine :case-study (funcall (fourth cs)) :policies (fourth p)))
(concatenate 'string "../results/lc/" (second e) "/" (second cs) "-" (second p) ".csv"))))))
(defun create-win-loss-table (>wins data)
"Compares data using Mann-Whitney and creates a win-loss table."
(let (table)
(dotimes (i (length data) (nreverse table))
(let ((wins 0)
(losses 0)
(ties 0))
(dotimes (j (length data))
(when (not (eq i j))
(case (mann-whitney (second (elt data i)) (second (elt data j)))
(-1 (if >wins (incf losses) (incf wins)))
( 0 (incf ties))
( 1 (if >wins (incf wins) (incf losses))))))
(push (list (first (elt data i)) wins losses ties (second (elt data i))) table)))))
(defun print-win-loss-table (str table)
"Prints a win-loss table to the specified stream."
(labels ((repeat-string (string n)
(let ((output ""))
(dotimes (i n output)
(setf output (concatenate 'string output string))))))
(let* ((label-width (apply #'max (mapcar #'(lambda (row) (length (first row))) table)))
(wlt-max (apply #'max (mapcar #'(lambda (row) (length (format nil "~a" (max (second row)
(third row)
(fourth row)))))
table)))
(wlt-width (max wlt-max 3)))
(format str "~vt ~v ~:*~v ~:*~v~%" label-width wlt-width)
(format str "~a ~a ~:*~a ~:*~a~%" (repeat-string "-" label-width) (repeat-string "-" wlt-width))
(dolist (row (sort (copy-tree table) #'(lambda (row1 row2)
(if (eql (third row1) (third row2))
(> (second row1) (second row2))
(< (third row1) (third row2))))))
(format str "~va" label-width (first row))
(format str " ~v<~a~>" wlt-width (second row))
(format str " ~v<~a~>" wlt-width (third row))
(format str " ~v<~a~>" wlt-width (fourth row))
(format str "~%")))))
(defun print-win-loss-tables (str tables)
"Prints multiple win-loss tables to the specified stream."
(let (totals)
(dolist (table tables)
(format str "~a:~%" (first table))
(print-win-loss-table str (second table))
(format str "~%")
(dolist (row (second table))
(let ((totals-row (assoc (first row) totals)))
(when (not totals-row)
(push (list (first row) 0 0 0) totals)
(setf totals-row (assoc (first row) totals)))
(incf (second totals-row) (second row))
(incf (third totals-row) (third row))
(incf (fourth totals-row) (fourth row)))))
(format str "Total:~%")
(print-win-loss-table str totals)))
(defun print-all-win-loss-tables (str)
"Prints win-loss tables for all engines to the specified stream."
(let ((tables nil))
(dolist (cs *case-studies*)
(dolist (p *policies*)
(push (list (format nil "~a/~a" (third cs) (third p))
(create-win-loss-table nil (score-engines #'energy?
:case-study-name (first cs)
:policy-name (first p)
:baseline t)))
tables)))
(print-win-loss-tables str tables)))
(defun print-boxplot (str >wins data title &optional (precision 0))
"Compares data using Mann-Whitney and prints a LaTeX boxplot to the specified stream."
(format str "{\\scriptsize\\begin{center}~%")
(format str "\\begin{tabular}{l@{~~} c@{~~} l@{~~} r@{~~} r@{~~} r@{~~} r@{~~} r@{~~} c}~%")
(format str "& & & \\multicolumn{5}{c}{quartiles} \\\\~%")
(format str "\\cline{4-8}~%")
(format str "& rank & & 0 & 25 & 50 & 75 & 100 \\\\~%")
(format str "\\hline~%")
(format str "~a " title)
(let* ((table (create-win-loss-table >wins data))
(all-data (apply #'append (mapcar #'fifth table)))
previous-row
rank)
(dolist (row (sort (copy-tree table) #'(lambda (row1 row2)
(if (eql (third row1) (third row2))
(> (second row1) (second row2))
(< (third row1) (third row2))))))
(let ((min (apply #'min (fifth row)))
(max (apply #'max (fifth row))))
(multiple-value-bind (med spread upper lower) (median (fifth row))
(if (not previous-row)
(setf rank 1)
(if (> (third row)
(third previous-row))
(setf rank (1+ rank))))
(if (zerop precision)
(format str "& ~d & ~a & ~d & ~d & ~d & ~d & ~d & "
rank
(first row)
(round min)
(round lower)
(round med)
(round upper)
(round max))
(format str "& ~d & ~a & ~,vf & ~,vf & ~,vf & ~,vf & ~,vf & "
rank
(first row)
precision min
precision lower
precision med
precision upper
precision max))
(let ((min% (percentile min all-data))
(lower% (percentile lower all-data))
(med% (percentile med all-data))
(upper% (percentile upper all-data))
(max% (percentile max all-data)))
(format str "\\boxplot{~f}{~f}{~f}{~f}{~f} \\\\~%"
lower%
(- med% lower%)
med%
(- upper% med%)
upper%))))
(setf previous-row row)))
(format str "\\hline~%")
(format str "\\multicolumn{8}{c}{~~} & 50\\%~%")
(format str "\\end{tabular}~%")
(format str "\\end{center}}~%"))
(defun print-simple-boxplot (str >wins data title &optional (precision 0))
"Compares data using Mann-Whitney and prints a LaTeX boxplot to the specified stream."
(format str "{\\scriptsize\\begin{center}~%")
(format str "\\begin{tabular}{c l @{} c @{} c @{} c @{}}~%")
(format str "\\multicolumn{5}{l}{~a} \\\\~%" title)
(format str "Rank & Change \\\\~%")
(format str "\\hline~%")
(let* ((table (create-win-loss-table >wins data))
(all-data (apply #'append (mapcar #'fifth table)))
previous-row
rank)
(dolist (row (sort (copy-tree table) #'(lambda (row1 row2)
(if (eql (third row1) (third row2))
(> (second row1) (second row2))
(< (third row1) (third row2))))))
(let ((min (apply #'min (fifth row)))
(max (apply #'max (fifth row))))
(multiple-value-bind (med spread upper lower) (median (fifth row))
(if (not previous-row)
(setf rank 1)
(if (> (third row)
(third previous-row))
(setf rank (1+ rank))))
(format str "~d & ~a & \\vline & "
rank
(first row))
(let ((min% (percentile min all-data))
(lower% (percentile lower all-data))
(med% (percentile med all-data))
(upper% (percentile upper all-data))
(max% (percentile max all-data)))
(format str "\\boxplot{~f}{~f}{~f}{~f}{~f} & \\vline \\\\~%"
lower%
(- med% lower%)
med%
(- upper% med%)
upper%))))
(setf previous-row row)))
(format str "\\hline~%")
(format str "& & 0\\% & 50\\% & 100\\%~%")
(format str "\\end{tabular}~%")
(format str "\\end{center}}~%"))
(defun print-boxplots (str engine-score-fn strategy-score-fn title &optional (precision 0))
"Compares data using Mann-Whitney and prints LaTeX boxplots to the specified stream."
(format str "\\documentclass{article}~%~%")
(format str "\\newcommand{\\boxplot}[5]{~%")
(format str "\\begin{picture}(100, 10)~%")
(format str "\\put(#1, 1){\\line(0, 1){6}}~%")
(format str "\\put(#1, 4){\\line(1, 0){#2}}~%")
(format str "\\put(#3, 4){\\circle*{3}}~%")
(format str "\\put(#3, 4){\\line(1, 0){#4}}~%")
(format str "\\put(#5, 1){\\line(0, 1){6}}~%")
(format str "\\put(50, 2){\\line(0, 1){4}}~%")
(format str "\\end{picture}~%")
(format str "}~%~%")
(format str "\\title{~a}~%" title)
(format str "\\author{}~%")
(format str "\\date{}~%~%")
(format str "\\begin{document}~%")
(format str "\\maketitle~%")
(dolist (cs *case-studies*)
(dolist (p *policies*)
(print-simple-boxplot str nil (append (score-engines engine-score-fn :case-study-name (first cs) :policy-name (first p))
(score-strategies strategy-score-fn :case-study (funcall (fourth cs))))
(concatenate 'string (third cs) "/" (third p))
precision)))
(format str "\\end{document}~%"))
(defun print-boxplots-only-engines (str engine-score-fn title &optional (precision 0))
"Compares data using Mann-Whitney and prints LaTeX boxplots to the specified stream."
(format str "\\documentclass{article}~%~%")
(format str "\\newcommand{\\boxplot}[5]{~%")
(format str "\\begin{picture}(100, 10)~%")
(format str "\\put(#1, 1){\\line(0, 1){6}}~%")
(format str "\\put(#1, 4){\\line(1, 0){#2}}~%")
(format str "\\put(#3, 4){\\circle*{3}}~%")
(format str "\\put(#3, 4){\\line(1, 0){#4}}~%")
(format str "\\put(#5, 1){\\line(0, 1){6}}~%")
(format str "\\put(50, 2){\\line(0, 1){4}}~%")
(format str "\\end{picture}~%")
(format str "}~%~%")
(format str "\\title{~a}~%" title)
(format str "\\author{}~%")
(format str "\\date{}~%~%")
(format str "\\begin{document}~%")
(format str "\\maketitle~%")
(dolist (cs *case-studies*)
(dolist (p *policies*)
(print-simple-boxplot str nil (score-engines engine-score-fn :case-study-name (first cs) :policy-name (first p))
(concatenate 'string (third cs) "/" (third p))
precision)))
(format str "\\end{document}~%"))
(defun write-all-boxplots ()
"Prints LaTeX boxplots for all four dimensions to the results folder."
(dolist (score-fn '((effort? effort "effort")
(months? months "months")
(defects? defects "defects")
(threat? threat "threat")))
(with-open-file (str (concatenate 'string "../results/boxplots/" (third score-fn) ".tex") :direction :output :if-exists :supersede)
(print-boxplots-only-engines str
(first score-fn)
; (second score-fn)
(string-capitalize (third score-fn))
(if (eq (first score-fn) 'threat?)
2
0)))))
(defun write-multi-plot (case-study-name policy-name)
"Writes a gnuplot script for constraints vs. energy multiplots. Assumes six engines."
(let* ((cs (assoc case-study-name *case-studies*))
(p (assoc policy-name *policies*))
(file (concatenate 'string "multi-" (second cs) "-" (second p)))
(plt (concatenate 'string "../results/plots/" file ".plt"))
(png (concatenate 'string file ".png")))
(with-open-file (str plt :direction :output :if-exists :supersede)
(format str "set terminal png font '/usr/share/fonts/truetype/ttf-bitstream-vera/Vera.ttf' 8~%")
(format str "set output '~a'~%" png)
(format str "set origin 0.0, 0.0~%")
(format str "set size 1.0, 1.0~%")
(format str "set multiplot~%")
(dotimes (i (length *engines*))
(let ((e (elt *engines* i)))
(format str "set origin ~,1f, ~,3f~%"
(if (zerop (mod i 2)) 0 0.5)
(if (> (1+ i) 2) 0.5 0))
(format str "set size 0.5, 0.5~%")
(format str "set title '~a'~%" (third e))
(format str "set xlabel 'Constraints'~%")
(format str "set ylabel 'Energy'~%")
(format str "unset key~%")
(format str "plot '-' with points pointsize 0 linetype 0, ")
(format str "'-' with points pointtype 5 pointsize 0.25 linetype -1~%")
(dolist (e2 *engines*)
(dolist (path (read-paths (first e2) case-study-name policy-name))
(let ((state (min-energy-ttest? path)))
(format str "~a ~f~%" (constraint-count state) (energy? state)))))
(format str "end~%")
(dolist (path (read-paths (first e) case-study-name policy-name))
(let ((state (min-energy-ttest? path)))
(format str "~a ~f~%" (constraint-count state) (energy? state))))
(format str "end~%"))))))
(defun write-scatter-plots (case-study-name policy-name)
"Writes a gnuplot script for constraints vs. energy scatterplots."
(let ((cs (assoc case-study-name *case-studies*))
(p (assoc policy-name *policies*)))
(dolist (e *engines*)
(let* ((file (concatenate 'string "scatter-" (second cs) "-" (second p) "-" (second e)))
(plt (concatenate 'string "../results/plots/" file ".plt"))
(png (concatenate 'string file ".png")))
(with-open-file (str plt :direction :output :if-exists :supersede)
(format str "set terminal png font '/usr/share/fonts/truetype/ttf-bitstream-vera/Vera.ttf' 8~%")
(format str "set output '~a'~%" png)
(format str "set title '~a'~%" (third e))
(format str "set xlabel 'Constraints'~%")
(format str "set ylabel 'Energy'~%")
(format str "unset key~%")
(format str "plot '-' with points pointtype 6 pointsize 1.25 linetype 0, ")
(format str "'-' with points pointtype 7 pointsize 1.25 linetype -1~%")
(dolist (e2 *engines*)
(dolist (path (read-paths (first e2) case-study-name policy-name))
(let ((state (min-energy-ttest? path)))
(format str "~a ~f~%" (constraint-count state) (energy? state)))))
(format str "end~%")
(dolist (path (read-paths (first e) case-study-name policy-name))
(let ((state (min-energy-ttest? path)))
(format str "~a ~f~%" (constraint-count state) (energy? state))))
(format str "end~%"))))))
(defun write-single-run-plot (engine case-study-name policy-name &optional (index 0))
"Writes a gnuplot script for a constraints vs. (en/ef/mo/de/th) single run plot."
(dolist (which '((energy? "energy" "Energy")
(effort? "effort" "Effort")
(months? "months" "Months")
(defects? "defects" "Defects")
(threat? "threat" "Threat")))
(let* ((e (assoc engine *engines*))
(cs (assoc case-study-name *case-studies*))
(p (assoc policy-name *policies*))
(file (concatenate 'string "single-" (second e) "-" (second cs) "-" (second p) "-" (second which)))
(plt (concatenate 'string "../results/plots/" file ".plt"))
(png (concatenate 'string file ".png")))
(with-open-file (str plt :direction :output :if-exists :supersede)
(format str "set terminal png font '/usr/share/fonts/truetype/ttf-bitstream-vera/Vera.ttf' 8~%")
(format str "set output '~a'~%" png)
(format str "set xlabel 'Constraints'~%")
(format str "set ylabel '~a'~%" (third which))
(format str "unset key~%")
(format str "plot '-' with linespoints pointtype 7 pointsize 1 linetype -1, '-' with points pointtype 6 pointsize 2 linetype -1~%")
(let ((path (elt (read-paths (first e) case-study-name policy-name) index)))
(dolist (state (states? path))
(format str "~a ~f~%"
(constraint-count state)
(funcall (first which) state)))
(format str "end~%")
(let ((decision-point (min-energy-ttest? path)))
(format str "~a ~f~%"
(constraint-count decision-point)
(funcall (first which) decision-point)))
(format str "end~%"))))))
(defun print-constraint-frequency (str case-study-name policy-name)
"Prints the number of times each constraint is seen in the minimum energy state."
(let (constraints
(num-paths 0))
(dolist (e *engines*)
(dolist (path (read-paths (first e) case-study-name policy-name))
(incf num-paths)
(dolist (constraint (constraints? (min-energy-ttest? path)))
(when (not (assoc constraint constraints :test #'equal))
(push (cons constraint 0) constraints))
(incf (rest (assoc constraint constraints :test #'equal))))))
(dolist (constraint (sort constraints #'(lambda (x y) (> (rest x) (rest y)))))
(format str "~8a ~3<~a~>%~%" (first constraint) (round (* (/ (rest constraint) num-paths) 100))))))
(defun write-all-constraint-frequencies ()
"Writes all constraint frequencies to the results folder."
(dolist (cs *case-studies*)
(dolist (p *policies*)
(with-open-file (str (concatenate 'string "../results/freq/" (second cs) "-" (second p) ".txt")
:direction :output :if-exists :supersede)
(print-constraint-frequency str (first cs) (first p))))))
(defun print-runtimes (engine)
"Prints average runtimes for the given engine."
(dolist (cs *case-studies*)
(dolist (p *policies*)
(let (runtimes)
(dolist (path (read-paths engine (first cs) (first p)))
(push (- (path-end path) (path-start path)) runtimes))
(format t "~a/~a ~20t~,1f~%" (third cs) (third p) (mean runtimes))))))