File: bayes.lsp

package info (click to toggle)
xlispstat 3.52.0-3
  • links: PTS
  • area: main
  • in suites: hamm, slink
  • size: 7,472 kB
  • ctags: 12,480
  • sloc: ansic: 89,534; lisp: 21,690; sh: 1,525; makefile: 520; csh: 1
file content (658 lines) | stat: -rw-r--r-- 27,366 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
;;;; XLISP-STAT 2.1 Copyright (c) 1990, by Luke Tierney
;;;; Additions to Xlisp 2.1, Copyright (c) 1989 by David Michael Betz
;;;; You may give out copies of this software; for conditions see the file
;;;; COPYING included with this distribution.

(require "maximize")
(provide "bayes")

;;;;
;;;; Objects Representing Functions
;;;;

;; Generic C2 Functions

(defproto c2-function-proto '(f h num-derivs))

(defmeth c2-function-proto :isnew (f &optional (h .001) (num-derivs 0))
  (setf (slot-value 'f) f)
  (setf (slot-value 'h) (if (numberp h) (list h h) h))
  (setf (slot-value 'num-derivs) num-derivs))

(defmeth c2-function-proto :f (&optional f)
  (if f (setf (slot-value 'f) f))
  (slot-value 'f))

(defmeth c2-function-proto :grad-h () (first (slot-value 'h)))
(defmeth c2-function-proto :hess-h () (second (slot-value 'h)))
(defmeth c2-function-proto :num-derivs () (slot-value 'num-derivs))

(defmeth c2-function-proto :value (x)
  (let ((f (send self :f)))
    (if (objectp f) 
        (send f :value x) 
        (let ((v (funcall f x)))
          (if (consp v) (first v) v)))))

(defmeth c2-function-proto :gradient (x &optional (h (send self :grad-h)))
  (let ((f (send self :f)))
    (if (objectp f) (send f :gradient x h) (numgrad f x nil h))))

(defmeth c2-function-proto :hessian (x &optional (h (send self :hess-h)))
  (let ((f (send self :f)))
    (if (objectp f) (send f :hessian x h) (numhess f x nil h))))

(defmeth c2-function-proto :vals (x &optional (h (send self :hess-h)))
  (let ((f (send self :f)))
    (if (objectp f)
        (send f :vals x h)
        (let ((v (funcall f x)))
          (if (consp v)
              (if (= (length v) 3)
                  v  
                  (list (first v) (second v) (send self :hessian x h)))
              (list v (send self :gradient x h) (send self :hessian x h)))))))

(defmeth c2-function-proto :vals (x &optional (h (send self :hess-h)))
  (let ((f (send self :f)))
    (if (objectp f) (send f :vals x h) (numhess f x nil h t))))


;; Scaled C2 Functions

(defproto scaled-c2-function-proto '(scaling) () c2-function-proto)

;;**** allow function objects?
(defmeth scaled-c2-function-proto :isnew (f &optional 
                                            theta
                                            sigma
                                            (center 0)
                                            (scale 1)
                                            (h 0.001))
  (let* ((value (funcall f theta))
         (num-derivs (if (consp value) (- (length value) 1) -1))
         (sigma-t (if (< 0 num-derivs) (transpose sigma))))
    (labels ((scale (v)
               (if v
                   (case num-derivs
                     (-1 (/ (- v center) scale))
                     (0 (/ (- (first v) center) scale))
                     (1 (list (/ (- (first v) center) scale)
                              (matmult sigma-t (/ (second v) scale))))
                     (2 (list (/ (- (first v) center) scale)
                              (matmult sigma-t (/ (second v) scale))
                              (matmult sigma-t (/ (third v) scale) sigma))))))
             (sf (x) (scale (funcall f (ax+y sigma x theta t)))))
      (call-next-method #'sf h num-derivs))))

;; Tilted C2 Functions
;; **** allow nil values?
(defproto tilt-function-proto '(tilt exptilt) () c2-function-proto)

(defmeth tilt-function-proto :isnew (&optional f (tilt .1) (h .001))
  (call-next-method f h)
  (setf (slot-value 'exptilt) t)
  (setf (slot-value 'tilt) tilt))

(defmeth tilt-function-proto :tilt (&optional tilt)
  (if tilt (setf (slot-value 'tilt) tilt))
  (slot-value 'tilt))

(defmeth tilt-function-proto :exptilt (&optional (new nil set))
  (if set (setf (slot-value 'exptilt) new))
  (slot-value 'exptilt))

(defmeth tilt-function-proto :value (x)
  (let ((f (send self :f))
        (tilt (send self :tilt))
        (exptilt (send self :exptilt)))
    (flet ((value (f)
             (let ((v (send f :value x)))
               (if exptilt v (log v)))))
      (* tilt (if (consp f) (apply #'+ (mapcar #'value f)) (value f))))))

(defmeth tilt-function-proto :gradient (x &optional (h (send self :grad-h)))
  (let ((f (send self :f))
        (tilt (send self :tilt))
        (exptilt (send self :exptilt)))
    (flet ((gradient (f)
             (if exptilt
                 (send f :gradient x h)
                 (let ((v (send f :value x))
                       (grad (send f :gradient x h)))
                   (/ grad v)))))
      (* tilt 
         (if (consp f) (apply #'+ (mapcar #'gradient f)) (gradient f))))))

(defmeth tilt-function-proto :hessian (x &optional (h (send self :hess-h)))
  (let ((f (send self :f))
        (tilt (send self :tilt))
        (exptilt (send self :exptilt)))
    (flet ((hessian (f)
             (let* ((vals (send f :vals x h))
                    (v (first vals))
                    (grad (if exptilt (second vals) (/ (second vals) v)))
                    (hess (if exptilt (third vals) (/ (third vals) v))))
               (if exptilt hess (- hess (outer-product grad grad))))))
      (* tilt (if (consp f) (apply #'+ (mapcar #'hessian f)) (hessian f))))))

(defmeth tilt-function-proto :vals (x &optional (h (send self :hess-h)))
  (let ((f (send self :f))
        (tilt (send self :tilt))
        (exptilt (send self :exptilt)))
    (flet ((vals (f)
             (let ((vals (send f :vals x h)))
               (if exptilt
                   vals
                   (let* ((v (first vals))
                          (grad (/ (second vals) v))
                          (hess (- (/ (third vals) v) 
                                   (outer-product grad grad))))
                     (list (log v) grad hess))))))
      (let ((v (if (consp f) (mapcar #'vals f) (vals f))))
        (* tilt (if (consp f) (apply #'+ v) v))))))
        
;; scaled log posterior prototype

(defproto scaled-logpost-proto 
  '(tilt-object init-pars) () scaled-c2-function-proto)

(defmeth scaled-logpost-proto :isnew (f &optional 
                                        theta sigma
                                        (center 0) (scale 1) (h .001))
  (let* ((n (length theta))
         (m (repeat 0 n))
         (m-grad (repeat 0 n))
         (m-hess (- (identity-matrix n)))
         (pars (list m m-grad m-hess)))
    (call-next-method f theta sigma center scale h)
    (setf (slot-value 'init-pars) pars)
    (setf (slot-value 'tilt-object) (send tilt-function-proto :new))))

(defmeth scaled-logpost-proto :log-laplace (g &optional
                                              (count-limit 2) det-only (h .1))
  (let* ((x (send self :tilt-newton g count-limit))
         (vals (send self :vals x h))
         (gvals (if g (send g :vals x h)))
         (hess (if g (+ (third vals) (third gvals)) (third vals)))
         (det (- (sum (log (diagonal (first (chol-decomp (- hess)))))))))
    (if det-only 
        det 
        (if g (+ (first vals) (first gvals) det) (+ (first vals) det)))))

(defmeth scaled-logpost-proto :tilt-newton (tilt &optional (count-limit 2))
  (let* ((pars (slot-value 'init-pars))
         (mode (first pars))
         (mode-grad (second pars))
         (mode-hess (third pars)))
    (flet ((gradhess (x initial) 
             (let ((gh (if (and initial mode-grad mode-hess)
                                 (list mode-grad mode-hess)
                                 (rest (send self :vals x)))))
               (if tilt (+ gh (rest (send tilt :vals x))) gh)))
           (newton-step (x gh) (- x (solve (second gh) (first gh)))))
      (do* ((count 1 (+ count 1))
            (gradhess (gradhess mode t) (gradhess x nil))
            (x (newton-step mode gradhess) (newton-step x gradhess)))
           ((>= count count-limit) x)))))

(defmeth scaled-logpost-proto :tilt-laplace (g tilt &optional 
                                               (exptilt t) maxiter det-only h)
  (let ((tilt-object (slot-value 'tilt-object)))
    (send tilt-object :exptilt exptilt)
    (send tilt-object :f g)
    (send tilt-object :tilt tilt)
    (send self :log-laplace tilt-object maxiter det-only h)))

(defmeth scaled-logpost-proto :tilt-mode (g tilt &key (exptilt t) (maxiter 2))
  (let ((tilt-object (slot-value 'tilt-object)))
    (send tilt-object :exptilt exptilt)
    (send tilt-object :f g)
    (send tilt-object :tilt tilt)
    (send self :tilt-newton tilt-object maxiter)))

;;;;
;;;; Bayes Model Prototype
;;;;

(defproto bayes-model-proto '(bayes-internals))

;; initialization methods and constructor function

(defmeth bayes-model-proto :isnew (logpost mode &key
                                           scale 
                                           (derivstep .001)
                                           (verbose t)
                                           (maximize t)
                                           domain)
  (send self :set-bayes-internals 
        logpost mode scale derivstep nil nil t domain)
  (if maximize (send self :maximize verbose)))

(defun bayes-model (logpost mode &rest args &key (quick t) (print t))
"Args: (logpost mode &key scale derivstep (verbose t)
       (quick t) (print t)))
LOGPOST computes the logposterior density. It should return the
function, or a list of the function value and gradient, or a list of
the function value, gradient and Hessian. MODE is an initial guess for
the mode. SCALE and DERIVSTEP are used for numerical derivatives and
scaling. VERBOSE controls printing of iteration information during
optimization, PRINT controls printing of summary information. If QUICK
is T the summary is based on first order approximations."
  (let ((m (apply #'send bayes-model-proto :new logpost mode args)))
    (if print (send m :display :quick quick))
    m))

;; display method

(defmeth bayes-model-proto :display (&key (quick t))
  (let* ((moments (send self (if quick :1stmoments :moments)))
         (means (first moments))
         (stdevs (second moments))
         (p-names (send self :parameter-names)))
    (if quick
        (format t "~2%First Order Approximations to Posterior Moments:~2%")
        (format t "~2%Approximate Posterior Moments:~2%"))
    (mapcar #'(lambda (name mu sd)
                #|(format t "~22a  ~10g (~a)~%" name mu sd)|#
                  (format t "~a~25t~13,6g~40t(~,6g)~%" name mu sd))
            p-names
            means
            stdevs)
    (format t "~%")))

(defmeth bayes-model-proto :parameter-names ()
  (let ((n (length (send self :mode))))
    (mapcar #'(lambda (x) (format nil "Parameter ~d" x)) (iseq 0 (- n 1)))))

;; implementation-dependent access methods

(defmeth bayes-model-proto :set-bayes-internals (lp m s h mval ch max dom)
  (setf (slot-value 'bayes-internals) 
        (vector lp m s h mval ch max dom)))

(defmeth bayes-model-proto :logpost (&optional new)
  (let ((internals (slot-value 'bayes-internals)))
    (when new
          (setf (select internals 0) new)
          (send self :needs-maximizing t))
    (select internals 0)))

(defmeth bayes-model-proto :domain (&optional new)
  (let ((internals (slot-value 'bayes-internals)))
    (if new (setf (select internals 7) new))
    (select internals 7)))

(defmeth bayes-model-proto :mode-values (&optional mode mval ch)
  (let ((internals (slot-value 'bayes-internals)))
    (when mode
          (setf (select internals 1) mode)
          (setf (select internals 4) mval)
          (setf (select internals 5) ch))
    (list (select internals 1)
          (select internals 4)
          (select internals 5))))

(defmeth bayes-model-proto :parameter-scale (&optional new)
  (let ((internals (slot-value 'bayes-internals)))
    (if new (setf (select internals 2) new))
    (select internals 2)))

(defmeth bayes-model-proto :parameter-dimension ()
  (length (select (slot-value 'bayes-internals) 1)))

(defmeth bayes-model-proto :derivstep ()
  (select (slot-value 'bayes-internals) 3))

(defmeth bayes-model-proto :needs-maximizing (&optional (new nil set))
  (let ((internals (slot-value 'bayes-internals)))
    (if set (setf (select internals 6) new))
    (select internals 6)))

;; Transformation-Related Methods
;; (These should be the only ones needing to be changed to handle 
;; an internal parameter transformation; perhaps also :logpost)

;; **** fix to be more careful about use of functionp
(defun function-list (g &optional n)
  (cond
    ((or (functionp g) (objectp g)) (list g))
    ((integerp g) 
     (if (null n)
         (list #'(lambda (x) (elt x g)))
         (let ((grad (make-array n :initial-element 0))
               (hess (make-array (list n n) :initial-element 0)))
           (setf (aref grad g) 1)
           (list #'(lambda (x) (list (elt x g) grad hess))))))
    (t (mapcar #'(lambda (x) (car (function-list x n))) g))))

(defmeth bayes-model-proto :mode ()
  (if (send self :needs-maximizing) (send self :maximize))
  (first (send self :mode-values)))

(defmeth bayes-model-proto :new-mode-guess (new)
  (send self :needs-maximizing t)
  (send self :mode-values new))

(defmeth bayes-model-proto :transformed-logpost ()
  (if (send self :needs-maximizing) (send self :maximize))
  (let* ((m-values (send self :mode-values))
         (mode (first m-values))
         (mval (second m-values))
         (ch (third m-values))
         (h (send self :derivstep))
         (f (send self :logpost)))
    (send scaled-logpost-proto :new f mode ch mval 1 h)))

;;**** need transformed domain here

(defmeth bayes-model-proto :transformed-functions (&optional g (c 0) (s 1))
  (if (send self :needs-maximizing) (send self :maximize))
  (let* ((m-values (send self :mode-values))
         (mode (first m-values))
         (mval (second m-values))
         (ch (third m-values))
         (h (send self :derivstep))
         (n (length mode))
         (g (function-list (if g g (iseq n)) n))
         (c (if (numberp c) (repeat c (length g)) c))
         (s (if (numberp s) (repeat s (length g)) s)))
    (mapcar #'(lambda (g c s) 
                (send scaled-c2-function-proto :new g mode ch c s h))
            g c s)))

;; computing methods

(defmeth bayes-model-proto :maximize (&optional (verbose 0))
  (let* ((lp (send self :logpost))
         (x (first (send self :mode-values)))
         (scale (send self :parameter-scale))
         (h (send self :derivstep))
         (minfo (newtonmax lp x 
                           :scale scale
                           :derivstep h
                           :verbose verbose
                           :return-derivs t))
         (mode (first minfo))
         (mval (second minfo))
         (ch (first (chol-decomp (inverse (- (fourth minfo)))))))
    (send self :mode-values mode mval ch)
    (send self :needs-maximizing nil)
    (send self :check-derivatives verbose)))

(defmeth bayes-model-proto :check-derivatives (&optional 
                                               (verbose 0) 
                                               (epsilon .00001))
  (let* ((verbose (if (numberp verbose) (< 0 verbose) verbose))
         (n (send self :parameter-dimension))
         (tlp (send self :transformed-logpost))
         (hess (send tlp :hessian (repeat 0 n)))
         (needs-max (send self :needs-maximizing)))
    (when (> (max (abs (+ hess (identity-matrix n)))) epsilon)
          (if verbose (format t "Adjusting derivatives...~%"))
          (let* ((ch (first (chol-decomp (- (inverse hess)))))
                 (mvals (send self :mode-values))
                 (m (matmult (third mvals) ch)))
            (send self :mode-values (first mvals) (second mvals) m)
            (if (not needs-max) (send self :needs-maximizing nil))
            (if verbose
                (let* ((tlp (send self :transformed-logpost))
                       (hess (send tlp :hessian (repeat 0 n))))
                  (if (> (max (abs (+ hess (identity-matrix n)))) epsilon)
                      (format t 
                              "Derivatives may not be well-behaved.~%"))))))))

;; moments

(defmeth bayes-model-proto :1stmoments (&optional gfuns &key covar)
"Args: (&optional gfuns &key covar) 
Computes first order approximations to posterior moments. GFUNS can be
a parameter index, list of indices, a function of the parameters or a
list of such functions. Returns a the list of first order approximate
means and standard deviations if COVAR is NIL. If COVAR is T the
covaraince is appended to the end of the result as well."
  (if (send self :needs-maximizing) (send self :maximize))
  (let* ((n (send self :parameter-dimension))
         (x (repeat 0 n))
         (g (send self :transformed-functions gfuns 0 1))
         (grads (apply #'bind-columns 
                       (mapcar #'(lambda (g) (send g :gradient x)) g)))
         (mean (mapcar #'(lambda (g) (send g :value x)) g))
         (cov (matmult  (transpose grads) grads)))
    (if covar
        (list mean (sqrt (diagonal cov)) cov)
        (list mean (sqrt (diagonal cov))))))

(defmeth bayes-model-proto :mgfmoments (&optional g &key covar 
                                                  (mgfdel .1)
                                                  ((:derivstep h) .1)
                                                  (maxiter 2))
  (let* ((moms1 (send self :1stmoments g :covar covar))
         (mean1 (first moms1))
         (stdev1 (second moms1))
         (cov1 (if covar (third moms1)))
         (l-object (send self :transformed-logpost))
         (g-objects (send self :transformed-functions g mean1 stdev1))
         (ldet0 (send l-object :log-laplace nil maxiter t h)))
    (labels ((lapdet (g tilt)
               (- (send l-object :tilt-laplace g tilt t maxiter t h) ldet0))
             (moms2 (m s g)
               (let ((ldet1 (lapdet g mgfdel))
                     (ldet2 (lapdet g (- mgfdel))))
                 (list (+ m (* s (/  (- ldet1 ldet2) (* 2 mgfdel))))
                       (* s (sqrt (+ 1 (/ (+ ldet1 ldet2) (^ mgfdel 2))))))))
             (covar (g mean-sd)
               (let* ((mu (first mean-sd))
                      (sd (second mean-sd))
                      (cov (diagonal (^ sd 2)))
                      (var1 (^ stdev1 2))
                      (var (^ sd 2))
                      (rvdiff (/ (- var var1) var))
                      (tilt mgfdel)
                      (2tilt2 (* 2 (^ tilt 2)))
                      (negtilt (- tilt)))
                 (dotimes (i (length g) cov)
                   (dotimes (j i)
                     (let* ((g (select g (list i j)))
                            (rvdi (select rvdiff i))
                            (rvdj (select rvdiff j))
                            (sdi (select sd i))
                            (sdj (select sd j))
                            (ldt1 (lapdet g tilt))
                            (ldt2 (lapdet g negtilt))
                            (del2 (/ (+ ldt1 ldt2) 2tilt2))
                            (d (- del2 (* 0.5 rvdi) (* 0.5 rvdj)))
                            (c (+ (aref cov1 i j) (* d sdi sdj))))
                       (setf (aref cov i j) c)
                       (setf (aref cov j i) c)))))))
      (let ((mean-sd (transpose (mapcar #'moms2 mean1 stdev1 g-objects))))
        (if covar 
            (append mean-sd (list (covar g-objects mean-sd)))
            mean-sd)))))

(defmeth bayes-model-proto :fullmoments (&optional g &key covar
                                                   ((:derivstep h) .1)
                                                   (maxiter 2))
  (let* ((moms1 (send self :1stmoments g))
         (mean1 (first moms1))
         (stdev1 (second moms1))
         (l-object (send self :transformed-logpost))
         (g-objects (send self :transformed-functions g 0 mean1))
         (loglap0 (send l-object :log-laplace nil maxiter nil h)))
    (labels ((loglap (g tilt)
               (- (send l-object :tilt-laplace g tilt nil maxiter nil h)
                  loglap0))
             (moms2 (g mu)
               (let ((mu1 (exp (loglap g 1.0)))
                     (mu2 (exp (loglap g 2.0))))
                 (* mu (list mu1 (sqrt (- mu2 (^ mu1 2)))))))
             (covar (g mean-sd)
               (let* ((mu (/ (first mean-sd) mean1))
                      (sd (second mean-sd))
                      (cov (diagonal (^ sd 2))))
                 (dotimes (i (length g) cov)
                   (dotimes (j i)
                     (let* ((g (select g (list i j)))
                            (muij (exp (loglap g 1.0)))
                            (mui (select mu i))
                            (muj (select mu j))
                            (mu1i (select mean1 i))
                            (mu1j (select mean1 j))
                            (c (* (- muij (* mui muj)) mu1i mu1j)))
                       (setf (aref cov i j) c)
                       (setf (aref cov j i) c)))))))
      (let ((mean-sd (transpose (mapcar #'moms2 g-objects mean1))))
        (if covar 
            (append mean-sd (list (covar g-objects mean-sd)))
            mean-sd)))))

(defmeth bayes-model-proto :2ndmoments (&rest args)
  (apply #'send self :mgfmoments args))

(defmeth bayes-model-proto :moments (&rest args)
"Args: (&optional gfuns &key covar) 
Computes second order approximations to posterior moments. GFUNS can be
a parameter index, list of indices, a function of the parameters or a
list of such functions. Returns a the list of second order approximate
means and standard deviations if COVAR is NIL. If COVAR is T the
covaraince is appended to the end of the result as well."
  (apply #'send self :2ndmoments args))

;; margins

(defproto laplace-margin-proto '(logpost g x val i j a grad gval lu h))

(defmeth laplace-margin-proto :isnew (logpost g n k h)
  (setf (slot-value 'logpost) logpost)
  (setf (slot-value 'g) g)
  (setf (slot-value 'x) (repeat 0 (+ n k)))
  (setf (slot-value 'i) (iseq n))
  (setf (slot-value 'j) (+ n (iseq k)))
  (setf (slot-value 'a)
        (make-array (list (+ n k) (+ n k)) :initial-element 0))
  (setf (slot-value 'h) h)
  (send self :adjust-internals t))

(defmeth laplace-margin-proto :adjust-internals (&optional initial)
  (let* ((logpost (slot-value 'logpost))
         (g (slot-value 'g))
         (i (slot-value 'i))
         (j (slot-value 'j))
         (x (slot-value 'x))
         (a (slot-value 'a))
         (h (slot-value 'h))
         (y (select x i))
         (lambda (select x j))
         (n (length y))
         (vals (if initial 
                   (list 0 (repeat 0 n) (- (identity-matrix n)))
                   (send logpost :vals y h)))
         (val (first vals))
         (grad (second vals))
         (hess (third vals))
         (gvals (mapcar #'(lambda (x) (send x :vals y h)) g))
         (gval (mapcar #'first gvals))
         (ggrad (mapcar #'second gvals))
         (ghess (mapcar #'third gvals))
         (ggradmat (apply #' bind-columns ggrad)))
    (setf (slot-value 'val) val)
    (setf (slot-value 'grad) (apply #'+ grad (* lambda ggrad)))
    (setf (slot-value 'gval) gval)
    (setf (select a i i) (apply #'+ hess (* lambda ghess)))
    (setf (select a i j) ggradmat)
    (setf (select a j i) (transpose ggradmat))
    (setf (slot-value 'lu) (lu-decomp a))))

;; **** test for nonsingularity?
(defmeth laplace-margin-proto :move-to (target)
  (let* ((x (slot-value 'x))
         (grad (slot-value 'grad))
         (gval (slot-value 'gval))
         (lu (slot-value 'lu))
         (next-x (- x (lu-solve lu (combine grad (- gval target))))))
    (setf (slot-value 'x) next-x)
    (send self :adjust-internals)))

(defmeth laplace-margin-proto :log-density (&optional profile)
  (let ((val (slot-value 'val)))
    (if profile 
        val
        (let* ((lu (slot-value 'lu))
               (nonsing (null (fourth lu))))
          (if nonsing
              (+ (* -0.5 (sum (log (abs (diagonal (first lu))))))
                 val))))))

;; ***** fix step choice
;; ***** Cut off at first nil?
(defmeth bayes-model-proto :log-margin1 (g x &key 
                                           ((:derivstep h) .05)
                                           (spline t)
                                           profile)
  (let* ((moms1 (send self :1stmoments g))
         (mean1 (select (first moms1) 0))
         (stdev1 (select (second moms1) 0))
         (n (send self :parameter-dimension))
         (l-ob (send self :transformed-logpost))
         (g-obs (send self :transformed-functions g mean1 stdev1))
         (xs (/ (- x mean1) stdev1))
         (xlow (coerce (sort-data (select xs (which (<= xs 0)))) 'list))
         (xhigh (coerce (sort-data (select xs (which (> xs 0)))) 'list)))
    (flet ((margin (x)
             (let ((margin (send laplace-margin-proto :new l-ob g-obs n 1 h)))
               (flet ((nextmargin (x)
                        (send margin :move-to x)
                        (send margin :log-density profile)))
                 (mapcar #'nextmargin x)))))
      (let* ((ylow (reverse (margin (reverse xlow))))
             (yhigh (margin xhigh))
             (x (append xlow xhigh))
             (y (append ylow yhigh))
             (i (which (mapcar #'numberp y)))
             (xi (select x i))
             (yi (select y i))
             (xy (if spline (spline xi yi) (list xi yi))))
        (list (+ mean1 (* stdev1 (first xy)))
              (- (second xy) (log stdev1) (* 0.5 (log (* 2 pi)))))))))

(defmeth bayes-model-proto :margin1 (g x &key 
                                       (derivstep .05)
                                       (spline t)
                                       profile)
"Args: (g x &key (:derivstep .05) (spline t) profile)
Computes Laplace approximation to marginal posterior density of G at
points X. G can be an index or a function of the parameter vector. X
is a sequence that should include the modal value of G. If SPLINE is
true the log density is splined. If PROFILE is true, a profile of the
posterior is returned."
  (let* ((logmar (send self :log-margin1 g x 
                       :derivstep derivstep 
                       :spline spline
                       :profile profile)))
    (list (first logmar) (exp (second logmar)))))
    
;;**** allow domain test function
(defmeth bayes-model-proto :impsample (&optional g &key (n 100) (df 2))
  (let* ((l-ob (send self :transformed-logpost))
         (g-obs (send self :transformed-functions g))
         (k (send self :parameter-dimension))
         (v (chisq-rand n df))
         (z (* (normal-rand (repeat k n)) (sqrt (/ df v))))
         (c (- (log-gamma (/ (+ k df) 2)) 
               (log-gamma (/ df 2)) 
               (* (/ k 2) (log (/ df 2))))))
    (flet ((w (z)
              (let ((lp (send l-ob :value z))
                    (lt (* -0.5 (+ k df) (log (+ 1 (/ (sum (* z z)) df))))))
                (if (realp lp) (exp (- lp lt c)) 0)))
           (gvals (z) (mapcar #'(lambda (g) (send g :value z)) g-obs)))
      (list (mapcar #'gvals z) (mapcar #'w z)))))

(defmeth bayes-model-proto :impmoments (&optional g &key (n 100) (df 2))
  (let* ((impsample (send self :impsample g :n n :df df))
         (means (/ (reduce #'+ (* (first impsample) (second impsample))) 
                   (reduce #'+ (second impsample))))
         (x (mapcar #'(lambda (z) (^ (- z means) 2)) (first impsample)))
         (vars (/ (reduce #'+ (* x (second impsample))) 
                  (reduce #'+ (second impsample)))))
    (list means (sqrt vars))))