qc.scm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. #| -*-Scheme-*-
  2. Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994,
  3. 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
  4. 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Massachusetts
  5. Institute of Technology
  6. This file is part of MIT/GNU Scheme.
  7. MIT/GNU Scheme is free software; you can redistribute it and/or modify
  8. it under the terms of the GNU General Public License as published by
  9. the Free Software Foundation; either version 2 of the License, or (at
  10. your option) any later version.
  11. MIT/GNU Scheme is distributed in the hope that it will be useful, but
  12. WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. General Public License for more details.
  15. You should have received a copy of the GNU General Public License
  16. along with MIT/GNU Scheme; if not, write to the Free Software
  17. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
  18. USA.
  19. |#
  20. ;;;; Quality-controlled adaptive integrators.
  21. (declare (usual-integrations))
  22. ;;; QUALITY-CONTROL upgrades a one-step method into an adaptive
  23. ;;; quality-control method. Given the one-step method and its "order"
  24. ;;; as required by Press et.al. (Numerical Recipes, section 15.2),
  25. ;;; QUALITY-CONTROL returns the STEPPER-MAKER, a procedure of two
  26. ;;; arguments: the system-derivative and an accuracy specification.
  27. ;;; STEPPER-MAKER, when called, returns a quality-controlled stepper
  28. ;;; that adjusts its step-size to meet the accuracy spec. The
  29. ;;; quality-controlled stepper, when given a state and a suggested
  30. ;;; increment of the independent variable, calls its continuation with
  31. ;;; the new state, the actual step taken, and a suggested next step
  32. ;;; size.
  33. ;;; The method supplied to QUALITY-CONTROL takes as arguments the
  34. ;;; system-derivative, the error tolerance, and possibly other
  35. ;;; parameters. Next is a (curried) initial state. It returns a
  36. ;;; stepper procedure that takes a proposed step-size and two
  37. ;;; continuations, one for success and one for failure. The stepper
  38. ;;; calls its success continuation with the new state if it succeeds
  39. ;;; in making the step. It calls the failure continuation if it
  40. ;;; cannot, say because of an attempt to invert a singular matrix, or
  41. ;;; some similar disaster.
  42. (define (quality-control method order)
  43. (let ((2^order (expt 2.0 order))
  44. (error-scale-down (/ -1.0 (+ (exact->inexact order) 1.0)))
  45. (error-scale-up (/ -1.0 (exact->inexact order))))
  46. (let ((halfweight (v:scale (/ 2^order (- 2^order 1.0))))
  47. (fullweight (v:scale (/ 1.0 (- 2^order 1.0))))
  48. (h-adjust-down
  49. (lambda (h err)
  50. (* qc-damping h (expt (max err qc-zero-protect) error-scale-down))))
  51. (h-adjust-up
  52. (lambda (h err)
  53. (* qc-damping h (expt (max err qc-zero-protect) error-scale-up)))))
  54. (define (qc-stepper-maker der tolerance . others)
  55. (let ((error-measure (parse-error-measure tolerance))
  56. (mder (apply method der tolerance others)))
  57. (define (qc-stepper state h-init continue)
  58. (let ((stepper (mder state)))
  59. (if qc-wallp? (write-line `(qc state: ,state)))
  60. (let loop ((h h-init))
  61. (if qc-wallp? (write-line `(qc h: ,h)))
  62. (let ((h/2 (* 0.5 h)))
  63. (stepper ;first halfstep
  64. h/2
  65. (lambda (halfstep nh) ;first halfstep succeeded
  66. ((mder halfstep) ;second halfstep
  67. h/2
  68. (lambda (2halfsteps n2h) ;second halfstep succeeded
  69. (stepper
  70. h
  71. (lambda (fullstep nf) ;fullstep succeeded
  72. (let ((err (error-measure 2halfsteps fullstep)))
  73. (if qc-wallp?
  74. (write-line `(qc fullstep err: ,err ,nh ,n2h ,nf)))
  75. (if (> err *qc-trigger-point*)
  76. (loop (h-adjust-up h err))
  77. (continue (vector-vector (halfweight 2halfsteps)
  78. (fullweight fullstep))
  79. h
  80. (h-adjust-down h err)))))
  81. (lambda () ;fullstep failed
  82. (if qc-wallp? (write-line `(qc: fullstep failed)))
  83. (loop (* *qc-fullstep-reduction-factor* h)))))
  84. (lambda () ;second halfstep failed
  85. (if qc-wallp? (write-line `(qc: second halfstep failed)))
  86. (loop (* *qc-2halfsteps-reduction-factor* h)))))
  87. (lambda () ;first halfstep failed
  88. (if qc-wallp? (write-line `(qc: first halfstep failed)))
  89. (loop (* *qc-halfstep-reduction-factor* h))))))))
  90. qc-stepper))
  91. qc-stepper-maker)))
  92. (define *qc-trigger-point* 1.5) ; to make dead zone around 1.0 -- GJS
  93. (define *qc-halfstep-reduction-factor* 0.3) ;must be less than .5
  94. (define *qc-2halfsteps-reduction-factor* 0.5)
  95. (define *qc-fullstep-reduction-factor* 0.5)
  96. (define qc-wallp? false)
  97. (define qc-damping .9)
  98. (define qc-zero-protect 1.0e-20)
  99. ;;; A "simple" explicit method, based on fourth-order Runge-Kutta:
  100. #| For example:
  101. ((advance-generator
  102. ((quality-control rk4 4) ;integration method
  103. (lambda (v) v) ;x' = x
  104. .0001)) ;error tolerated
  105. #(1.0) ;initial state (at t = t0)
  106. 1.0 ;proceed to t = t0 + 1
  107. 1.0 ;first step no larger than .1
  108. 0.5 ;no step larger than .5
  109. (lambda (ns dt h cont)
  110. (pp ns)
  111. (cont))
  112. (lambda (ns dt sdt)
  113. ;; assert ns = #(2.718...)
  114. ;; assert dt = 1.000...+-
  115. (list ns dt sdt)))
  116. #(1.)
  117. #(1.648716933638961)
  118. #(2.588254411801423)
  119. ;Value: (#(2.7182707063734948) 1. .4041654277154577)
  120. |#
  121. (define (rk4 der tolerance) ;ignores tolerance
  122. (define 2* (v:scale 2.0))
  123. (define 1/2* (v:scale 0.5))
  124. (define 1/6* (v:scale (/ 1.0 6.0)))
  125. (lambda (state)
  126. (let ((dydx (der state)))
  127. (define (rkstep h succeed fail)
  128. (let* ((h* (v:scale h))
  129. (k0 (h* dydx))
  130. (k1 (h* (der (vector+vector state (1/2* k0)))))
  131. (k2 (h* (der (vector+vector state (1/2* k1)))))
  132. (k3 (h* (der (vector+vector state k2)))))
  133. (succeed
  134. (vector+vector state
  135. (1/6* (vector+vector (vector+vector k0 (2* k1))
  136. (vector+vector (2* k2) k3))))
  137. 1)))
  138. rkstep)))
  139. (define %numerics-ode-qc-dummy-1
  140. (add-integrator! 'rkqc
  141. (lambda (derivative lte-tolerance start-state
  142. step-required h-suggested max-h continue done)
  143. ((advance-generator ((quality-control rk4 4) derivative lte-tolerance))
  144. start-state
  145. step-required
  146. h-suggested
  147. max-h
  148. continue
  149. done))
  150. '(derivative lte-tolerance start-state step-required h-suggested max-h continue done)))
  151. ;;; The following are predictor-corrector methods.
  152. (define pc-wallp? false)
  153. ;;; A trapezoid method: xn+1 is found by corrector iteration.
  154. #| For example:
  155. ((advance-generator
  156. ((quality-control c-trapezoid 2) ;integration method
  157. (lambda (v) v) ;x' = x
  158. 0.0001 ;qc error tolerated
  159. 1.0e-5)) ;corrector convergence
  160. #(1.0) ;initial state (at t = t0)
  161. 1.0 ;proceed to t = t0 + 1
  162. 0.1 ;first step no larger than .1
  163. 0.5 ;no step larger than .5
  164. (lambda (ns dt h cont)
  165. (pp ns)
  166. (cont))
  167. (lambda (ns dt sdt)
  168. ;; assert ns = #(2.718...)
  169. ;; assert dt = 1.000...+-
  170. (list ns dt sdt)))
  171. #(1.)
  172. #(1.1051688554687495)
  173. #(1.2583037182508854)
  174. #(1.4307307288261986)
  175. #(1.6229900169138303)
  176. #(1.8372249433735863)
  177. #(2.0758338727890635)
  178. #(2.3414853586609374)
  179. #(2.637148056178347)
  180. ;Value: (#(2.7182832352360498) 1. .11894979864256087)
  181. |#
  182. (define* (c-trapezoid f qc-tolerance #:optional convergence-tolerance)
  183. (let ((error-measure
  184. (parse-error-measure
  185. (if (default-object? convergence-tolerance)
  186. qc-tolerance
  187. convergence-tolerance))))
  188. (lambda (xn)
  189. (let ((d (f xn)))
  190. (define (trapstep dt succeed fail)
  191. (let* ((dt/2 (* dt 0.5))
  192. (predicted (vector+vector xn (scalar*vector dt d)))
  193. (corrected
  194. (vector+vector xn
  195. (scalar*vector dt/2
  196. (vector+vector (f predicted) d)))))
  197. (let lp ((predicted predicted) (corrected corrected) (count 1))
  198. (let ((verr (error-measure predicted corrected)))
  199. (if (< verr 2.0)
  200. (succeed corrected count)
  201. (let* ((ncorr
  202. (vector+vector xn
  203. (scalar*vector dt/2
  204. (vector+vector (f corrected)
  205. d))))
  206. (nverr (error-measure ncorr corrected)))
  207. (if (< nverr verr)
  208. (lp corrected ncorr (fix:+ count 1))
  209. (begin (if pc-wallp? (write-line `(pc failed: ,nverr ,verr)))
  210. (fail)))))))))
  211. trapstep))))
  212. (define %numerics-ode-qc-dummy-2
  213. (add-integrator! 'ctqc
  214. (lambda (derivative lte-tolerance convergence-tolerance start-state
  215. step-required h-suggested max-h continue done)
  216. ((advance-generator
  217. ((quality-control c-trapezoid 2) derivative lte-tolerance convergence-tolerance))
  218. start-state
  219. step-required
  220. h-suggested
  221. max-h
  222. continue
  223. done))
  224. '(derivative lte-tolerance convergence-tolerance start-state
  225. step-required h-suggested max-h continue done)))
  226. ;;; A trapezoid method: xn+1 is found by Newton iteration, rather
  227. ;;; than corrector iteration as in c-trapezoid, above.
  228. ;;; Be careful, the predictor here is not good enough for stiff systems.
  229. ;;; Note, here f yields both the derivative and the Jacobian.
  230. #| For example:
  231. ((advance-generator
  232. ((quality-control n-trapezoid 2) ;integration method
  233. (lambda (v cont) ;x' = x
  234. (cont v (array->matrix #(#(1.0)))))
  235. 0.0001 ;qc-error tolerated
  236. 1 ;state dimension
  237. 1.0e-5)) ;corrector convergence
  238. #(1.0) ;initial state (at t = t0)
  239. 1.0 ;proceed to t = t0 + 1
  240. 0.1 ;first step no larger than .1
  241. 0.5 ;no step larger than .5
  242. (lambda (ns dt h cont)
  243. (pp ns)
  244. (cont))
  245. (lambda (ns dt sdt)
  246. ;; assert ns = #(2.718...)
  247. ;; assert dt = 1.000...+-
  248. (list ns dt sdt)))
  249. #(1.)
  250. #(1.1051708824988178)
  251. #(1.259109256732086)
  252. #(1.4307187104000767)
  253. #(1.6219876372976312)
  254. #(1.8350462370303233)
  255. #(2.0722642596439016)
  256. #(2.3362773683519777)
  257. #(2.630016590666874)
  258. ;Value: (#(2.718279922395027) 1. .11684285320335219)
  259. |#
  260. (define* (n-trapezoid f-df qc-tolerance dimension #:optional convergence-tolerance)
  261. (let* ((convergence-tolerance
  262. (if (default-object? convergence-tolerance)
  263. qc-tolerance
  264. convergence-tolerance))
  265. (error-measure (parse-error-measure convergence-tolerance))
  266. (clip-vector (vector-clipper dimension))
  267. (pad-vector (vector-padder dimension))
  268. (I (m:make-identity (J-dimension dimension))))
  269. (lambda (xn)
  270. (f-df xn
  271. (lambda (fn dfn)
  272. (define (trapstep h succeed fail)
  273. (let ((h/2 (* h 0.5))
  274. (predicted (vector+vector xn (scalar*vector h fn))))
  275. (define (corrector xn+1)
  276. (f-df xn+1
  277. (lambda (fn+1 dfn+1)
  278. (let ((A (matrix-matrix (scalar*matrix h/2 dfn+1) I))
  279. (b (vector-vector xn+1
  280. (vector+vector xn
  281. (scalar*vector h/2
  282. (vector+vector fn fn+1))))))
  283. (gauss-jordan-invert-and-solve
  284. A (clip-vector b)
  285. (lambda (dx . ignore)
  286. (vector+vector (pad-vector dx) xn+1))
  287. (lambda ()
  288. (if pc-wallp? (pp `(gj failed: ,A ,b)))
  289. (fail)))))))
  290. (let lp ((predicted predicted)
  291. (corrected (corrector predicted))
  292. (count 1))
  293. (let ((verr (error-measure predicted corrected)))
  294. (if (< verr 2.0)
  295. (succeed corrected count)
  296. (let* ((ncorr (corrector corrected))
  297. (nverr (error-measure ncorr corrected)))
  298. (if (< nverr verr)
  299. (lp corrected ncorr (fix:+ count 1))
  300. (begin (if pc-wallp?
  301. (write-line `(gj nr failed: ,nverr ,verr)))
  302. (fail)))))))))
  303. trapstep)))))
  304. (define %numerics-ode-qc-dummy-3
  305. (add-integrator!
  306. 'ntqc
  307. (lambda (derivative-and-jacobian
  308. dimension
  309. lte-tolerance
  310. convergence-tolerance
  311. start-state
  312. step-required
  313. h-suggested
  314. max-h
  315. continue
  316. done)
  317. ((advance-generator
  318. ((quality-control n-trapezoid 2)
  319. derivative-and-jacobian lte-tolerance dimension convergence-tolerance))
  320. start-state
  321. step-required
  322. h-suggested
  323. max-h
  324. continue
  325. done))
  326. '(derivative-and-jacobian
  327. dimension
  328. lte-tolerance
  329. convergence-tolerance
  330. start-state
  331. step-required
  332. h-suggested
  333. max-h
  334. continue
  335. done)))