nchebpoly.scm 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  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. ;;;; Chebyshev polynomial routines
  21. (declare (usual-integrations))
  22. ;;; Must be loaded into an environment where polynomial manipulations exist.
  23. ;;; Edited by GJS 10Jan09
  24. ;;; 1/24/90 (gjs) converted to flush dependencies on dense list representation.
  25. ;;; 9/22/89 (gjs) reduce->a-reduce
  26. ;;; 12/23/87 (mh) modified CHEB-ECON to return original poly if not trimmed
  27. (define (add-lists l1 l2)
  28. (cond ((null? l1) l2)
  29. ((null? l2) l1)
  30. (else
  31. (cons (+ (car l1) (car l2))
  32. (add-lists (cdr l1) (cdr l2))))))
  33. (define (scale-list s l)
  34. (map (lambda (x) (* s x)) l))
  35. ;;; We define the stream of Chebyshev polynomials,
  36. ;;; using the recurrence relation
  37. ;;; T[0] = 1, T[1] = x, T[n] = 2xT[n-1] - T[n-2]
  38. (define chebyshev-polynomials
  39. (let ((x poly:identity)
  40. (2x (poly:scale poly:identity 2)))
  41. (cons-stream 1
  42. (cons-stream x
  43. (map-streams (lambda (p1 p2)
  44. (poly:- (poly:* 2x p1) p2))
  45. (tail chebyshev-polynomials)
  46. chebyshev-polynomials)))))
  47. ;;; The following procedure returns the Nth Chebyshev polynomial
  48. (define (chebyshev-polynomial n)
  49. (stream-ref chebyshev-polynomials n))
  50. ;;; In the following, we define a CHEB-EXP to be an expansion in
  51. ;;; Chebyshev polynomials. The expansion is
  52. ;;; written as a list of coefficients (a0 a1 ... aN), and represents
  53. ;;; the formal series a0*T[0](x) + ... + aN*T[N](x).
  54. ;;; The stream of scaled Chebyshev expansions corresponding
  55. ;;; to the sequence 1, x, 2x^2, 4x^3, ..., 2^(n-1)x^n ... Note that the
  56. ;;; general form doesn't cover the first term. What is generally wanted
  57. ;;; is the expansion corresponding to x^n; the power of 2 is thrown in
  58. ;;; so that the resulting expansion is exact in integer arithmetic.
  59. (define (2x cheb-exp)
  60. (let ((t1 (cdr cheb-exp))
  61. (t2 (append (list 0) cheb-exp))
  62. (t3 (list 0 (car cheb-exp))))
  63. (add-lists t1 (add-lists t2 t3))))
  64. (define scaled-chebyshev-expansions
  65. (cons-stream '(1)
  66. (cons-stream '(0 1)
  67. (map-stream 2x
  68. (tail scaled-chebyshev-expansions)))))
  69. ;;; For convenience, we also provide the non-scaled Chebyshev expansions
  70. (define chebyshev-expansions
  71. (letrec (;; s = {1 1 2 4 8 16 ...}
  72. (s (cons-stream 1
  73. (cons-stream 1
  74. (map-stream (lambda (x) (+ x x)) (tail s)))))
  75. (c scaled-chebyshev-expansions))
  76. (map-streams (lambda (factor expansion)
  77. (scale-list (/ 1 factor) expansion))
  78. s
  79. c)))
  80. ;;; Convert from polynomial form to Chebyshev expansion
  81. (define (poly->cheb-exp poly)
  82. (let* ((maxcoeff (apply max (map abs (poly/coefficients poly))))
  83. (zero-tolerance (* 10 maxcoeff *machine-epsilon*))
  84. (=0?
  85. (lambda (p)
  86. (and (number? p)
  87. (< (abs p) zero-tolerance)))))
  88. (let lp ((p poly) (c chebyshev-expansions) (s '(0)))
  89. (if (=0? p) ;(equal? p poly:zero) NO!
  90. s
  91. (let ((v (poly:value p 0)))
  92. (poly:divide (poly:- p v) poly:identity
  93. (lambda (q r)
  94. (if (not (equal? r poly:zero))
  95. (error "POLY->CHEB-EXP"))
  96. (lp q
  97. (tail c)
  98. (add-lists (scale-list v (head c)) s)))))))))
  99. ;;; Convert from Chebyshev expansion to polynomial form
  100. (define (cheb-exp->poly e)
  101. (let ((n (length e)))
  102. (let ((cheb (stream-head chebyshev-polynomials n)))
  103. (a-reduce poly:+ (map poly:scale cheb e)))))
  104. ;;; Given a Cheb-expansion and an error criterion EPS, trim the tail of
  105. ;;; those coefficients whose absolute sum is <= EPS.
  106. (define (trim-cheb-exp cheb eps)
  107. (let ((r (reverse cheb)))
  108. (let loop ((sum (abs (car r))) (r r))
  109. (if (fix:= (length r) 1)
  110. (if (<= sum eps) '(0) r)
  111. (if (> sum eps)
  112. (reverse r)
  113. (loop (+ sum (abs (cadr r))) (cdr r)))))))
  114. ;;; The next procedure performs Chebyshev economization on a polynomial p
  115. ;;; over a specified interval [a,b]: the returned polynomial is guaranteed
  116. ;;; to differ from the original by no more than eps over [a, b].
  117. (define (cheb-econ p a b eps)
  118. (let ((q (poly-domain->canonical p a b)))
  119. (let ((r (poly->cheb-exp q)))
  120. (let ((s (trim-cheb-exp r eps)))
  121. (if (fix:= (length s) (length r)) ;nothing got trimmed
  122. p
  123. (let ((t (cheb-exp->poly s)))
  124. (poly-domain->general t a b)))))))
  125. ;;; Return the root-list of the Nth Chebyshev polynomial
  126. (define (cheb-root-list n)
  127. (define (root i)
  128. (if (and (odd? n)
  129. (fix:= (fix:* 2 i) (fix:- n 1)))
  130. 0
  131. (- (cos (/ (* (+ i 1/2) pi) n)))))
  132. (let loop ((i 0))
  133. (if (fix:= i n)
  134. '()
  135. (cons (root i)
  136. (loop (fix:+ i 1))))))
  137. ;;; This procedure accepts an integer n > 0 and a real x, and returns
  138. ;;; a list of the values T[0](x) ... T[n-1](x). If an optional third
  139. ;;; argument is given as the symbol 'HALF, then the first value in the
  140. ;;; list will have the value 0.5; otherwise it has the value 1.
  141. (define (first-n-cheb-values n x . optionals)
  142. (let ((first (if (and (not (null? optionals))
  143. (eq? (car optionals) 'HALF))
  144. 1/2
  145. 1)))
  146. (cond ((fix:< n 1) '())
  147. ((fix:= n 1) (list first))
  148. ((fix:= n 2) (list first x))
  149. (else (let loop ((ans (list x first)) (a 1) (b x) (count 2))
  150. (if (fix:= count n)
  151. (reverse ans)
  152. (let ((next (- (* 2 x b) a)))
  153. (loop (cons next ans) b next (fix:+ count 1)))))))))
  154. ;;; The following procedure evaluates a cheb-expansion directly, in a
  155. ;;; manner analogous to Horner's method for evaluating polynomials --
  156. ;;; actually, it isn't as analogous as it should be, and should
  157. ;;; probably be replaced by Clenshaw's method which truly is like
  158. ;;; Horner's.
  159. (define (cheb-exp-value cheb x)
  160. (let ((n (length cheb)))
  161. (let ((vals (first-n-cheb-values n x)))
  162. (a-reduce + (map * cheb vals)))))
  163. ;;; This procedure generates a Chebyshev expansion to a given order N,
  164. ;;; for a function f specified on an interval [a,b]. The interval is
  165. ;;; mapped onto [-1,1] and the function approximated is g, defined on
  166. ;;; [-1,1] to behave the same as f on [a,b].
  167. ;;; Note: the returned list of coefficients is N long, and is associated
  168. ;;; with Chebyshev polynomials from T[0] to T[N-1].
  169. (define (generate-cheb-exp f a b n)
  170. (if (<= b a)
  171. (error "Bad interval in GENERATE-CHEB-EXP"))
  172. (let ((interval-map ;map [-1,1] onto [a,b]
  173. (let ((c (/ (+ a b) 2))
  174. (d (/ (- b a) 2)))
  175. (lambda (x) (+ c (* d x))))))
  176. (let ((roots (cheb-root-list n))
  177. (polys (stream-head chebyshev-polynomials n)))
  178. (let ((vals (map f (map interval-map roots))))
  179. (let loop ((coeffs '()) (i 0))
  180. (if (fix:= i n)
  181. (reverse coeffs)
  182. (let ((chebf (lambda (x)
  183. (poly:value (list-ref polys i) x))))
  184. (let ((chebvals (map chebf roots)))
  185. (let ((sum (a-reduce + (map * vals chebvals))))
  186. (let ((term (if (zero? i) (/ sum n) (/ (* 2 sum) n))))
  187. (loop (cons term coeffs) (fix:+ i 1))))))))))))
  188. ;;; This procedure accepts a function f, an interval [a,b], a number
  189. ;;; N of points at which to base a Chebyshev interpolation, and an
  190. ;;; optional precision EPS. The method is to map f onto the canonical
  191. ;;; interval [-1,1], generate the Chebyshev expansion based on the
  192. ;;; first N Chebyshev polynomials interpolating at the roots of the
  193. ;;; N+1st Chebyshev polynomial T[N]. If EPS has been specified, an
  194. ;;; economization is performed at this point; otherwise not. Finally,
  195. ;;; the Chebyshev expansion is reconverted to a polynomial and mapped
  196. ;;; back onto the original interval [a,b].
  197. (define (generate-approx-poly f a b n . optionals)
  198. (let ((eps (if (null? optionals) false (car optionals))))
  199. (let ((p (generate-cheb-exp f a b n)))
  200. (let ((pp (if eps (trim-cheb-exp p eps) p)))
  201. (poly-domain->general (cheb-exp->poly pp) a b)))))
  202. #|
  203. (define win (frame 0 pi/2 0 1))
  204. (define s
  205. (let ((p (generate-approx-poly sin 0 pi/2 3)))
  206. (lambda (x) (poly:value p x))))
  207. (plot-function win sin 0 pi/2 .01)
  208. (plot-function win s 0 pi/2 .01)
  209. (graphics-close win)
  210. (define win (frame 0 pi/2 -0.1 +0.1))
  211. (plot-function win (- s sin) 0 pi/2 .01)
  212. (graphics-close win)
  213. (define win (frame 0 pi/2 -0.002 +0.002))
  214. (define s1
  215. (let ((p (generate-approx-poly sin 0 pi/2 5)))
  216. (lambda (x) (poly:value p x))))
  217. (plot-function win (- s1 sin) 0 pi/2 .01)
  218. (define s2
  219. (let ((p (generate-approx-poly sin 0 pi/2 5 .01)))
  220. (lambda (x) (poly:value p x))))
  221. (plot-function win (- s2 sin) 0 pi/2 .01)
  222. (graphics-close win)
  223. |#