calc-nlfit.el 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820
  1. ;;; calc-nlfit.el --- nonlinear curve fitting for Calc
  2. ;; Copyright (C) 2007-2012 Free Software Foundation, Inc.
  3. ;; Maintainer: Jay Belanger <jay.p.belanger@gmail.com>
  4. ;; This file is part of GNU Emacs.
  5. ;; GNU Emacs is free software: you can redistribute it and/or modify
  6. ;; it under the terms of the GNU General Public License as published by
  7. ;; the Free Software Foundation, either version 3 of the License, or
  8. ;; (at your option) any later version.
  9. ;; GNU Emacs is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. ;; GNU General Public License for more details.
  13. ;; You should have received a copy of the GNU General Public License
  14. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  15. ;;; Commentary:
  16. ;; This code uses the Levenberg-Marquardt method, as described in
  17. ;; _Numerical Analysis_ by H. R. Schwarz, to fit data to
  18. ;; nonlinear curves. Currently, the only the following curves are
  19. ;; supported:
  20. ;; The logistic S curve, y=a/(1+exp(b*(t-c)))
  21. ;; Here, y is usually interpreted as the population of some
  22. ;; quantity at time t. So we will think of the data as consisting
  23. ;; of quantities q0, q1, ..., qn and their respective times
  24. ;; t0, t1, ..., tn.
  25. ;; The logistic bell curve, y=A*exp(B*(t-C))/(1+exp(B*(t-C)))^2
  26. ;; Note that this is the derivative of the formula for the S curve.
  27. ;; We get A=-a*b, B=b and C=c. Here, y is interpreted as the rate
  28. ;; of growth of a population at time t. So we will think of the
  29. ;; data as consisting of rates p0, p1, ..., pn and their
  30. ;; respective times t0, t1, ..., tn.
  31. ;; The Hubbert Linearization, y/x=A*(1-x/B)
  32. ;; Here, y is thought of as the rate of growth of a population
  33. ;; and x represents the actual population. This is essentially
  34. ;; the differential equation describing the actual population.
  35. ;; The Levenberg-Marquardt method is an iterative process: it takes
  36. ;; an initial guess for the parameters and refines them. To get an
  37. ;; initial guess for the parameters, we'll use a method described by
  38. ;; Luis de Sousa in "Hubbert's Peak Mathematics". The idea is that
  39. ;; given quantities Q and the corresponding rates P, they should
  40. ;; satisfy P/Q= mQ+a. We can use the parameter a for an
  41. ;; approximation for the parameter a in the S curve, and
  42. ;; approximations for b and c are found using least squares on the
  43. ;; linearization log((a/y)-1) = log(bb) + cc*t of
  44. ;; y=a/(1+bb*exp(cc*t)), which is equivalent to the above s curve
  45. ;; formula, and then translating it to b and c. From this, we can
  46. ;; also get approximations for the bell curve parameters.
  47. ;;; Code:
  48. (require 'calc-arith)
  49. (require 'calcalg3)
  50. ;; Declare functions which are defined elsewhere.
  51. (declare-function calc-get-fit-variables "calcalg3" (nv nc &optional defv defc with-y homog))
  52. (declare-function math-map-binop "calcalg3" (binop args1 args2))
  53. (defun math-nlfit-least-squares (xdata ydata &optional sdata sigmas)
  54. "Return the parameters A and B for the best least squares fit y=a+bx."
  55. (let* ((n (length xdata))
  56. (s2data (if sdata
  57. (mapcar 'calcFunc-sqr sdata)
  58. (make-list n 1)))
  59. (S (if sdata 0 n))
  60. (Sx 0)
  61. (Sy 0)
  62. (Sxx 0)
  63. (Sxy 0)
  64. D)
  65. (while xdata
  66. (let ((x (car xdata))
  67. (y (car ydata))
  68. (s (car s2data)))
  69. (setq Sx (math-add Sx (if s (math-div x s) x)))
  70. (setq Sy (math-add Sy (if s (math-div y s) y)))
  71. (setq Sxx (math-add Sxx (if s (math-div (math-mul x x) s)
  72. (math-mul x x))))
  73. (setq Sxy (math-add Sxy (if s (math-div (math-mul x y) s)
  74. (math-mul x y))))
  75. (if sdata
  76. (setq S (math-add S (math-div 1 s)))))
  77. (setq xdata (cdr xdata))
  78. (setq ydata (cdr ydata))
  79. (setq s2data (cdr s2data)))
  80. (setq D (math-sub (math-mul S Sxx) (math-mul Sx Sx)))
  81. (let ((A (math-div (math-sub (math-mul Sxx Sy) (math-mul Sx Sxy)) D))
  82. (B (math-div (math-sub (math-mul S Sxy) (math-mul Sx Sy)) D)))
  83. (if sigmas
  84. (let ((C11 (math-div Sxx D))
  85. (C12 (math-neg (math-div Sx D)))
  86. (C22 (math-div S D)))
  87. (list (list 'sdev A (calcFunc-sqrt C11))
  88. (list 'sdev B (calcFunc-sqrt C22))
  89. (list 'vec
  90. (list 'vec C11 C12)
  91. (list 'vec C12 C22))))
  92. (list A B)))))
  93. ;;; The methods described by de Sousa require the cumulative data qdata
  94. ;;; and the rates pdata. We will assume that we are given either
  95. ;;; qdata and the corresponding times tdata, or pdata and the corresponding
  96. ;;; tdata. The following two functions will find pdata or qdata,
  97. ;;; given the other..
  98. ;;; First, given two lists; one of values q0, q1, ..., qn and one of
  99. ;;; corresponding times t0, t1, ..., tn; return a list
  100. ;;; p0, p1, ..., pn of the rates of change of the qi with respect to t.
  101. ;;; p0 is the right hand derivative (q1 - q0)/(t1 - t0).
  102. ;;; pn is the left hand derivative (qn - q(n-1))/(tn - t(n-1)).
  103. ;;; The other pis are the averages of the two:
  104. ;;; (1/2)((qi - q(i-1))/(ti - t(i-1)) + (q(i+1) - qi)/(t(i+1) - ti)).
  105. (defun math-nlfit-get-rates-from-cumul (tdata qdata)
  106. (let ((pdata (list
  107. (math-div
  108. (math-sub (nth 1 qdata)
  109. (nth 0 qdata))
  110. (math-sub (nth 1 tdata)
  111. (nth 0 tdata))))))
  112. (while (> (length qdata) 2)
  113. (setq pdata
  114. (cons
  115. (math-mul
  116. '(float 5 -1)
  117. (math-add
  118. (math-div
  119. (math-sub (nth 2 qdata)
  120. (nth 1 qdata))
  121. (math-sub (nth 2 tdata)
  122. (nth 1 tdata)))
  123. (math-div
  124. (math-sub (nth 1 qdata)
  125. (nth 0 qdata))
  126. (math-sub (nth 1 tdata)
  127. (nth 0 tdata)))))
  128. pdata))
  129. (setq qdata (cdr qdata)))
  130. (setq pdata
  131. (cons
  132. (math-div
  133. (math-sub (nth 1 qdata)
  134. (nth 0 qdata))
  135. (math-sub (nth 1 tdata)
  136. (nth 0 tdata)))
  137. pdata))
  138. (reverse pdata)))
  139. ;;; Next, given two lists -- one of rates p0, p1, ..., pn and one of
  140. ;;; corresponding times t0, t1, ..., tn -- and an initial values q0,
  141. ;;; return a list q0, q1, ..., qn of the cumulative values.
  142. ;;; q0 is the initial value given.
  143. ;;; For i>0, qi is computed using the trapezoid rule:
  144. ;;; qi = q(i-1) + (1/2)(pi + p(i-1))(ti - t(i-1))
  145. (defun math-nlfit-get-cumul-from-rates (tdata pdata q0)
  146. (let* ((qdata (list q0)))
  147. (while (cdr pdata)
  148. (setq qdata
  149. (cons
  150. (math-add (car qdata)
  151. (math-mul
  152. (math-mul
  153. '(float 5 -1)
  154. (math-add (nth 1 pdata) (nth 0 pdata)))
  155. (math-sub (nth 1 tdata)
  156. (nth 0 tdata))))
  157. qdata))
  158. (setq pdata (cdr pdata))
  159. (setq tdata (cdr tdata)))
  160. (reverse qdata)))
  161. ;;; Given the qdata, pdata and tdata, find the parameters
  162. ;;; a, b and c that fit q = a/(1+b*exp(c*t)).
  163. ;;; a is found using the method described by de Sousa.
  164. ;;; b and c are found using least squares on the linearization
  165. ;;; log((a/q)-1) = log(b) + c*t
  166. ;;; In some cases (where the logistic curve may well be the wrong
  167. ;;; model), the computed a will be less than or equal to the maximum
  168. ;;; value of q in qdata; in which case the above linearization won't work.
  169. ;;; In this case, a will be replaced by a number slightly above
  170. ;;; the maximum value of q.
  171. (defun math-nlfit-find-qmax (qdata pdata tdata)
  172. (let* ((ratios (math-map-binop 'math-div pdata qdata))
  173. (lsdata (math-nlfit-least-squares ratios tdata))
  174. (qmax (math-max-list (car qdata) (cdr qdata)))
  175. (a (math-neg (math-div (nth 1 lsdata) (nth 0 lsdata)))))
  176. (if (math-lessp a qmax)
  177. (math-add '(float 5 -1) qmax)
  178. a)))
  179. (defun math-nlfit-find-logistic-parameters (qdata pdata tdata)
  180. (let* ((a (math-nlfit-find-qmax qdata pdata tdata))
  181. (newqdata
  182. (mapcar (lambda (q) (calcFunc-ln (math-sub (math-div a q) 1)))
  183. qdata))
  184. (bandc (math-nlfit-least-squares tdata newqdata)))
  185. (list
  186. a
  187. (calcFunc-exp (nth 0 bandc))
  188. (nth 1 bandc))))
  189. ;;; Next, given the pdata and tdata, we can find the qdata if we know q0.
  190. ;;; We first try to find q0, using the fact that when p takes on its largest
  191. ;;; value, q is half of its maximum value. So we'll find the maximum value
  192. ;;; of q given various q0, and use bisection to approximate the correct q0.
  193. ;;; First, given pdata and tdata, find what half of qmax would be if q0=0.
  194. (defun math-nlfit-find-qmaxhalf (pdata tdata)
  195. (let ((pmax (math-max-list (car pdata) (cdr pdata)))
  196. (qmh 0))
  197. (while (math-lessp (car pdata) pmax)
  198. (setq qmh
  199. (math-add qmh
  200. (math-mul
  201. (math-mul
  202. '(float 5 -1)
  203. (math-add (nth 1 pdata) (nth 0 pdata)))
  204. (math-sub (nth 1 tdata)
  205. (nth 0 tdata)))))
  206. (setq pdata (cdr pdata))
  207. (setq tdata (cdr tdata)))
  208. qmh))
  209. ;;; Next, given pdata and tdata, approximate q0.
  210. (defun math-nlfit-find-q0 (pdata tdata)
  211. (let* ((qhalf (math-nlfit-find-qmaxhalf pdata tdata))
  212. (q0 (math-mul 2 qhalf))
  213. (qdata (math-nlfit-get-cumul-from-rates tdata pdata q0)))
  214. (while (math-lessp (math-nlfit-find-qmax
  215. (mapcar
  216. (lambda (q) (math-add q0 q))
  217. qdata)
  218. pdata tdata)
  219. (math-mul
  220. '(float 5 -1)
  221. (math-add
  222. q0
  223. qhalf)))
  224. (setq q0 (math-add q0 qhalf)))
  225. (let* ((qmin (math-sub q0 qhalf))
  226. (qmax q0)
  227. (qt (math-nlfit-find-qmax
  228. (mapcar
  229. (lambda (q) (math-add q0 q))
  230. qdata)
  231. pdata tdata))
  232. (i 0))
  233. (while (< i 10)
  234. (setq q0 (math-mul '(float 5 -1) (math-add qmin qmax)))
  235. (if (math-lessp
  236. (math-nlfit-find-qmax
  237. (mapcar
  238. (lambda (q) (math-add q0 q))
  239. qdata)
  240. pdata tdata)
  241. (math-mul '(float 5 -1) (math-add qhalf q0)))
  242. (setq qmin q0)
  243. (setq qmax q0))
  244. (setq i (1+ i)))
  245. (math-mul '(float 5 -1) (math-add qmin qmax)))))
  246. ;;; To improve the approximations to the parameters, we can use
  247. ;;; Marquardt method as described in Schwarz's book.
  248. ;;; Small numbers used in the Givens algorithm
  249. (defvar math-nlfit-delta '(float 1 -8))
  250. (defvar math-nlfit-epsilon '(float 1 -5))
  251. ;;; Maximum number of iterations
  252. (defvar math-nlfit-max-its 100)
  253. ;;; Next, we need some functions for dealing with vectors and
  254. ;;; matrices. For convenience, we'll work with Emacs lists
  255. ;;; as vectors, rather than Calc's vectors.
  256. (defun math-nlfit-set-elt (vec i x)
  257. (setcar (nthcdr (1- i) vec) x))
  258. (defun math-nlfit-get-elt (vec i)
  259. (nth (1- i) vec))
  260. (defun math-nlfit-make-matrix (i j)
  261. (let ((row (make-list j 0))
  262. (mat nil)
  263. (k 0))
  264. (while (< k i)
  265. (setq mat (cons (copy-sequence row) mat))
  266. (setq k (1+ k)))
  267. mat))
  268. (defun math-nlfit-set-matx-elt (mat i j x)
  269. (setcar (nthcdr (1- j) (nth (1- i) mat)) x))
  270. (defun math-nlfit-get-matx-elt (mat i j)
  271. (nth (1- j) (nth (1- i) mat)))
  272. ;;; For solving the linearized system.
  273. ;;; (The Givens method, from Schwarz.)
  274. (defun math-nlfit-givens (C d)
  275. (let* ((C (copy-tree C))
  276. (d (copy-tree d))
  277. (n (length (car C)))
  278. (N (length C))
  279. (j 1)
  280. (r (make-list N 0))
  281. (x (make-list N 0))
  282. w
  283. gamma
  284. sigma
  285. rho)
  286. (while (<= j n)
  287. (let ((i (1+ j)))
  288. (while (<= i N)
  289. (let ((cij (math-nlfit-get-matx-elt C i j))
  290. (cjj (math-nlfit-get-matx-elt C j j)))
  291. (when (not (math-equal 0 cij))
  292. (if (math-lessp (calcFunc-abs cjj)
  293. (math-mul math-nlfit-delta (calcFunc-abs cij)))
  294. (setq w (math-neg cij)
  295. gamma 0
  296. sigma 1
  297. rho 1)
  298. (setq w (math-mul
  299. (calcFunc-sign cjj)
  300. (calcFunc-sqrt
  301. (math-add
  302. (math-mul cjj cjj)
  303. (math-mul cij cij))))
  304. gamma (math-div cjj w)
  305. sigma (math-neg (math-div cij w)))
  306. (if (math-lessp (calcFunc-abs sigma) gamma)
  307. (setq rho sigma)
  308. (setq rho (math-div (calcFunc-sign sigma) gamma))))
  309. (setq cjj w
  310. cij rho)
  311. (math-nlfit-set-matx-elt C j j w)
  312. (math-nlfit-set-matx-elt C i j rho)
  313. (let ((k (1+ j)))
  314. (while (<= k n)
  315. (let* ((cjk (math-nlfit-get-matx-elt C j k))
  316. (cik (math-nlfit-get-matx-elt C i k))
  317. (h (math-sub
  318. (math-mul gamma cjk) (math-mul sigma cik))))
  319. (setq cik (math-add
  320. (math-mul sigma cjk)
  321. (math-mul gamma cik)))
  322. (setq cjk h)
  323. (math-nlfit-set-matx-elt C i k cik)
  324. (math-nlfit-set-matx-elt C j k cjk)
  325. (setq k (1+ k)))))
  326. (let* ((di (math-nlfit-get-elt d i))
  327. (dj (math-nlfit-get-elt d j))
  328. (h (math-sub
  329. (math-mul gamma dj)
  330. (math-mul sigma di))))
  331. (setq di (math-add
  332. (math-mul sigma dj)
  333. (math-mul gamma di)))
  334. (setq dj h)
  335. (math-nlfit-set-elt d i di)
  336. (math-nlfit-set-elt d j dj))))
  337. (setq i (1+ i))))
  338. (setq j (1+ j)))
  339. (let ((i n)
  340. s)
  341. (while (>= i 1)
  342. (math-nlfit-set-elt r i 0)
  343. (setq s (math-nlfit-get-elt d i))
  344. (let ((k (1+ i)))
  345. (while (<= k n)
  346. (setq s (math-add s (math-mul (math-nlfit-get-matx-elt C i k)
  347. (math-nlfit-get-elt x k))))
  348. (setq k (1+ k))))
  349. (math-nlfit-set-elt x i
  350. (math-neg
  351. (math-div s
  352. (math-nlfit-get-matx-elt C i i))))
  353. (setq i (1- i))))
  354. (let ((i (1+ n)))
  355. (while (<= i N)
  356. (math-nlfit-set-elt r i (math-nlfit-get-elt d i))
  357. (setq i (1+ i))))
  358. (let ((j n))
  359. (while (>= j 1)
  360. (let ((i N))
  361. (while (>= i (1+ j))
  362. (setq rho (math-nlfit-get-matx-elt C i j))
  363. (if (math-equal rho 1)
  364. (setq gamma 0
  365. sigma 1)
  366. (if (math-lessp (calcFunc-abs rho) 1)
  367. (setq sigma rho
  368. gamma (calcFunc-sqrt
  369. (math-sub 1 (math-mul sigma sigma))))
  370. (setq gamma (math-div 1 (calcFunc-abs rho))
  371. sigma (math-mul (calcFunc-sign rho)
  372. (calcFunc-sqrt
  373. (math-sub 1 (math-mul gamma gamma)))))))
  374. (let ((ri (math-nlfit-get-elt r i))
  375. (rj (math-nlfit-get-elt r j))
  376. h)
  377. (setq h (math-add (math-mul gamma rj)
  378. (math-mul sigma ri)))
  379. (setq ri (math-sub
  380. (math-mul gamma ri)
  381. (math-mul sigma rj)))
  382. (setq rj h)
  383. (math-nlfit-set-elt r i ri)
  384. (math-nlfit-set-elt r j rj))
  385. (setq i (1- i))))
  386. (setq j (1- j))))
  387. x))
  388. (defun math-nlfit-jacobian (grad xlist parms &optional slist)
  389. (let ((j nil))
  390. (while xlist
  391. (let ((row (apply grad (car xlist) parms)))
  392. (setq j
  393. (cons
  394. (if slist
  395. (mapcar (lambda (x) (math-div x (car slist))) row)
  396. row)
  397. j)))
  398. (setq slist (cdr slist))
  399. (setq xlist (cdr xlist)))
  400. (reverse j)))
  401. (defun math-nlfit-make-ident (l n)
  402. (let ((m (math-nlfit-make-matrix n n))
  403. (i 1))
  404. (while (<= i n)
  405. (math-nlfit-set-matx-elt m i i l)
  406. (setq i (1+ i)))
  407. m))
  408. (defun math-nlfit-chi-sq (xlist ylist parms fn &optional slist)
  409. (let ((cs 0))
  410. (while xlist
  411. (let ((c
  412. (math-sub
  413. (apply fn (car xlist) parms)
  414. (car ylist))))
  415. (if slist
  416. (setq c (math-div c (car slist))))
  417. (setq cs
  418. (math-add cs
  419. (math-mul c c))))
  420. (setq xlist (cdr xlist))
  421. (setq ylist (cdr ylist))
  422. (setq slist (cdr slist)))
  423. cs))
  424. (defun math-nlfit-init-lambda (C)
  425. (let ((l 0)
  426. (n (length (car C)))
  427. (N (length C)))
  428. (while C
  429. (let ((row (car C)))
  430. (while row
  431. (setq l (math-add l (math-mul (car row) (car row))))
  432. (setq row (cdr row))))
  433. (setq C (cdr C)))
  434. (calcFunc-sqrt (math-div l (math-mul n N)))))
  435. (defun math-nlfit-make-Ctilda (C l)
  436. (let* ((n (length (car C)))
  437. (bot (math-nlfit-make-ident l n)))
  438. (append C bot)))
  439. (defun math-nlfit-make-d (fn xdata ydata parms &optional sdata)
  440. (let ((d nil))
  441. (while xdata
  442. (setq d (cons
  443. (let ((dd (math-sub (apply fn (car xdata) parms)
  444. (car ydata))))
  445. (if sdata (math-div dd (car sdata)) dd))
  446. d))
  447. (setq xdata (cdr xdata))
  448. (setq ydata (cdr ydata))
  449. (setq sdata (cdr sdata)))
  450. (reverse d)))
  451. (defun math-nlfit-make-dtilda (d n)
  452. (append d (make-list n 0)))
  453. (defun math-nlfit-fit (xlist ylist parms fn grad &optional slist)
  454. (let*
  455. ((C (math-nlfit-jacobian grad xlist parms slist))
  456. (d (math-nlfit-make-d fn xlist ylist parms slist))
  457. (chisq (math-nlfit-chi-sq xlist ylist parms fn slist))
  458. (lambda (math-nlfit-init-lambda C))
  459. (really-done nil)
  460. (iters 0))
  461. (while (and
  462. (not really-done)
  463. (< iters math-nlfit-max-its))
  464. (setq iters (1+ iters))
  465. (let ((done nil))
  466. (while (not done)
  467. (let* ((Ctilda (math-nlfit-make-Ctilda C lambda))
  468. (dtilda (math-nlfit-make-dtilda d (length (car C))))
  469. (zeta (math-nlfit-givens Ctilda dtilda))
  470. (newparms (math-map-binop 'math-add (copy-tree parms) zeta))
  471. (newchisq (math-nlfit-chi-sq xlist ylist newparms fn slist)))
  472. (if (math-lessp newchisq chisq)
  473. (progn
  474. (if (math-lessp
  475. (math-div
  476. (math-sub chisq newchisq) newchisq) math-nlfit-epsilon)
  477. (setq really-done t))
  478. (setq lambda (math-div lambda 10))
  479. (setq chisq newchisq)
  480. (setq parms newparms)
  481. (setq done t))
  482. (setq lambda (math-mul lambda 10)))))
  483. (setq C (math-nlfit-jacobian grad xlist parms slist))
  484. (setq d (math-nlfit-make-d fn xlist ylist parms slist))))
  485. (list chisq parms)))
  486. ;;; The functions that describe our models, and their gradients.
  487. (defun math-nlfit-s-logistic-fn (x a b c)
  488. (math-div a (math-add 1 (math-mul b (calcFunc-exp (math-mul c x))))))
  489. (defun math-nlfit-s-logistic-grad (x a b c)
  490. (let* ((ep (calcFunc-exp (math-mul c x)))
  491. (d (math-add 1 (math-mul b ep)))
  492. (d2 (math-mul d d)))
  493. (list
  494. (math-div 1 d)
  495. (math-neg (math-div (math-mul a ep) d2))
  496. (math-neg (math-div (math-mul a (math-mul b (math-mul x ep))) d2)))))
  497. (defun math-nlfit-b-logistic-fn (x a c d)
  498. (let ((ex (calcFunc-exp (math-mul c (math-sub x d)))))
  499. (math-div
  500. (math-mul a ex)
  501. (math-sqr
  502. (math-add
  503. 1 ex)))))
  504. (defun math-nlfit-b-logistic-grad (x a c d)
  505. (let* ((ex (calcFunc-exp (math-mul c (math-sub x d))))
  506. (ex1 (math-add 1 ex))
  507. (xd (math-sub x d)))
  508. (list
  509. (math-div
  510. ex
  511. (math-sqr ex1))
  512. (math-sub
  513. (math-div
  514. (math-mul a (math-mul xd ex))
  515. (math-sqr ex1))
  516. (math-div
  517. (math-mul 2 (math-mul a (math-mul xd (math-sqr ex))))
  518. (math-pow ex1 3)))
  519. (math-sub
  520. (math-div
  521. (math-mul 2 (math-mul a (math-mul c (math-sqr ex))))
  522. (math-pow ex1 3))
  523. (math-div
  524. (math-mul a (math-mul c ex))
  525. (math-sqr ex1))))))
  526. ;;; Functions to get the final covariance matrix and the sdevs
  527. (defun math-nlfit-find-covar (grad xlist pparms)
  528. (let ((j nil))
  529. (while xlist
  530. (setq j (cons (cons 'vec (apply grad (car xlist) pparms)) j))
  531. (setq xlist (cdr xlist)))
  532. (setq j (cons 'vec (reverse j)))
  533. (setq j
  534. (math-mul
  535. (calcFunc-trn j) j))
  536. (calcFunc-inv j)))
  537. (defun math-nlfit-get-sigmas (grad xlist pparms chisq)
  538. (let* ((sgs nil)
  539. (covar (math-nlfit-find-covar grad xlist pparms))
  540. (n (1- (length covar)))
  541. (N (length xlist))
  542. (i 1))
  543. (when (> N n)
  544. (while (<= i n)
  545. (setq sgs (cons (calcFunc-sqrt (nth i (nth i covar))) sgs))
  546. (setq i (1+ i)))
  547. (setq sgs (reverse sgs)))
  548. (list sgs covar)))
  549. ;;; Now the Calc functions
  550. (defun math-nlfit-s-logistic-params (xdata ydata)
  551. (let ((pdata (math-nlfit-get-rates-from-cumul xdata ydata)))
  552. (math-nlfit-find-logistic-parameters ydata pdata xdata)))
  553. (defun math-nlfit-b-logistic-params (xdata ydata)
  554. (let* ((q0 (math-nlfit-find-q0 ydata xdata))
  555. (qdata (math-nlfit-get-cumul-from-rates xdata ydata q0))
  556. (abc (math-nlfit-find-logistic-parameters qdata ydata xdata))
  557. (B (nth 1 abc))
  558. (C (nth 2 abc))
  559. (A (math-neg
  560. (math-mul
  561. (nth 0 abc)
  562. (math-mul B C))))
  563. (D (math-neg (math-div (calcFunc-ln B) C)))
  564. (A (math-div A B)))
  565. (list A C D)))
  566. ;;; Some functions to turn the parameter lists and variables
  567. ;;; into the appropriate functions.
  568. (defun math-nlfit-s-logistic-solnexpr (pms var)
  569. (let ((a (nth 0 pms))
  570. (b (nth 1 pms))
  571. (c (nth 2 pms)))
  572. (list '/ a
  573. (list '+
  574. 1
  575. (list '*
  576. b
  577. (calcFunc-exp
  578. (list '*
  579. c
  580. var)))))))
  581. (defun math-nlfit-b-logistic-solnexpr (pms var)
  582. (let ((a (nth 0 pms))
  583. (c (nth 1 pms))
  584. (d (nth 2 pms)))
  585. (list '/
  586. (list '*
  587. a
  588. (calcFunc-exp
  589. (list '*
  590. c
  591. (list '- var d))))
  592. (list '^
  593. (list '+
  594. 1
  595. (calcFunc-exp
  596. (list '*
  597. c
  598. (list '- var d))))
  599. 2))))
  600. (defun math-nlfit-enter-result (n prefix vals)
  601. (setq calc-aborted-prefix prefix)
  602. (calc-pop-push-record-list n prefix vals)
  603. (calc-handle-whys))
  604. (defun math-nlfit-fit-curve (fn grad solnexpr initparms &optional sdv)
  605. (calc-slow-wrapper
  606. (let* ((sdevv (or (eq sdv 'calcFunc-efit) (eq sdv 'calcFunc-xfit)))
  607. (calc-display-working-message nil)
  608. (data (calc-top 1))
  609. (xdata (cdr (car (cdr data))))
  610. (ydata (cdr (car (cdr (cdr data)))))
  611. (sdata (if (math-contains-sdev-p ydata)
  612. (mapcar (lambda (x) (math-get-sdev x t)) ydata)
  613. nil))
  614. (ydata (mapcar (lambda (x) (math-get-value x)) ydata))
  615. (calc-curve-varnames nil)
  616. (calc-curve-coefnames nil)
  617. (calc-curve-nvars 1)
  618. (fitvars (calc-get-fit-variables 1 3))
  619. (var (nth 1 calc-curve-varnames))
  620. (parms (cdr calc-curve-coefnames))
  621. (parmguess
  622. (funcall initparms xdata ydata))
  623. (fit (math-nlfit-fit xdata ydata parmguess fn grad sdata))
  624. (finalparms (nth 1 fit))
  625. (sigmacovar
  626. (if sdevv
  627. (math-nlfit-get-sigmas grad xdata finalparms (nth 0 fit))))
  628. (sigmas
  629. (if sdevv
  630. (nth 0 sigmacovar)))
  631. (finalparms
  632. (if sigmas
  633. (math-map-binop
  634. (lambda (x y) (list 'sdev x y)) finalparms sigmas)
  635. finalparms))
  636. (soln (funcall solnexpr finalparms var)))
  637. (let ((calc-fit-to-trail t)
  638. (traillist nil))
  639. (while parms
  640. (setq traillist (cons (list 'calcFunc-eq (car parms) (car finalparms))
  641. traillist))
  642. (setq finalparms (cdr finalparms))
  643. (setq parms (cdr parms)))
  644. (setq traillist (calc-normalize (cons 'vec (nreverse traillist))))
  645. (cond ((eq sdv 'calcFunc-efit)
  646. (math-nlfit-enter-result 1 "efit" soln))
  647. ((eq sdv 'calcFunc-xfit)
  648. (let (sln)
  649. (setq sln
  650. (list 'vec
  651. soln
  652. traillist
  653. (nth 1 sigmacovar)
  654. '(vec)
  655. (nth 0 fit)
  656. (let ((n (length xdata))
  657. (m (length finalparms)))
  658. (if (and sdata (> n m))
  659. (calcFunc-utpc (nth 0 fit)
  660. (- n m))
  661. '(var nan var-nan)))))
  662. (math-nlfit-enter-result 1 "xfit" sln)))
  663. (t
  664. (math-nlfit-enter-result 1 "fit" soln)))
  665. (calc-record traillist "parm")))))
  666. (defun calc-fit-s-shaped-logistic-curve (arg)
  667. (interactive "P")
  668. (math-nlfit-fit-curve 'math-nlfit-s-logistic-fn
  669. 'math-nlfit-s-logistic-grad
  670. 'math-nlfit-s-logistic-solnexpr
  671. 'math-nlfit-s-logistic-params
  672. arg))
  673. (defun calc-fit-bell-shaped-logistic-curve (arg)
  674. (interactive "P")
  675. (math-nlfit-fit-curve 'math-nlfit-b-logistic-fn
  676. 'math-nlfit-b-logistic-grad
  677. 'math-nlfit-b-logistic-solnexpr
  678. 'math-nlfit-b-logistic-params
  679. arg))
  680. (defun calc-fit-hubbert-linear-curve (&optional sdv)
  681. (calc-slow-wrapper
  682. (let* ((sdevv (or (eq sdv 'calcFunc-efit) (eq sdv 'calcFunc-xfit)))
  683. (calc-display-working-message nil)
  684. (data (calc-top 1))
  685. (qdata (cdr (car (cdr data))))
  686. (pdata (cdr (car (cdr (cdr data)))))
  687. (sdata (if (math-contains-sdev-p pdata)
  688. (mapcar (lambda (x) (math-get-sdev x t)) pdata)
  689. nil))
  690. (pdata (mapcar (lambda (x) (math-get-value x)) pdata))
  691. (poverqdata (math-map-binop 'math-div pdata qdata))
  692. (parmvals (math-nlfit-least-squares qdata poverqdata sdata sdevv))
  693. (finalparms (list (nth 0 parmvals)
  694. (math-neg
  695. (math-div (nth 0 parmvals)
  696. (nth 1 parmvals)))))
  697. (calc-curve-varnames nil)
  698. (calc-curve-coefnames nil)
  699. (calc-curve-nvars 1)
  700. (fitvars (calc-get-fit-variables 1 2))
  701. (var (nth 1 calc-curve-varnames))
  702. (parms (cdr calc-curve-coefnames))
  703. (soln (list '* (nth 0 finalparms)
  704. (list '- 1
  705. (list '/ var (nth 1 finalparms))))))
  706. (let ((calc-fit-to-trail t)
  707. (traillist nil))
  708. (setq traillist
  709. (list 'vec
  710. (list 'calcFunc-eq (nth 0 parms) (nth 0 finalparms))
  711. (list 'calcFunc-eq (nth 1 parms) (nth 1 finalparms))))
  712. (cond ((eq sdv 'calcFunc-efit)
  713. (math-nlfit-enter-result 1 "efit" soln))
  714. ((eq sdv 'calcFunc-xfit)
  715. (let (sln
  716. (chisq
  717. (math-nlfit-chi-sq
  718. qdata poverqdata
  719. (list (nth 1 (nth 0 finalparms))
  720. (nth 1 (nth 1 finalparms)))
  721. (lambda (x a b)
  722. (math-mul a
  723. (math-sub
  724. 1
  725. (math-div x b))))
  726. sdata)))
  727. (setq sln
  728. (list 'vec
  729. soln
  730. traillist
  731. (nth 2 parmvals)
  732. (list
  733. 'vec
  734. '(calcFunc-fitdummy 1)
  735. (list 'calcFunc-neg
  736. (list '/
  737. '(calcFunc-fitdummy 1)
  738. '(calcFunc-fitdummy 2))))
  739. chisq
  740. (let ((n (length qdata)))
  741. (if (and sdata (> n 2))
  742. (calcFunc-utpc
  743. chisq
  744. (- n 2))
  745. '(var nan var-nan)))))
  746. (math-nlfit-enter-result 1 "xfit" sln)))
  747. (t
  748. (math-nlfit-enter-result 1 "fit" soln)))
  749. (calc-record traillist "parm")))))
  750. (provide 'calc-nlfit)