Mercurial > emacs
view lisp/calc/calc-nlfit.el @ 96044:c1ef445563bb
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org-colview.el (org-columns-next-allowed-value): Bug fix.
* org-colview-xemacs.el (org-columns-next-allowed-value): Bug fix.
* org-agenda.el (org-agenda-get-closed): Get the end time into the
agenda prefix as well.
* org-publish.el (org-publish-org-index): Make a properly indented
list.
* org.el (org-calendar-agenda-action-key): New option.
(org-get-cursor-date): New function.
(org-mark-entry-for-agenda-action): New command.
(org-overriding-default-time): New variable.
(org-read-date): Respect `org-overriding-default-time'.
* org-remember.el (org-remember-apply-template): Respect the
ovverriding default time.
* org-agenda.el (org-agenda-action-marker): New variable.
(org-agenda-action): New command.
(org-agenda-do-action): New function.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org.el (org-schedule, org-deadline): Protect scheduled and
deadline tasks against changes that accidently remove the
repeater. Also show a message with the new date when done.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org.el (org-beginning-of-line): Cater for the case when there
are tags but no headline text.
(org-align-tags-here): Convert to tabs only when indent-tabs-mode
it set.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org-mhe.el (org-mhe-get-message-folder-from-index): Make sure
the return value is nil instead of "nil" when there is no match.
* org-exp.el (org-insert-centered): Use fill-column instead of
80.
(org-export-as-ascii): Use string-width to measure the width of
the heading.
* org.el (org-diary-to-ical-string): No longer kill buffer
FROMBUF, this is now done by the caller.
* org-exp.el (org-print-icalendar-entries): Move the call to
`org-diary-to-ical-string' out of the loop, and kill the buffer
afterwords.
* org-remember.el (org-remember-visit-immediately): Position
cursor after moving to the note.
(org-remember-apply-template): Use a text property to record the
cursor position.
(org-remember-handler): Align tags after pasting the note.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org-bbdb.el (org-bbdb-follow-anniversary-link): New function.
* org-agenda.el (org-agenda-open-link): If there is an
org-bbdb-name property in the current line, jump to that bbdb
entry.
* org-bbdb.el (org-bbdb-anniversaries): Add the bbdb-name as a
text property, so that the agenda knows where this entry comes
from.
* org-agenda.el (org-agenda-clock-in): Fixed bug in the
interaction between clocking-in from the agenda, and automatic
task state switching.
* org-macs.el (org-with-point-at): Bug fix in macro defintion.
* org.el (org-beginning-of-line, org-end-of-line): Make sure the
zmacs-region stays after this command in XEmacs.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org.el (org-scan-tags): Allow new values for ACTION parameter.
* org-remember.el (org-remember-templates): Fix bug in
customization type definition.
* org.el (org-map-entries): New function.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org-agenda.el (org-agenda-skip-comment-trees): New option.
(org-agenda-skip): Respect `org-agenda-skip-comment-trees'.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org-remember.el (org-jump-to-target-location): New variable.
(org-remember-apply-template): Set
`org-remember-apply-template' if requested by template.
(org-remember-handler): Start an idle timer to jump to
remember location.
* org-exp.el (org-get-current-options): Add the FILETAGS setting.
* org.el (org-set-regexps-and-options): Fix bug with parsing of
file tags.
(org-get-tags-at): Add the content of `org-file-tags'.
* org-exp.el (org-export-handle-comments): Fix bug with several
comment lines after each other.
(org-number-to-roman, org-number-to-counter): New functions.
(org-export-section-number-format): New option.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org-exp.el (org-export-protect-examples): Catch the case of a
missing end_example line.
* org.el (org-set-regexps-and-options): Set `org-file-properties' and
`org-file-tags' to nil.
* org-colview.el (org-columns-next-allowed-value): Handle next
argument NTH to directly select a value.
* org-colview-xemacs.el (org-columns-next-allowed-value): Handle next
argument NTH to directly select a value.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org-agenda.el (org-agenda-scheduled-leaders): Fix docstring.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org.el (org-columns-ellipses): New option.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org-colview.el (org-columns-add-ellipses): New function.
(org-columns-compact-links): New function.
(org-columns-cleanup-item): Call `org-columns-compact-links'.
(org-columns-display-here): Call `org-agenda-columns-cleanup-item'
when in agenda.
(org-columns-edit-value): Fixed bug with editing values from
agenda column view.
(org-columns-redo): Also redo the agenda itself.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org-agenda.el (org-agenda-columns-remove-prefix-from-item): New
option.
* org-colview.el (org-agenda-columns-cleanup-item): New function.
* org-exp.el (org-export-ascii-preprocess): Renamed from
`org-export-ascii-clean-string'.
(org-export-kill-licensed-text)
(org-export-define-heading-targets)
(org-export-handle-invisible-targets)
(org-export-target-internal-links)
(org-export-remove-or-extract-drawers)
(org-export-remove-archived-trees)
(org-export-protect-quoted-subtrees)
(org-export-protect-verbatim, org-export-protect-examples)
(org-export-select-backend-specific-text)
(org-export-mark-blockquote-and-verse)
(org-export-remove-comment-blocks-and-subtrees)
(org-export-handle-comments, org-export-mark-radio-links)
(org-export-remove-special-table-lines)
(org-export-normalize-links)
(org-export-concatenate-multiline-links)
(org-export-concatenate-multiline-emphasis): New functions,
obtained from spliiting the export preprocessor.
* org-table.el (org-table-recalculate): Improve error message if
the row number is invalid.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org-archive.el (org-archive-save-context-info): Fix bugs in
customization setup and docstring.
* org-exp.el (org-export-html-style): Changed the size of in the
<pre> element to 90%.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org.el (org-find-src-example-start): Function removed.
(org-edit-src-find-region-and-lang): New function.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org.el (org-edit-src-exit): New function.
(org-exit-edit-mode): New minor mode.
* org-exp.el (org-export-preprocess-string): Fix bug with removing
comment-like lines from protected examples.
* org.el (org-edit-src-example, org-find-src-example-start)
(org-protect-source-example, org-edit-special): New functions.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org-publish.el (org-publish-project-alist): Fix typo in
docstring.
(org-publish-project-alist): Handle :index-title property.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org-export-latex.el (org-export-as-latex): Make sure region
bounds are correct. Parse subtree properties relating to export.
* org-exp.el (org-export-add-options-to-plist): New function.
(org-infile-export-plist): Use `org-export-add-options-to-plist'.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org.el (org-default-properties): Add EXPORT_FILE_NAME and
EXPORT_TITLE.
* org-exp.el (org-export-get-title-from-subtree)
(org-export-as-ascii, org-export-as-html): Make sure the original
region-beginning and region-end are used, even after moving
point.
(org-export-get-title-from-subtree): Also try the EXPORT_TITLE
property.
* org-remember.el (org-remember-last-stored-marker): New variable.
(org-remember-goto-last-stored): Use `org-goto-marker-or-bmk'.
(org-remember-handler): Also use marker to remember
last-stored position.
* org.el (org-goto-marker-or-bmk): New function.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org.el (org-file-properties): Renamed from `org-local-properties'.
(org-scan-tags): Take file tags into account.
(org-tags-match-list-sublevels): Default changed to t.
* org-exp.el (org-export-as-html): Close paragraph after a
footnote.
* org.el (org-update-parent-todo-statistics): New function.
* org-exp.el (org-icalendar-store-UID): New option.
(org-icalendar-force-UID): Option removed.
(org-print-icalendar-entries): IMplement UIDs.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org-mhe.el (org-mhe-follow-link): Fix bug in mhe searches.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org-faces.el (org-column): Document how this face is being used
and why sometimes the background faces shine through.
* org-mhe.el (org-mhe-follow-link): Improve handling of searches.
* org-publish.el (org-publish-attachment): Create publishing
directory if it does not yet exist.
* org-table.el (org-calc-default-modes): Change default number
format to (float 8).
* org.el (org-olpath-completing-read): New function.
(org-time-clocksum-format): New option.
(org-minutes-to-hh:mm-string): Use `org-time-clocksum-format'.
* org-clock.el (org-clock-display, org-clock-out)
(org-update-mode-line): Use `org-time-clocksum-format'.
* org-colview-xemacs.el (org-columns-number-to-string): Use
`org-time-clocksum-format'.
* org-colview.el (org-columns-number-to-string): Use
`org-time-clocksum-format'.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org-id.el: New file, move from contrib to core.
* org-exp.el (org-icalendar-force-UID): New option.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org-exp.el (org-print-icalendar-entries): Make sure DTEND is
shifted by one day if theere is a date range without an end
time.
* org.el (org-try-structure-completion): New function.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org.el (org-set-font-lock-defaults): Improve fontification of
description lists.
(org-insert-item): Handle description lists.
(org-adaptive-fill-function): Improve auto indentation in
description lists.
* org-exp.el (org-export-as-html, org-export-preprocess-string):
Implement VERSE environment.
(org-export-preprocess-string): Implement the COMMENT
environment.
* org-export-latex.el (org-export-latex-preprocess): Implement
VERSE environment.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org-jsinfo.el (org-infojs-opts-table): Add entry for FIXED_TOC
option.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org-table.el (orgtbl-to-tsv, orgtbl-to-csv): New functions.
* org.el (org-quote-csv-field): New functions.
* org-table.el (org-table-export-default-format): Remove :splice
from default format, we get the same effect by not specifying
:tstart and :tend.
(org-table-export): Improve setup, distinguish better between
interactive and non-interactive use, allow specifying the format
on the fly, better protection against wrong file names.
(orgtbl-to-generic): Fix documentation. Do not require :tstart
and :tend when :splice is omitted.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org-clock.el (org-clock-select-task): Make sure the selection
letters are 1-9 and A-Z, no special characters.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org-exp.el (org-export-htmlize): New group.
(org-export-htmlize-output-type)
(org-export-htmlize-css-font-prefix): New options.
(org-export-htmlize-region-for-paste): New function.
(org-export-htmlize-generate-css): New command.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org.el (org-set-visibility-according-to-property): New function.
(org-ctrl-c-ctrl-c): Do not restart org-mode, just get the options
and compute the regular expressions, and update font-lock.
(org-property-re): Allow a dash in property names.
* org-archive.el (org-extract-archive-file): Insert the file name
without the path into the format, to allow the location format to
contain a subdirectory.
* org-agenda.el (org-agenda-post-command-hook): If point is at end
of buffer, and the `org-agenda-type' property undefined, use the
value from the character before.
* org.el (org-add-planning-info): Don't let indentation for
would-be timestamp become extra whitespace at the end of headline.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org.el (org-remove-double-quotes, org-file-contents): New
functions.
* org-exp.el (org-infile-export-plist): Also parse the
contents of #+SETUPFILE files, recursively.
* org.el (org-set-regexps-and-options): Also parse the
contents of #+SETUPFILE files, recursively.
* org-exp.el (org-export-handle-include-files): New function.
(org-export-preprocess-string): Call
`org-export-handle-include-files'.
* org.el (org-delete-property-globally)
(org-delete-property, org-set-property): Ignore case during
completion.
(org-set-property): Use `org-completing-read' instead of
`completing-read'.
* org.el (org-complete-expand-structure-template): New,
experimental function.
(org-structure-template-alist): New, experimental option.
(org-complete): Call `org-complete-expand-structure-template'.
2008-06-17 Bastien Guerry <bzg@altern.org>
* org-export-latex.el (org-export-latex-preprocess): Added
support for blockquotes.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org.el (org-read-date-analyze): Catch the case where only a
weekday is given.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org.el (org-set-font-lock-defaults): Make the description
tag bold.
* org-exp.el (org-export-as-html, org-close-li): Implement
description lists.
2008-06-17 Jason Riedy <jason@acm.org>
* org-table.el (*orgtbl-default-fmt*): New variable.
(orgtbl-format-line): Use the value of *orgtbl-default-fmt*
when there is no other fmt available.
(orgtbl-to-generic): Allow an explicitly nil :tstart or
:tend to suppress the appropriate string.
(orgtbl-to-orgtbl): New function for translating to another orgtbl
table.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org.el (org-read-date-analyze): "." as an alias for "+0" in
read date.
* org-clock.el (org-clock-save-markers-for-cut-and-paste):
New function.
* org-agenda.el (org-agenda-save-markers-for-cut-and-paste):
New function.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org-clock.el (org-clock-find-position): Don't include notes
into clock drawer.
* org-archive.el (org-archive-subtree): No longer remove an
extra line after cutting the subtree. `org-cut-subtree' already
takes care of this.
* org-remember.el (org-remember-handler): Only kill the target
buffer if it does not contain the running clock.
* org.el (org-markers-to-move): New variable.
(org-save-markers-in-region, org-check-and-save-marker)
(org-reinstall-markers-in-region): New function.
(org-move-subtree-down, org-copy-subtree): Remember relative
marker positions before cutting.
(org-move-subtree-down, org-paste-subtree): Restore relative
marker positions after pasting.
* org-remember.el (org-remember-clock-out-on-exit): New option.
(org-remember-finalize): Clock out only if the setting in
`org-remember-clock-out-on-exit' requires it.
(org-remember-handler): Do the cleanup in the buffer, to make sure
that the clock marker remains in tact.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org-clock.el (org-clock-goto): Widen buffer if necessary.
(org-clock-in): Make sure that also tasks outside the narrowed
region will be clocked in correctly.
(org-clock-insert-selection-line): Widen the buffer so that we can
find the correct task heading.
* org.el (org-base-buffer): New function.
* org-exp.el (org-icalendar-cleanup-string): Make sure ',"
and ";" are escaped.
(org-print-icalendar-entries): Also apply
`org-icalendar-cleanup-string' to the headline, not only to the
summary property.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org-exp.el (org-export-preprocess-hook): New hook.
(org-export-preprocess-string): Call
`org-export-preprocess-hook'.
* org.el (org-font-lock-hook): New variable.
(org-font-lock-hook): New function.
(org-set-font-lock-defaults): Call `org-font-lock-hook'.
2008-06-17 Carsten Dominik <dominik@science.uva.nl>
* org.texi: Modify license to no longer include back- and front
cover matters.
(Using the mapping API): New section.
(Agenda column view): New section.
(Moving subtrees): Document archiving to the archive
sibling.
(Agenda commands): Document columns view in the agenda.
(Using the property API): Document the API for
multi-valued properties.
author | Carsten Dominik <dominik@science.uva.nl> |
---|---|
date | Tue, 17 Jun 2008 15:22:00 +0000 |
parents | 6c9af2bfcfee |
children | a9dc0e7c3f2b |
line wrap: on
line source
;;; calc-nlfit.el --- nonlinear curve fitting for Calc ;; Copyright (C) 2007, 2008 Free Software Foundation, Inc. ;; Maintainer: Jay Belanger <jay.p.belanger@gmail.com> ;; This file is part of GNU Emacs. ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: ;; This code uses the Levenberg-Marquardt method, as described in ;; _Numerical Analysis_ by H. R. Schwarz, to fit data to ;; nonlinear curves. Currently, the only the following curves are ;; supported: ;; The logistic S curve, y=a/(1+exp(b*(t-c))) ;; Here, y is usually interpreted as the population of some ;; quantity at time t. So we will think of the data as consisting ;; of quantities q0, q1, ..., qn and their respective times ;; t0, t1, ..., tn. ;; The logistic bell curve, y=A*exp(B*(t-C))/(1+exp(B*(t-C)))^2 ;; Note that this is the derivative of the formula for the S curve. ;; We get A=-a*b, B=b and C=c. Here, y is interpreted as the rate ;; of growth of a population at time t. So we will think of the ;; data as consisting of rates p0, p1, ..., pn and their ;; respective times t0, t1, ..., tn. ;; The Hubbert Linearization, y/x=A*(1-x/B) ;; Here, y is thought of as the rate of growth of a population ;; and x represents the actual population. This is essentially ;; the differential equation describing the actual population. ;; The Levenberg-Marquardt method is an iterative process: it takes ;; an initial guess for the parameters and refines them. To get an ;; initial guess for the parameters, we'll use a method described by ;; Luis de Sousa in "Hubbert's Peak Mathematics". The idea is that ;; given quantities Q and the corresponding rates P, they should ;; satisfy P/Q= mQ+a. We can use the parameter a for an ;; approximation for the parameter a in the S curve, and ;; approximations for b and c are found using least squares on the ;; linearization log((a/y)-1) = log(bb) + cc*t of ;; y=a/(1+bb*exp(cc*t)), which is equivalent to the above s curve ;; formula, and then tranlating it to b and c. From this, we can ;; also get approximations for the bell curve parameters. ;;; Code: (require 'calc-arith) (require 'calcalg3) ;; Declare functions which are defined elsewhere. (declare-function calc-get-fit-variables "calcalg3" (nv nc &optional defv defc with-y homog)) (declare-function math-map-binop "calcalg3" (binop args1 args2)) (defun math-nlfit-least-squares (xdata ydata &optional sdata sigmas) "Return the parameters A and B for the best least squares fit y=a+bx." (let* ((n (length xdata)) (s2data (if sdata (mapcar 'calcFunc-sqr sdata) (make-list n 1))) (S (if sdata 0 n)) (Sx 0) (Sy 0) (Sxx 0) (Sxy 0) D) (while xdata (let ((x (car xdata)) (y (car ydata)) (s (car s2data))) (setq Sx (math-add Sx (if s (math-div x s) x))) (setq Sy (math-add Sy (if s (math-div y s) y))) (setq Sxx (math-add Sxx (if s (math-div (math-mul x x) s) (math-mul x x)))) (setq Sxy (math-add Sxy (if s (math-div (math-mul x y) s) (math-mul x y)))) (if sdata (setq S (math-add S (math-div 1 s))))) (setq xdata (cdr xdata)) (setq ydata (cdr ydata)) (setq s2data (cdr s2data))) (setq D (math-sub (math-mul S Sxx) (math-mul Sx Sx))) (let ((A (math-div (math-sub (math-mul Sxx Sy) (math-mul Sx Sxy)) D)) (B (math-div (math-sub (math-mul S Sxy) (math-mul Sx Sy)) D))) (if sigmas (let ((C11 (math-div Sxx D)) (C12 (math-neg (math-div Sx D))) (C22 (math-div S D))) (list (list 'sdev A (calcFunc-sqrt C11)) (list 'sdev B (calcFunc-sqrt C22)) (list 'vec (list 'vec C11 C12) (list 'vec C12 C22)))) (list A B))))) ;;; The methods described by de Sousa require the cumulative data qdata ;;; and the rates pdata. We will assume that we are given either ;;; qdata and the corresponding times tdata, or pdata and the corresponding ;;; tdata. The following two functions will find pdata or qdata, ;;; given the other.. ;;; First, given two lists; one of values q0, q1, ..., qn and one of ;;; corresponding times t0, t1, ..., tn; return a list ;;; p0, p1, ..., pn of the rates of change of the qi with respect to t. ;;; p0 is the right hand derivative (q1 - q0)/(t1 - t0). ;;; pn is the left hand derivative (qn - q(n-1))/(tn - t(n-1)). ;;; The other pis are the averages of the two: ;;; (1/2)((qi - q(i-1))/(ti - t(i-1)) + (q(i+1) - qi)/(t(i+1) - ti)). (defun math-nlfit-get-rates-from-cumul (tdata qdata) (let ((pdata (list (math-div (math-sub (nth 1 qdata) (nth 0 qdata)) (math-sub (nth 1 tdata) (nth 0 tdata)))))) (while (> (length qdata) 2) (setq pdata (cons (math-mul '(float 5 -1) (math-add (math-div (math-sub (nth 2 qdata) (nth 1 qdata)) (math-sub (nth 2 tdata) (nth 1 tdata))) (math-div (math-sub (nth 1 qdata) (nth 0 qdata)) (math-sub (nth 1 tdata) (nth 0 tdata))))) pdata)) (setq qdata (cdr qdata))) (setq pdata (cons (math-div (math-sub (nth 1 qdata) (nth 0 qdata)) (math-sub (nth 1 tdata) (nth 0 tdata))) pdata)) (reverse pdata))) ;;; Next, given two lists -- one of rates p0, p1, ..., pn and one of ;;; corresponding times t0, t1, ..., tn -- and an initial values q0, ;;; return a list q0, q1, ..., qn of the cumulative values. ;;; q0 is the initial value given. ;;; For i>0, qi is computed using the trapezoid rule: ;;; qi = q(i-1) + (1/2)(pi + p(i-1))(ti - t(i-1)) (defun math-nlfit-get-cumul-from-rates (tdata pdata q0) (let* ((qdata (list q0))) (while (cdr pdata) (setq qdata (cons (math-add (car qdata) (math-mul (math-mul '(float 5 -1) (math-add (nth 1 pdata) (nth 0 pdata))) (math-sub (nth 1 tdata) (nth 0 tdata)))) qdata)) (setq pdata (cdr pdata)) (setq tdata (cdr tdata))) (reverse qdata))) ;;; Given the qdata, pdata and tdata, find the parameters ;;; a, b and c that fit q = a/(1+b*exp(c*t)). ;;; a is found using the method described by de Sousa. ;;; b and c are found using least squares on the linearization ;;; log((a/q)-1) = log(b) + c*t ;;; In some cases (where the logistic curve may well be the wrong ;;; model), the computed a will be less than or equal to the maximum ;;; value of q in qdata; in which case the above linearization won't work. ;;; In this case, a will be replaced by a number slightly above ;;; the maximum value of q. (defun math-nlfit-find-qmax (qdata pdata tdata) (let* ((ratios (math-map-binop 'math-div pdata qdata)) (lsdata (math-nlfit-least-squares ratios tdata)) (qmax (math-max-list (car qdata) (cdr qdata))) (a (math-neg (math-div (nth 1 lsdata) (nth 0 lsdata))))) (if (math-lessp a qmax) (math-add '(float 5 -1) qmax) a))) (defun math-nlfit-find-logistic-parameters (qdata pdata tdata) (let* ((a (math-nlfit-find-qmax qdata pdata tdata)) (newqdata (mapcar (lambda (q) (calcFunc-ln (math-sub (math-div a q) 1))) qdata)) (bandc (math-nlfit-least-squares tdata newqdata))) (list a (calcFunc-exp (nth 0 bandc)) (nth 1 bandc)))) ;;; Next, given the pdata and tdata, we can find the qdata if we know q0. ;;; We first try to find q0, using the fact that when p takes on its largest ;;; value, q is half of its maximum value. So we'll find the maximum value ;;; of q given various q0, and use bisection to approximate the correct q0. ;;; First, given pdata and tdata, find what half of qmax would be if q0=0. (defun math-nlfit-find-qmaxhalf (pdata tdata) (let ((pmax (math-max-list (car pdata) (cdr pdata))) (qmh 0)) (while (math-lessp (car pdata) pmax) (setq qmh (math-add qmh (math-mul (math-mul '(float 5 -1) (math-add (nth 1 pdata) (nth 0 pdata))) (math-sub (nth 1 tdata) (nth 0 tdata))))) (setq pdata (cdr pdata)) (setq tdata (cdr tdata))) qmh)) ;;; Next, given pdata and tdata, approximate q0. (defun math-nlfit-find-q0 (pdata tdata) (let* ((qhalf (math-nlfit-find-qmaxhalf pdata tdata)) (q0 (math-mul 2 qhalf)) (qdata (math-nlfit-get-cumul-from-rates tdata pdata q0))) (while (math-lessp (math-nlfit-find-qmax (mapcar (lambda (q) (math-add q0 q)) qdata) pdata tdata) (math-mul '(float 5 -1) (math-add q0 qhalf))) (setq q0 (math-add q0 qhalf))) (let* ((qmin (math-sub q0 qhalf)) (qmax q0) (qt (math-nlfit-find-qmax (mapcar (lambda (q) (math-add q0 q)) qdata) pdata tdata)) (i 0)) (while (< i 10) (setq q0 (math-mul '(float 5 -1) (math-add qmin qmax))) (if (math-lessp (math-nlfit-find-qmax (mapcar (lambda (q) (math-add q0 q)) qdata) pdata tdata) (math-mul '(float 5 -1) (math-add qhalf q0))) (setq qmin q0) (setq qmax q0)) (setq i (1+ i))) (math-mul '(float 5 -1) (math-add qmin qmax))))) ;;; To improve the approximations to the parameters, we can use ;;; Marquardt method as described in Schwarz's book. ;;; Small numbers used in the Givens algorithm (defvar math-nlfit-delta '(float 1 -8)) (defvar math-nlfit-epsilon '(float 1 -5)) ;;; Maximum number of iterations (defvar math-nlfit-max-its 100) ;;; Next, we need some functions for dealing with vectors and ;;; matrices. For convenience, we'll work with Emacs lists ;;; as vectors, rather than Calc's vectors. (defun math-nlfit-set-elt (vec i x) (setcar (nthcdr (1- i) vec) x)) (defun math-nlfit-get-elt (vec i) (nth (1- i) vec)) (defun math-nlfit-make-matrix (i j) (let ((row (make-list j 0)) (mat nil) (k 0)) (while (< k i) (setq mat (cons (copy-sequence row) mat)) (setq k (1+ k))) mat)) (defun math-nlfit-set-matx-elt (mat i j x) (setcar (nthcdr (1- j) (nth (1- i) mat)) x)) (defun math-nlfit-get-matx-elt (mat i j) (nth (1- j) (nth (1- i) mat))) ;;; For solving the linearized system. ;;; (The Givens method, from Schwarz.) (defun math-nlfit-givens (C d) (let* ((C (copy-tree C)) (d (copy-tree d)) (n (length (car C))) (N (length C)) (j 1) (r (make-list N 0)) (x (make-list N 0)) w gamma sigma rho) (while (<= j n) (let ((i (1+ j))) (while (<= i N) (let ((cij (math-nlfit-get-matx-elt C i j)) (cjj (math-nlfit-get-matx-elt C j j))) (when (not (math-equal 0 cij)) (if (math-lessp (calcFunc-abs cjj) (math-mul math-nlfit-delta (calcFunc-abs cij))) (setq w (math-neg cij) gamma 0 sigma 1 rho 1) (setq w (math-mul (calcFunc-sign cjj) (calcFunc-sqrt (math-add (math-mul cjj cjj) (math-mul cij cij)))) gamma (math-div cjj w) sigma (math-neg (math-div cij w))) (if (math-lessp (calcFunc-abs sigma) gamma) (setq rho sigma) (setq rho (math-div (calcFunc-sign sigma) gamma)))) (setq cjj w cij rho) (math-nlfit-set-matx-elt C j j w) (math-nlfit-set-matx-elt C i j rho) (let ((k (1+ j))) (while (<= k n) (let* ((cjk (math-nlfit-get-matx-elt C j k)) (cik (math-nlfit-get-matx-elt C i k)) (h (math-sub (math-mul gamma cjk) (math-mul sigma cik)))) (setq cik (math-add (math-mul sigma cjk) (math-mul gamma cik))) (setq cjk h) (math-nlfit-set-matx-elt C i k cik) (math-nlfit-set-matx-elt C j k cjk) (setq k (1+ k))))) (let* ((di (math-nlfit-get-elt d i)) (dj (math-nlfit-get-elt d j)) (h (math-sub (math-mul gamma dj) (math-mul sigma di)))) (setq di (math-add (math-mul sigma dj) (math-mul gamma di))) (setq dj h) (math-nlfit-set-elt d i di) (math-nlfit-set-elt d j dj)))) (setq i (1+ i)))) (setq j (1+ j))) (let ((i n) s) (while (>= i 1) (math-nlfit-set-elt r i 0) (setq s (math-nlfit-get-elt d i)) (let ((k (1+ i))) (while (<= k n) (setq s (math-add s (math-mul (math-nlfit-get-matx-elt C i k) (math-nlfit-get-elt x k)))) (setq k (1+ k)))) (math-nlfit-set-elt x i (math-neg (math-div s (math-nlfit-get-matx-elt C i i)))) (setq i (1- i)))) (let ((i (1+ n))) (while (<= i N) (math-nlfit-set-elt r i (math-nlfit-get-elt d i)) (setq i (1+ i)))) (let ((j n)) (while (>= j 1) (let ((i N)) (while (>= i (1+ j)) (setq rho (math-nlfit-get-matx-elt C i j)) (if (math-equal rho 1) (setq gamma 0 sigma 1) (if (math-lessp (calcFunc-abs rho) 1) (setq sigma rho gamma (calcFunc-sqrt (math-sub 1 (math-mul sigma sigma)))) (setq gamma (math-div 1 (calcFunc-abs rho)) sigma (math-mul (calcFunc-sign rho) (calcFunc-sqrt (math-sub 1 (math-mul gamma gamma))))))) (let ((ri (math-nlfit-get-elt r i)) (rj (math-nlfit-get-elt r j)) h) (setq h (math-add (math-mul gamma rj) (math-mul sigma ri))) (setq ri (math-sub (math-mul gamma ri) (math-mul sigma rj))) (setq rj h) (math-nlfit-set-elt r i ri) (math-nlfit-set-elt r j rj)) (setq i (1- i)))) (setq j (1- j)))) x)) (defun math-nlfit-jacobian (grad xlist parms &optional slist) (let ((j nil)) (while xlist (let ((row (apply grad (car xlist) parms))) (setq j (cons (if slist (mapcar (lambda (x) (math-div x (car slist))) row) row) j))) (setq slist (cdr slist)) (setq xlist (cdr xlist))) (reverse j))) (defun math-nlfit-make-ident (l n) (let ((m (math-nlfit-make-matrix n n)) (i 1)) (while (<= i n) (math-nlfit-set-matx-elt m i i l) (setq i (1+ i))) m)) (defun math-nlfit-chi-sq (xlist ylist parms fn &optional slist) (let ((cs 0)) (while xlist (let ((c (math-sub (apply fn (car xlist) parms) (car ylist)))) (if slist (setq c (math-div c (car slist)))) (setq cs (math-add cs (math-mul c c)))) (setq xlist (cdr xlist)) (setq ylist (cdr ylist)) (setq slist (cdr slist))) cs)) (defun math-nlfit-init-lambda (C) (let ((l 0) (n (length (car C))) (N (length C))) (while C (let ((row (car C))) (while row (setq l (math-add l (math-mul (car row) (car row)))) (setq row (cdr row)))) (setq C (cdr C))) (calcFunc-sqrt (math-div l (math-mul n N))))) (defun math-nlfit-make-Ctilda (C l) (let* ((n (length (car C))) (bot (math-nlfit-make-ident l n))) (append C bot))) (defun math-nlfit-make-d (fn xdata ydata parms &optional sdata) (let ((d nil)) (while xdata (setq d (cons (let ((dd (math-sub (apply fn (car xdata) parms) (car ydata)))) (if sdata (math-div dd (car sdata)) dd)) d)) (setq xdata (cdr xdata)) (setq ydata (cdr ydata)) (setq sdata (cdr sdata))) (reverse d))) (defun math-nlfit-make-dtilda (d n) (append d (make-list n 0))) (defun math-nlfit-fit (xlist ylist parms fn grad &optional slist) (let* ((C (math-nlfit-jacobian grad xlist parms slist)) (d (math-nlfit-make-d fn xlist ylist parms slist)) (chisq (math-nlfit-chi-sq xlist ylist parms fn slist)) (lambda (math-nlfit-init-lambda C)) (really-done nil) (iters 0)) (while (and (not really-done) (< iters math-nlfit-max-its)) (setq iters (1+ iters)) (let ((done nil)) (while (not done) (let* ((Ctilda (math-nlfit-make-Ctilda C lambda)) (dtilda (math-nlfit-make-dtilda d (length (car C)))) (zeta (math-nlfit-givens Ctilda dtilda)) (newparms (math-map-binop 'math-add (copy-tree parms) zeta)) (newchisq (math-nlfit-chi-sq xlist ylist newparms fn slist))) (if (math-lessp newchisq chisq) (progn (if (math-lessp (math-div (math-sub chisq newchisq) newchisq) math-nlfit-epsilon) (setq really-done t)) (setq lambda (math-div lambda 10)) (setq chisq newchisq) (setq parms newparms) (setq done t)) (setq lambda (math-mul lambda 10))))) (setq C (math-nlfit-jacobian grad xlist parms slist)) (setq d (math-nlfit-make-d fn xlist ylist parms slist)))) (list chisq parms))) ;;; The functions that describe our models, and their gradients. (defun math-nlfit-s-logistic-fn (x a b c) (math-div a (math-add 1 (math-mul b (calcFunc-exp (math-mul c x)))))) (defun math-nlfit-s-logistic-grad (x a b c) (let* ((ep (calcFunc-exp (math-mul c x))) (d (math-add 1 (math-mul b ep))) (d2 (math-mul d d))) (list (math-div 1 d) (math-neg (math-div (math-mul a ep) d2)) (math-neg (math-div (math-mul a (math-mul b (math-mul x ep))) d2))))) (defun math-nlfit-b-logistic-fn (x a c d) (let ((ex (calcFunc-exp (math-mul c (math-sub x d))))) (math-div (math-mul a ex) (math-sqr (math-add 1 ex))))) (defun math-nlfit-b-logistic-grad (x a c d) (let* ((ex (calcFunc-exp (math-mul c (math-sub x d)))) (ex1 (math-add 1 ex)) (xd (math-sub x d))) (list (math-div ex (math-sqr ex1)) (math-sub (math-div (math-mul a (math-mul xd ex)) (math-sqr ex1)) (math-div (math-mul 2 (math-mul a (math-mul xd (math-sqr ex)))) (math-pow ex1 3))) (math-sub (math-div (math-mul 2 (math-mul a (math-mul c (math-sqr ex)))) (math-pow ex1 3)) (math-div (math-mul a (math-mul c ex)) (math-sqr ex1)))))) ;;; Functions to get the final covariance matrix and the sdevs (defun math-nlfit-find-covar (grad xlist pparms) (let ((j nil)) (while xlist (setq j (cons (cons 'vec (apply grad (car xlist) pparms)) j)) (setq xlist (cdr xlist))) (setq j (cons 'vec (reverse j))) (setq j (math-mul (calcFunc-trn j) j)) (calcFunc-inv j))) (defun math-nlfit-get-sigmas (grad xlist pparms chisq) (let* ((sgs nil) (covar (math-nlfit-find-covar grad xlist pparms)) (n (1- (length covar))) (N (length xlist)) (i 1)) (when (> N n) (while (<= i n) (setq sgs (cons (calcFunc-sqrt (nth i (nth i covar))) sgs)) (setq i (1+ i))) (setq sgs (reverse sgs))) (list sgs covar))) ;;; Now the Calc functions (defun math-nlfit-s-logistic-params (xdata ydata) (let ((pdata (math-nlfit-get-rates-from-cumul xdata ydata))) (math-nlfit-find-logistic-parameters ydata pdata xdata))) (defun math-nlfit-b-logistic-params (xdata ydata) (let* ((q0 (math-nlfit-find-q0 ydata xdata)) (qdata (math-nlfit-get-cumul-from-rates xdata ydata q0)) (abc (math-nlfit-find-logistic-parameters qdata ydata xdata)) (B (nth 1 abc)) (C (nth 2 abc)) (A (math-neg (math-mul (nth 0 abc) (math-mul B C)))) (D (math-neg (math-div (calcFunc-ln B) C))) (A (math-div A B))) (list A C D))) ;;; Some functions to turn the parameter lists and variables ;;; into the appropriate functions. (defun math-nlfit-s-logistic-solnexpr (pms var) (let ((a (nth 0 pms)) (b (nth 1 pms)) (c (nth 2 pms))) (list '/ a (list '+ 1 (list '* b (calcFunc-exp (list '* c var))))))) (defun math-nlfit-b-logistic-solnexpr (pms var) (let ((a (nth 0 pms)) (c (nth 1 pms)) (d (nth 2 pms))) (list '/ (list '* a (calcFunc-exp (list '* c (list '- var d)))) (list '^ (list '+ 1 (calcFunc-exp (list '* c (list '- var d)))) 2)))) (defun math-nlfit-enter-result (n prefix vals) (setq calc-aborted-prefix prefix) (calc-pop-push-record-list n prefix vals) (calc-handle-whys)) (defun math-nlfit-fit-curve (fn grad solnexpr initparms &optional sdv) (calc-slow-wrapper (let* ((sdevv (or (eq sdv 'calcFunc-efit) (eq sdv 'calcFunc-xfit))) (calc-display-working-message nil) (data (calc-top 1)) (xdata (cdr (car (cdr data)))) (ydata (cdr (car (cdr (cdr data))))) (sdata (if (math-contains-sdev-p ydata) (mapcar (lambda (x) (math-get-sdev x t)) ydata) nil)) (ydata (mapcar (lambda (x) (math-get-value x)) ydata)) (calc-curve-varnames nil) (calc-curve-coefnames nil) (calc-curve-nvars 1) (fitvars (calc-get-fit-variables 1 3)) (var (nth 1 calc-curve-varnames)) (parms (cdr calc-curve-coefnames)) (parmguess (funcall initparms xdata ydata)) (fit (math-nlfit-fit xdata ydata parmguess fn grad sdata)) (finalparms (nth 1 fit)) (sigmacovar (if sdevv (math-nlfit-get-sigmas grad xdata finalparms (nth 0 fit)))) (sigmas (if sdevv (nth 0 sigmacovar))) (finalparms (if sigmas (math-map-binop (lambda (x y) (list 'sdev x y)) finalparms sigmas) finalparms)) (soln (funcall solnexpr finalparms var))) (let ((calc-fit-to-trail t) (traillist nil)) (while parms (setq traillist (cons (list 'calcFunc-eq (car parms) (car finalparms)) traillist)) (setq finalparms (cdr finalparms)) (setq parms (cdr parms))) (setq traillist (calc-normalize (cons 'vec (nreverse traillist)))) (cond ((eq sdv 'calcFunc-efit) (math-nlfit-enter-result 1 "efit" soln)) ((eq sdv 'calcFunc-xfit) (let (sln) (setq sln (list 'vec soln traillist (nth 1 sigmacovar) '(vec) (nth 0 fit) (let ((n (length xdata)) (m (length finalparms))) (if (and sdata (> n m)) (calcFunc-utpc (nth 0 fit) (- n m)) '(var nan var-nan))))) (math-nlfit-enter-result 1 "xfit" sln))) (t (math-nlfit-enter-result 1 "fit" soln))) (calc-record traillist "parm"))))) (defun calc-fit-s-shaped-logistic-curve (arg) (interactive "P") (math-nlfit-fit-curve 'math-nlfit-s-logistic-fn 'math-nlfit-s-logistic-grad 'math-nlfit-s-logistic-solnexpr 'math-nlfit-s-logistic-params arg)) (defun calc-fit-bell-shaped-logistic-curve (arg) (interactive "P") (math-nlfit-fit-curve 'math-nlfit-b-logistic-fn 'math-nlfit-b-logistic-grad 'math-nlfit-b-logistic-solnexpr 'math-nlfit-b-logistic-params arg)) (defun calc-fit-hubbert-linear-curve (&optional sdv) (calc-slow-wrapper (let* ((sdevv (or (eq sdv 'calcFunc-efit) (eq sdv 'calcFunc-xfit))) (calc-display-working-message nil) (data (calc-top 1)) (qdata (cdr (car (cdr data)))) (pdata (cdr (car (cdr (cdr data))))) (sdata (if (math-contains-sdev-p pdata) (mapcar (lambda (x) (math-get-sdev x t)) pdata) nil)) (pdata (mapcar (lambda (x) (math-get-value x)) pdata)) (poverqdata (math-map-binop 'math-div pdata qdata)) (parmvals (math-nlfit-least-squares qdata poverqdata sdata sdevv)) (finalparms (list (nth 0 parmvals) (math-neg (math-div (nth 0 parmvals) (nth 1 parmvals))))) (calc-curve-varnames nil) (calc-curve-coefnames nil) (calc-curve-nvars 1) (fitvars (calc-get-fit-variables 1 2)) (var (nth 1 calc-curve-varnames)) (parms (cdr calc-curve-coefnames)) (soln (list '* (nth 0 finalparms) (list '- 1 (list '/ var (nth 1 finalparms)))))) (let ((calc-fit-to-trail t) (traillist nil)) (setq traillist (list 'vec (list 'calcFunc-eq (nth 0 parms) (nth 0 finalparms)) (list 'calcFunc-eq (nth 1 parms) (nth 1 finalparms)))) (cond ((eq sdv 'calcFunc-efit) (math-nlfit-enter-result 1 "efit" soln)) ((eq sdv 'calcFunc-xfit) (let (sln (chisq (math-nlfit-chi-sq qdata poverqdata (list (nth 1 (nth 0 finalparms)) (nth 1 (nth 1 finalparms))) (lambda (x a b) (math-mul a (math-sub 1 (math-div x b)))) sdata))) (setq sln (list 'vec soln traillist (nth 2 parmvals) (list 'vec '(calcFunc-fitdummy 1) (list 'calcFunc-neg (list '/ '(calcFunc-fitdummy 1) '(calcFunc-fitdummy 2)))) chisq (let ((n (length qdata))) (if (and sdata (> n 2)) (calcFunc-utpc chisq (- n 2)) '(var nan var-nan))))) (math-nlfit-enter-result 1 "xfit" sln))) (t (math-nlfit-enter-result 1 "fit" soln))) (calc-record traillist "parm"))))) (provide 'calc-nlfit) ;; arch-tag: 6eba3cd6-f48b-4a84-8174-10c15a024928