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
) (("") ,@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
))
259 #-gcl
(:load-toplevel
:execute
)
260 (let (($context
'$global
) (context '$global
))
261 (meval '(($declare
) $pochhammer $complex
))))
263 (defmvar $pochhammer_max_index
100)
265 ;; This disallows noninteger assignments to $pochhammer_max_index.
267 (setf (get '$pochhammer_max_index
'assign
)
270 (if (not (and (atom b
) (integerp b
)))
272 (mtell "The value of `pochhammer_max_index' must be an integer.~%")
275 (defun $pochhammer
(x n
)
276 (take '($pochhammer
) x n
))
278 (in-package #-gcl
#:bigfloat
#+gcl
"BIGFLOAT")
280 ;; Numerical evaluation of pochhammer using the bigfloat package.
281 (defun pochhammer (x n
)
283 (if (< n
0) (/ 1 (pochhammer (+ x n
) (- n
)))
285 (setq acc
(* acc
(+ k x
)))))))
289 (defun simp-pochhammer (e y z
)
291 (let ((x) (n) (return-a-rat))
293 (setq return-a-rat
($ratp
(second e
)))
294 (setq x
(simplifya (specrepcheck (second e
)) z
))
295 (setq n
(simplifya (specrepcheck (third e
)) z
))
297 (cond ((or ($taylorp
(second e
)) ($taylorp
(third e
)))
298 (setq x
(simplifya (second e
) z
))
299 (setq n
(simplifya (third e
) z
))
300 ($taylor
(div (take '(%gamma
) (add x n
)) (take '(%gamma
) x
))))
304 ;; pochhammer(1,n) = n! (factorial is faster than bigfloat::pochhammer.)
305 ((eql x
1) (take '(mfactorial) n
))
307 ;; pure numeric evaluation--use numeric package.
308 ((and (integerp n
) (complex-number-p x
'$numberp
))
309 (maxima::to
(bigfloat::pochhammer
(bigfloat::to x
) (bigfloat::to n
))))
311 ((and (integerp (mul 2 n
)) (integerp (mul 2 x
)) (> (mul 2 n
) 0) (> (mul 2 x
) 0))
312 (div (take '(%gamma
) (add x n
)) (take '(%gamma
) x
)))
314 ;; Use a reflection identity when (great (neg n) n) is true. Specifically,
315 ;; use pochhammer(x,-n) * pochhammer(x-n,n) = 1; thus pochhammer(x,n) = 1/pochhammer(x+n,-n).
317 (div 1 (take '($pochhammer
) (add x n
) (neg n
))))
319 ;; Expand when n is an integer and either abs(n) < $expop or abs(n) < $pochhammer_max_index.
320 ;; Let's give $expand a bit of early help.
321 ((and (integerp n
) (or (< (abs n
) $expop
) (< (abs n
) $pochhammer_max_index
)))
322 (if (< n
0) (div 1 (take '($pochhammer
) (add x n
) (neg n
)))
324 (if (or (< (abs n
) $expop
) return-a-rat
) (setq acc
($rat acc
) x
($rat x
)))
325 (dotimes (k n
(if return-a-rat acc
($ratdisrep acc
)))
326 (setq acc
(mul acc
(add k x
)))))))
328 ;; return a nounform.
329 (t `(($pochhammer simp
) ,x
,n
)))))
331 (putprop '$pochhammer
333 ((mtimes) (($pochhammer
) x n
)
334 ((mplus) ((mtimes) -
1 ((mqapply) (($psi array
) 0) x
))
335 ((mqapply) (($psi array
) 0) ((mplus) n x
))))
336 ((mtimes) (($pochhammer
) x n
)
337 ((mqapply) (($psi array
) 0) ((mplus) n x
))))
340 (defprop $pochhammer tex-pochhammer tex
)
342 (defun tex-pochhammer (x l r
)
343 (setq x
(mapcar #'(lambda (s) (tex s nil nil nil nil
)) (cdr x
)))
352 (setf (get '$pochhammer
'dimension
) 'dimension-pochhammer
)
354 (defun dimension-pochhammer (form result
)
355 (setq form
`(( (("") ,(nth 1 form
)) simp array
) ,(nth 2 form
)))
356 (dimension-array form result
))
358 ;; pochhammer-quotient(a b x n) returns pochhammer( a,n) / pochhammer(b,n).
359 ;; Only when one of a, b, or x is a floating point number and the others are
360 ;; numeric types (that is when (use-float a b x) evaluates to true) does this
361 ;; function differ from using (div ($pochhammer a n) ($pochhammer b n)).
362 ;; In the floating point case, we arrange the operations to reduce rounding
363 ;; errors and to reduce over and underflows.
365 (defun pochhammer-quotient (a b x n
)
367 (pochhammer-quotient b a x
(neg n
)))
368 ((and (integerp n
) (use-float a b x
))
370 (setq a
(maxima-to-lisp-complex-float a
))
371 (setq b
(maxima-to-lisp-complex-float b
))
372 (dotimes (i n
(lisp-float-to-maxima-float acc
))
373 (setq acc
(* acc
(/ (+ i a
) (+ i b
)))))))
374 (t (div ($pochhammer a n
) ($pochhammer b n
)))))
376 (defun use-hypergeo (n x
)
378 (or (and (integerp n
) (> n -
1))
379 ($featurep n
'$integer
)))
380 ; (and ($featurep n '$integer) ($ratnump x))))
382 ;; See A&S 22.5.42, page 779. For integer n, use the identity
383 ;; binomial(n+a,n) = pochhammer(a+1,n)/pochhammer(1,n)
385 ;; The condition number of the pochhammer function is
386 ;; |1 + x/(x+1) + x/(x+2) + ... + x/(x+n-1)| <= n.
387 ;; The relative floating point error in computing the pochhammer
388 ;; function is bounded by 3 n eps. Putting these errors together,
389 ;; the error in d is bounded 4 n |d| eps.
391 ;; We're looking at (d +|- 4 n eps |d|)(f +|- e) = d (f +|- (e + 4 n eps |f|)) +
394 (defun $jacobi_p
(n a b x
)
395 (cond ((use-hypergeo n x
)
397 ;(setq d (div ($pochhammer (add a 1) n) ($pochhammer 1 n)))
398 (setq d
(pochhammer-quotient (add a
1) 1 x n
))
399 (multiple-value-setq (f e
)
400 ($hypergeo21
(mul -
1 n
) (add n a b
1) (add a
1)
401 (div (add 1 (mul -
1 x
)) 2) n
))
402 (setq e
(if e
(+ e
(* 4 n
(abs f
) flonum-epsilon
)) nil
))
403 (orthopoly-return-handler d f e
)))
404 (t `(($jacobi_p simp
) ,n
,a
,b
,x
))))
408 ((unk) "$first" "$jacobi_p")
409 ((unk) "$second" "$jacobi_p")
410 ((unk) "$third" "$jacobi_p")
413 ((mexpt) ((mplus ) a b
((mtimes ) 2 n
)) -
1)
417 ((mplus) a n
) ((mplus) b n
)
418 (($jacobi_p
) ((mplus) -
1 n
) a b x
))
419 ((mtimes) n
(($jacobi_p
) n a b x
)
420 ((mplus) a
((mtimes ) -
1 b
)
422 ((mplus) ((mtimes) -
1 a
) ((mtimes ) -
1 b
)
423 ((mtimes) -
2 n
)) x
))))
424 ((mexpt) ((mplus) 1 ((mtimes) -
1 ((mexpt ) x
2))) -
1)))
427 (defprop $jacobi_p tex-jacobi-poly tex
)
429 (defun tex-jacobi-poly (x l r
)
430 (tex-sub-and-super-scripted-function "P" `(0) nil
`(1 2) t
3 x l r
))
432 (setf (get '$jacobi_p
'dimension
) 'dimension-jacobi-p
)
434 (defun dimension-jacobi-p (form result
)
436 (dimension-sub-and-super-scripted-function "P" `(1) `(2 3) t
4 form
)
439 ;; See A&S 22.5.46, page 779.
441 (defun $ultraspherical
(n a x
)
442 (cond ((use-hypergeo n x
)
444 ;(setq d (div ($pochhammer (mul 2 a) n) ($pochhammer 1 n)))
445 (setq d
(pochhammer-quotient (mul 2 a
) 1 x n
))
446 (multiple-value-setq (f e
)
447 ($hypergeo21
(mul -
1 n
) (add n
(mul 2 a
)) (add a
(div 1 2))
448 (div (add 1 (mul -
1 x
)) 2) n
))
449 (setq e
(if e
(+ e
(* 4 n
(abs f
) flonum-epsilon
)) nil
))
450 (orthopoly-return-handler d f e
)))
451 (t `(($ultraspherical simp
) ,n
,a
,x
))))
453 (putprop '$ultraspherical
455 ((unk) "$first" "$ultraspherical")
456 ((unk) "$second" "$ultrapsherical")
461 ((mplus) -
1 ((mtimes) 2 a
) n
)
462 (($ultraspherical
) ((mplus) -
1 n
) a x
))
463 ((mtimes) -
1 n
(($ultraspherical
) n a x
) x
))
464 ((mexpt) ((mplus) 1 ((mtimes) -
1 ((mexpt) x
2))) -
1)))
467 (defprop $ultraspherical tex-ultraspherical tex
)
469 (defun tex-ultraspherical (x l r
)
470 (tex-sub-and-super-scripted-function "C" `(0) nil
`(1) t
2 x l r
))
472 (setf (get '$ultraspherical
'dimension
) 'dimension-ultraspherical
)
474 (defun dimension-ultraspherical (form result
)
476 (dimension-sub-and-super-scripted-function "C" `(1) `(2) t
3 form
)
479 (defun $chebyshev_t
(n x
)
480 (cond ((use-hypergeo n x
)
482 (multiple-value-setq (f e
)
483 ($hypergeo21
(mul -
1 n
) n
(rat 1 2) (div (add 1 (mul -
1 x
)) 2) n
))
484 (orthopoly-return-handler 1 f e
)))
485 (t `(($chebyshev_t simp
) ,n
,x
))))
487 (putprop '$chebyshev_t
489 ((unk) "$first" "$chebyshev_t")
492 ((mtimes) n
(($chebyshev_t
) ((mplus ) -
1 n
) x
))
493 ((mtimes ) -
1 n
(($chebyshev_t
) n x
) x
))
494 ((mexpt) ((mplus ) 1 ((mtimes) -
1 ((mexpt) x
2))) -
1)))
497 (defprop $chebyshev_t tex-chebyshev-t tex
)
499 (defun tex-chebyshev-t (x l r
)
500 (tex-sub-and-super-scripted-function "T" `(0) nil nil nil
1 x l r
))
502 (setf (get '$chebyshev_t
'dimension
) 'dimension-chebyshev-t
)
504 (defun dimension-chebyshev-t (form result
)
506 (dimension-sub-and-super-scripted-function "T" `(1) nil nil
2 form
)
509 ;; See A & S 22.5.48, page 779.
511 (defun $chebyshev_u
(n x
)
512 (cond ((use-hypergeo n x
)
515 (multiple-value-setq (f e
)
516 ($hypergeo21
(mul -
1 n
) (add 2 n
) (rat 3 2)
517 (div (add 1 (mul -
1 x
)) 2) n
))
518 (orthopoly-return-handler d f e
)))
519 (t `(($chebyshev_u simp
) ,n
,x
))))
521 (putprop '$chebyshev_u
523 ((unk) "$first" "$chebyshev_u")
528 ((mplus) 1 n
) (($chebyshev_u
) ((mplus) -
1 n
) x
))
529 ((mtimes) -
1 n
(($chebyshev_u
) n x
) x
))
530 ((mexpt) ((mplus ) 1 ((mtimes) -
1 ((mexpt) x
2))) -
1)))
533 (defprop $chebyshev_u tex-chebyshev-u tex
)
535 (defun tex-chebyshev-u (x l r
)
536 (tex-sub-and-super-scripted-function "U" `(0) nil nil nil
1 x l r
))
538 (setf (get '$chebyshev_u
'dimension
) 'dimension-chebyshev-u
)
540 (defun dimension-chebyshev-u (form result
)
542 (dimension-sub-and-super-scripted-function "U" `(1) nil nil
2 form
)
545 ;; See A&S 8.2.1 page 333 and 22.5.35 page 779. We evaluate the legendre
546 ;; polynomials as jacobi_p(n,0,0,x). Eat less exercise more.
548 (defun $legendre_p
(n x
)
549 (cond ((use-hypergeo n x
)
550 (if (and (integerp n
) (< n
0))
551 ($legendre_p
(- (abs n
) 1) x
)
552 ($jacobi_p n
0 0 x
)))
553 (t `(($legendre_p simp
) ,n
,x
))))
555 (putprop '$legendre_p
557 ((unk) "$first" "$legendre_p")
560 ((mtimes) n
(($legendre_p
) ((mplus) -
1 n
) x
))
561 ((mtimes) -
1 n
(($legendre_p
) n x
) x
))
562 ((mexpt) ((mplus) 1 ((mtimes) -
1 ((mexpt) x
2))) -
1)))
565 (defprop $legendre_p tex-legendre-p tex
)
567 (defun tex-legendre-p (x l r
)
568 (tex-sub-and-super-scripted-function "P" `(0) nil nil nil
1 x l r
))
570 (setf (get '$legendre_p
'dimension
) 'dimension-legendre-p
)
572 (defun dimension-legendre-p (form result
)
574 (dimension-sub-and-super-scripted-function "P" `(1) nil nil
2 form
)
577 (defun $legendre_q
(n x
)
578 (if (and (integerp n
) (> n -
1))
579 ($assoc_legendre_q n
0 x
)
580 `(($legendre_q simp
) ,n
,x
)))
582 (putprop '$legendre_q
584 ((unk) "$first" "$legendre_p")
586 ((mtimes) -
1 ((%kron_delta
) 0 n
)
587 ((mexpt) ((mplus) -
1 ((mexpt) x
2)) -
1))
590 ((mtimes) n
(($legendre_q
) ((mplus) -
1 n
) x
))
591 ((mtimes) -
1 n
(($legendre_q
) n x
) x
))
592 ((mexpt) ((mplus) 1 ((mtimes) -
1 ((mexpt) x
2))) -
1))))
595 (defprop $legendre_q tex-legendre-q tex
)
597 (defun tex-legendre-q (x l r
)
598 (tex-sub-and-super-scripted-function "Q" `(0) nil nil nil
1 x l r
))
600 (setf (get '$legendre_q
'dimension
) 'dimension-legendre-q
)
602 (defun dimension-legendre-q (form result
)
604 (dimension-sub-and-super-scripted-function "Q" `(1) nil nil
2 form
)
607 ;; See A & S 8.6.7 and 8.2.6 pages 333 and 334. I chose the
608 ;; definition that is real valued on (-1,1).
610 ;; For negative m, A & S 8.2.6 page 333 and G & R 8.706 page 1000
611 ;; disagree; the factor of exp(i m pi) in A & S 8.1.6 suggests to me that
612 ;; A & S 8.2.6 is bogus. As further evidence, Macsyma 2.4 seems to
613 ;; agree with G & R 8.706. I'll use G & R.
615 ;; Return assoc_legendre(0,m,x). This isn't a user-level function; we
616 ;; don't check that m is a positive integer.
620 (merror "function q0m called with negative order. File a bug report"))
622 (div (simplify `((%log
) ,(div (add 1 x
) (sub 1 x
)))) 2))
624 (mul (factorial (- m
1)) `((rat simp
) 1 2)
625 (if (oddp m
) -
1 1) (power (sub 1 (mult x x
)) (div m
2))
627 (mul (if (oddp m
) 1 -
1) (power (add 1 x
) (neg m
)))
628 (power (sub 1 x
) (neg m
)))))))
630 ;; Return assoc_legendre(1,m,x). This isn't a user-level function; we
631 ;; don't check that m is a positive integer; we don't check that m is
632 ;; a positive integer.
636 (merror "function q1m called with negative order. File a bug report"))
638 (sub (mul x
(q0m 0 x
)) 1))
640 (mul -
1 (power (sub 1 (mult x x
)) `((rat simp
) 1 2))
641 (sub (q0m 0 x
) (div x
(sub (mul x x
) 1)))))
643 (mul (if (oddp m
) -
1 1) (power (sub 1 (mult x x
)) (div m
2))
645 (mul (factorial (- m
2)) `((rat simp
) 1 2) (if (oddp m
) -
1 1)
646 (sub (power (add x
1) (sub 1 m
))
647 (power (sub x
1) (sub 1 m
))))
649 (mul (factorial (- m
1)) `((rat simp
) 1 2) (if (oddp m
) -
1 1)
650 (add (power (add x
1) (neg m
))
651 (power (sub x
1) (neg m
)))))))))
653 ;; Return assoc_legendre_q(n,n,x). I don't have a reference that gives
654 ;; a formula for assoc_legendre_q(n,n,x). To figure one out, I used
655 ;; A&S 8.2.1 and a formula for assoc_legendre_p(n,n,x).
657 ;; After finishing the while loop, q = int(1/(1-t^2)^(n+1),t,0,x).
659 (defun assoc-legendre-q-nn (n x
)
661 (z (sub 1 (mul x x
)))
663 (setq q
(div (simplify `((%log
) ,(div (add 1 x
) (sub 1 x
)))) 2))
665 (setq q
(add (mul (sub 1 `((rat) 1 ,(* 2 i
))) q
)
666 (div x
(mul 2 i
(power z i
)))))
668 (mul (expt -
2 n
) (factorial n
) (power z
(div n
2)) q
)))
670 ;; Use degree recursion to find the assoc_legendre_q function.
671 ;; See A&S 8.5.3. When i = m in the while loop, we have a special
672 ;; case. For negative order, see A&S 8.2.6.
674 (defun $assoc_legendre_q
(n m x
)
675 (cond ((and (integerp n
) (> n -
1) (integerp m
) (<= (abs m
) n
))
677 (mul (div (factorial (+ n m
)) (factorial (- n m
)))
678 ($assoc_legendre_q n
(- m
) x
)))
680 (if (not (or (floatp x
) ($bfloatp x
))) (setq x
($rat x
)))
681 (let* ((q0 (q0m m x
))
682 (q1 (if (= n
0) q0
(q1m m x
)))
684 (use-rat (or ($ratp x
) (floatp x
) ($bfloatp x
))))
687 (setq q
(if (= i m
) (assoc-legendre-q-nn i x
)
688 (div (sub (mul (- (* 2 i
) 1) x q1
)
689 (mul (+ i -
1 m
) q0
)) (- i m
))))
693 (if use-rat q1
($ratsimp q1
))))))
694 (t `(($assoc_legendre_q simp
) ,n
,m
,x
))))
696 ;; See G & R, 8.733, page 1005 first equation.
698 (putprop '$assoc_legendre_q
700 ((unk) "$first" "$assoc_legendre_q")
701 ((unk) "$second" "$assoc_legendre_q")
706 ((mtimes) -
1 ((mplus) 1 ((mtimes) -
1 m
) n
)
707 (($assoc_legendre_q
) ((mplus ) 1 n
) m x
))
708 ((mtimes) ((mplus) 1 n
)
709 (($assoc_legendre_q
) n m x
) x
))
710 ((mexpt) ((mplus) 1 ((mtimes) -
1 ((mexpt) x
2))) -
1))))
713 (defprop $assoc_legendre_q tex-assoc-legendre-q tex
)
715 (defun tex-assoc-legendre-q (x l r
)
716 (tex-sub-and-super-scripted-function "Q" `(0) nil
`(1) nil
2 x l r
))
718 (setf (get '$assoc_legendre_q
'dimension
) 'dimension-assoc-legendre-q
)
720 (defun dimension-assoc-legendre-q (form result
)
722 (dimension-sub-and-super-scripted-function "Q" `(1) `(2) nil
3 form
)
725 ;; See A & S 22.5.37 page 779, A & S 8.6.6 (second equation) page 334, and
726 ;; A & S 8.2.5 page 333. For n < 0, see A&S 8.2.1 page 333.
728 (defun $assoc_legendre_p
(n m x
)
729 (let ((f) (d) (dx 0))
730 (cond ((and (integerp n
) (integerp m
))
732 (setq f
($assoc_legendre_p
(- (abs n
) 1) m x
))
739 (setq f
($assoc_legendre_p n
(neg m
) x
))
740 ;; Adding a factor (-1)^m to the transformation to get the
741 ;; expected results for odd negative integers. DK 09/2009
742 (setq d
(mul (power -
1 m
)
743 (div (factorial (+ n m
)) (factorial (- n m
)))))
750 `((%genfact
) ,(- (* 2 m
) 1) ,(- m
1) 2)))
751 (setq d
(mul d
(if (oddp m
) -
1 1)))
752 (setq d
(mul d
(power (sub 1 (mul x x
)) (div m
2))))))
755 ($ultraspherical
(- n m
) (add m
(rat 1 2)) x
)))))
758 (setq f
`(($assoc_legendre_p simp
) ,n
,m
,x
))))
759 (interval-mult d f
(* flonum-epsilon dx
))))
762 ;; For the derivative of the associated legendre p function, see
763 ;; A & S 8.5.4 page 334.
765 (putprop `$assoc_legendre_p
767 ((unk) "$first" "$assoc_legendre_p")
768 ((unk) "$second" "$assoc_legendre_p")
771 ((mtimes simp
) -
1 ((mplus simp
) m n
) (($unit_step
) n
)
772 (($assoc_legendre_p simp
) ((mplus simp
) -
1 n
) m x
))
773 ((mtimes simp
) n
(($assoc_legendre_p simp
) n m x
) x
))
774 ((mexpt simp
) ((mplus simp
) -
1 ((mexpt simp
) x
2)) -
1)))
777 (defprop $assoc_legendre_p tex-assoc-legendre-p tex
)
779 (defun tex-assoc-legendre-p (x l r
)
780 (tex-sub-and-super-scripted-function "P" `(0) nil
`(1) nil
2 x l r
))
782 (setf (get '$assoc_legendre_p
'dimension
) 'dimension-assoc-legendre-p
)
784 (defun dimension-assoc-legendre-p (form result
)
786 (dimension-sub-and-super-scripted-function "P" `(1) `(2) nil
3 form
)
789 ;; See A&S 22.5.55 and 22.5.56, page 780.
791 (defun $hermite
(n x
)
796 (multiple-value-setq (f e
)
797 ($hypergeo11
(- n
) (rat 1 2) (mul x x
) n
))
798 (setq d
(mul (if (oddp n
) -
1 1) (factorial (* 2 n
))
799 (div 1 (factorial n
)))))
801 (setq n
(/ (- n
1) 2))
802 (multiple-value-setq (f e
)
803 ($hypergeo11
(- n
) (rat 3 2) (mul x x
) n
))
804 (setq d
(mul (if (oddp n
) -
1 1) (factorial (+ 1 (* 2 n
))) 2 x
805 (div 1 (factorial n
))))))
806 (orthopoly-return-handler d f e
)))
807 (t `(($hermite
) ,n
,x
))))
811 ((unk) "$first" "$hermite")
812 ((mtimes) 2 n
(($hermite
) ((mplus) -
1 n
) x
)))
815 (defprop $hermite tex-hermite tex
)
817 (defun tex-hermite (x l r
)
818 (tex-sub-and-super-scripted-function "H" `(0) nil nil nil
1 x l r
))
820 (setf (get '$hermite
'dimension
) 'dimension-hermite
)
822 (defun dimension-hermite (form result
)
824 (dimension-sub-and-super-scripted-function "H" `(1) nil nil
2 form
)
827 ;; See A & S 22.5.54, page 780. For integer n, use the identity
828 ;; binomial(n+a,n) = pochhammer(a+1,n)/pochhammer(1,n)
830 (defun $gen_laguerre
(n a x
)
831 (cond ((use-hypergeo n x
)
833 ;(setq d (div ($pochhammer (add a 1) n) ($pochhammer 1 n)))
834 (setq d
(pochhammer-quotient (add a
1) 1 x n
))
835 (multiple-value-setq (f e
)
836 ($hypergeo11
(mul -
1 n
) (add 1 a
) x n
))
837 (setq e
(if e
(+ e
(* 4 (abs f
) flonum-epsilon n
)) nil
))
838 (orthopoly-return-handler d f e
)))
840 `(($gen_laguerre
) ,n
,a
,x
))))
842 (putprop '$gen_laguerre
844 ((unk) "$first" "$gen_laguerre")
845 ((unk) "$second" "$gen_laguerre")
848 ((mtimes) -
1 ((mplus) a n
)
849 (($unit_step
) n
) (($gen_laguerre
) ((mplus) -
1 n
) a x
))
850 ((mtimes) n
(($gen_laguerre
) n a x
)))
854 (defprop $gen_laguerre tex-gen-laguerre tex
)
856 (defun tex-gen-laguerre (x l r
)
857 (tex-sub-and-super-scripted-function "L" `(0) nil
`(1) t
1 x l r
))
859 (setf (get '$gen_laguerre
'dimension
) 'dimension-gen-laguerre
)
861 (defun dimension-gen-laguerre (form result
)
863 (dimension-sub-and-super-scripted-function "L" `(1) `(2) t
3 form
)
866 ;; See A & S 22.5.16, page 778.
868 (defun $laguerre
(n x
)
869 (cond ((use-hypergeo n x
)
871 (multiple-value-setq (f e
) ($hypergeo11
(mul -
1 n
) 1 x n
))
872 (orthopoly-return-handler 1 f e
)))
874 `(($laguerre
) ,n
,x
))))
878 ((unk) "$first" "$laguerre")
881 ((mtimes) -
1 n
(($laguerre
) ((mplus) -
1 n
) x
))
882 ((mtimes) n
(($laguerre
) n x
)))
886 (defprop $laguerre tex-laguerre tex
)
888 (defun tex-laguerre (x l r
)
889 (tex-sub-and-super-scripted-function "L" `(0) nil nil nil
1 x l r
))
891 (setf (get '$laguerre
'dimension
) 'dimension-laguerre
)
893 (defun dimension-laguerre (form result
)
895 (dimension-sub-and-super-scripted-function "L" `(1) nil nil
2 form
)
898 (defun $spherical_hankel1
(n x
)
900 (cond ((and (integerp n
) (< n
0))
901 (setq d
(mul '$%i
(if (oddp n
) 1 -
1)))
902 (multiple-value-setq (f e
)
903 ($spherical_hankel1
(add -
1 (mul -
1 n
)) x
))
904 (orthopoly-return-handler d f e
))
906 (multiple-value-setq (f e
)
907 ($hypergeo11
(mul -
1 n
) (mul -
2 n
) (mul -
2 '$%i x
) n
))
908 (setq d
(mul '$%i
(if (= 0 n
) 1
909 (simplify `((%genfact
) ,(add (mul 2 n
) -
1)
910 ,(add n
(rat -
1 2)) 2)))
911 (power '$%e
(mul '$%i x
)) (div -
1 (power x
(add 1 n
)))))
912 (orthopoly-return-handler d f e
))
914 `(($spherical_hankel1
) ,n
,x
)))))
916 (putprop '$spherical_hankel1
918 ((unk) "$first" "$spherical_hankel1")
919 ((mplus simp
) (($spherical_hankel1
) ((mplus) -
1 n
) x
)
920 ((mtimes simp
) -
1 ((mplus) 1 n
)
921 (($spherical_hankel1
) n x
) ((mexpt) x -
1))))
924 (defprop $spherical_hankel1 tex-spherical-hankel-1 tex
)
926 (defun tex-spherical-hankel-1 (x l r
)
927 (tex-sub-and-super-scripted-function "h^{(1)}" `(0) nil nil nil
1 x l r
))
929 (setf (get '$spherical_hankel1
'dimension
) 'dimension-spherical-hankel-1
)
931 (defun dimension-spherical-hankel-1 (form result
)
932 (let ((form1 `((mexpt) (($\h simp array
) ,(nth 1 form
))
934 (dimension-function `((,form1 simp
) ,(nth 2 form
)) result
)))
936 ;; See A & S 10.1.36.
938 (defun $spherical_hankel2
(n x
)
940 (setq x
(mul x
(power '$%e
(mul '$%i
'$%pi
(add (mul 2 n
) 1)))))
942 (setq f
($spherical_hankel1 n x
))
943 (if (oddp n
) (interval-mult -
1 f
) f
)))
944 (t `(($spherical_hankel2
) ,n
,x
))))
946 (putprop '$spherical_hankel2
948 ((unk) "$first" "$spherical_hankel2")
949 ((mplus simp
) (($spherical_hankel2
) ((mplus) -
1 n
) x
)
950 ((mtimes simp
) -
1 ((mplus) 1 n
)
951 (($spherical_hankel2
) n x
) ((mexpt) x -
1))))
954 (defprop $spherical_hankel2 tex-spherical-hankel-2 tex
)
956 (defun tex-spherical-hankel-2 (x l r
)
957 (tex-sub-and-super-scripted-function "h^{(2)}" `(0) nil nil nil
1 x l r
))
959 (setf (get '$spherical_hankel2
'dimension
) 'dimension-spherical-hankel-2
)
961 (defun dimension-spherical-hankel-2 (form result
)
962 (let ((form1 `((mexpt) (($\h simp array
) ,(nth 1 form
)) (2))))
963 (dimension-function `((,form1 simp
) ,(nth 2 form
)) result
)))
965 ;;---------------------------------------------------------------------
966 ;; The spherical_bessel functions use the functions p-fun and q-fun.
967 ;; See A&S 10.1.8 and 10.1.9 page 437.
975 (setq w
(div (mul w
`((rat) ,(* -
1 (+ n m2
2) (+ n m2
1)
976 (- n m2
) (- n
(+ m2
1)))
977 ,(* 4 (+ m2
1) (+ m2
2)))) x2
))
978 (setq s
(add s w
)))))
981 (let ((s (if (= 0 n
) 0 1))
982 (w 1) (m2) (x2 (mul x x
))
983 (n1 (floor (/ (- n
1) 2))))
984 (dotimes (m n1
(div (mul n
(+ n
1) s
) (mul 2 x
)))
986 (setq w
(div (mul w
`((rat) ,(* -
1 (+ n m2
3) (+ n m2
2)
987 (- n
(+ m2
1)) (- n
(+ m2
2)))
988 ,(* 4 (+ m2
3) (+ m2
2)))) x2
))
989 (setq s
(add s w
)))))
992 ;; See A&S 10.1.8 page 437 and A&S 10.1.15 page 439. When the order
993 ;; is an integer and x is a float or a bigfloat, use the slatec code
994 ;; for numerical evaluation. Yes, we coerce bigfloats to floats and
997 ;; For numerical evaluation, we do our own analytic continuation -- otherwise
998 ;; we get factors exp(%i n %pi / 2) that should evaluate to 1,%i,-1,-%, but
999 ;; numerically have "fuzz" in their values. The fuzz can cause the spherical
1000 ;; bessel functions to have nonzero (but small) imaginary values on the
1001 ;; negative real axis. See A&S 10.1.34
1003 (defun $spherical_bessel_j
(n x
)
1004 (cond ((and (eq '$zero
(csign ($ratdisrep x
)))
1005 (or (integerp n
) ($featurep n
'$integer
)))
1006 `((%kron_delta
) 0 ,n
))
1008 ((and (use-float x
) (integerp n
))
1009 (let ((d 1) (xr) (xi) (z))
1010 (setq x
($rectform
($float x
)))
1011 (setq xr
($realpart x
))
1012 (setq xi
($imagpart x
))
1013 (setq z
(complex xr xi
))
1015 (setq d
(if (oddp n
) -
1 1))
1018 (setq n
(+ 0.5 ($float n
)))
1019 (setq d
(* d
(sqrt (/ pi
(* 2 z
)))))
1020 (setq d
(lisp-float-to-maxima-float d
))
1021 ($expand
(mul ($rectform d
) ($bessel_j n x
)))))
1023 ((and (integerp n
) (> n -
1))
1024 (let ((xt (sub x
(div (mul n
'$%pi
) 2))))
1026 (mul (p-fun n x
) (simplify `((%sin
) ,xt
)))
1027 (mul (q-fun n x
) (simplify `((%cos
) ,xt
)))) x
)))
1030 (mul (if (oddp n
) -
1 1) ($spherical_bessel_y
(- (+ n
1)) x
)))
1033 `(($spherical_bessel_j
) ,n
,x
))))
1035 (putprop '$spherical_bessel_j
1037 ((unk) "$first" "$spherical_bessel_j")
1038 ((mtimes) ((mexpt) ((mplus) 1 ((mtimes) 2 n
)) -
1)
1040 ((mtimes) n
(($spherical_bessel_j
) ((mplus) -
1 n
) x
))
1041 ((mtimes) -
1 ((mplus) 1 n
)
1042 (($spherical_bessel_j
) ((mplus) 1 n
) x
)))))
1045 (defprop $spherical_bessel_j tex-spherical-bessel-j tex
)
1047 (defun tex-spherical-bessel-j (x l r
)
1048 (tex-sub-and-super-scripted-function "j^{(2)}" `(0) nil nil nil
1 x l r
))
1050 (setf (get '$spherical_bessel_j
'dimension
) 'dimension-spherical-bessel-j
)
1052 (defun dimension-spherical-bessel-j (form result
)
1053 (let ((form1 `(($\j simp array
) ,(nth 1 form
))))
1054 (dimension-function `((,form1 simp
) ,(nth 2 form
)) result
)))
1056 ;; For analytic continuation, see A&S 10.1.35.
1058 (defun $spherical_bessel_y
(n x
)
1059 (cond ((and (use-float x
) (integerp n
))
1060 (let ((d 1) (xr) (xi) (z))
1061 (setq x
($rectform
($float x
)))
1062 (setq xr
($realpart x
))
1063 (setq xi
($imagpart x
))
1064 (setq z
(complex xr xi
))
1066 (setq d
(if (oddp n
) 1 -
1))
1069 (setq n
(+ 0.5 ($float n
)))
1070 (setq d
(* d
(sqrt (/ pi
(* 2 z
)))))
1071 (setq d
(lisp-float-to-maxima-float d
))
1072 ($expand
(mul ($rectform d
) ($bessel_y n x
)))))
1074 ((and (integerp n
) (> n -
1))
1075 (let ((xt (add x
(div (mul n
'$%pi
) 2))))
1076 (mul (if (oddp n
) 1 -
1)
1078 (mul (p-fun n x
) (simplify `((%cos
) ,xt
)))
1079 (mul (q-fun n x
) (simplify `((%sin
) ,xt
)))) x
))))
1082 (mul (if (oddp n
) 1 -
1) ($spherical_bessel_j
(- (+ n
1)) x
)))
1083 (t `(($spherical_bessel_y
) ,n
,x
))))
1085 (putprop '$spherical_bessel_y
1087 ((unk) "$first" "$spherical_bessel_y")
1088 ((mtimes) ((mexpt) ((mplus) 1 ((mtimes) 2 n
)) -
1)
1090 ((mtimes) n
(($spherical_bessel_y
) ((mplus) -
1 n
) x
))
1091 ((mtimes) -
1 ((mplus) 1 n
)
1092 (($spherical_bessel_y
) ((mplus) 1 n
) x
)))))
1095 (defprop $spherical_bessel_y tex-spherical-bessel-y tex
)
1097 (defun tex-spherical-bessel-y (x l r
)
1098 (tex-sub-and-super-scripted-function "y^{(2)}" `(0) nil nil nil
1 x l r
))
1100 (setf (get '$spherical_bessel_y
'dimension
) 'dimension-spherical-bessel-y
)
1102 (defun dimension-spherical-bessel-y (form result
)
1103 (let ((form1 `(($\y simp array
) ,(nth 1 form
))))
1104 (dimension-function `((,form1 simp
) ,(nth 2 form
)) result
)))
1106 ;; Compute P_n^m(cos(theta)). See Merzbacher, 9.59 page 184
1107 ;; and 9.64 page 185, and A & S 22.5.37 page 779. This function
1108 ;; lacks error checking; it should only be called by spherical_harmonic.
1110 ;; We need to be careful -- for the spherical harmonics we can't use
1111 ;; assoc_legendre_p(n,m,cos(x)). If we did, we'd get factors
1112 ;; (1 - cos^2(x))^(m/2) that simplify to |sin(x)|^m but we want them
1113 ;; to simplify to sin^m(x). Oh my!
1115 (defun assoc-leg-cos (n m x
)
1117 (if (= m
0) 1 (mul (take '(%genfact
) (sub (mul 2 m
) 1) (sub m
(div 1 2)) 2) (power (take '(%sin
) x
) m
)))
1118 ($ultraspherical
(sub n m
) (add m
(div 1 2)) (take '(%cos
) x
))))
1120 (defun $spherical_harmonic
(n m th p
)
1121 (cond ((and (integerp n
) (integerp m
) (> n -
1))
1122 (cond ((> (abs m
) n
)
1125 (interval-mult (if (oddp m
) -
1 1)
1126 ($spherical_harmonic n
(- m
) th
(mul -
1 p
))))
1129 (mul ($exp
(mul '$%i m p
))
1130 (power (div (* (+ (* 2 n
) 1) (factorial (- n m
)))
1131 (mul '$%pi
(* 4 (factorial (+ n m
)))))
1133 (assoc-leg-cos n m th
)))))
1135 `(($spherical_harmonic
) ,n
,m
,th
,p
))))
1137 (defprop $spherical_harmonic tex-spherical-harmonic tex
)
1139 (defun tex-spherical-harmonic (x l r
)
1140 (tex-sub-and-super-scripted-function "Y" `(0) nil
`(1) nil
2 x l r
))
1142 (setf (get '$spherical_harmonic
'dimension
) 'dimension-spherical-harmonic
)
1144 (defun dimension-spherical-harmonic (form result
)
1146 (dimension-sub-and-super-scripted-function "Y" `(1) `(2) nil
3 form
)
1149 (putprop '$spherical_harmonic
1151 ((unk) "$first" "$spherical_harmonic")
1152 ((unk) "$second" "$spherical_harmonic")
1154 ((mtimes) ((rat ) -
1 2)
1156 ((mtimes) ((mplus) ((mtimes) -
1 m
) n
)
1159 (($spherical_harmonic
) n
((mplus) 1 m
) theta phi
)
1160 ((mexpt) $%e
((mtimes) -
1 $%i phi
)))
1161 ((mtimes) ((rat) 1 2)
1163 ((mtimes) ((mplus) 1 ((mtimes) -
1 m
) n
)
1166 (($spherical_harmonic
) n
((mplus) -
1 m
) theta phi
)
1167 ((mexpt) $%e
((mtimes) $%i phi
))))
1169 ((mtimes) $%i m
(($spherical_harmonic
) n m theta phi
)))
1172 (defun maxima-float-to-lisp-float (y)
1173 (let* ((x ($rectform y
))
1176 (cond ((or (floatp xr
) (floatp xi
))
1179 (if (= 0.0 xi
) xr
(complex xr xi
)))
1183 (defun lisp-float-to-maxima-float (x)
1185 (add (realpart x
) (mul '$%i
(imagpart x
)))
1188 (defun hypergeo11-float (n b x
)
1189 (let ((f0) (fm1) (f) (i 0) (k) (dk) (ak) (bk) (err)
1190 (as (make-array (- 1 n
)
1191 :initial-element
0.0))
1192 (bs (make-array (- 1 n
)
1193 :initial-element
0.0))
1194 (fs (make-array (- 1 n
)
1195 :initial-element
0.0))
1203 (setf (aref fs i
) f0
)
1205 (setq ak
(/ (+ (* 2 i
) x
) dk
))
1206 (setq bk
(/ (- i
) dk
))
1207 (setq f
(+ (* ak f0
) (* bk fm1
)))
1208 (setf (aref as i
) ak
)
1209 (setf (aref bs i
) bk
)
1213 (setf (aref fs i
) f0
)
1220 (setq u
(+ (* (aref as k
) u0
)
1221 (* (aref bs
(+ 1 k
)) um1
)))
1224 (setq err
(+ err
(abs (* u0
(aref fs k
)))))
1226 (values f0
(* 12 flonum-epsilon err
))))
1228 (defun hypergeo21-float (n b c x
)
1229 (let ((f0) (fm1) (f) (i 0) (k) (dk) (ak) (bk) (err)
1230 (as (make-array (- 1 n
)
1231 :initial-element
0.0))
1232 (bs (make-array (- 1 n
)
1233 :initial-element
0.0))
1234 (fs (make-array (- 1 n
)
1235 :initial-element
0.0))
1242 (setf (aref fs i
) f0
)
1244 (setq ak
(/ (+ (* 2 i
) c
(- (* x
(+ b i
)))) dk
))
1245 (setq bk
(/ (* i
(- x
1)) dk
))
1246 (setq f
(+ (* ak f0
) (* bk fm1
)))
1247 (setf (aref as i
) ak
)
1248 (setf (aref bs i
) bk
)
1252 (setf (aref fs i
) f0
)
1259 (setq u
(+ (* (aref as k
) u0
)
1260 (* (aref bs
(+ 1 k
)) um1
)))
1264 (setq err
(+ err
(abs (* (aref fs k
) u0
)))))
1265 (values f0
(* 12 flonum-epsilon err
))))
1267 ;; For recursion relations, see A & S 22.7 page 782.
1269 ;; legendre_p(n+1,x) = ((2*n+1)*legendre_p(n,x)*x-n*legendre_p(n-1,x))/(n+1)
1271 ;; jacobi_p(n+1,a,b,x) = (((2*n+a+b+1)*(a^2-b^2) +
1272 ;; x*pochhammer(2*n+a+b,3)) * jacobi_p(n,a,b,x) -
1273 ;; 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))
1275 ;; ultraspherical(n+1,a,x) = (2*(n+a)*x * ultraspherical(n,a,x) -
1276 ;; (n+2*a-1)*ultraspherical(n-1,a,x))/(n+1)
1278 ;; chebyshev_t(n+1,x) = 2*x*chebyshev_t(n,x) -chebyshev_t(n-1,x)
1280 ;; chebyshev_u(n+1,x) = 2*x*chebyshev_u(n,x) -chebyshev_u(n-1,x)
1282 ;; laguerre(n+1,x) = (((2*n+1) - x)*laguerre(n,x) -(n)*laguerre(n-1,x))/(n+1)
1284 ;; gen_laguerre(n+1,a,x) = (((2*n+a+1) - x)*gen_laguerre(n,a,x)
1285 ;; -(n+a)*gen_laguerre(n-1,a,x))/(n+1)
1287 ;; hermite(n+1,x) = 2*x*hermite(n,x) -2*n*hermite(n-1,x)
1289 ;; See G & R 8.733.2; A & S 22.7.11 might be wrong -- or maybe I need
1292 ;; (2*n+1)*x*assoc_legendre_p(n,m,x) = (n-m+1)*assoc_legendre_p(n+1,m,x)
1293 ;; + (n+m)*assoc_legendre_p(n-1,m,x)
1295 ;; For the half-integer bessel functions, See A & S 10.1.19
1297 ;; fn(n-1,x) + fn(n+1,x) = (2*n+1)*fn(n,x)/x;
1299 (defun check-arg-length (fn n m
)
1301 (merror "Function ~:M needs ~:M arguments, instead it received ~:M"
1304 (defun $orthopoly_recur
(fn arg
)
1305 (if (not ($listp arg
))
1306 (merror "The second argument to orthopoly_recur must be a list"))
1307 (cond ((eq fn
'$jacobi_p
)
1308 (check-arg-length fn
4 (- (length arg
) 1))
1309 (let ((n (nth 1 arg
))
1314 `((mequal) (($jacobi_p
) ((mplus) 1 ,n
) ,a
,b
,x
)
1315 ((mtimes) ((rat) 1 2) ((mexpt) ((mplus) 1 ,n
) -
1)
1316 ((mexpt) ((mplus) 1 ,a
,b
,n
) -
1)
1317 ((mexpt) ((mplus) ,a
,b
((mtimes) 2 ,n
)) -
1)
1319 ((mtimes) -
2 ((mplus) ,a
,n
) ((mplus) ,b
,n
)
1320 ((mplus) 2 ,a
,b
((mtimes) 2 ,n
))
1321 (($jacobi_p
) ((mplus) -
1 ,n
) ,a
,b
,x
))
1322 ((mtimes) (($jacobi_p
) ,n
,a
,b
,x
)
1325 ((mplus) ((mexpt) ,a
2)
1326 ((mtimes) -
1 ((mexpt) ,b
2)))
1327 ((mplus) 1 ,a
,b
((mtimes) 2 ,n
)))
1328 ((mtimes) ((mplus) ,a
,b
((mtimes) 2 ,n
))
1329 ((mplus) 1 ,a
,b
((mtimes) 2 ,n
))
1330 ((mplus) 2 ,a
,b
((mtimes) 2 ,n
)) ,x
)))))))))
1332 ((eq fn
'$ultraspherical
)
1333 (check-arg-length fn
3 (- (length arg
) 1))
1334 (let ((n (nth 1 arg
))
1338 `((mequal) (($ultraspherical
) ((mplus) 1 ,n
) ,a
,x
)
1339 ((mtimes) ((mexpt) ((mplus) 1 ,n
) -
1)
1341 ((mtimes) -
1 ((mplus) -
1 ((mtimes) 2 ,a
) ,n
)
1342 (($ultraspherical
) ((mplus) -
1 ,n
) ,a
,x
))
1343 ((mtimes) 2 ((mplus) ,a
,n
)
1344 (($ultraspherical
) ,n
,a
,x
) ,x
)))))))
1346 ((member fn
`($chebyshev_t $chebyshev_u
) :test
'eq
)
1347 (check-arg-length fn
2 (- (length arg
) 1))
1348 (let ((n (nth 1 arg
))
1351 `((mequal ) ((,fn
) ((mplus ) 1 ,n
) ,x
)
1353 ((mtimes ) -
1 ((,fn
) ((mplus ) -
1 ,n
) ,x
))
1354 ((mtimes ) 2 ((,fn
) ,n
,x
) ,x
))))))
1356 ((member fn
'($legendre_p $legendre_q
) :test
'eq
)
1357 (check-arg-length fn
2 (- (length arg
) 1))
1358 (let* ((n (nth 1 arg
))
1360 (z (if (eq fn
'$legendre_q
)
1361 `((mtimes) -
1 ((%kron_delta
) ,n
0)) 0)))
1363 `((mequal) ((,fn
) ((mplus) 1 ,n
) ,x
)
1365 ((mtimes) ((mexpt) ((mplus) 1 ,n
) -
1)
1367 ((mtimes) ((mtimes) -
1 ,n
)
1368 ((,fn
) ((mplus) -
1 ,n
) ,x
))
1369 ((mtimes) ((mplus) 1 ((mtimes) 2 ,n
))
1373 ((member fn
'($assoc_legendre_p $assoc_legendre_q
) :test
'eq
)
1374 (check-arg-length fn
3 (- (length arg
) 1))
1375 (let ((n (nth 1 arg
))
1379 `((mequal) ((,fn
) ((mplus) 1 ,n
) ,m
,x
)
1381 ((mexpt) ((mplus) 1 ((mtimes) -
1 ,m
) ,n
) -
1)
1384 ((mplus) ((mtimes) -
1 ,m
)
1386 ((,fn
) ((mplus) -
1 ,n
) ,m
,x
))
1387 ((mtimes) ((mplus) 1 ((mtimes) 2 ,n
))
1388 ((,fn
) ,n
,m
,x
) ,x
)))))))
1391 (check-arg-length fn
2 (- (length arg
) 1))
1392 (let ((n (nth 1 arg
))
1395 `((mequal ) (($laguerre
) ((mplus ) 1 ,n
) ,x
)
1396 ((mtimes ) ((mexpt ) ((mplus ) 1 ,n
) -
1)
1398 ((mtimes ) -
1 ,n
(($laguerre
) ((mplus ) -
1 ,n
) ,x
))
1399 ((mtimes ) (($laguerre
) ,n
,x
)
1400 ((mplus ) 1 ((mtimes ) 2 ,n
) ((mtimes ) -
1 ,x
)))))))))
1402 ((eq fn
'$gen_laguerre
)
1403 (check-arg-length fn
3 (- (length arg
) 1))
1404 (let ((n (nth 1 arg
))
1408 `((mequal) (($gen_laguerre
) ((mplus) 1 ,n
) ,a
,x
)
1409 ((mtimes) ((mexpt ) ((mplus) 1 ,n
) -
1)
1411 ((mtimes) -
1 ((mplus) ,a
,n
)
1412 (($gen_laguerre
) ((mplus) -
1 ,n
) ,a
,x
))
1413 ((mtimes) (($gen_laguerre
) ,n
,a
,x
)
1414 ((mplus) 1 ,a
((mtimes) 2 ,n
) ((mtimes ) -
1 ,x
)))))))))
1417 (check-arg-length fn
2 (- (length arg
) 1))
1418 (let ((n (nth 1 arg
))
1421 `((mequal) (($hermite
) ((mplus) 1 ,n
) ,x
)
1423 ((mtimes) -
2 ,n
(($hermite
) ((mplus) -
1 ,n
) ,x
))
1424 ((mtimes) 2 (($hermite
) ,n
,x
) ,x
))))))
1426 ((member fn
`($spherical_bessel_j $spherical_bessel_y
1427 $spherical_hankel1 $spherical_hankel2
)
1429 (check-arg-length fn
2 (- (length arg
) 1))
1430 (let ((n (nth 1 arg
))
1433 `((mequal) ((,fn
) ((mplus) 1 ,n
) ,x
)
1434 ((mplus) ((mtimes) -
1 ((,fn
) ((mplus) -
1 ,n
) ,x
))
1435 ((mtimes) ((,fn
) ,n
,x
) ((mexpt) ,x -
1))
1436 ((mtimes) 2 ,n
((,fn
) ,n
,x
) ((mexpt) ,x -
1)))))))
1438 (t (merror "A recursion relation for ~:M isn't known to Maxima" fn
))))
1440 ;; See A & S Table 22.2, page 774.
1442 (defun $orthopoly_weight
(fn arg
)
1443 (if (not ($listp arg
))
1444 (merror "The second argument to orthopoly_weight must be a list"))
1446 (if (not (or ($symbolp
(car (last arg
))) ($subvarp
(car (last arg
)))))
1447 (merror "The last element of the second argument to orthopoly_weight must
1448 be a symbol or a subscripted symbol, instead found ~:M" (car (last arg
))))
1450 (if (not (every #'(lambda (s)
1451 ($freeof
(car (last arg
)) s
)) (butlast (cdr arg
))))
1452 (merror "Only the last element of ~:M may depend on the integration
1453 variable ~:M" arg
(car (last arg
))))
1455 (cond ((eq fn
'$jacobi_p
)
1456 (check-arg-length fn
4 (- (length arg
) 1))
1457 (let ((a (nth 2 arg
))
1462 ((mtimes) ((mexpt) ((mplus) 1 ((mtimes) -
1 ,x
)) ,a
)
1463 ((mexpt) ((mplus ) 1 ,x
) ,b
))
1466 ((eq fn
'$ultraspherical
)
1467 (check-arg-length fn
3 (- (length arg
) 1))
1468 (let ((a (nth 2 arg
))
1472 ((mexpt) ((mplus) 1 ((mtimes) -
1 ((mexpt) ,x
2)))
1473 ((mplus) ((rat) -
1 2) ,a
)) -
1 1))))
1475 ((eq fn
'$chebyshev_t
)
1476 (check-arg-length fn
2 (- (length arg
) 1))
1477 (let ((x (nth 2 arg
)))
1480 ((mexpt) ((mplus) 1 ((mtimes) -
1 ((mexpt) ,x
2)))
1481 ((rat) -
1 2)) -
1 1))))
1483 ((eq fn
'$chebyshev_u
)
1484 (check-arg-length fn
2 (- (length arg
) 1))
1485 (let ((x (nth 2 arg
)))
1488 ((mexpt) ((mplus) 1 ((mtimes) -
1 ((mexpt) ,x
2)))
1489 ((rat) 1 2)) -
1 1))))
1491 ((eq fn
'$legendre_p
)
1492 (check-arg-length fn
2 (- (length arg
) 1))
1495 ; This is for a fixed order. There is also an orthogonality
1496 ; condition for fixed degree with weight function 1/(1-x^2).
1497 ; See A & S 8.14.11 and 8.14.12.
1498 ((eq fn
'$assoc_legendre_p
)
1499 (check-arg-length fn
3 (- (length arg
) 1))
1503 (check-arg-length fn
2 (- (length arg
) 1))
1504 (let ((x (nth 2 arg
)))
1506 `((mlist) ((mexpt) $%e
((mtimes) -
1 ,x
)) 0 $inf
))))
1508 ((eq fn
'$gen_laguerre
)
1509 (check-arg-length fn
3 (- (length arg
) 1))
1510 (let ((a (nth 2 arg
))
1514 ((mtimes) ((mexpt) ,x
,a
)
1515 ((mexpt) $%e
((mtimes) -
1 ,x
))) 0 $inf
))))
1518 (check-arg-length fn
2 (- (length arg
) 1))
1519 (let ((x (nth 2 arg
)))
1521 `((mlist) ((mexpt) $%e
((mtimes) -
1 ((mexpt) ,x
2)))
1522 ((mtimes ) -
1 $inf
) $inf
))))
1524 (t (merror "A weight for ~:M isn't known to Maxima" fn
))))