Mercurial > hg > amuse
view utils/harmony/gamma.lisp @ 61:c911d65ae94d
pointy-clicky misspelling correction
darcs-hash:20070627102839-dc3a5-16cb5156dafee4b4a1aa3442274fab6c7260ed9c.gz
author | c.rhodes <c.rhodes@gold.ac.uk> |
---|---|
date | Wed, 27 Jun 2007 11:28:39 +0100 |
parents | d1010755f507 |
children |
line wrap: on
line source
;; CSR's gamma functions. I don't really know what's happening here, ;; but it seems to work... Called from within harmonic_analysis.lisp (in-package #:amuse-harmony) (let ((p (make-array 9 :element-type 'double-float :initial-contents ; cf. Lanczos' Approximation on Wikipedia. '(0.99999999999980993d0 676.5203681218851d0 -1259.1392167224028d0 771.32342877765313d0 -176.61502916214059d0 12.507343278686905d0 -0.13857109526572012d0 9.9843695780195716d-6 1.5056327351493116d-7))) (c (make-array 8 :element-type 'double-float :initial-contents (mapcar (lambda (x) (float x 1d0)) '(1/12 -1/360 1/1260 -1/1680 1/1188 -691/360360 1/156 -3617/122400))))) (defun gamma/posreal (x) (declare (type (double-float (0d0)) x)) (locally (declare (optimize speed)) (labels ((corr (x) (declare (type double-float x)) (let ((y (/ 1.0 (* x x)))) (do* ((i 7 (1- i)) (r (aref c i) (+ (* r y) (aref c i)))) ((<= i 0) (the (double-float (0d0)) (exp (/ r x)))) (declare (type double-float r))))) (lngamma/lanczos (x) (declare (type (double-float 0.5d0) x)) (let ((x (1- x))) (do* ((i 0 (1+ i)) (ag (aref p 0) (+ ag (/ (aref p i) (+ x i))))) ((>= i 8) (let ((term1 (* (+ x 0.5d0) (log (/ (+ x 7.5d0) #.(exp 1d0))))) (term2 (+ #.(log (sqrt (* 2 pi))) (log ag)))) (+ term1 (- term2 7.0d0)))) (declare (type (double-float (0d0)) ag))))) (gamma/xgthalf (x) (declare (type (double-float 0.5d0) x)) (cond ((= x 0.5d0) 1.77245385090551602729817d0) ((< x 5d0) (exp (lngamma/lanczos x))) ;; the GNU scientific library suggests a third branch ;; for x < 10, but in fact for our purposes the ;; errors are under control. (t (let* ((p (expt x (* x 0.5))) (e (exp (- x))) (q (* (* p e) p)) (pre (* #.(sqrt 2) #.(sqrt pi) q (/ (sqrt x))))) (* pre (corr x))))))) (if (> x 0.5d0) (gamma/xgthalf x) (let ((g (gamma/xgthalf (- 1d0 x)))) (declare (type double-float g)) (/ pi g (sin (* pi x))))))))) (defun beta (alpha) (let ((sum 0) (product 1)) (dotimes (i (length alpha) (/ product (gamma/posreal (float sum 1d0)))) (let ((alpha_i (aref alpha i))) (setq product (* product (gamma/posreal (float alpha_i 1d0)))) (incf sum alpha_i))))) ;; this is p(d|c_i), where the chord model is represented as a ;; Dirichlet distribution with parameters alpha_i #+nil (defun likelihood (observations alpha) ;; observations is a 12d vector summing to 1, alpha summing to 3 (assert (< (abs (1- (loop for d across observations sum d))) (* single-float-epsilon 12))) (let ((pairs (loop for alpha_i across alpha for d_i across observations if (zerop alpha_i) do (unless (zerop d_i) (return-from likelihood 0.0d0)) else collect (cons alpha_i d_i)))) ;; now all the CARs of pairs have strictly positive alpha_i ;; values, and the d_i are the corresponding observations. (let ((alpha_prime (map 'simple-vector #'car pairs))) (* (/ (beta alpha_prime)) (reduce #'* pairs :key (lambda (pair) (expt (cdr pair) (1- (car pair))))))))) (defun likelihood (observations alpha &optional (power 1)) (assert (< (abs (1- (loop for d across observations sum d))) (* single-float-epsilon 12))) (let* ((alpha_0 (loop for a across alpha sum a)) (alpha_0+1 (1+ alpha_0)) (alpha_00+1 (* alpha_0+1 power)) (alpha_00 (- alpha_00+1 1)) (alpha (map 'simple-vector (lambda (x) (* x (/ alpha_00 alpha_0))) alpha)) (quantum (expt 0.0005 power))) ; sake of argument (let ((pairs (loop for d_i across observations for alpha_i across alpha if (zerop alpha_i) do (unless (zerop d_i) (return-from likelihood 0.0d0)) else collect (cons d_i alpha_i)))) (assert (= (length pairs) 12)) (let ((alpha_prime (map 'simple-vector #'cdr pairs))) (* (/ (beta alpha_prime)) (/ quantum) (reduce #'* pairs :key (lambda (pair) (if (zerop (car pair)) (* (/ (cdr pair)) (expt quantum (cdr pair))) (* quantum (expt (car pair) (1- (cdr pair)))))))))))) (defun 4ple-likelihood (pitch-classes chord-probabilities intervals level sum) (let ((observations)) (dolist (interval intervals) (push (aref pitch-classes interval) observations) (decf sum (aref pitch-classes interval))) (push sum observations) (likelihood/fourwise (make-array (length observations) :initial-contents (reverse observations)) chord-probabilities level))) (let ((alpha_0s '((1 . 12) (1/2 . 9) (1/4 . 30)))) (defun likelihood/fourwise (observations alpha &optional (power 1)) (assert (< (abs (1- (loop for d across observations sum d))) (* single-float-epsilon 4))) (let* ((alpha_0 (loop for a across alpha sum a)) (alpha (map 'simple-vector (lambda (x) (* x (/ (cdr (assoc power alpha_0s)) alpha_0))) alpha)) (quantum (expt 0.0005 power))) ; sake of argument (let ((pairs (loop for d_i across observations for alpha_i across alpha if (zerop alpha_i) do (unless (zerop d_i) (return-from likelihood/fourwise 0.0d0)) else collect (cons d_i alpha_i)))) (assert (= (length pairs) 4)) (let ((alpha_prime (map 'simple-vector #'cdr pairs))) (* (/ (beta alpha_prime)) (/ quantum) (reduce #'* pairs :key (lambda (pair) (if (zerop (car pair)) (* (/ (cdr pair)) (expt quantum (cdr pair))) (* quantum (expt (car pair) (1- (cdr pair))))))))))))) (defparameter *alpha-scale* 1) (defparameter *beta-scale* 1) (defparameter *alpha* #(2.925 1.95 1.625)) (defparameter *beta* #(0.87 4.46)) (defparameter *minimal-betas* '((1 . 8.75) (1/2 . 3) (1/4 . 2.5))) (defparameter *full-betas-1* '((1 . 14) (1/2 . 6) (1/4 . 8))) ;; gets 144|51 (defparameter *full-betas* '((1 . 14) (3/4 . 6) (1/2 . 6) (1/4 . 8))) ;; guess... (defparameter *betas* *full-betas*) (defun 3ple-likelihood (pitch-classes chord-probabilities non-chord intervals level sum &optional (alpha *alpha*) (beta *beta*)) (declare (ignore chord-probabilities non-chord)) (let ((observations)) (dolist (interval intervals) (push (aref pitch-classes interval) observations) (decf sum (aref pitch-classes interval))) (push sum observations) (when (= sum 1) ;; So there are no chord notes at all... ?? move to likelihood function (return-from 3ple-likelihood 0)) (likelihood/threeplusonewise (make-array (length observations) :initial-contents (reverse observations)) alpha beta level) #+nil (likelihood/threeplusonewise (make-array (length observations) :initial-contents (reverse observations)) (map 'vector #'(lambda (x) (* x *alpha-scale*)) #(2.925 1.95 1.625)) #+nil (map 'vector #'(lambda (x) (* x *beta-scale*)) #(0.87 4.46)) (make-array 2 :initial-contents (list 0.87 *beta-scale*)) level))) ;; #(0.87 4.46) worked ;;; observations is a non-relativistic four-vector of proportions: ;;; #(tonic mediant dominant other). ;;; ;;; alpha is a three-vector of Dirichlet parameters for proportions of ;;; tonic, mediant, dominant of the total chord notes. ;;; ;;; beta is a two-vector of Dirichlet parameters for proportions of ;;; non-chord vs chord notes. ;;; ;;; suggested values: ;;; alpha: #(2.925 1.95 1.625) ; (tonic, mediant, dominant) ;;; beta: #(0.44 1.76) ; (non-chord, chord) ;;; ;;; model: ;;; p(tmdo|c) = p(o|c)p(tmd|oc) ;;; where ;;; p(o|c) ~ Beta(0.44,1.76) (i.e. p({o,o'}|c) ~ Dir({0.44,1.76}) ;;; and ;;; p(tmd|oc) ~ (1-o) Dir({2.925,1.95,1.625}) (defun likelihood/threeplusonewise (observations alpha beta &optional (power 1)) (assert (< (abs (1- (loop for d across observations sum d))) (* single-float-epsilon 4))) (let* ((quantum (expt 0.00000005 power))) ; sake of argument (let* ((o (aref observations 3)) (pairs (loop repeat 3 for d_i across observations for alpha_i across alpha if (zerop alpha_i) do (unless (zerop d_i) (return-from likelihood/threeplusonewise 0.0d0)) else collect (cons (/ d_i (- 1 o)) alpha_i)))) (assert (= (length pairs) 3)) (assert (< (abs (1- (loop for (d) in pairs sum d))) (* single-float-epsilon 3))) (flet ((key (pair) (if (zerop (car pair)) (* (/ (cdr pair)) (expt quantum (cdr pair))) (* quantum (expt (car pair) (1- (cdr pair))))))) (let ((alpha_prime (map 'simple-vector #'cdr pairs))) (* ;; p(o|c) (/ (beta beta)) (/ quantum) (reduce #'* (list (cons o (aref beta 0)) (cons (- 1 o) (aref beta 1))) :key #'key) ;; p(tmd|oc) (/ (beta alpha_prime)) (/ quantum) (reduce #'* pairs :key #'key)))))))