polyroot.scm 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  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. ;;; General Polynomial Root Finder
  21. (declare (usual-integrations))
  22. ;; (needs "../numerical/cluster")
  23. ;; (to-initialize "poly-env")
  24. (define leading-coefficient poly:leading-coefficient)
  25. (define lowest-order poly:lowest-order)
  26. (define trailing-coefficient poly:trailing-coefficient)
  27. (define identity-poly poly:identity)
  28. (define horners-rule-with-error poly:horners-rule-with-error)
  29. (define (complex-random modulus)
  30. (make-polar modulus (random :2pi)))
  31. ;;get the value of p at x, and the error estimate, by Horners rule
  32. (define (horners-rule p x)
  33. (horners-rule-with-error p x list))
  34. (define (roots->poly roots)
  35. (a-reduce poly:*
  36. (map (lambda (r) (poly:- identity-poly r))
  37. roots)))
  38. ;;; This finds the roots of a univariate polynomial.
  39. (define* (poly->roots given-poly #:optional expand-multiplicities?)
  40. (if (default-object? expand-multiplicities?)
  41. (set! expand-multiplicities? #t))
  42. (let* ((given-poly (ensure-real given-poly))
  43. (all-real?
  44. (for-all? (poly/coefficients given-poly)
  45. (lambda (c) (zero? (imag-part c))))))
  46. (define (search kernel-poly initial-roots)
  47. (let ((polish (root-polisher kernel-poly)))
  48. (let find-loop ((deflated-poly kernel-poly) (roots initial-roots))
  49. (if root-wallp (write-line (list 'finder-loop deflated-poly roots)))
  50. (if (fix:< (poly:degree deflated-poly) 1)
  51. (if (not (fix:= (length roots) (poly:degree given-poly)))
  52. (begin (error "Root finder failed" given-poly) 'foo)
  53. (let ((rs
  54. (sort (identify-multiple-roots roots)
  55. (lambda (r1 r2)
  56. (let ((mr1 (magnitude (cdr r1)))
  57. (mr2 (magnitude (cdr r2))))
  58. (or (< mr1 mr2)
  59. (and (= mr1 mr2)
  60. (< (real-part (cdr r1))
  61. (real-part (cdr r2))))))))))
  62. (if expand-multiplicities? (expand-multiplicities rs) rs)))
  63. (let ((root
  64. (clean-up-root
  65. (polish
  66. (rescale-poly-roots deflated-poly root-searcher)))))
  67. (if (and all-real? (obviously-complex? root))
  68. (let ((cr (conjugate root)))
  69. (find-loop (ensure-real
  70. (deflate-poly deflated-poly (list root cr)))
  71. (cons root (cons cr roots))))
  72. (find-loop (deflate-poly deflated-poly (list root))
  73. (cons root roots))))))))
  74. (let ((n (poly:degree given-poly))
  75. (m (lowest-order given-poly)))
  76. (cond ((fix:< n 1) '())
  77. ((fix:= m 0)
  78. (search given-poly '()))
  79. (else ;factors of the indeterminate to be removed.
  80. (let ((zero-roots (make-list m 0.0)))
  81. (search (deflate-poly given-poly zero-roots)
  82. zero-roots)))))))
  83. (define (ensure-real poly)
  84. (let lp ((poly poly))
  85. (if (pair? poly)
  86. (cons (lp (car poly)) (lp (cdr poly)))
  87. (let ((c poly))
  88. (if (and (number? c) (inexact? c))
  89. (bring-to-real c)
  90. c)))))
  91. (define (bring-to-real c)
  92. (if (< (abs (imag-part c))
  93. (* imaginary-part-tolerance *machine-epsilon* (abs (real-part c))))
  94. (real-part c)
  95. c))
  96. (define (rescale-poly-roots poly searcher)
  97. (let ((Nn (poly:degree poly))
  98. (N0 (lowest-order poly)))
  99. (if (fix:= Nn N0)
  100. 0
  101. (let ((An (leading-coefficient poly))
  102. (A0 (trailing-coefficient poly)))
  103. (let ((k (/ A0 An)))
  104. (let ((c (expt k (/ 1.0 (- Nn N0)))))
  105. (if (< (/ 1.0 max-scale) (magnitude k) max-scale)
  106. (searcher poly)
  107. (* (searcher (poly:* (poly:arg-scale poly (list c))
  108. (/ 1.0 (* A0 (expt c N0)))))
  109. c))))))))
  110. ;;; Heuristic test
  111. (define (obviously-complex? root)
  112. (and (> (magnitude root) minimum-magnitude)
  113. (or (and (zero? (real-part root)) (not (zero? (imag-part root))))
  114. (> (abs (imag-part root))
  115. (* (abs (real-part root))
  116. obviousity-factor)))))
  117. ;;; The following gets rid of microscopic imaginary parts or real
  118. ;;; parts that may arise from roundoff errors.
  119. (define (clean-up-root root)
  120. (let ((rt (* rationalization-tolerance *machine-epsilon*)))
  121. (cond ((zero? root) root)
  122. ((real? root) (rationalize root rt))
  123. (else
  124. (let ((rr (rationalize (real-part root) rt))
  125. (ri (rationalize (imag-part root) rt)))
  126. (let ((ar (abs rr)) (ai (abs ri)))
  127. (cond ((> (* ar on-axis-tolerance *machine-epsilon*) ai)
  128. rr)
  129. ((> (* ai on-axis-tolerance *machine-epsilon*) ar)
  130. (* ri +i))
  131. (else
  132. (make-rectangular rr ri)))))))))
  133. ;;; Deflation
  134. (define (deflate-poly p roots)
  135. (poly:divide p
  136. (roots->poly roots)
  137. (lambda (q r)
  138. ;; We will test r for approximate zero to be sure
  139. (if root-wallp (write-line (list q r)))
  140. q)))
  141. ;;; Clustering roots of polynomials to determine multiplicities can
  142. ;;; be used to improve the accuracy, by a theorem of Wilkenson.
  143. (define identify-multiple-roots
  144. (let ()
  145. (define (mult-scan cl)
  146. (let ((n (length (cluster-elements cl))))
  147. (if (fix:= n 1)
  148. (list (cons 1 (car (cluster-elements cl))))
  149. (let ((mid (/ (apply + (cluster-elements cl)) n)))
  150. (if (< (cluster-diameter cl)
  151. (expt (magnitude
  152. (* cluster-tolerance
  153. (+ 1.0 mid)
  154. *machine-epsilon*))
  155. (/ 1.0 n)))
  156. (list (cons n (clean-up-root mid)))
  157. (append (mult-scan (car (cluster-subclusters cl)))
  158. (mult-scan (cadr (cluster-subclusters cl)))))))))
  159. (define (d z1 z2) (magnitude (- z1 z2)))
  160. (define sd (set-separation d))
  161. (lambda (roots)
  162. (cond ((null? roots) '())
  163. (clustering (mult-scan (car (cluster roots sd d))))
  164. (else (map (lambda (r) (cons 1 r)) roots))))))
  165. ;;; To undo this damage:
  166. (define (expand-multiplicities roots)
  167. (if (null? roots)
  168. '()
  169. (append (make-list (caar roots) (cdar roots))
  170. (expand-multiplicities (cdr roots)))))
  171. ;;; Control for root-finder search
  172. (define (root-searcher p)
  173. (let ((improve (root-searcher-method p)))
  174. (define (try xn xn-1 vxn-1 dvxn-1 iter-count shrink-count)
  175. (let* ((xn (bring-to-real xn))
  176. (vxn/err (horners-rule p xn))
  177. (dx (- xn xn-1)))
  178. (let ((vxn (car vxn/err)) (dvxn (cadr vxn/err)) (err (cadddr vxn/err)))
  179. (if root-wallp (write-line `(trying ,xn ,vxn ,err)))
  180. (cond ((< (magnitude vxn)
  181. (* root-searcher-value-to-noise (magnitude err)))
  182. (if root-wallp (write-line `(found-winner-at ,xn ,vxn ,err)))
  183. xn) ;good enuf.
  184. ((<= (magnitude vxn-1) (magnitude vxn)) ;losing
  185. (cond ((< shrink-count root-searcher-max-shrink)
  186. ;; Cuthbert's hack -- fatal with Laguerre method!
  187. ;; try x^40 + 1 = 0
  188. (try (+ xn-1 (/ dx root-searcher-shrink-factor))
  189. xn-1 vxn-1 dvxn-1
  190. iter-count (fix:+ shrink-count 1)))
  191. ((< iter-count root-searcher-max-iter)
  192. ;; Try a desparate root-searcher-jiggle
  193. (try (+ xn-1
  194. (complex-random
  195. (* iter-count (magnitude xn-1)
  196. root-searcher-jiggle)))
  197. xn-1 vxn-1 dvxn-1 (+ iter-count 1) 0))
  198. (else
  199. (error "Cannot make progress" p xn vxn)
  200. 'foo)))
  201. ((< (magnitude dx)
  202. (* root-searcher-minimum-progress *machine-epsilon*
  203. (magnitude xn)))
  204. (if root-wallp
  205. (write-line `(found-lazy-winner-at ,xn ,vxn ,err)))
  206. xn)
  207. ((and (not (< (magnitude dvxn-1) minimum-denominator))
  208. (not (< (magnitude dvxn) minimum-denominator))
  209. (let* ((f (/ vxn dvxn))
  210. (d (- f (/ vxn-1 dvxn-1))))
  211. (and (not (< (magnitude d) minimum-denominator))
  212. (let* ((q (magnitude (/ (- xn xn-1) d)))
  213. (iq (round q)))
  214. (and (> iq 1)
  215. (< (abs (- q iq)) (* *kahan-threshold* q))
  216. (begin
  217. (if root-wallp
  218. (write-line `(trying-kahan-trick ,iq)))
  219. (try (- xn (* f iq))
  220. xn vxn dvxn
  221. (fix:+ iter-count 1) 0))))))))
  222. ((< iter-count root-searcher-max-iter)
  223. (improve xn vxn/err
  224. (lambda (xn+1)
  225. (try xn+1 xn vxn dvxn (fix:+ iter-count 1) 0))
  226. (lambda ()
  227. ; (error "zero-divide failure")
  228. (if root-wallp
  229. (write-line `(zero-divide-at ,xn in searcher)))
  230. xn)))
  231. (else
  232. (error "Search exceeded max iterations"
  233. p xn vxn)
  234. 'foo)))))
  235. (let ((vx0/err (horners-rule p root-searcher-x0)))
  236. (if root-wallp (write-line `(hunting-starting-at ,root-searcher-x0 ,p)))
  237. (let ((vx0 (car vx0/err)) (dvx0 (cadr vx0/err)) (err0 (cadddr vx0/err)))
  238. (if (< (magnitude vx0)
  239. (* root-searcher-value-to-noise (magnitude err0)))
  240. (begin
  241. (if root-wallp
  242. (write-line
  243. `(won-immediately-at ,root-searcher-x0 ,vx0 ,err0)))
  244. root-searcher-x0) ;good enuf.
  245. (improve root-searcher-x0 vx0/err
  246. (lambda (x1)
  247. (try x1 root-searcher-x0 vx0 dvx0 1 0))
  248. (lambda ()
  249. ; (error "zero-divide failure")
  250. (if root-wallp
  251. (write-line
  252. `(zero-divide-at ,root-searcher-x0
  253. in startup of searcher)))
  254. root-searcher-x0)))))))
  255. (define (root-polisher p)
  256. (let ((improve (root-polisher-method p)))
  257. (define (try x vp-err)
  258. (let ((vp (car vp-err)) (vperr (cadddr vp-err)))
  259. (if root-wallp (write-line `(polishing root ,x ,vp ,vperr)))
  260. (if (< (magnitude vp) ;x is good enough
  261. (* root-polisher-value-to-noise (magnitude vperr)))
  262. (begin (if root-wallp (write-line `(win-at ,x)))
  263. x)
  264. (improve x vp-err
  265. (lambda (nx)
  266. (if (< (magnitude (- x nx)) ;insufficient improvement
  267. (* root-polisher-minimum-progress
  268. *machine-epsilon*
  269. (+ (magnitude x) 1.0)))
  270. (begin
  271. (if root-wallp
  272. (write-line `(good-enuf-at ,nx)))
  273. nx)
  274. (let* ((nvp-err (horners-rule p nx))
  275. (nvp (car nvp-err)))
  276. (if (< (magnitude nvp) (magnitude vp))
  277. (try nx nvp-err)
  278. (begin
  279. (if root-wallp
  280. (write-line `(got-worse-at ,nx)))
  281. x)))))
  282. (lambda ()
  283. ; (error "zero-divide failure")
  284. (if root-wallp
  285. (write-line `(zero-divide-at ,x in polisher)))
  286. x)))))
  287. (lambda (x) (try x (horners-rule p x)))))
  288. ;;; Newton's method
  289. ;;; To find a root of P(x)
  290. ;;; Start with a guess of X0 and iterate the following steps
  291. ;;; Xn+1 = Xn - P(Xn)/P'(Xn)
  292. (define (poly-newton-method p)
  293. (define (newton-improve x vp/err succeed fail)
  294. (let ((vp (car vp/err)) (dvp (cadr vp/err)))
  295. (if (< (magnitude dvp) minimum-denominator)
  296. (fail)
  297. (succeed (- x (/ vp dvp))))))
  298. newton-improve)
  299. ;;; Laguerre's method
  300. ;;; To find a root of P(x)
  301. ;;; Start with a guess of X0 and iterate the following steps
  302. ;;; Let G = P'(Xn)/P(Xn)
  303. ;;; Let H = G^2 - P''(Xn)/P(Xn)
  304. ;;; Xn+1 = Xn - n/(G +- sqrt((n-1)*(n*H-G^2)))
  305. (define (poly-laguerre-method p)
  306. (let ((n (poly:degree p)))
  307. (define (laguerre-improve x vp/err succeed fail)
  308. (let ((vp (car vp/err)) (vdp (cadr vp/err)) (vddp (caddr vp/err)))
  309. (if (< (magnitude vp) minimum-denominator)
  310. (fail)
  311. (let* ((g (/ vdp vp))
  312. (g2 (* g g))
  313. (d (sqrt (* (- n 1) (- (* n (- g2 (/ vddp vp))) g2))))
  314. (gplus (+ g d))
  315. (gminus (- g d))
  316. (denom (if (> (magnitude gplus) (magnitude gminus))
  317. gplus
  318. gminus)))
  319. (succeed (- x
  320. (if (< (magnitude denom) (* n minimum-denominator))
  321. (expt (magnitude (/ vp (leading-coefficient p)))
  322. (/ 1.0 n))
  323. (/ n denom))))))))
  324. laguerre-improve))
  325. #|
  326. ;;; Kahan's secant method -- this is not in the right form.
  327. ;;; But see the searcher.
  328. (define (kahan-method p)
  329. (let ((dp (derivative p)))
  330. (let ((psi (lambda (x vx) (/ vx (dp x)))))
  331. (lambda (xn vxn xn-1 vxn-1)
  332. (let ((f (psi xn vxn)))
  333. (- xn
  334. (* f
  335. (round (magnitude (/ (- xn xn-1)
  336. (- f (psi xn-1 vxn-1))))))))))))
  337. |#
  338. (define root-wallp false)
  339. (define minimum-magnitude 1e-10)
  340. (define obviousity-factor 1e-3)
  341. (define imaginary-part-tolerance 10.0) ;in machine-epsilons
  342. (define on-axis-tolerance 1000.0) ;in machine-epsilons
  343. (define rationalization-tolerance 100.0)
  344. (define max-scale 1.0e30)
  345. (define clustering #t) ;Turns on the clustering process
  346. (define cluster-tolerance 1.0e2)
  347. (define minimum-denominator 1e-100)
  348. (define *kahan-threshold* .01)
  349. (define root-searcher-method poly-laguerre-method)
  350. (define root-searcher-x0 .1+.1i) ;.1+1i ;cuthbert
  351. (define root-searcher-max-iter 100)
  352. (define root-searcher-max-shrink 10)
  353. (define root-searcher-jiggle .1)
  354. (define root-searcher-shrink-factor 4)
  355. (define root-searcher-value-to-noise 0.75)
  356. (define root-searcher-minimum-progress 1.0)
  357. (define root-polisher-method poly-newton-method)
  358. (define root-polisher-value-to-noise 0.75)
  359. (define root-polisher-minimum-progress 1.0)