ppa.scm 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  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. ;;;; PPA: Piecewise polynomial approximations
  21. ;;; Edited by GJS 10Jan09
  22. ;;; Smooth versions of PPA added 5/19/89 (mh)
  23. ;;; bug fix 1/13/88
  24. (declare (usual-integrations))
  25. ;;; To make a piecewise polynomial approximation of a function, f,
  26. ;;; we specify the range, [low, high], the maximum order of polynomial
  27. ;;; that fits may be made with, and the accuracy required.
  28. (define (make-ppa f low high max-order accuracy)
  29. (let* ((c (/ (+ low high) 2))
  30. (d (/ (- high low) 2))
  31. (g (lambda (x) (f (+ x c))))
  32. (result (get-poly-and-errors g (- d) d max-order))
  33. (p (car result))
  34. (eps (cadr result)))
  35. (if (< eps accuracy)
  36. (ppa-make-from-poly low high
  37. (cheb-econ p (- d) d (- accuracy eps)))
  38. (ppa-adjoin (make-ppa f low c max-order accuracy)
  39. (make-ppa f c high max-order accuracy)))))
  40. ;;; PPA-VALUE will evaluate a PPA at any given point, x.
  41. (define (ppa-value ppa x)
  42. (define (ppa-search low high body)
  43. (cond ((ppa-terminal? body)
  44. (poly:value (ppa-poly body) (- x (/ (+ low high) 2))))
  45. ((ppa-split? body)
  46. (let ((s (ppa-split body)))
  47. (if (< x s)
  48. (ppa-search low s (ppa-low-side body))
  49. (ppa-search s high (ppa-high-side body)))))
  50. (else (error "Bad body -- PPA-SEARCH"))))
  51. (let ((low (ppa-low-bound ppa))
  52. (high (ppa-high-bound ppa)))
  53. (if (and (<= low x) (<= x high))
  54. (ppa-search low high (ppa-body ppa))
  55. (error "Out of bounds -- PPA-VALUE"))))
  56. ;;; We may use PPAs to memoize functions.
  57. (define (ppa-memo f low high max-order accuracy)
  58. (let ((ppa (make-ppa f low high max-order accuracy)))
  59. (lambda (x) (ppa-value ppa x))))
  60. ;;; When derivatives of a numerical procedure are available, they may
  61. ;;; be used to increase the accuracy easily achievable in the piecewise
  62. ;;; approximating process. The first argument is a list of the numerical
  63. ;;; procedure f and as many of its successive derivative procedures as
  64. ;;; we care to use. For example,
  65. ;;; (make-smooth-ppa (list sin cos) 0 pi/2 1e-7)
  66. ;;; will use Hermite fitting with first derivative contact, employing
  67. ;;; piecewise cubics for the process.
  68. (define (make-smooth-ppa flist low high accuracy)
  69. (let* ((n (length flist))
  70. (f (car flist))
  71. (herm (make-hermite-interpolator (- n 1))))
  72. (let loop ((a low) (b high))
  73. (let* ((c (/ (+ a b) 2))
  74. (d (/ (- b a) 2))
  75. (-d (- d))
  76. (avals (map (lambda (f) (f a)) flist))
  77. (bvals (map (lambda (f) (f b)) flist))
  78. (p (herm (cons -d avals) (cons d bvals)))
  79. (g (lambda (x) (f (+ c x))))
  80. (erf (lambda (t) (abs (- (g t) (poly:value p t)))))
  81. (eps (cadr (gsmax erf -d d 'function-tol .01))))
  82. (if (< eps accuracy)
  83. (ppa-make-from-poly a b p)
  84. (let ((mid (/ (+ a b) 2)))
  85. (ppa-adjoin (loop a mid)
  86. (loop mid b))))))))
  87. (define (smooth-ppa-memo flist low high accuracy)
  88. (let ((ppa (make-smooth-ppa flist low high accuracy)))
  89. (lambda (x) (ppa-value ppa x))))
  90. ;;; Implementation of PPA data structures
  91. (define (ppa-make-from-poly low high poly)
  92. (cons (cons low high)
  93. (cons 'ppa-terminal poly)))
  94. (define (ppa-adjoin ppalow ppahigh)
  95. (if (= (cdar ppalow) (caar ppahigh))
  96. (cons (cons (caar ppalow) (cdar ppahigh))
  97. (cons 'ppa-split
  98. (cons (cdar ppalow)
  99. (cons (cdr ppalow) (cdr ppahigh)))))
  100. (error "PPAs not adjacent -- PPA-ADJOIN")))
  101. (define ppa-low-bound caar)
  102. (define ppa-high-bound cdar)
  103. (define ppa-body cdr)
  104. (define (ppa-terminal? b)
  105. (eq? (car b) 'ppa-terminal))
  106. (define ppa-poly cdr)
  107. (define (ppa-split? b)
  108. (eq? (car b) 'ppa-split))
  109. (define ppa-split cadr)
  110. (define ppa-low-side caddr)
  111. (define ppa-high-side cdddr)
  112. #|
  113. (define win (frame 0 pi/2 -0.0001 +0.0001))
  114. (define s0
  115. (let ((p (make-ppa sin 0 pi/2 5 .0001)))
  116. (lambda (x) (ppa-value p x))))
  117. (plot-function win (- s0 sin) 0 (- pi/2 .01) .001)
  118. ;;; S0 has a nasty discontinuity at the ppa split.
  119. (define s1
  120. (let ((p (make-smooth-ppa (list sin) 0 pi/2 .0001)))
  121. (lambda (x) (ppa-value p x))))
  122. (plot-function win (- s1 sin) 0 (- pi/2 .01) .001)
  123. ;;; s1 is continuous, but otherwise horrible!
  124. (define s2
  125. (let ((p (make-smooth-ppa (list sin cos) 0 pi/2 .0001)))
  126. (lambda (x) (ppa-value p x))))
  127. (plot-function win (- s2 sin) 0 (- pi/2 .01) .001)
  128. ;;; s2 is very oscillatory and not very accurate, but it is smooth.
  129. (define s3
  130. (let ((p (make-smooth-ppa (list sin cos (- sin)) 0 pi/2 .0001)))
  131. (lambda (x) (ppa-value p x))))
  132. (plot-function win (- s3 sin) 0 (- pi/2 .01) .001)
  133. ;;; S3 is really better! More smooth, accurate, and only one split.
  134. (graphics-clear win)
  135. (graphics-close win)
  136. |#