(Arne Babenhauserheide)
2016-11-07: also plot the development of the ensemble also plot the development of the ensemble
diff --git a/examples/ensemble-estimation.w b/examples/ensemble-estimation.w --- a/examples/ensemble-estimation.w +++ b/examples/ensemble-estimation.w @@ -1,5 +1,6 @@ #!/usr/bin/env sh # -*- wisp -*- +guile -L $(dirname $(dirname $(realpath "$0"))) -c '(import (wisp-scheme) (language wisp spec))' exec guile -L $(dirname $(dirname $(realpath "$0"))) --language=wisp -e '(@@ (examples ensemble-estimation) main)' -s "$0" "$@" ; !# @@ -78,19 +79,24 @@ define* : write-multiple . x ;; Start with the simple case: One variable and independent observations (R diagonal) ;; First define a truth -define x^seed '(0.5 0.6 2 0.1) ; 0.7 0.9 0.8 0.4) +define x^seed '(0.5 2 0.6 0.1) ; 0.7 0.9 0.8 0.4) ;; The size is the length of the seed, squared, each multiplied by each define x^true : append-ec (: i (length x^seed)) : list-ec (: j x^seed) : * j : list-ref x^seed i ;; And add an initial guess of the parameters define x^b : append-ec (: i (length x^seed)) '(1 1 1 1) ; 1 1 1 1) ; initial guess -define P : make-covariance-matrix-from-standard-deviations : append-ec (: i (length x^seed)) '(0.5 0.1 0.3 0.1) ; 0.2 0.2 0.2 0.2) +;; set x^b as x^true to test losing uncertainty +define x^b x^true +define x^seed-std : append-ec (: i (length x^seed)) '(0.5 0.1 0.3 0.1) ; 0.2 0.2 0.2 0.2) +define P : make-covariance-matrix-from-standard-deviations x^seed-std ;; Then generate observations -define y⁰-num 1000 +define y⁰-num 600 ; careful: N² in memory, 1000 is still OK define y⁰-pos-max 100 ;; At the positions where they are measured. Drawn randomly to avoid ;; giving an undue weight to later values. -define y⁰-pos : list-ec (: i y⁰-num) : * (random:uniform) y⁰-pos-max +define y⁰-pos-sorted : list-ec (: i y⁰-num) : exact->inexact : * y⁰-pos-max : / i y⁰-num +define y⁰-pos-random : list-ec (: i y⁰-num) : * (random:uniform) y⁰-pos-max +define y⁰-pos y⁰-pos-random define : H-single-parameter xi xi-pos pos . "Observation function for a single parameter." @@ -124,7 +130,7 @@ x are parameters to be optimized, pos is ;; the equivalent of measured observations define y^true : list-ec (: i y⁰-pos) : H x^true i ;; now we disturb the observations with a fixed standard deviation. This assumes uncorrelated observations. -define y⁰-std 10 +define y⁰-std 1 define y⁰ : list-ec (: i y^true) : + i : * y⁰-std : random:normal ;; and define the covariance matrix. This assumes uncorrelated observations. define R : make-covariance-matrix-from-standard-deviations : list-ec (: i y⁰-num) y⁰-std @@ -135,6 +141,8 @@ define R : make-covariance-matrix-from-s ;; define y⁰ : list-ec (: i y⁰-num) : + y⁰-mean : * y⁰-std : random:normal +define x^steps '() + define : EnSRT H x P y R y-pos N . "Observation function H, parameters x, parameter-covariance P, observations y, observation covariance R @@ -147,11 +155,12 @@ Limitations: y is a single value. R and observation-variances : list-ec (: i (length y)) : list-ref (list-ref R i) i observation-positions y-pos x^b x - x-deviations + x-deviations list-ec (: i N) list-ec (: j (length x)) * : random:normal sqrt : list-ref (list-ref P j) j ; only for diagonal P! + set! x^steps : cons x-deviations x^steps cond : null? observations-to-process list x^b x-deviations @@ -160,7 +169,7 @@ Limitations: y is a single value. R and ; newline let* : y_cur : car observations-to-process - R_cur : car observation-variances + R_cur : * 1 : car observation-variances y-pos_cur : car observation-positions Hx^b_i list-ec (: i x-deviations) @@ -207,36 +216,67 @@ Limitations: y is a single value. R and . x^a-deviations +define : x-deviations->y-deviations H x-opt x-deviations y⁰-pos + . "Calculate y-deviations for each measurement from the x-deviations. + + return ((y_0'0 y_0'1 ...) ...) + " + define (~ li i) (list-ref li i) + ; for each ensemble member calculate the y-deviations + let* + : y-opt : map (λ (x) (H x-opt x)) y⁰-pos + y-ensemble + list-ec (: x-dev x-deviations) ; x-dev has one number (x'_i) per x-parameter + let* + : ; calculate x-parameters for the ensemble + x-opt+dev + list-ec (: j (length x-opt)) + + : ~ x-opt j + ~ x-dev j + ; calculate all y-values for the ensemble + y-opt+dev : map (λ (x) (H x-opt+dev x)) y⁰-pos + ; calculate all differences + map (λ (x y) (- x y)) y-opt+dev y-opt + ; reshape into one ensemble per value + list-ec (: ensidx (length (~ y-ensemble 0))) + list-ec (: obsidx (length y-ensemble)) + ~ (~ y-ensemble obsidx) ensidx + +define : flatten li + append-ec (: i li) i + define : main args let* - : optimized : EnSRT H x^b P y⁰ R y⁰-pos 40 + : ensemble-member-count 16 + optimized : EnSRT H x^b P y⁰ R y⁰-pos ensemble-member-count x-opt : list-ref optimized 0 x-deviations : list-ref optimized 1 - ; std : sqrt : * {1 / {(length x-deviations) - 1}} : sum-ec (: i x-deviations) : expt i 2 - format #t "x⁰: ~A ± ~A\nx: ~A ± ~A\nx^t: ~A\nx-t/σ:~A\ny̅: ~A ± ~A\ny̅⁰: ~A ± ~A\ny̅^t: ~A\nnoise:~A\n" + x-std + list-ec (: i (length x-opt)) + apply standard-deviation-from-deviations : list-ec (: j x-deviations) : list-ref j i + y-deviations : x-deviations->y-deviations H x-opt x-deviations y⁰-pos + x^b-deviations-approx + list-ec (: i ensemble-member-count) + list-ec (: j (length x^b)) + * : random:normal + sqrt : list-ref (list-ref P j) j ; only for diagonal P! + y^b-deviations : x-deviations->y-deviations H x^b x^b-deviations-approx y⁰-pos + y-std + apply standard-deviation-from-deviations + flatten y-deviations + y-stds : list-ec (: i y-deviations) : apply standard-deviation-from-deviations i + y^b-stds : list-ec (: i y^b-deviations) : apply standard-deviation-from-deviations i + format #t "x⁰: ~A\n ± ~A\nx: ~A\n ± ~A\nx^t: ~A\nx-t/σ:~A\ny̅: ~A ± ~A\ny̅⁰: ~A ± ~A\ny̅^t: ~A\nnoise:~A\n" . x^b list-ec (: i (length x^b)) : list-ref (list-ref P i) i . x-opt - list-ec (: i (length x-opt)) - apply standard-deviation-from-deviations : list-ec (: j x-deviations) : list-ref j i + . x-std . x^true list-ec (: i (length x-opt)) / : - (list-ref x-opt i) (list-ref x^true i) apply standard-deviation-from-deviations : list-ec (: j x-deviations) : list-ref j i - * {1 / (length y⁰)} : apply + : map (lambda (x) (H x-opt x)) y⁰-pos - apply standard-deviation-from-deviations - append-ec (: i (length x-deviations)) - let* - : - x-opt+dev - list-ec (: j (length x-opt)) - + : list-ref x-opt j - list-ref - list-ref x-deviations i - . j - y-opt+dev : map (lambda (x) (H x-opt+dev x)) y⁰-pos - y-opt : map (lambda (x) (H x-opt x)) y⁰-pos - map (lambda (x y) (- x y)) y-opt+dev y-opt + mean : map (lambda (x) (H x-opt x)) y⁰-pos + . y-std ; list-ec (: i (length y-opt)) ; - (list-ref y-opt+dev i) (list-ref y-opt i) ; apply standard-deviation-from-deviations : map H x-deviations ; FIXME: This only works for trivial H. @@ -246,17 +286,28 @@ define : main args . y⁰-std ; now plot the result let : : port : open-output-pipe "python" - format port "import pylab as pl\n" + format port "import pylab as pl\nimport matplotlib as mpl\n" format port "y0 = [float(i) for i in '~A'[1:-1].split(' ')]\n" y⁰ format port "ypos = [float(i) for i in '~A'[1:-1].split(' ')]\n" y⁰-pos format port "yinit = [float(i) for i in '~A'[1:-1].split(' ')]\n" : list-ec (: i y⁰-pos) : H x^b i + format port "yinitstds = [float(i) for i in '~A'[1:-1].split(' ')]\n" y^b-stds format port "ytrue = [float(i) for i in '~A'[1:-1].split(' ')]\n" : list-ec (: i y⁰-pos) : H x^true i format port "yopt = [float(i) for i in '~A'[1:-1].split(' ')]\n" : list-ec (: i y⁰-pos) : H x-opt i - format port "pl.plot(*zip(*sorted(zip(ypos, yinit))), label='prior')\n" + format port "yoptstds = [float(i) for i in '~A'[1:-1].split(' ')]\n" y-stds + format port "pl.errorbar(*zip(*sorted(zip(ypos, yinit))), yerr=zip(*sorted(zip(ypos, yinitstds)))[1], label='prior')\n" format port "pl.plot(*zip(*sorted(zip(ypos, ytrue))), label='true')\n" - format port "pl.plot(*zip(*sorted(zip(ypos, yopt))), label='optimized')\n" + format port "pl.errorbar(*zip(*sorted(zip(ypos, yopt))), yerr=zip(*sorted(zip(ypos, yoptstds)))[1], label='optimized')\n" format port "pl.plot(*zip(*sorted(zip(ypos, y0))), marker='+', linewidth=0, label='measurements')\n" - format port "pl.legend()\n" + list-ec (: step 0 (length x^steps) 10) ; stepsize 10: sample one in 10 steps + list-ec (: member (list-ref x^steps (- (length x^steps) step 1))) ; reversed + begin + format port "paired = pl.get_cmap('Paired') +cNorm = mpl.colors.Normalize(vmin=~A, vmax=~A) +scalarMap = mpl.cm.ScalarMappable(norm=cNorm, cmap=paired)\n" 0 (length member) + list-ec (: param-idx 0 (length member) 4) ; step = 4 + ; plot parameter 0 + format port "pl.plot(~A, ~A, marker='.', color=scalarMap.to_rgba(~A), linewidth=0, label='', alpha=0.3, zorder=-1)\n" (/ step 10) (+ 80 (* 60 (list-ref member param-idx))) param-idx + format port "pl.legend(loc='upper right')\n" format port "pl.xlabel('position [arbitrary units]')\n" format port "pl.ylabel('value [arbitrary units]')\n" format port "pl.title('ensemble optimization results')\n"