;;
;;
;; Chicken Scheme bindings for the CMA-ES optimization library.
;;
;; CMA-ES is copyright 1996, 2003, 2007 Nikolaus Hansen.
;; Chicken Scheme code is copyright 2012 Ivan Raikov.
;;
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License,
;; version 2, as published by the Free Software Foundation.
;;
;; This program 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
;; along with this program. If not, see .
;;
;;
(module cmaes
(init run
init-from-file terminated? get-parameter get-value
sample-population update-distribution
read-signals-from-file read-signals
write-to-file
terminate free
)
(import scheme chicken foreign)
(require-extension srfi-4)
(require-library lolevel posix files srfi-1 srfi-13)
(import (only lolevel move-memory! make-pointer-vector pointer-vector-ref pointer-vector-set! free)
(only srfi-1 fold every)
(only srfi-13 string-concatenate)
(only data-structures ->string)
(only posix file-close file-write open-output-file* file-mkstemp)
(only files make-pathname)
)
(define tempdir (make-parameter "/tmp"))
(foreign-declare #<
#include "cmaes.h"
extern double * cmaes_init(cmaes_t *, int dimension , double *xstart,
double *stddev, long seed, int lambda,
const char *input_parameter_filename);
void cmaes_resume_distribution(cmaes_t *evo_ptr, char *filename);
void cmaes_exit(cmaes_t *);
double * const * cmaes_SamplePopulation(cmaes_t *);
double * cmaes_UpdateDistribution(cmaes_t *,
const double *rgFitnessValues);
const char * cmaes_TestForTermination(cmaes_t *);
double * const * cmaes_ReSampleSingle( cmaes_t *t, int index);
double const * cmaes_ReSampleSingle_old(cmaes_t *, double *rgx);
double * cmaes_SampleSingleInto( cmaes_t *t, double *rgx);
void cmaes_UpdateEigensystem(cmaes_t *, int flgforce);
double cmaes_Get(cmaes_t *, char const *keyword);
const double * cmaes_GetPtr(cmaes_t *, char const *keyword); /* e.g. "xbestever" */
double * cmaes_GetNew( cmaes_t *t, char const *keyword);
double * cmaes_GetInto( cmaes_t *t, char const *keyword, double *mem);
void cmaes_ReadSignals(cmaes_t *, char const *filename);
void cmaes_WriteToFile(cmaes_t *, const char *szKeyWord,
const char *output_filename);
char * cmaes_SayHello(cmaes_t *);
double * cmaes_NewDouble(int n);
void cmaes_FATAL(char const *s1, char const *s2, char const *s3,
char const *s4);
cmaes_t * cmaes_alloc(void)
{
cmaes_t * h;
assert ((h = malloc (sizeof(*h))) != NULL);
return h;
}
double *cmaes_init_from_file (cmaes_t *h, char *filepath)
{
double *result;
result = cmaes_init (h, 0, NULL, NULL, 0, 0, filepath);
return result;
}
double cmaes_lambda (cmaes_t *h)
{
return cmaes_Get (h, "lambda");
}
int cmaes_dimension (cmaes_t *h)
{
return (int)cmaes_Get (h, "N");
}
double *double_array_pointer_deref (double **ptr, int offset)
{
return *(ptr+offset);
}
EOF
)
(define-foreign-type cmaesobj "cmaes_t")
(define c_double_array_pointer_deref
(foreign-safe-lambda (nonnull-c-pointer double) "double_array_pointer_deref" (c-pointer (c-pointer double )) int))
(define c_cmaes_alloc
(foreign-safe-lambda (nonnull-c-pointer cmaesobj) "cmaes_alloc" ))
(define c_cmaes_lambda
(foreign-safe-lambda int "cmaes_lambda" (nonnull-c-pointer cmaesobj) ))
(define c_cmaes_dimension
(foreign-safe-lambda int "cmaes_dimension" (nonnull-c-pointer cmaesobj) ))
(define c_cmaes_init_from_file
(foreign-safe-lambda (nonnull-c-pointer double) "cmaes_init_from_file" (nonnull-c-pointer cmaesobj) c-string ))
(define c_cmaes_read_signals_from_file
(foreign-safe-lambda void "cmaes_ReadSignals" (nonnull-c-pointer cmaesobj) c-string ))
(define c_cmaes_terminationp
(foreign-safe-lambda c-string "cmaes_TestForTermination" (nonnull-c-pointer cmaesobj) ))
(define c_cmaes_sample_population
(foreign-safe-lambda (c-pointer (c-pointer double)) "cmaes_SamplePopulation" (nonnull-c-pointer cmaesobj) ))
(define c_cmaes_get_parameter
(foreign-safe-lambda double "cmaes_Get" (nonnull-c-pointer cmaesobj) c-string ))
(define c_cmaes_update_distribution
(foreign-safe-lambda (c-pointer double) "cmaes_UpdateDistribution" (nonnull-c-pointer cmaesobj) f64vector ))
(define c_cmaes_get_new
(foreign-safe-lambda (c-pointer double) "cmaes_GetNew" (nonnull-c-pointer cmaesobj) c-string ))
(define c_cmaes_write_to_file
(foreign-safe-lambda void "cmaes_WriteToFile" (nonnull-c-pointer cmaesobj) c-string c-string ))
(define c_cmaes_exit
(foreign-safe-lambda void "cmaes_exit" (nonnull-c-pointer cmaesobj)))
(define (make-init-parameter-string kv)
(if (not (pair? kv))
(error 'make-init-parameter-string "invalid parameter" kv))
(let ((k (car kv)) (v (cdr kv)))
(case k
((N) ; problem dimension
(if (and (integer? v) (positive? v))
(sprintf "~A ~A # problem dimension~%" k v)
(error 'make-init-parameter-string "parameter N (problem dimension) must be a positive integer")))
((initialX) ; initial search point
(if (every number? v)
(let ((n (length v)))
(string-concatenate
(cons (sprintf "~A ~A: # initial search point~%" k n)
(map (lambda (n) (sprintf " ~A~%" n)) v))
))
(error 'make-init-parameter-string "parameter initialX (initial search point) must be a list of numbers")))
((typicalX) ; typical search point
(if (every number? v)
(let ((n (length v)))
(string-concatenate
(cons (sprintf "~A ~A: # typical search point (overwritten by initialX)~%" k n)
(map (lambda (n) (sprintf " ~A~%" n)) v))
))
(error 'make-init-parameter-string "parameter typicalX (typical search point) must be a list of numbers")))
((initialStandardDeviations)
(if (every number? v)
(let ((n (length v)))
(string-concatenate
(cons (sprintf "~A ~A: # typical search point (overwritten by initialX)~%" k n)
(map (lambda (n) (sprintf " ~A~%" n)) v))
))
(error 'make-init-parameter-string "parameter initialStandardDeviations (typical search point) must be a list of numbers")))
((stopMaxFunEvals)
(if (and (integer? v) (positive? v))
(sprintf "~A ~A # max number of f-evaluations~%" k v)
(error 'make-init-parameter-string "parameter stopMaxFunEvals (max number of f-evaluations) must be a positive integer")))
((stopMaxIter)
(if (and (integer? v) (positive? v))
(sprintf "~A ~A # max number of iterations (generations)~%" k v)
(error 'make-init-parameter-string "parameter stopMaxIter (max number of iterations) must be a positive integer")))
((stopTolFun)
(if (and (number? v) (positive? v))
(sprintf "~A ~A # stop if function value differences are smaller than this value ~%" k v)
(error 'make-init-parameter-string "parameter stopTolFun (stop if value differences are too small) must be a positive number")))
((stopTolFunHist)
(if (and (number? v) (positive? v))
(sprintf "~A ~A # stop if best function value differences are smaller than this value ~%" k v)
(error 'make-init-parameter-string "parameter stopTolFun (stop if best value differences are too small) must be a positive number")))
((stopTolX)
(if (and (number? v) (positive? v))
(sprintf "~A ~A # stop if step sizes/steps in x-space are smaller than this value ~%" k v)
(error 'make-init-parameter-string "parameter stopTolX (stop if step sizes are too small) must be a positive number")))
((stopTolUpXFactor)
(if (and (number? v) (positive? v))
(sprintf "~A ~A # stop if std dev increases more than this value ~%" k v)
(error 'make-init-parameter-string "parameter stopTolUpXFactor (stop if std dev increases are too big) must be a positive number")))
((seed)
(if (number? v)
(sprintf "~A ~A ~%" k v)
(error 'make-init-parameter-string "parameter seed must be a number")))
(else (error 'make-init-parameter-string "unknown parameter" kv))
)))
(define (init-from-file filepath)
(let* ((h (c_cmaes_alloc))
(fitvals (c_cmaes_init_from_file h filepath))
(lam (c_cmaes_lambda h)))
(let ((v (make-f64vector lam)))
(move-memory! fitvals v (* 8 lam))
(values h v))
))
(define (read-signals-from-file h filepath)
(c_cmaes_read_signals_from_file h filepath))
(define (make-signal-parameter-string kv)
(if (not (pair? kv))
(error 'make-signal-parameter-string "invalid parameter" kv))
(let ((k (car kv)) (v (cdr kv)))
(if (pair? v)
(sprintf "~A ~A ~A~%" k (car v) (cadr v))
(sprintf "~A ~A~%" k v))
))
(define (read-signals h parameters)
(let-values (((fd temp-path) (file-mkstemp (make-pathname (tempdir) "cmaes.sig.XXXXXX"))))
(let ((strs (map make-signal-parameter-string parameters)))
(for-each (lambda (s) (file-write fd s)) strs))
(read-signals-from-file h temp-path)
(file-close fd)
(delete-file temp-path)
))
(define (terminated? h)
(c_cmaes_terminationp h))
(define (sample-population h)
(let ((ptr (c_cmaes_sample_population h ))
(lam (c_cmaes_lambda h))
(n (c_cmaes_dimension h)))
(let ((vptr (make-pointer-vector lam))
(v (make-vector lam)))
(let recur ((i 0))
(if (< i lam)
(begin
(pointer-vector-set! vptr i (c_double_array_pointer_deref ptr i))
(recur (+ 1 i)))))
(let recur ((i 0))
(if (< i lam)
(let ((vv (make-f64vector n 0.)))
(move-memory! (pointer-vector-ref vptr i) vv (* 8 n))
(vector-set! v i vv)
(recur (+ 1 i)))
v)
))
))
(define (get-parameter h s)
(c_cmaes_get_parameter h (->string s)))
(define (update-distribution h lam)
(let ((ptr (c_cmaes_update_distribution h lam))
(n (c_cmaes_dimension h)))
(let ((v (make-f64vector n)))
(move-memory! ptr v (* 8 n))
v)))
(define (get-value h s)
(let ((ptr (c_cmaes_get_new h (->string s)))
(n (c_cmaes_dimension h)))
(let ((v (make-f64vector n)))
(move-memory! ptr v (* 8 n))
v)))
(define (write-to-file h s fn)
(c_cmaes_write_to_file h (->string s) fn))
(define (terminate h)
(c_cmaes_exit h))
(define (init parameters)
(let-values (((fd temp-path) (file-mkstemp (make-pathname (tempdir) "cmaes.par.XXXXXX"))))
(let ((strs (map make-init-parameter-string parameters)))
(for-each (lambda (s) (file-write fd s)) strs))
(let-values (((h funvals) (init-from-file temp-path)))
(file-close fd)
(delete-file temp-path)
(values h funvals)
))
)
(define (run f h funvals signals #!key
(output-file "all.dat")
(result-values '(xbest xmean)))
(read-signals h signals)
(let-values (((fd temp-path) (file-mkstemp (make-pathname (tempdir) "cmaes.sig.XXXXXX"))))
(let ((strs (map make-signal-parameter-string signals)))
(for-each (lambda (s) (file-write fd s)) strs))
(file-close fd)
(let recur ((h h))
(let ((stop (terminated? h)))
(if (not stop)
(let ((pop (sample-population h))
(lam (get-parameter h 'lambda))
(n (get-parameter h 'dim)))
(let inner-recur ((i 0))
(if (< i lam)
(begin
(f64vector-set! funvals i (f (vector-ref pop i)))
(inner-recur (+ 1 i)))))
(update-distribution h funvals)
(read-signals-from-file h temp-path)
(recur h)
)
(print stop)
)))
(delete-file temp-path)
)
(if output-file (write-to-file h 'all output-file))
(let ((result (map (lambda (x) (cons x (get-value h (->string x)))) result-values)))
(terminate h)
(free h)
result
)
)
)