2 ;; Copyright (C) 2000, 2001, 2003, 2008, 2009 Barton Willis
5 This is free software
; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
,
7 http
://www.gnu.org
/copyleft
/gpl.html.
9 This software has NO WARRANTY
, not even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 Maxima code for evaluating orthogonal polynomials listed in Chapter
22 of Abramowitz and Stegun
(A & S
).
17 ($put
'$orthopoly
1.0 '$version
)
19 ;; If the input can be evaluated to a floating point number (either
20 ;; real or complex), convert the input to a Common Lisp complex number.
21 ;; When the input doesn't evaluate to a float, return the input.
23 (defun maxima-to-lisp-complex-float (a)
24 (let* (($ratprint nil
)
26 (br ($float
($realpart b
)))
27 (bi ($float
($imagpart b
))))
28 (cond ((and (numberp br
) (numberp bi
))
29 (if (= bi
0) (float br
)
34 ;; Return true iff a is a float or a complex number with either a
35 ;; real or imaginary part that is a float.
39 (and (complexp a
) (or (floatp (realpart a
)) (floatp (imagpart a
))))))
41 ;; Convert a Lisp complex number into a Maxima complex number. When the
42 ;; input isn't a Lisp complex number, return the argument.
44 (defun lisp-complex-to-maxima (x)
45 (if (complexp x
) (add (realpart x
) (mul '$%i
(imagpart x
))) x
))
47 ;; When orthopoly_returns_intervals is true, floating point evaluation
48 ;; returns an interval using the form (($interval) c r), where c is the
49 ;; center of the interval and r is its radius. We don't provide the user
50 ;; with any tools for working with intervals; if a user wants
52 (defmvar $orthopoly_returns_intervals t
)
54 (defun orthopoly-return-handler (d f e
)
55 (cond ((or (floatp f
) (complexp f
))
56 (let ((df (maxima-to-lisp-complex-float d
))
58 (setq f
(lisp-complex-to-maxima
59 (if (numberp df
) (* df f
) (mul d f
))))
60 (setq ef
(if (and (numberp df
) (numberp e
)) (abs (* df e
)) nil
))
61 (cond ((and $orthopoly_returns_intervals
(floatp ef
))
62 `(($interval simp
) ,f
,ef
))
66 ;; When a user requests the derivative of an a function in this package
67 ;; with respect to the order or some other parameter, return a form
68 ;; ((unk) input from user). We "simplify" this form by printing an error.
70 (defprop unk simp-unk operators
)
71 (defun simp-unk (x y z
)
72 (declare (ignore y z
))
73 (merror "Maxima doesn't know the derivative of ~:M with respect the ~:M argument" (nth 2 x
) (nth 1 x
)))
75 ;; A left continuous unit step function; thus
77 ;; unit_step(x) = 0 for x <= 0 and 1 for x > 0.
79 ;; This function differs from (1 + signum(x))/2 which isn't left or right
82 (defprop $unit_step
"\\Theta" texword
)
84 (defun simp-unit-step (a y z
)
86 (setq y
(simpcheck (cadr a
) z
))
88 (cond ((or (eq s
'$nz
) (eq s
'$zero
) (eq s
'$neg
)) 0)
90 (t `(($unit_step simp
) ,y
)))))
92 ;; We barely support intervals; these functions aren't for user-level use.
93 ;; The function intervalp returns true iff its argument is an interval.
96 (and (consp a
) (consp (car a
)) (eq (caar a
) '$interval
)))
98 ;; Multiply x by a, where x isn't an interval and a might be an
99 ;; interval. When a isn't an interval or x is an interval, return
100 ;; x * a. The optional argument dx is an upper bound for the
101 ;; relative error in x.
103 (defun interval-mult (x a
&optional dx
)
104 (if (null dx
) (setq dx
0))
105 (cond ((and ($intervalp a
) (not ($intervalp x
)))
108 (if (or (floatp p
) (floatp q
))
109 (setq x
($float
($rectform x
))))
110 (setq p
($expand
(mult x p
)))
111 (setq q
($expand
(mult x
(add q
(simplify `((mabs) ,(mul p dx
)))))))
112 (setq q
(simplify `((mabs) ,q
)))
113 `(($interval
) ,p
,q
)))
116 ;; TeX a function with subscripts and superscripts. The string fn is the
117 ;; function name, the list sub holds the positions of the subscripts, the list
118 ;; sup holds the positions of the superscripts, and i is the position of the
119 ;; function argument. When b1 is true, the subscript is surrounded by parens;
120 ;; when b2 is true, the superscript is surrounded by parens. The lists sub and
121 ;; sup may be nil, but the function must have at least on argument.
123 (defun tex-sub-and-super-scripted-function (fn sub b1 sup b2 i x l r
)
125 (let ((lo) (hi) (s1) (s2))
126 (setq s1
(if b1
`("\\left(") nil
))
127 (setq s2
(if b1
`("\\right)") nil
))
129 (setq lo
(cons (nth i x
) lo
)))
130 (setq lo
(if lo
(tex-list (nreverse lo
) s1 s2
",") nil
))
131 (setq s1
(if b2
`("\\left(") nil
))
132 (setq s2
(if b2
`("\\right)") nil
))
134 (setq hi
(cons (nth i x
) hi
)))
135 (setq hi
(if hi
(tex-list (nreverse hi
) s1 s2
",") nil
))
137 (if lo
`("_{",@lo
"}") nil
)
138 (if hi
`("^{" ,@hi
"}") nil
)
139 `(,@(tex-list (nthcdr i x
) `("\\left(") `("\\right)") ","))
142 (defun dimension-sub-and-super-scripted-function (fn sub sup b2 k x
)
143 (let ((lo) (hi) (form))
145 (setq lo
(cons (nth i x
) lo
)))
146 (setq lo
(nreverse lo
))
148 (setq hi
(cons (nth i x
) hi
)))
149 (setq hi
(nreverse hi
))
151 (setq form
`((,fn simp array
) ,@lo
)))
153 (setq form
`((mexpt) ((,fn simp array
) ,@lo
) ((mprogn) ,@hi
))))
155 (setq form
`((mexpt) ((,fn simp array
) ,@lo
) ,@hi
))))
156 `((,form simp
) ,@(nthcdr k x
))))
159 ;; (1) each member of the list a evaluates to a floating
160 ;; point number (using $float) and
161 ;; (2) at least one member of a has a real or imaginary part that is a
162 ;; floating point number *or* a bigfloat.
163 ;; When we find an member of a that doesn't evaluate to a float,
164 ;; immediately bail out and return nil.
166 (defun use-float (&rest a
)
167 (let ((xr) (xi) (float-found nil
) (okay t
))
169 (setq x
($rectform x
))
170 (setq xr
($realpart x
))
171 (setq xi
($imagpart x
))
172 (setq float-found
(or float-found
(floatp xr
) (floatp xi
)
173 ($bfloatp xr
) ($bfloatp xi
)))
174 (setq okay
(and (floatp ($float xr
)) (floatp ($float xi
))))
175 (if (not okay
) (return)))
176 (and okay float-found
)))
178 ;; When n is a nonnegative integer, return 1F1(a,b,x); that is return
179 ;; sum(pochhammer(a,k) * x^k /pochhammer(b,k),k,0,n). Regardless of the
180 ;; value of n, when x = 0, immediately return 1.
182 ;; Any orthopoly function that calls hypergeo should check that n is an
183 ;; integer with n > -1; thus the summation code shouldn't get called for
184 ;; any orthopoly function. It wouldn't be terrible if it happened -- I
185 ;; think the summation form isn't useful and a user can get into trouble
186 ;; with things like subst(0,x,sum(x^k,k,0,n)).
188 (defun $hypergeo11
(a b x n
)
190 ((and (integerp n
) (> n -
1))
191 (cond ((and (like a
(- n
)) (use-float b x
))
193 (multiple-value-setq (f e
)
194 (hypergeo11-float (- n
) (maxima-to-lisp-complex-float b
)
195 (maxima-to-lisp-complex-float x
)))
198 (let ((sum 1) (cf 1))
200 (setq cf
(div (mul cf
(add i a
) x
) (mul (add i b
) (+ 1 i
))))
201 (setq sum
(add cf sum
)))))))
203 ; The following is replaced with simplifying code.
205 ; ((mtimes) ((mexpt) ((mfactorial) ,$genindex) -1)
206 ; (($pochhammer) ,a ,$genindex)
207 ; ((mexpt) (($pochhammer ) ,b ,$genindex) -1)
208 ; ((mexpt) ,x ,$genindex)) ,$genindex 0 ,n))))
209 (let ((index (gensumindex)))
212 (mul (inv (take '(mfactorial) index
))
213 (take '($pochhammer
) a index
)
214 (inv (take '($pochhammer
) b index
))
218 ;; return the F[2,1] hypergeometic sum from 0 to n. Use genindex as the
219 ;; sum index; genindex is defined in asum.lisp
221 (defun $hypergeo21
(a b c x n
)
223 ((and (integerp n
) (> n -
1))
224 (cond ((and (like a
(- n
)) (use-float b c x
))
226 (multiple-value-setq (f e
)
227 (hypergeo21-float (- n
)
228 (maxima-to-lisp-complex-float b
)
229 (maxima-to-lisp-complex-float c
)
230 (maxima-to-lisp-complex-float x
)))
233 (let ((sum 1) (cf 1))
235 (setq cf
(div (mul cf
(add i a
) (add i b
) x
) (mul (+ 1 i
)
237 (setq sum
(add cf sum
)))))))
240 ; The following is replaced with simplifying code.
242 ; ((mtimes) (($pochhammer) ,a ,$genindex) (($pochhammer) ,b ,$genindex)
243 ; ((mexpt) (($pochhammer) ,c ,$genindex) -1)
244 ; ((mexpt) ((mfactorial) ,$genindex) -1)
245 ; ((mexpt) ,x ,$genindex))
246 ; ,$genindex 0 ,n))))
247 (let ((index (gensumindex)))
250 (mul (take '($pochhammer
) a index
)
251 (take '($pochhammer
) b index
)
252 (inv (take '($pochhammer
) c index
))
253 (inv (take '(mfactorial) index
))
258 (:load-toplevel
:execute
)
259 (let (($context
'$global
) (context '$global
))
260 (meval '(($declare
) $pochhammer $complex
))))
262 (defmvar $pochhammer_max_index
100)
264 ;; This disallows noninteger assignments to $pochhammer_max_index.
266 (setf (get '$pochhammer_max_index
'assign
)
269 (if (not (and (atom b
) (integerp b
)))
271 (mtell "The value of `pochhammer_max_index' must be an integer.~%")
274 (defun $pochhammer
(x n
)
275 (take '($pochhammer
) x n
))
277 (in-package #:bigfloat
)
279 ;; Numerical evaluation of pochhammer using the bigfloat package.
280 (defun pochhammer (x n
)
282 (if (< n
0) (/ 1 (pochhammer (+ x n
) (- n
)))
284 (setq acc
(* acc
(+ k x
)))))))
288 (defun simp-pochhammer (e y z
)
290 (let ((x) (n) (return-a-rat))
292 (setq return-a-rat
($ratp
(second e
)))
293 (setq x
(simplifya (specrepcheck (second e
)) z
))
294 (setq n
(simplifya (specrepcheck (third e
)) z
))
296 (cond ((or ($taylorp
(second e
)) ($taylorp
(third e
)))
297 (setq x
(simplifya (second e
) z
))
298 (setq n
(simplifya (third e
) z
))
299 ($taylor
(div (take '(%gamma
) (add x n
)) (take '(%gamma
) x
))))
303 ;; pochhammer(1,n) = n! (factorial is faster than bigfloat::pochhammer.)
304 ((eql x
1) (take '(mfactorial) n
))
306 ;; pure numeric evaluation--use numeric package.
307 ((and (integerp n
) (complex-number-p x
'$numberp
))
308 (maxima::to
(bigfloat::pochhammer
(bigfloat::to x
) (bigfloat::to n
))))
310 ((and (integerp (mul 2 n
)) (integerp (mul 2 x
)) (> (mul 2 n
) 0) (> (mul 2 x
) 0))
311 (div (take '(%gamma
) (add x n
)) (take '(%gamma
) x
)))
313 ;; Use a reflection identity when (great (neg n) n) is true. Specifically,
314 ;; use pochhammer(x,-n) * pochhammer(x-n,n) = 1; thus pochhammer(x,n) = 1/pochhammer(x+n,-n).
316 (div 1 (take '($pochhammer
) (add x n
) (neg n
))))
318 ;; Expand when n is an integer and either abs(n) < $expop or abs(n) < $pochhammer_max_index.
319 ;; Let's give $expand a bit of early help.
320 ((and (integerp n
) (or (< (abs n
) $expop
) (< (abs n
) $pochhammer_max_index
)))
321 (if (< n
0) (div 1 (take '($pochhammer
) (add x n
) (neg n
)))
323 (if (or (< (abs n
) $expop
) return-a-rat
) (setq acc
($rat acc
) x
($rat x
)))
324 (dotimes (k n
(if return-a-rat acc
($ratdisrep acc
)))
325 (setq acc
(mul acc
(add k x
)))))))
327 ;; return a nounform.
328 (t `(($pochhammer simp
) ,x
,n
)))))
330 (putprop '$pochhammer
332 ((mtimes) (($pochhammer
) x n
)
333 ((mplus) ((mtimes) -
1 ((mqapply) (($psi array
) 0) x
))
334 ((mqapply) (($psi array
) 0) ((mplus) n x
))))
335 ((mtimes) (($pochhammer
) x n
)
336 ((mqapply) (($psi array
) 0) ((mplus) n x
))))
339 (defprop $pochhammer tex-pochhammer tex
)
341 (defun tex-pochhammer (x l r
)
342 (setq x
(mapcar #'(lambda (s) (tex s nil nil nil nil
)) (cdr x
)))
351 (setf (get '$pochhammer
'dimension
) 'dimension-pochhammer
)
353 (defun dimension-pochhammer (form result
)
354 (setq form
`(( ((mprogn) ,(nth 1 form
)) simp array
) ,(nth 2 form
)))
355 (dimension-array form result
))
357 ;; pochhammer-quotient(a b x n) returns pochhammer( a,n) / pochhammer(b,n).
358 ;; Only when one of a, b, or x is a floating point number and the others are
359 ;; numeric types (that is when (use-float a b x) evaluates to true) does this
360 ;; function differ from using (div ($pochhammer a n) ($pochhammer b n)).
361 ;; In the floating point case, we arrange the operations to reduce rounding
362 ;; errors and to reduce over and underflows.
364 (defun pochhammer-quotient (a b x n
)
366 (pochhammer-quotient b a x
(neg n
)))
367 ((and (integerp n
) (use-float a b x
))
369 (setq a
(maxima-to-lisp-complex-float a
))
370 (setq b
(maxima-to-lisp-complex-float b
))
371 (dotimes (i n
(lisp-float-to-maxima-float acc
))
372 (setq acc
(* acc
(/ (+ i a
) (+ i b
)))))))
373 (t (div ($pochhammer a n
) ($pochhammer b n
)))))
375 (defun use-hypergeo (n x
)
377 (or (and (integerp n
) (> n -
1))
378 ($featurep n
'$integer
)))
379 ; (and ($featurep n '$integer) ($ratnump x))))
381 ;; See A&S 22.5.42, page 779. For integer n, use the identity
382 ;; binomial(n+a,n) = pochhammer(a+1,n)/pochhammer(1,n)
384 ;; The condition number of the pochhammer function is
385 ;; |1 + x/(x+1) + x/(x+2) + ... + x/(x+n-1)| <= n.
386 ;; The relative floating point error in computing the pochhammer
387 ;; function is bounded by 3 n eps. Putting these errors together,
388 ;; the error in d is bounded 4 n |d| eps.
390 ;; We're looking at (d +|- 4 n eps |d|)(f +|- e) = d (f +|- (e + 4 n eps |f|)) +
393 (defun $jacobi_p
(n a b x
)
394 (cond ((use-hypergeo n x
)
396 ;(setq d (div ($pochhammer (add a 1) n) ($pochhammer 1 n)))
397 (setq d
(pochhammer-quotient (add a
1) 1 x n
))
398 (multiple-value-setq (f e
)
399 ($hypergeo21
(mul -
1 n
) (add n a b
1) (add a
1)
400 (div (add 1 (mul -
1 x
)) 2) n
))
401 (setq e
(if e
(+ e
(* 4 n
(abs f
) +flonum-epsilon
+)) nil
))
402 (orthopoly-return-handler d f e
)))
403 (t `(($jacobi_p simp
) ,n
,a
,b
,x
))))
407 ((unk) "$first" "$jacobi_p")
408 ((unk) "$second" "$jacobi_p")
409 ((unk) "$third" "$jacobi_p")
412 ((mexpt) ((mplus ) a b
((mtimes ) 2 n
)) -
1)
416 ((mplus) a n
) ((mplus) b n
)
417 (($jacobi_p
) ((mplus) -
1 n
) a b x
))
418 ((mtimes) n
(($jacobi_p
) n a b x
)
419 ((mplus) a
((mtimes ) -
1 b
)
421 ((mplus) ((mtimes) -
1 a
) ((mtimes ) -
1 b
)
422 ((mtimes) -
2 n
)) x
))))
423 ((mexpt) ((mplus) 1 ((mtimes) -
1 ((mexpt ) x
2))) -
1)))
426 (defprop $jacobi_p tex-jacobi-poly tex
)
428 (defun tex-jacobi-poly (x l r
)
429 (tex-sub-and-super-scripted-function "P" `(0) nil
`(1 2) t
3 x l r
))
431 (setf (get '$jacobi_p
'dimension
) 'dimension-jacobi-p
)
433 (defun dimension-jacobi-p (form result
)
435 (dimension-sub-and-super-scripted-function '|$p|
`(1) `(2 3) t
4 form
)
438 ;; See A&S 22.5.46, page 779.
440 (defun $ultraspherical
(n a x
)
441 (cond ((use-hypergeo n x
)
443 ;(setq d (div ($pochhammer (mul 2 a) n) ($pochhammer 1 n)))
444 (setq d
(pochhammer-quotient (mul 2 a
) 1 x n
))
445 (multiple-value-setq (f e
)
446 ($hypergeo21
(mul -
1 n
) (add n
(mul 2 a
)) (add a
(div 1 2))
447 (div (add 1 (mul -
1 x
)) 2) n
))
448 (setq e
(if e
(+ e
(* 4 n
(abs f
) +flonum-epsilon
+)) nil
))
449 (orthopoly-return-handler d f e
)))
450 (t `(($ultraspherical simp
) ,n
,a
,x
))))
452 (putprop '$ultraspherical
454 ((unk) "$first" "$ultraspherical")
455 ((unk) "$second" "$ultrapsherical")
460 ((mplus) -
1 ((mtimes) 2 a
) n
)
461 (($ultraspherical
) ((mplus) -
1 n
) a x
))
462 ((mtimes) -
1 n
(($ultraspherical
) n a x
) x
))
463 ((mexpt) ((mplus) 1 ((mtimes) -
1 ((mexpt) x
2))) -
1)))
466 (defprop $ultraspherical tex-ultraspherical tex
)
468 (defun tex-ultraspherical (x l r
)
469 (tex-sub-and-super-scripted-function "C" `(0) nil
`(1) t
2 x l r
))
471 (setf (get '$ultraspherical
'dimension
) 'dimension-ultraspherical
)
473 (defun dimension-ultraspherical (form result
)
475 (dimension-sub-and-super-scripted-function '|$c|
`(1) `(2) t
3 form
)
478 (defun $chebyshev_t
(n x
)
479 (cond ((use-hypergeo n x
)
481 (multiple-value-setq (f e
)
482 ($hypergeo21
(mul -
1 n
) n
(rat 1 2) (div (add 1 (mul -
1 x
)) 2) n
))
483 (orthopoly-return-handler 1 f e
)))
484 (t `(($chebyshev_t simp
) ,n
,x
))))
486 (putprop '$chebyshev_t
488 ((unk) "$first" "$chebyshev_t")
491 ((mtimes) n
(($chebyshev_t
) ((mplus ) -
1 n
) x
))
492 ((mtimes ) -
1 n
(($chebyshev_t
) n x
) x
))
493 ((mexpt) ((mplus ) 1 ((mtimes) -
1 ((mexpt) x
2))) -
1)))
496 (defprop $chebyshev_t tex-chebyshev-t tex
)
498 (defun tex-chebyshev-t (x l r
)
499 (tex-sub-and-super-scripted-function "T" `(0) nil nil nil
1 x l r
))
501 (setf (get '$chebyshev_t
'dimension
) 'dimension-chebyshev-t
)
503 (defun dimension-chebyshev-t (form result
)
505 (dimension-sub-and-super-scripted-function '|$t|
`(1) nil nil
2 form
)
508 ;; See A & S 22.5.48, page 779.
510 (defun $chebyshev_u
(n x
)
511 (cond ((use-hypergeo n x
)
514 (multiple-value-setq (f e
)
515 ($hypergeo21
(mul -
1 n
) (add 2 n
) (rat 3 2)
516 (div (add 1 (mul -
1 x
)) 2) n
))
517 (orthopoly-return-handler d f e
)))
518 (t `(($chebyshev_u simp
) ,n
,x
))))
520 (putprop '$chebyshev_u
522 ((unk) "$first" "$chebyshev_u")
527 ((mplus) 1 n
) (($chebyshev_u
) ((mplus) -
1 n
) x
))
528 ((mtimes) -
1 n
(($chebyshev_u
) n x
) x
))
529 ((mexpt) ((mplus ) 1 ((mtimes) -
1 ((mexpt) x
2))) -
1)))
532 (defprop $chebyshev_u tex-chebyshev-u tex
)
534 (defun tex-chebyshev-u (x l r
)
535 (tex-sub-and-super-scripted-function "U" `(0) nil nil nil
1 x l r
))
537 (setf (get '$chebyshev_u
'dimension
) 'dimension-chebyshev-u
)
539 (defun dimension-chebyshev-u (form result
)
541 (dimension-sub-and-super-scripted-function '|$u|
`(1) nil nil
2 form
)
544 ;; See A&S 8.2.1 page 333 and 22.5.35 page 779. We evaluate the legendre
545 ;; polynomials as jacobi_p(n,0,0,x). Eat less exercise more.
547 (defun $legendre_p
(n x
)
548 (cond ((use-hypergeo n x
)
549 (if (and (integerp n
) (< n
0))
550 ($legendre_p
(- (abs n
) 1) x
)
551 ($jacobi_p n
0 0 x
)))
552 (t `(($legendre_p simp
) ,n
,x
))))
554 (putprop '$legendre_p
556 ((unk) "$first" "$legendre_p")
559 ((mtimes) n
(($legendre_p
) ((mplus) -
1 n
) x
))
560 ((mtimes) -
1 n
(($legendre_p
) n x
) x
))
561 ((mexpt) ((mplus) 1 ((mtimes) -
1 ((mexpt) x
2))) -
1)))
564 (defprop $legendre_p tex-legendre-p tex
)
566 (defun tex-legendre-p (x l r
)
567 (tex-sub-and-super-scripted-function "P" `(0) nil nil nil
1 x l r
))
569 (setf (get '$legendre_p
'dimension
) 'dimension-legendre-p
)
571 (defun dimension-legendre-p (form result
)
573 (dimension-sub-and-super-scripted-function '|$p|
`(1) nil nil
2 form
)
576 (defun $legendre_q
(n x
)
577 (if (and (integerp n
) (> n -
1))
578 ($assoc_legendre_q n
0 x
)
579 `(($legendre_q simp
) ,n
,x
)))
581 (putprop '$legendre_q
583 ((unk) "$first" "$legendre_p")
585 ((mtimes) -
1 ((%kron_delta
) 0 n
)
586 ((mexpt) ((mplus) -
1 ((mexpt) x
2)) -
1))
589 ((mtimes) n
(($legendre_q
) ((mplus) -
1 n
) x
))
590 ((mtimes) -
1 n
(($legendre_q
) n x
) x
))
591 ((mexpt) ((mplus) 1 ((mtimes) -
1 ((mexpt) x
2))) -
1))))
594 (defprop $legendre_q tex-legendre-q tex
)
596 (defun tex-legendre-q (x l r
)
597 (tex-sub-and-super-scripted-function "Q" `(0) nil nil nil
1 x l r
))
599 (setf (get '$legendre_q
'dimension
) 'dimension-legendre-q
)
601 (defun dimension-legendre-q (form result
)
603 (dimension-sub-and-super-scripted-function '|$q|
`(1) nil nil
2 form
)
606 ;; See A & S 8.6.7 and 8.2.6 pages 333 and 334. I chose the
607 ;; definition that is real valued on (-1,1).
609 ;; For negative m, A & S 8.2.6 page 333 and G & R 8.706 page 1000
610 ;; disagree; the factor of exp(i m pi) in A & S 8.1.6 suggests to me that
611 ;; A & S 8.2.6 is bogus. As further evidence, Macsyma 2.4 seems to
612 ;; agree with G & R 8.706. I'll use G & R.
614 ;; Return assoc_legendre(0,m,x). This isn't a user-level function; we
615 ;; don't check that m is a positive integer.
619 (merror "function q0m called with negative order. File a bug report"))
621 (div (simplify `((%log
) ,(div (add 1 x
) (sub 1 x
)))) 2))
623 (mul (factorial (- m
1)) `((rat simp
) 1 2)
624 (if (oddp m
) -
1 1) (power (sub 1 (mult x x
)) (div m
2))
626 (mul (if (oddp m
) 1 -
1) (power (add 1 x
) (neg m
)))
627 (power (sub 1 x
) (neg m
)))))))
629 ;; Return assoc_legendre(1,m,x). This isn't a user-level function; we
630 ;; don't check that m is a positive integer; we don't check that m is
631 ;; a positive integer.
635 (merror "function q1m called with negative order. File a bug report"))
637 (sub (mul x
(q0m 0 x
)) 1))
639 (mul -
1 (power (sub 1 (mult x x
)) `((rat simp
) 1 2))
640 (sub (q0m 0 x
) (div x
(sub (mul x x
) 1)))))
642 (mul (if (oddp m
) -
1 1) (power (sub 1 (mult x x
)) (div m
2))
644 (mul (factorial (- m
2)) `((rat simp
) 1 2) (if (oddp m
) -
1 1)
645 (sub (power (add x
1) (sub 1 m
))
646 (power (sub x
1) (sub 1 m
))))
648 (mul (factorial (- m
1)) `((rat simp
) 1 2) (if (oddp m
) -
1 1)
649 (add (power (add x
1) (neg m
))
650 (power (sub x
1) (neg m
)))))))))
652 ;; Return assoc_legendre_q(n,n,x). I don't have a reference that gives
653 ;; a formula for assoc_legendre_q(n,n,x). To figure one out, I used
654 ;; A&S 8.2.1 and a formula for assoc_legendre_p(n,n,x).
656 ;; After finishing the while loop, q = int(1/(1-t^2)^(n+1),t,0,x).
658 (defun assoc-legendre-q-nn (n x
)
660 (z (sub 1 (mul x x
)))
662 (setq q
(div (simplify `((%log
) ,(div (add 1 x
) (sub 1 x
)))) 2))
664 (setq q
(add (mul (sub 1 `((rat) 1 ,(* 2 i
))) q
)
665 (div x
(mul 2 i
(power z i
)))))
667 (mul (expt -
2 n
) (factorial n
) (power z
(div n
2)) q
)))
669 ;; Use degree recursion to find the assoc_legendre_q function.
670 ;; See A&S 8.5.3. When i = m in the while loop, we have a special
671 ;; case. For negative order, see A&S 8.2.6.
673 (defun $assoc_legendre_q
(n m x
)
674 (cond ((and (integerp n
) (> n -
1) (integerp m
) (<= (abs m
) n
))
676 (mul (div (factorial (+ n m
)) (factorial (- n m
)))
677 ($assoc_legendre_q n
(- m
) x
)))
679 (if (not (or (floatp x
) ($bfloatp x
))) (setq x
($rat x
)))
680 (let* ((q0 (q0m m x
))
681 (q1 (if (= n
0) q0
(q1m m x
)))
683 (use-rat (or ($ratp x
) (floatp x
) ($bfloatp x
))))
686 (setq q
(if (= i m
) (assoc-legendre-q-nn i x
)
687 (div (sub (mul (- (* 2 i
) 1) x q1
)
688 (mul (+ i -
1 m
) q0
)) (- i m
))))
692 (if use-rat q1
($ratsimp q1
))))))
693 (t `(($assoc_legendre_q simp
) ,n
,m
,x
))))
695 ;; See G & R, 8.733, page 1005 first equation.
697 (putprop '$assoc_legendre_q
699 ((unk) "$first" "$assoc_legendre_q")
700 ((unk) "$second" "$assoc_legendre_q")
705 ((mtimes) -
1 ((mplus) 1 ((mtimes) -
1 m
) n
)
706 (($assoc_legendre_q
) ((mplus ) 1 n
) m x
))
707 ((mtimes) ((mplus) 1 n
)
708 (($assoc_legendre_q
) n m x
) x
))
709 ((mexpt) ((mplus) 1 ((mtimes) -
1 ((mexpt) x
2))) -
1))))
712 (defprop $assoc_legendre_q tex-assoc-legendre-q tex
)
714 (defun tex-assoc-legendre-q (x l r
)
715 (tex-sub-and-super-scripted-function "Q" `(0) nil
`(1) nil
2 x l r
))
717 (setf (get '$assoc_legendre_q
'dimension
) 'dimension-assoc-legendre-q
)
719 (defun dimension-assoc-legendre-q (form result
)
721 (dimension-sub-and-super-scripted-function '|$q|
`(1) `(2) nil
3 form
)
724 ;; See A & S 22.5.37 page 779, A & S 8.6.6 (second equation) page 334, and
725 ;; A & S 8.2.5 page 333. For n < 0, see A&S 8.2.1 page 333.
727 (defun $assoc_legendre_p
(n m x
)
728 (let ((f) (d) (dx 0))
729 (cond ((and (integerp n
) (integerp m
))
731 (setq f
($assoc_legendre_p
(- (abs n
) 1) m x
))
738 (setq f
($assoc_legendre_p n
(neg m
) x
))
739 ;; Adding a factor (-1)^m to the transformation to get the
740 ;; expected results for odd negative integers. DK 09/2009
741 (setq d
(mul (power -
1 m
)
742 (div (factorial (+ n m
)) (factorial (- n m
)))))
749 `((%genfact
) ,(- (* 2 m
) 1) ,(- m
1) 2)))
750 (setq d
(mul d
(if (oddp m
) -
1 1)))
751 (setq d
(mul d
(power (sub 1 (mul x x
)) (div m
2))))))
754 ($ultraspherical
(- n m
) (add m
(rat 1 2)) x
)))))
757 (setq f
`(($assoc_legendre_p simp
) ,n
,m
,x
))))
758 (interval-mult d f
(* +flonum-epsilon
+ dx
))))
761 ;; For the derivative of the associated legendre p function, see
762 ;; A & S 8.5.4 page 334.
764 (putprop `$assoc_legendre_p
766 ((unk) "$first" "$assoc_legendre_p")
767 ((unk) "$second" "$assoc_legendre_p")
770 ((mtimes simp
) -
1 ((mplus simp
) m n
) (($unit_step
) n
)
771 (($assoc_legendre_p simp
) ((mplus simp
) -
1 n
) m x
))
772 ((mtimes simp
) n
(($assoc_legendre_p simp
) n m x
) x
))
773 ((mexpt simp
) ((mplus simp
) -
1 ((mexpt simp
) x
2)) -
1)))
776 (defprop $assoc_legendre_p tex-assoc-legendre-p tex
)
778 (defun tex-assoc-legendre-p (x l r
)
779 (tex-sub-and-super-scripted-function "P" `(0) nil
`(1) nil
2 x l r
))
781 (setf (get '$assoc_legendre_p
'dimension
) 'dimension-assoc-legendre-p
)
783 (defun dimension-assoc-legendre-p (form result
)
785 (dimension-sub-and-super-scripted-function '|$p|
`(1) `(2) nil
3 form
)
788 ;; See A&S 22.5.55 and 22.5.56, page 780.
790 (defun $hermite
(n x
)
795 (multiple-value-setq (f e
)
796 ($hypergeo11
(- n
) (rat 1 2) (mul x x
) n
))
797 (setq d
(mul (if (oddp n
) -
1 1) (factorial (* 2 n
))
798 (div 1 (factorial n
)))))
800 (setq n
(/ (- n
1) 2))
801 (multiple-value-setq (f e
)
802 ($hypergeo11
(- n
) (rat 3 2) (mul x x
) n
))
803 (setq d
(mul (if (oddp n
) -
1 1) (factorial (+ 1 (* 2 n
))) 2 x
804 (div 1 (factorial n
))))))
805 (orthopoly-return-handler d f e
)))
806 (t `(($hermite
) ,n
,x
))))
810 ((unk) "$first" "$hermite")
811 ((mtimes) 2 n
(($hermite
) ((mplus) -
1 n
) x
)))
814 (defprop $hermite tex-hermite tex
)
816 (defun tex-hermite (x l r
)
817 (tex-sub-and-super-scripted-function "H" `(0) nil nil nil
1 x l r
))
819 (setf (get '$hermite
'dimension
) 'dimension-hermite
)
821 (defun dimension-hermite (form result
)
823 (dimension-sub-and-super-scripted-function '|$h|
`(1) nil nil
2 form
)
826 ;; See A & S 22.5.54, page 780. For integer n, use the identity
827 ;; binomial(n+a,n) = pochhammer(a+1,n)/pochhammer(1,n)
829 (defun $gen_laguerre
(n a x
)
830 (cond ((use-hypergeo n x
)
832 ;(setq d (div ($pochhammer (add a 1) n) ($pochhammer 1 n)))
833 (setq d
(pochhammer-quotient (add a
1) 1 x n
))
834 (multiple-value-setq (f e
)
835 ($hypergeo11
(mul -
1 n
) (add 1 a
) x n
))
836 (setq e
(if e
(+ e
(* 4 (abs f
) +flonum-epsilon
+ n
)) nil
))
837 (orthopoly-return-handler d f e
)))
839 `(($gen_laguerre
) ,n
,a
,x
))))
841 (putprop '$gen_laguerre
843 ((unk) "$first" "$gen_laguerre")
844 ((unk) "$second" "$gen_laguerre")
847 ((mtimes) -
1 ((mplus) a n
)
848 (($unit_step
) n
) (($gen_laguerre
) ((mplus) -
1 n
) a x
))
849 ((mtimes) n
(($gen_laguerre
) n a x
)))
853 (defprop $gen_laguerre tex-gen-laguerre tex
)
855 (defun tex-gen-laguerre (x l r
)
856 (tex-sub-and-super-scripted-function "L" `(0) nil
`(1) t
1 x l r
))
858 (setf (get '$gen_laguerre
'dimension
) 'dimension-gen-laguerre
)
860 (defun dimension-gen-laguerre (form result
)
862 (dimension-sub-and-super-scripted-function '|$l|
`(1) `(2) t
3 form
)
865 ;; See A & S 22.5.16, page 778.
867 (defun $laguerre
(n x
)
868 (cond ((use-hypergeo n x
)
870 (multiple-value-setq (f e
) ($hypergeo11
(mul -
1 n
) 1 x n
))
871 (orthopoly-return-handler 1 f e
)))
873 `(($laguerre
) ,n
,x
))))
877 ((unk) "$first" "$laguerre")
880 ((mtimes) -
1 n
(($laguerre
) ((mplus) -
1 n
) x
))
881 ((mtimes) n
(($laguerre
) n x
)))
885 (defprop $laguerre tex-laguerre tex
)
887 (defun tex-laguerre (x l r
)
888 (tex-sub-and-super-scripted-function "L" `(0) nil nil nil
1 x l r
))
890 (setf (get '$laguerre
'dimension
) 'dimension-laguerre
)
892 (defun dimension-laguerre (form result
)
894 (dimension-sub-and-super-scripted-function '|$l|
`(1) nil nil
2 form
)
897 (defun $spherical_hankel1
(n x
)
899 (cond ((and (integerp n
) (< n
0))
900 (setq d
(mul '$%i
(if (oddp n
) 1 -
1)))
901 (multiple-value-setq (f e
)
902 ($spherical_hankel1
(add -
1 (mul -
1 n
)) x
))
903 (orthopoly-return-handler d f e
))
905 (multiple-value-setq (f e
)
906 ($hypergeo11
(mul -
1 n
) (mul -
2 n
) (mul -
2 '$%i x
) n
))
907 (setq d
(mul '$%i
(if (= 0 n
) 1
908 (simplify `((%genfact
) ,(add (mul 2 n
) -
1)
909 ,(add n
(rat -
1 2)) 2)))
910 (power '$%e
(mul '$%i x
)) (div -
1 (power x
(add 1 n
)))))
911 (orthopoly-return-handler d f e
))
913 `(($spherical_hankel1
) ,n
,x
)))))
915 (putprop '$spherical_hankel1
917 ((unk) "$first" "$spherical_hankel1")
918 ((mplus simp
) (($spherical_hankel1
) ((mplus) -
1 n
) x
)
919 ((mtimes simp
) -
1 ((mplus) 1 n
)
920 (($spherical_hankel1
) n x
) ((mexpt) x -
1))))
923 (defprop $spherical_hankel1 tex-spherical-hankel-1 tex
)
925 (defun tex-spherical-hankel-1 (x l r
)
926 (tex-sub-and-super-scripted-function "h^{(1)}" `(0) nil nil nil
1 x l r
))
928 (setf (get '$spherical_hankel1
'dimension
) 'dimension-spherical-hankel-1
)
930 (defun dimension-spherical-hankel-1 (form result
)
931 (let ((form1 `((mexpt) (($\h simp array
) ,(nth 1 form
))
933 (dimension-function `((,form1 simp
) ,(nth 2 form
)) result
)))
935 ;; See A & S 10.1.36.
937 (defun $spherical_hankel2
(n x
)
939 (setq x
(mul x
(power '$%e
(mul '$%i
'$%pi
(add (mul 2 n
) 1)))))
941 (setq f
($spherical_hankel1 n x
))
942 (if (oddp n
) (interval-mult -
1 f
) f
)))
943 (t `(($spherical_hankel2
) ,n
,x
))))
945 (putprop '$spherical_hankel2
947 ((unk) "$first" "$spherical_hankel2")
948 ((mplus simp
) (($spherical_hankel2
) ((mplus) -
1 n
) x
)
949 ((mtimes simp
) -
1 ((mplus) 1 n
)
950 (($spherical_hankel2
) n x
) ((mexpt) x -
1))))
953 (defprop $spherical_hankel2 tex-spherical-hankel-2 tex
)
955 (defun tex-spherical-hankel-2 (x l r
)
956 (tex-sub-and-super-scripted-function "h^{(2)}" `(0) nil nil nil
1 x l r
))
958 (setf (get '$spherical_hankel2
'dimension
) 'dimension-spherical-hankel-2
)
960 (defun dimension-spherical-hankel-2 (form result
)
961 (let ((form1 `((mexpt) (($\h simp array
) ,(nth 1 form
)) (2))))
962 (dimension-function `((,form1 simp
) ,(nth 2 form
)) result
)))
964 ;;---------------------------------------------------------------------
965 ;; The spherical_bessel functions use the functions p-fun and q-fun.
966 ;; See A&S 10.1.8 and 10.1.9 page 437.
974 (setq w
(div (mul w
(div
975 (* -
1 (+ n m2
2) (+ n m2
1)
976 (- n m2
) (- n
(+ m2
1)))
977 (* 4 (+ m2
1) (+ m2
2))))
979 (setq s
(add s w
)))))
982 (let ((s (if (= 0 n
) 0 1))
983 (w 1) (m2) (x2 (mul x x
))
984 (n1 (floor (/ (- n
1) 2))))
985 (dotimes (m n1
(div (mul n
(+ n
1) s
) (mul 2 x
)))
987 (setq w
(div (mul w
(div
988 (* -
1 (+ n m2
3) (+ n m2
2)
989 (- n
(+ m2
1)) (- n
(+ m2
2)))
990 (* 4 (+ m2
3) (+ m2
2))))
992 (setq s
(add s w
)))))
995 ;; See A&S 10.1.8 page 437 and A&S 10.1.15 page 439. When the order
996 ;; is an integer and x is a float or a bigfloat, use the slatec code
997 ;; for numerical evaluation. Yes, we coerce bigfloats to floats and
1000 ;; For numerical evaluation, we do our own analytic continuation -- otherwise
1001 ;; we get factors exp(%i n %pi / 2) that should evaluate to 1,%i,-1,-%, but
1002 ;; numerically have "fuzz" in their values. The fuzz can cause the spherical
1003 ;; bessel functions to have nonzero (but small) imaginary values on the
1004 ;; negative real axis. See A&S 10.1.34
1006 (defun $spherical_bessel_j
(n x
)
1007 (cond ((and (eq '$zero
(csign ($ratdisrep x
)))
1008 (or (integerp n
) ($featurep n
'$integer
)))
1009 `((%kron_delta
) 0 ,n
))
1011 ((and (use-float x
) (integerp n
))
1012 (let ((d 1) (xr) (xi) (z))
1013 (setq x
($rectform
($float x
)))
1014 (setq xr
($realpart x
))
1015 (setq xi
($imagpart x
))
1016 (setq z
(complex xr xi
))
1018 (setq d
(if (oddp n
) -
1 1))
1021 (setq n
(+ 0.5 ($float n
)))
1022 (setq d
(* d
(sqrt (/ pi
(* 2 z
)))))
1023 (setq d
(lisp-float-to-maxima-float d
))
1024 ($expand
(mul ($rectform d
) ($bessel_j n x
)))))
1026 ((and (integerp n
) (> n -
1))
1027 (let ((xt (sub x
(div (mul n
'$%pi
) 2))))
1029 (mul (p-fun n x
) (simplify `((%sin
) ,xt
)))
1030 (mul (q-fun n x
) (simplify `((%cos
) ,xt
)))) x
)))
1033 (mul (if (oddp n
) -
1 1) ($spherical_bessel_y
(- (+ n
1)) x
)))
1036 `(($spherical_bessel_j
) ,n
,x
))))
1038 (putprop '$spherical_bessel_j
1040 ((unk) "$first" "$spherical_bessel_j")
1041 ((mtimes) ((mexpt) ((mplus) 1 ((mtimes) 2 n
)) -
1)
1043 ((mtimes) n
(($spherical_bessel_j
) ((mplus) -
1 n
) x
))
1044 ((mtimes) -
1 ((mplus) 1 n
)
1045 (($spherical_bessel_j
) ((mplus) 1 n
) x
)))))
1048 (defprop $spherical_bessel_j tex-spherical-bessel-j tex
)
1050 (defun tex-spherical-bessel-j (x l r
)
1051 (tex-sub-and-super-scripted-function "j^{(2)}" `(0) nil nil nil
1 x l r
))
1053 (setf (get '$spherical_bessel_j
'dimension
) 'dimension-spherical-bessel-j
)
1055 (defun dimension-spherical-bessel-j (form result
)
1056 (let ((form1 `(($\j simp array
) ,(nth 1 form
))))
1057 (dimension-function `((,form1 simp
) ,(nth 2 form
)) result
)))
1059 ;; For analytic continuation, see A&S 10.1.35.
1061 (defun $spherical_bessel_y
(n x
)
1062 (cond ((and (use-float x
) (integerp n
))
1063 (let ((d 1) (xr) (xi) (z))
1064 (setq x
($rectform
($float x
)))
1065 (setq xr
($realpart x
))
1066 (setq xi
($imagpart x
))
1067 (setq z
(complex xr xi
))
1069 (setq d
(if (oddp n
) 1 -
1))
1072 (setq n
(+ 0.5 ($float n
)))
1073 (setq d
(* d
(sqrt (/ pi
(* 2 z
)))))
1074 (setq d
(lisp-float-to-maxima-float d
))
1075 ($expand
(mul ($rectform d
) ($bessel_y n x
)))))
1077 ((and (integerp n
) (> n -
1))
1078 (let ((xt (add x
(div (mul n
'$%pi
) 2))))
1079 (mul (if (oddp n
) 1 -
1)
1081 (mul (p-fun n x
) (simplify `((%cos
) ,xt
)))
1082 (mul (q-fun n x
) (simplify `((%sin
) ,xt
)))) x
))))
1085 (mul (if (oddp n
) 1 -
1) ($spherical_bessel_j
(- (+ n
1)) x
)))
1086 (t `(($spherical_bessel_y
) ,n
,x
))))
1088 (putprop '$spherical_bessel_y
1090 ((unk) "$first" "$spherical_bessel_y")
1091 ((mtimes) ((mexpt) ((mplus) 1 ((mtimes) 2 n
)) -
1)
1093 ((mtimes) n
(($spherical_bessel_y
) ((mplus) -
1 n
) x
))
1094 ((mtimes) -
1 ((mplus) 1 n
)
1095 (($spherical_bessel_y
) ((mplus) 1 n
) x
)))))
1098 (defprop $spherical_bessel_y tex-spherical-bessel-y tex
)
1100 (defun tex-spherical-bessel-y (x l r
)
1101 (tex-sub-and-super-scripted-function "y^{(2)}" `(0) nil nil nil
1 x l r
))
1103 (setf (get '$spherical_bessel_y
'dimension
) 'dimension-spherical-bessel-y
)
1105 (defun dimension-spherical-bessel-y (form result
)
1106 (let ((form1 `(($\y simp array
) ,(nth 1 form
))))
1107 (dimension-function `((,form1 simp
) ,(nth 2 form
)) result
)))
1109 ;; Compute P_n^m(cos(theta)). See Merzbacher, 9.59 page 184
1110 ;; and 9.64 page 185, and A & S 22.5.37 page 779. This function
1111 ;; lacks error checking; it should only be called by spherical_harmonic.
1113 ;; We need to be careful -- for the spherical harmonics we can't use
1114 ;; assoc_legendre_p(n,m,cos(x)). If we did, we'd get factors
1115 ;; (1 - cos^2(x))^(m/2) that simplify to |sin(x)|^m but we want them
1116 ;; to simplify to sin^m(x). Oh my!
1118 (defun assoc-leg-cos (n m x
)
1120 (if (= m
0) 1 (mul (take '(%genfact
) (sub (mul 2 m
) 1) (sub m
(div 1 2)) 2) (power (take '(%sin
) x
) m
)))
1121 ($ultraspherical
(sub n m
) (add m
(div 1 2)) (take '(%cos
) x
))))
1123 (defun $spherical_harmonic
(n m th p
)
1124 (cond ((and (integerp n
) (integerp m
) (> n -
1))
1125 (cond ((> (abs m
) n
)
1128 (interval-mult (if (oddp m
) -
1 1)
1129 ($spherical_harmonic n
(- m
) th
(mul -
1 p
))))
1132 (mul ($exp
(mul '$%i m p
))
1133 (power (div (* (+ (* 2 n
) 1) (factorial (- n m
)))
1134 (mul '$%pi
(* 4 (factorial (+ n m
)))))
1136 (assoc-leg-cos n m th
)))))
1138 `(($spherical_harmonic
) ,n
,m
,th
,p
))))
1140 (defprop $spherical_harmonic tex-spherical-harmonic tex
)
1142 (defun tex-spherical-harmonic (x l r
)
1143 (tex-sub-and-super-scripted-function "Y" `(0) nil
`(1) nil
2 x l r
))
1145 (setf (get '$spherical_harmonic
'dimension
) 'dimension-spherical-harmonic
)
1147 (defun dimension-spherical-harmonic (form result
)
1149 (dimension-sub-and-super-scripted-function '|$y|
`(1) `(2) nil
3 form
)
1152 (putprop '$spherical_harmonic
1154 ((unk) "$first" "$spherical_harmonic")
1155 ((unk) "$second" "$spherical_harmonic")
1157 ((mtimes) ((rat ) -
1 2)
1159 ((mtimes) ((mplus) ((mtimes) -
1 m
) n
)
1162 (($spherical_harmonic
) n
((mplus) 1 m
) theta phi
)
1163 ((mexpt) $%e
((mtimes) -
1 $%i phi
)))
1164 ((mtimes) ((rat) 1 2)
1166 ((mtimes) ((mplus) 1 ((mtimes) -
1 m
) n
)
1169 (($spherical_harmonic
) n
((mplus) -
1 m
) theta phi
)
1170 ((mexpt) $%e
((mtimes) $%i phi
))))
1172 ((mtimes) $%i m
(($spherical_harmonic
) n m theta phi
)))
1175 (defun maxima-float-to-lisp-float (y)
1176 (let* ((x ($rectform y
))
1179 (cond ((or (floatp xr
) (floatp xi
))
1182 (if (= 0.0 xi
) xr
(complex xr xi
)))
1186 (defun lisp-float-to-maxima-float (x)
1188 (add (realpart x
) (mul '$%i
(imagpart x
)))
1191 (defun hypergeo11-float (n b x
)
1192 (let ((f0) (fm1) (f) (i 0) (k) (dk) (ak) (bk) (err)
1193 (as (make-array (- 1 n
)
1194 :initial-element
0.0))
1195 (bs (make-array (- 1 n
)
1196 :initial-element
0.0))
1197 (fs (make-array (- 1 n
)
1198 :initial-element
0.0))
1206 (setf (aref fs i
) f0
)
1208 (setq ak
(/ (+ (* 2 i
) x
) dk
))
1209 (setq bk
(/ (- i
) dk
))
1210 (setq f
(+ (* ak f0
) (* bk fm1
)))
1211 (setf (aref as i
) ak
)
1212 (setf (aref bs i
) bk
)
1216 (setf (aref fs i
) f0
)
1223 (setq u
(+ (* (aref as k
) u0
)
1224 (* (aref bs
(+ 1 k
)) um1
)))
1227 (setq err
(+ err
(abs (* u0
(aref fs k
)))))
1229 (values f0
(* 12 +flonum-epsilon
+ err
))))
1231 (defun hypergeo21-float (n b c x
)
1232 (let ((f0) (fm1) (f) (i 0) (k) (dk) (ak) (bk) (err)
1233 (as (make-array (- 1 n
)
1234 :initial-element
0.0))
1235 (bs (make-array (- 1 n
)
1236 :initial-element
0.0))
1237 (fs (make-array (- 1 n
)
1238 :initial-element
0.0))
1245 (setf (aref fs i
) f0
)
1247 (setq ak
(/ (+ (* 2 i
) c
(- (* x
(+ b i
)))) dk
))
1248 (setq bk
(/ (* i
(- x
1)) dk
))
1249 (setq f
(+ (* ak f0
) (* bk fm1
)))
1250 (setf (aref as i
) ak
)
1251 (setf (aref bs i
) bk
)
1255 (setf (aref fs i
) f0
)
1262 (setq u
(+ (* (aref as k
) u0
)
1263 (* (aref bs
(+ 1 k
)) um1
)))
1267 (setq err
(+ err
(abs (* (aref fs k
) u0
)))))
1268 (values f0
(* 12 +flonum-epsilon
+ err
))))
1270 ;; For recursion relations, see A & S 22.7 page 782.
1272 ;; legendre_p(n+1,x) = ((2*n+1)*legendre_p(n,x)*x-n*legendre_p(n-1,x))/(n+1)
1274 ;; jacobi_p(n+1,a,b,x) = (((2*n+a+b+1)*(a^2-b^2) +
1275 ;; x*pochhammer(2*n+a+b,3)) * jacobi_p(n,a,b,x) -
1276 ;; 2*(n+a)*(n+b)*(2*n+a+b+2)*jacobi_p(n-1,a,b,x))/(2*(n+1)*(n+a+b+1)*(2*n+a+b))
1278 ;; ultraspherical(n+1,a,x) = (2*(n+a)*x * ultraspherical(n,a,x) -
1279 ;; (n+2*a-1)*ultraspherical(n-1,a,x))/(n+1)
1281 ;; chebyshev_t(n+1,x) = 2*x*chebyshev_t(n,x) -chebyshev_t(n-1,x)
1283 ;; chebyshev_u(n+1,x) = 2*x*chebyshev_u(n,x) -chebyshev_u(n-1,x)
1285 ;; laguerre(n+1,x) = (((2*n+1) - x)*laguerre(n,x) -(n)*laguerre(n-1,x))/(n+1)
1287 ;; gen_laguerre(n+1,a,x) = (((2*n+a+1) - x)*gen_laguerre(n,a,x)
1288 ;; -(n+a)*gen_laguerre(n-1,a,x))/(n+1)
1290 ;; hermite(n+1,x) = 2*x*hermite(n,x) -2*n*hermite(n-1,x)
1292 ;; See G & R 8.733.2; A & S 22.7.11 might be wrong -- or maybe I need
1295 ;; (2*n+1)*x*assoc_legendre_p(n,m,x) = (n-m+1)*assoc_legendre_p(n+1,m,x)
1296 ;; + (n+m)*assoc_legendre_p(n-1,m,x)
1298 ;; For the half-integer bessel functions, See A & S 10.1.19
1300 ;; fn(n-1,x) + fn(n+1,x) = (2*n+1)*fn(n,x)/x;
1302 (defun check-arg-length (fn n m
)
1304 (merror "Function ~:M needs ~:M arguments, instead it received ~:M"
1307 (defun $orthopoly_recur
(fn arg
)
1308 (if (not ($listp arg
))
1309 (merror "The second argument to orthopoly_recur must be a list"))
1310 (cond ((eq fn
'$jacobi_p
)
1311 (check-arg-length fn
4 (- (length arg
) 1))
1312 (let ((n (nth 1 arg
))
1317 `((mequal) (($jacobi_p
) ((mplus) 1 ,n
) ,a
,b
,x
)
1318 ((mtimes) ((rat) 1 2) ((mexpt) ((mplus) 1 ,n
) -
1)
1319 ((mexpt) ((mplus) 1 ,a
,b
,n
) -
1)
1320 ((mexpt) ((mplus) ,a
,b
((mtimes) 2 ,n
)) -
1)
1322 ((mtimes) -
2 ((mplus) ,a
,n
) ((mplus) ,b
,n
)
1323 ((mplus) 2 ,a
,b
((mtimes) 2 ,n
))
1324 (($jacobi_p
) ((mplus) -
1 ,n
) ,a
,b
,x
))
1325 ((mtimes) (($jacobi_p
) ,n
,a
,b
,x
)
1328 ((mplus) ((mexpt) ,a
2)
1329 ((mtimes) -
1 ((mexpt) ,b
2)))
1330 ((mplus) 1 ,a
,b
((mtimes) 2 ,n
)))
1331 ((mtimes) ((mplus) ,a
,b
((mtimes) 2 ,n
))
1332 ((mplus) 1 ,a
,b
((mtimes) 2 ,n
))
1333 ((mplus) 2 ,a
,b
((mtimes) 2 ,n
)) ,x
)))))))))
1335 ((eq fn
'$ultraspherical
)
1336 (check-arg-length fn
3 (- (length arg
) 1))
1337 (let ((n (nth 1 arg
))
1341 `((mequal) (($ultraspherical
) ((mplus) 1 ,n
) ,a
,x
)
1342 ((mtimes) ((mexpt) ((mplus) 1 ,n
) -
1)
1344 ((mtimes) -
1 ((mplus) -
1 ((mtimes) 2 ,a
) ,n
)
1345 (($ultraspherical
) ((mplus) -
1 ,n
) ,a
,x
))
1346 ((mtimes) 2 ((mplus) ,a
,n
)
1347 (($ultraspherical
) ,n
,a
,x
) ,x
)))))))
1349 ((member fn
`($chebyshev_t $chebyshev_u
) :test
'eq
)
1350 (check-arg-length fn
2 (- (length arg
) 1))
1351 (let ((n (nth 1 arg
))
1354 `((mequal ) ((,fn
) ((mplus ) 1 ,n
) ,x
)
1356 ((mtimes ) -
1 ((,fn
) ((mplus ) -
1 ,n
) ,x
))
1357 ((mtimes ) 2 ((,fn
) ,n
,x
) ,x
))))))
1359 ((member fn
'($legendre_p $legendre_q
) :test
'eq
)
1360 (check-arg-length fn
2 (- (length arg
) 1))
1361 (let* ((n (nth 1 arg
))
1363 (z (if (eq fn
'$legendre_q
)
1364 `((mtimes) -
1 ((%kron_delta
) ,n
0)) 0)))
1366 `((mequal) ((,fn
) ((mplus) 1 ,n
) ,x
)
1368 ((mtimes) ((mexpt) ((mplus) 1 ,n
) -
1)
1370 ((mtimes) ((mtimes) -
1 ,n
)
1371 ((,fn
) ((mplus) -
1 ,n
) ,x
))
1372 ((mtimes) ((mplus) 1 ((mtimes) 2 ,n
))
1376 ((member fn
'($assoc_legendre_p $assoc_legendre_q
) :test
'eq
)
1377 (check-arg-length fn
3 (- (length arg
) 1))
1378 (let ((n (nth 1 arg
))
1382 `((mequal) ((,fn
) ((mplus) 1 ,n
) ,m
,x
)
1384 ((mexpt) ((mplus) 1 ((mtimes) -
1 ,m
) ,n
) -
1)
1387 ((mplus) ((mtimes) -
1 ,m
)
1389 ((,fn
) ((mplus) -
1 ,n
) ,m
,x
))
1390 ((mtimes) ((mplus) 1 ((mtimes) 2 ,n
))
1391 ((,fn
) ,n
,m
,x
) ,x
)))))))
1394 (check-arg-length fn
2 (- (length arg
) 1))
1395 (let ((n (nth 1 arg
))
1398 `((mequal ) (($laguerre
) ((mplus ) 1 ,n
) ,x
)
1399 ((mtimes ) ((mexpt ) ((mplus ) 1 ,n
) -
1)
1401 ((mtimes ) -
1 ,n
(($laguerre
) ((mplus ) -
1 ,n
) ,x
))
1402 ((mtimes ) (($laguerre
) ,n
,x
)
1403 ((mplus ) 1 ((mtimes ) 2 ,n
) ((mtimes ) -
1 ,x
)))))))))
1405 ((eq fn
'$gen_laguerre
)
1406 (check-arg-length fn
3 (- (length arg
) 1))
1407 (let ((n (nth 1 arg
))
1411 `((mequal) (($gen_laguerre
) ((mplus) 1 ,n
) ,a
,x
)
1412 ((mtimes) ((mexpt ) ((mplus) 1 ,n
) -
1)
1414 ((mtimes) -
1 ((mplus) ,a
,n
)
1415 (($gen_laguerre
) ((mplus) -
1 ,n
) ,a
,x
))
1416 ((mtimes) (($gen_laguerre
) ,n
,a
,x
)
1417 ((mplus) 1 ,a
((mtimes) 2 ,n
) ((mtimes ) -
1 ,x
)))))))))
1420 (check-arg-length fn
2 (- (length arg
) 1))
1421 (let ((n (nth 1 arg
))
1424 `((mequal) (($hermite
) ((mplus) 1 ,n
) ,x
)
1426 ((mtimes) -
2 ,n
(($hermite
) ((mplus) -
1 ,n
) ,x
))
1427 ((mtimes) 2 (($hermite
) ,n
,x
) ,x
))))))
1429 ((member fn
`($spherical_bessel_j $spherical_bessel_y
1430 $spherical_hankel1 $spherical_hankel2
)
1432 (check-arg-length fn
2 (- (length arg
) 1))
1433 (let ((n (nth 1 arg
))
1436 `((mequal) ((,fn
) ((mplus) 1 ,n
) ,x
)
1437 ((mplus) ((mtimes) -
1 ((,fn
) ((mplus) -
1 ,n
) ,x
))
1438 ((mtimes) ((,fn
) ,n
,x
) ((mexpt) ,x -
1))
1439 ((mtimes) 2 ,n
((,fn
) ,n
,x
) ((mexpt) ,x -
1)))))))
1441 (t (merror "A recursion relation for ~:M isn't known to Maxima" fn
))))
1443 ;; See A & S Table 22.2, page 774.
1445 (defun $orthopoly_weight
(fn arg
)
1446 (if (not ($listp arg
))
1447 (merror "The second argument to orthopoly_weight must be a list"))
1449 (if (not (or ($symbolp
(car (last arg
))) ($subvarp
(car (last arg
)))))
1450 (merror "The last element of the second argument to orthopoly_weight must
1451 be a symbol or a subscripted symbol, instead found ~:M" (car (last arg
))))
1453 (if (not (every #'(lambda (s)
1454 ($freeof
(car (last arg
)) s
)) (butlast (cdr arg
))))
1455 (merror "Only the last element of ~:M may depend on the integration
1456 variable ~:M" arg
(car (last arg
))))
1458 (cond ((eq fn
'$jacobi_p
)
1459 (check-arg-length fn
4 (- (length arg
) 1))
1460 (let ((a (nth 2 arg
))
1465 ((mtimes) ((mexpt) ((mplus) 1 ((mtimes) -
1 ,x
)) ,a
)
1466 ((mexpt) ((mplus ) 1 ,x
) ,b
))
1469 ((eq fn
'$ultraspherical
)
1470 (check-arg-length fn
3 (- (length arg
) 1))
1471 (let ((a (nth 2 arg
))
1475 ((mexpt) ((mplus) 1 ((mtimes) -
1 ((mexpt) ,x
2)))
1476 ((mplus) ((rat) -
1 2) ,a
)) -
1 1))))
1478 ((eq fn
'$chebyshev_t
)
1479 (check-arg-length fn
2 (- (length arg
) 1))
1480 (let ((x (nth 2 arg
)))
1483 ((mexpt) ((mplus) 1 ((mtimes) -
1 ((mexpt) ,x
2)))
1484 ((rat) -
1 2)) -
1 1))))
1486 ((eq fn
'$chebyshev_u
)
1487 (check-arg-length fn
2 (- (length arg
) 1))
1488 (let ((x (nth 2 arg
)))
1491 ((mexpt) ((mplus) 1 ((mtimes) -
1 ((mexpt) ,x
2)))
1492 ((rat) 1 2)) -
1 1))))
1494 ((eq fn
'$legendre_p
)
1495 (check-arg-length fn
2 (- (length arg
) 1))
1498 ; This is for a fixed order. There is also an orthogonality
1499 ; condition for fixed degree with weight function 1/(1-x^2).
1500 ; See A & S 8.14.11 and 8.14.12.
1501 ((eq fn
'$assoc_legendre_p
)
1502 (check-arg-length fn
3 (- (length arg
) 1))
1506 (check-arg-length fn
2 (- (length arg
) 1))
1507 (let ((x (nth 2 arg
)))
1509 `((mlist) ((mexpt) $%e
((mtimes) -
1 ,x
)) 0 $inf
))))
1511 ((eq fn
'$gen_laguerre
)
1512 (check-arg-length fn
3 (- (length arg
) 1))
1513 (let ((a (nth 2 arg
))
1517 ((mtimes) ((mexpt) ,x
,a
)
1518 ((mexpt) $%e
((mtimes) -
1 ,x
))) 0 $inf
))))
1521 (check-arg-length fn
2 (- (length arg
) 1))
1522 (let ((x (nth 2 arg
)))
1524 `((mlist) ((mexpt) $%e
((mtimes) -
1 ((mexpt) ,x
2)))
1525 ((mtimes ) -
1 $inf
) $inf
))))
1527 (t (merror "A weight for ~:M isn't known to Maxima" fn
))))