;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; utilities.lisp ;;; Written by: Shaul Markovitch ;;; Last time modified: 10/1/2000 ;;; ;;; This file contains a list of general functions that are used ;;; in this project. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; random-selection ;;; ;;; Returns a random element of a list ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun random-selection (a-set) (elt a-set (random (length a-set)))) (defun random-in-range (a b) (+ a (random (- b a)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; random-subset ;;; ;;; Gets a set of elements and a second argument that can be an ;;; integer greater or equal to one or a float between 0 and 1. If it ;;; is 1 or larger, then it is the size of the random subset. If it ;;; it smaller than 1 then it is the portion of the set that will be ;;; the random subset. The function returns the random subset. As a ;;; second value it returns the difference set (i.e. set minus the ;;; subset) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun random-subset (set portion) (let* ((set-copy (copy-list set)) (subset-size (if (>= portion 1) (truncate portion) (truncate (* (length set) portion)))) (new-set (loop repeat subset-size for random-index = (random (length set-copy)) for element = (elt set-copy random-index) do (setq set-copy (delete element set-copy)) collect element))) (values new-set set-copy))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; compute-seconds ;;; ;;; Get a start time (that was saved by (get-internal-run-time)) and ;;; returns the times passed in seconds. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun compute-seconds (start-time) (float (/ (- (get-internal-run-time) start-time) internal-time-units-per-second))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; collect-statistics ;;; ;;; Gets a list of numbers and returns statistics aboyt this list. ;;; The statistics includes the mean, variance, std, min and max and ;;; is return in the form of the statistics structure. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun collect-statistics (s &key (key #'identity)) (let* ((n (length s)) (mean (float (/ (loop for i in s sum (funcall key i)) n))) (var (float (/ (loop for i in s sum (expt (- (funcall key i) mean) 2)) n))) (std (float (sqrt var))) (max (loop for i in s maximize (funcall key i))) (min (loop for i in s minimize (funcall key i))) (median (if (oddp n) (funcall key (elt s (truncate (/ n 2)))) (let ((half (truncate (/ (1- n) 2)))) (/ (+ (funcall key (elt s half)) (funcall key (elt s (1+ half)))) 2))))) (make-statistics :mean mean :var var :std std :max max :min min :median median :n n))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; shuffle ;;; ;;; Gets a list and returns a random permutation of the list. If the ;;; second option argument is not nil, the list will be permutated ;;; destructively. Otherwise, a new list will be generated. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun shuffle (list &optional (destructive nil) &aux (new-list (if destructive list (copy-list list))) (n (length new-list))) (loop for i below n do (rotatef (elt new-list i) (elt new-list (random n)))) new-list) (defconstant dq #\") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; generate-plot ;;; ;;; This is a rather useful utility for geenrating graphs using the ;;; "gnuplot" program which is available on most unix machines. The ;;; program gets a list of x-values and one or more lists of y-values. ;;; If the "eps-flag" keyword is not nil, and eps file is generated. ;;; If it is nil, the graphs are displayed on the screen. ;;; In such a case the user is prompted to enter return in order to ;;; continue. ;;; The x-label and y-label keyword arguments are used for labeling ;;; the x axis and y axis. The plot-label keyword argument is used ;;; for labeling the various plots. The number of items in the ;;; plo-labels list should be equal to the number of items in y-lists. ;;; The filename argument is used as the core file name for the two ;;; files that the program generates. If it is not given the program ;;; generates a name based on the date and the time. The files are ;;; a data file and a command file that is given to gnuplot. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun generate-plot (x-list y-lists &key (x-label) (y-label) (plot-labels (loop for i from 1 to (length y-lists) collect i)) (eps-flag nil) (filename nil) ) (let* ((cur-date (multiple-value-list (get-decoded-time))) (corefilename (or filename (format nil "g-~d.~d-~d.~d.~d" (fourth cur-date) (fifth cur-date) (third cur-date) (second cur-date) (first cur-date)))) (gnuplotfile (format nil "~a.gnuplot" corefilename)) (datafile (format nil "~a.data" corefilename))) (with-open-file (f datafile :if-exists :supersede :if-does-not-exist :create :direction :output) (loop for x in x-list for index from 0 do (format f "~a " x) (loop for list in y-lists do (format f "~a " (elt list index))) (format f "~%"))) (with-open-file (f gnuplotfile :if-exists :supersede :if-does-not-exist :create :direction :output) (cond (eps-flag (format f "set terminal postscript eps~%") (format f "set output ~a~a.eps~a~%" dq corefilename dq)) (t (format f "set terminal x11~%"))) (cond ((= (length y-lists) 1) (format f "set nokey~%")) (t (format f "set key~%"))) (when x-label (format f "set xlabel ~a~a~a~%" dq x-label dq)) (when y-label (format f "set ylabel ~a~a~a~%" dq y-label dq)) (cond ((= (length y-lists) 1) (format f "plot ~a~a~a with linesp~%" dq datafile dq)) (t (format f "plot ") (loop for label in plot-labels for j from 2 do (when (> j 2)(format f ",")) (format f " ~a~a~a using 1:~d title ~a~a~a with linesp" dq datafile dq j dq label dq )) (format f "~%"))) (when (not eps-flag) (format f "pause -1 ~a hit return to continue ~a " dq dq))) (run-program "gnuplot" (list gnuplotfile) :input t :output t) )) (defun output-var (v-names file-name &optional (parameters nil)) (when (and v-names (atom v-names)) (setq v-names (list v-names))) (with-open-file (f file-name :if-exists :supersede :if-does-not-exist :create :direction :output) (when parameters (format f "#|~%") (loop for p in parameters for name = (if (symbolp p) p (first p)) for val = (if (symbolp p) (eval p)(second p)) do (format f "~%~A~40T~A" name val)) (format f "~%|#~%")) (loop for v in v-names for name = (if (symbolp v) v (first v)) for val = (if (symbolp v) (eval v)(second v)) do (format f "~%~:W~%" `(setq ,name ',val)))))