fixes typos and a missing reference.
[maxima.git] / share / orthopoly / orthopoly.lisp
blobd0805e4ccb792c83c1363082d1804eb02c2ab446
1 ;; 8/8 | 5/5
2 ;; Copyright (C) 2000, 2001, 2003, 2008, 2009 Barton Willis
4 #|
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).
16 (in-package :maxima)
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)
25 (b ($rectform a))
26 (br ($float ($realpart b)))
27 (bi ($float ($imagpart b))))
28 (cond ((and (numberp br) (numberp bi))
29 (if (= bi 0) (float br)
30 (complex (float br)
31 (float bi))))
32 (t a))))
34 ;; Return true iff a is a float or a complex number with either a
35 ;; real or imaginary part that is a float.
37 (defun xcomplexp (a)
38 (or (floatp a)
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))
57 (ef))
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))
63 (t f))))
64 (t (mul d f))))
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
80 ;; continuous at 0.
82 (defprop $unit_step "\\Theta" texword)
84 (defun simp-unit-step (a y z)
85 (oneargcheck a)
86 (setq y (simpcheck (cadr a) z))
87 (let ((s (csign y)))
88 (cond ((or (eq s '$nz) (eq s '$zero) (eq s '$neg)) 0)
89 ((eq s '$pos) 1)
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.
95 (defun $intervalp (a)
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)))
106 (let ((p (nth 1 a))
107 (q (nth 2 a)))
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)))
114 (t (mult x a))))
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)
124 (setq x (cdr x))
125 (let ((lo) (hi) (s1) (s2))
126 (setq s1 (if b1 `("\\left(") nil))
127 (setq s2 (if b1 `("\\right)") nil))
128 (dolist (i sub)
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))
133 (dolist (i sup)
134 (setq hi (cons (nth i x) hi)))
135 (setq hi (if hi (tex-list (nreverse hi) s1 s2 ",") nil))
136 (append l `(,fn)
137 (if lo `("_{",@lo "}") nil)
138 (if hi `("^{" ,@hi "}") nil)
139 `(,@(tex-list (nthcdr i x) `("\\left(") `("\\right)") ","))
140 r)))
142 (defun dimension-sub-and-super-scripted-function (fn sub sup b2 k x)
143 (let ((lo) (hi) (form))
144 (dolist (i sub)
145 (setq lo (cons (nth i x) lo)))
146 (setq lo (nreverse lo))
147 (dolist (i sup)
148 (setq hi (cons (nth i x) hi)))
149 (setq hi (nreverse hi))
150 (cond ((null 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))))
158 ;; Return true iff
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))
168 (dolist (x a)
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)
189 (cond ((like x 0) 1)
190 ((and (integerp n) (> n -1))
191 (cond ((and (like a (- n)) (use-float b x))
192 (let ((f) (e))
193 (multiple-value-setq (f e)
194 (hypergeo11-float (- n) (maxima-to-lisp-complex-float b)
195 (maxima-to-lisp-complex-float x)))
196 (values f e)))
198 (let ((sum 1) (cf 1))
199 (dotimes (i n sum)
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.
204 ; `((%sum )
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)))
210 (simplify
211 (list '(%sum)
212 (mul (inv (take '(mfactorial) index))
213 (take '($pochhammer) a index)
214 (inv (take '($pochhammer) b index))
215 (power x index))
216 index 0 n))))))
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)
222 (cond ((like x 0) 1)
223 ((and (integerp n) (> n -1))
224 (cond ((and (like a (- n)) (use-float b c x))
225 (let ((f) (e))
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)))
231 (values f e)))
233 (let ((sum 1) (cf 1))
234 (dotimes (i n sum)
235 (setq cf (div (mul cf (add i a) (add i b) x) (mul (+ 1 i)
236 (add i c))))
237 (setq sum (add cf sum)))))))
240 ; The following is replaced with simplifying code.
241 ; `((%sum)
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)))
248 (simplify
249 (list '(%sum)
250 (mul (take '($pochhammer) a index)
251 (take '($pochhammer) b index)
252 (inv (take '($pochhammer) c index))
253 (inv (take '(mfactorial) index))
254 (power x index))
255 index 0 n))))))
257 (eval-when
258 #+gcl (load eval)
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)
268 #'(lambda (a b)
269 (declare (ignore a))
270 (if (not (and (atom b) (integerp b)))
271 (progn
272 (mtell "The value of `pochhammer_max_index' must be an integer.~%")
273 'munbindp))))
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)
282 (let ((acc 1))
283 (if (< n 0) (/ 1 (pochhammer (+ x n) (- n)))
284 (dotimes (k n acc)
285 (setq acc (* acc (+ k x)))))))
287 (in-package :maxima)
289 (defun simp-pochhammer (e y z)
290 (declare (ignore y))
291 (let ((x) (n) (return-a-rat))
292 (twoargcheck e)
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))))
302 ((eql n 0) 1)
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).
316 ((great (neg 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)))
323 (let ((acc 1))
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
332 '((x n)
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))))
338 'grad)
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)))
344 (append l
345 `("\\left(")
346 (nth 0 x)
347 `("\\right)_{")
348 (nth 1 x)
349 `("}")
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)
366 (cond ((mminusp n)
367 (pochhammer-quotient b a x (neg n)))
368 ((and (integerp n) (use-float a b x))
369 (let ((acc 1.0))
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)
377 (declare (ignore 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|)) +
392 ;; O(eps^2).
394 (defun $jacobi_p (n a b x)
395 (cond ((use-hypergeo n x)
396 (let ((f) (d) (e))
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))))
406 (putprop '$jacobi_p
407 '((n a b x)
408 ((unk) "$first" "$jacobi_p")
409 ((unk) "$second" "$jacobi_p")
410 ((unk) "$third" "$jacobi_p")
412 ((mtimes)
413 ((mexpt) ((mplus ) a b ((mtimes ) 2 n)) -1)
414 ((mplus)
415 ((mtimes) 2
416 (($unit_step) n)
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)
421 ((mtimes)
422 ((mplus) ((mtimes) -1 a) ((mtimes ) -1 b)
423 ((mtimes) -2 n)) x))))
424 ((mexpt) ((mplus) 1 ((mtimes) -1 ((mexpt ) x 2))) -1)))
425 'grad)
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)
435 (dimension-function
436 (dimension-sub-and-super-scripted-function "P" `(1) `(2 3) t 4 form)
437 result))
439 ;; See A&S 22.5.46, page 779.
441 (defun $ultraspherical (n a x)
442 (cond ((use-hypergeo n x)
443 (let ((f) (d) (e))
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
454 '((n a x)
455 ((unk) "$first" "$ultraspherical")
456 ((unk) "$second" "$ultrapsherical")
457 ((mtimes)
458 ((mplus)
459 ((mtimes)
460 (($unit_step) n)
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)))
465 'grad)
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)
475 (dimension-function
476 (dimension-sub-and-super-scripted-function "C" `(1) `(2) t 3 form)
477 result))
479 (defun $chebyshev_t (n x)
480 (cond ((use-hypergeo n x)
481 (let ((f) (e))
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
488 '((n x)
489 ((unk) "$first" "$chebyshev_t")
490 ((mtimes)
491 ((mplus)
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)))
495 'grad)
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)
505 (dimension-function
506 (dimension-sub-and-super-scripted-function "T" `(1) nil nil 2 form)
507 result))
509 ;; See A & S 22.5.48, page 779.
511 (defun $chebyshev_u (n x)
512 (cond ((use-hypergeo n x)
513 (let ((f) (d) (e))
514 (setq d (add 1 n))
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
522 '((n x)
523 ((unk) "$first" "$chebyshev_u")
524 ((mtimes)
525 ((mplus)
526 ((mtimes)
527 (($unit_step) n)
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)))
531 'grad)
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)
541 (dimension-function
542 (dimension-sub-and-super-scripted-function "U" `(1) nil nil 2 form)
543 result))
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
556 '((n x)
557 ((unk) "$first" "$legendre_p")
558 ((mtimes)
559 ((mplus)
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)))
563 'grad)
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)
573 (dimension-function
574 (dimension-sub-and-super-scripted-function "P" `(1) nil nil 2 form)
575 result))
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
583 '((n x)
584 ((unk) "$first" "$legendre_p")
585 ((mplus)
586 ((mtimes) -1 ((%kron_delta) 0 n)
587 ((mexpt) ((mplus) -1 ((mexpt) x 2)) -1))
588 ((mtimes)
589 ((mplus)
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))))
593 'grad)
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)
603 (dimension-function
604 (dimension-sub-and-super-scripted-function "Q" `(1) nil nil 2 form)
605 result))
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.
618 (defun q0m (m x)
619 (cond ((< m 0)
620 (merror "function q0m called with negative order. File a bug report"))
621 ((= m 0)
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))
626 (add
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.
634 (defun q1m (m x)
635 (cond ((< m 0)
636 (merror "function q1m called with negative order. File a bug report"))
637 ((= m 0)
638 (sub (mul x (q0m 0 x)) 1))
639 ((= m 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))
644 (add
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)
660 (let ((q)
661 (z (sub 1 (mul x x)))
662 (i 1))
663 (setq q (div (simplify `((%log) ,(div (add 1 x) (sub 1 x)))) 2))
664 (while (<= i n)
665 (setq q (add (mul (sub 1 `((rat) 1 ,(* 2 i))) q)
666 (div x (mul 2 i (power z i)))))
667 (incf 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))
676 (cond ((< m 0)
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)))
683 (q) (i 2)
684 (use-rat (or ($ratp x) (floatp x) ($bfloatp x))))
686 (while (<= i n)
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))))
690 (setq q0 q1)
691 (setq q1 q)
692 (incf i))
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
699 '((n m x)
700 ((unk) "$first" "$assoc_legendre_q")
701 ((unk) "$second" "$assoc_legendre_q")
703 ((mplus)
704 ((mtimes)
705 ((mplus)
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))))
711 'grad)
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)
721 (dimension-function
722 (dimension-sub-and-super-scripted-function "Q" `(1) `(2) nil 3 form)
723 result))
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))
731 (cond ((< n 0)
732 (setq f ($assoc_legendre_p (- (abs n) 1) m x))
733 (setq d 1)
734 (setq dx 1))
735 ((> (abs m) n)
736 (setq f 0)
737 (setq d 1))
738 ((< m 0)
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)))))
744 (setq dx 1))
746 (cond ((eql m 0)
747 (setq d 1))
749 (setq d (simplify
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))))))
753 (setq dx 4)
754 (setq f
755 ($ultraspherical (- n m) (add m (rat 1 2)) x)))))
757 (setq d 1)
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
766 '((n m x)
767 ((unk) "$first" "$assoc_legendre_p")
768 ((unk) "$second" "$assoc_legendre_p")
769 ((mtimes simp)
770 ((mplus simp)
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)))
775 'grad)
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)
785 (dimension-function
786 (dimension-sub-and-super-scripted-function "P" `(1) `(2) nil 3 form)
787 result))
789 ;; See A&S 22.5.55 and 22.5.56, page 780.
791 (defun $hermite (n x)
792 (cond ((integerp n)
793 (let ((f) (d) (e))
794 (cond (($evenp n)
795 (setq n (/ n 2))
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)))))
800 (($oddp 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))))
809 (putprop '$hermite
810 '((n x)
811 ((unk) "$first" "$hermite")
812 ((mtimes) 2 n (($hermite) ((mplus) -1 n) x)))
813 'grad)
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)
823 (dimension-function
824 (dimension-sub-and-super-scripted-function "H" `(1) nil nil 2 form)
825 result))
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)
832 (let ((f) (d) (e))
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
843 '((n a x)
844 ((unk) "$first" "$gen_laguerre")
845 ((unk) "$second" "$gen_laguerre")
846 ((mtimes)
847 ((mplus)
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)))
851 ((mexpt) x -1)))
852 'grad)
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)
862 (dimension-function
863 (dimension-sub-and-super-scripted-function "L" `(1) `(2) t 3 form)
864 result))
866 ;; See A & S 22.5.16, page 778.
868 (defun $laguerre (n x)
869 (cond ((use-hypergeo n x)
870 (let ((f) (e))
871 (multiple-value-setq (f e) ($hypergeo11 (mul -1 n) 1 x n))
872 (orthopoly-return-handler 1 f e)))
874 `(($laguerre) ,n ,x))))
876 (putprop '$laguerre
877 '((n x)
878 ((unk) "$first" "$laguerre")
879 ((mtimes)
880 ((mplus)
881 ((mtimes) -1 n (($laguerre) ((mplus) -1 n) x))
882 ((mtimes) n (($laguerre) n x)))
883 ((mexpt) x -1)))
884 'grad)
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)
894 (dimension-function
895 (dimension-sub-and-super-scripted-function "L" `(1) nil nil 2 form)
896 result))
898 (defun $spherical_hankel1 (n x)
899 (let ((f) (d) (e))
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))
905 ((use-hypergeo n x)
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
917 '((n x)
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))))
922 'grad)
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))
933 (1))))
934 (dimension-function `((,form1 simp) ,(nth 2 form)) result)))
936 ;; See A & S 10.1.36.
938 (defun $spherical_hankel2 (n x)
939 (cond ((integerp n)
940 (setq x (mul x (power '$%e (mul '$%i '$%pi (add (mul 2 n) 1)))))
941 (let ((f))
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
947 '((n x)
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))))
952 'grad)
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.
969 (defun p-fun (n x)
970 (let ((s 1) (w 1)
971 (n1 (floor (/ n 2)))
972 (x2 (mul x x)) (m2))
973 (dotimes (m n1 s)
974 (setq m2 (* 2 m))
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)))))
980 (defun q-fun (n x)
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)))
985 (setq m2 (* 2 m))
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
995 ;; return a float.
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))
1014 (cond ((< xr 0.0)
1015 (setq d (if (oddp n) -1 1))
1016 (setq x (mul -1 x))
1017 (setq z (* -1 z))))
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))))
1025 (div (add
1026 (mul (p-fun n x) (simplify `((%sin) ,xt)))
1027 (mul (q-fun n x) (simplify `((%cos) ,xt)))) x)))
1029 ((integerp n)
1030 (mul (if (oddp n) -1 1) ($spherical_bessel_y (- (+ n 1)) x)))
1033 `(($spherical_bessel_j) ,n ,x))))
1035 (putprop '$spherical_bessel_j
1036 '((n x)
1037 ((unk) "$first" "$spherical_bessel_j")
1038 ((mtimes) ((mexpt) ((mplus) 1 ((mtimes) 2 n)) -1)
1039 ((mplus)
1040 ((mtimes) n (($spherical_bessel_j) ((mplus) -1 n) x))
1041 ((mtimes) -1 ((mplus) 1 n)
1042 (($spherical_bessel_j) ((mplus) 1 n) x)))))
1043 'grad)
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))
1065 (cond ((< xr 0.0)
1066 (setq d (if (oddp n) 1 -1))
1067 (setq x (mul -1 x))
1068 (setq z (* -1 z))))
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)
1077 (div (sub
1078 (mul (p-fun n x) (simplify `((%cos) ,xt)))
1079 (mul (q-fun n x) (simplify `((%sin) ,xt)))) x))))
1081 ((integerp n)
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
1086 '((n x)
1087 ((unk) "$first" "$spherical_bessel_y")
1088 ((mtimes) ((mexpt) ((mplus) 1 ((mtimes) 2 n)) -1)
1089 ((mplus)
1090 ((mtimes) n (($spherical_bessel_y) ((mplus) -1 n) x))
1091 ((mtimes) -1 ((mplus) 1 n)
1092 (($spherical_bessel_y) ((mplus) 1 n) x)))))
1093 'grad)
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)
1116 (interval-mult
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)
1124 ((< m 0)
1125 (interval-mult (if (oddp m) -1 1)
1126 ($spherical_harmonic n (- m) th (mul -1 p))))
1128 (interval-mult
1129 (mul ($exp (mul '$%i m p))
1130 (power (div (* (+ (* 2 n) 1) (factorial (- n m)))
1131 (mul '$%pi (* 4 (factorial (+ n m)))))
1132 `((rat) 1 2)))
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)
1145 (dimension-function
1146 (dimension-sub-and-super-scripted-function "Y" `(1) `(2) nil 3 form)
1147 result))
1149 (putprop '$spherical_harmonic
1150 '((n m theta phi)
1151 ((unk) "$first" "$spherical_harmonic")
1152 ((unk) "$second" "$spherical_harmonic")
1153 ((mplus)
1154 ((mtimes) ((rat ) -1 2)
1155 ((mexpt)
1156 ((mtimes) ((mplus) ((mtimes) -1 m) n)
1157 ((mplus) 1 m n))
1158 ((rat) 1 2))
1159 (($spherical_harmonic) n ((mplus) 1 m) theta phi)
1160 ((mexpt) $%e ((mtimes) -1 $%i phi)))
1161 ((mtimes) ((rat) 1 2)
1162 ((mexpt)
1163 ((mtimes) ((mplus) 1 ((mtimes) -1 m) n)
1164 ((mplus) m n))
1165 ((rat) 1 2))
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)))
1170 'grad)
1172 (defun maxima-float-to-lisp-float (y)
1173 (let* ((x ($rectform y))
1174 (xr ($realpart x))
1175 (xi ($imagpart x)))
1176 (cond ((or (floatp xr) (floatp xi))
1177 (setq xr (float xr)
1178 xi (float xi))
1179 (if (= 0.0 xi) xr (complex xr xi)))
1181 y))))
1183 (defun lisp-float-to-maxima-float (x)
1184 (if (complexp 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))
1196 (u) (u0) (um1))
1198 (setq f0 1.0)
1199 (setq fm1 0.0)
1200 (setq x (- b x))
1201 (setq n (- n))
1202 (while (< i n)
1203 (setf (aref fs i) f0)
1204 (setq dk (+ b i))
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)
1210 (setq fm1 f0)
1211 (setq f0 f)
1212 (incf i))
1213 (setf (aref fs i) f0)
1214 (setq i 1)
1215 (setq err 1.0)
1216 (setq u0 1.0)
1217 (setq um1 0.0)
1218 (while (< i n)
1219 (setq k (- n i))
1220 (setq u (+ (* (aref as k) u0)
1221 (* (aref bs (+ 1 k)) um1)))
1222 (setq um1 u0)
1223 (setq u0 u)
1224 (setq err (+ err (abs (* u0 (aref fs k)))))
1225 (incf i))
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))
1236 (u) (u0) (um1))
1238 (setq f0 1.0)
1239 (setq fm1 0.0)
1240 (setq n (- n))
1241 (while (< i n)
1242 (setf (aref fs i) f0)
1243 (setq dk (+ c i))
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)
1249 (setq fm1 f0)
1250 (setq f0 f)
1251 (incf i))
1252 (setf (aref fs i) f0)
1253 (setq i 1)
1254 (setq err 1.0)
1255 (setq u0 1.0)
1256 (setq um1 0.0)
1257 (while (< i n)
1258 (setq k (- n i))
1259 (setq u (+ (* (aref as k) u0)
1260 (* (aref bs (+ 1 k)) um1)))
1261 (setq um1 u0)
1262 (setq u0 u)
1263 (incf i)
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
1290 ;; reading glasses.
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)
1300 (if (not (= n m))
1301 (merror "Function ~:M needs ~:M arguments, instead it received ~:M"
1302 fn n 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))
1310 (a (nth 2 arg))
1311 (b (nth 3 arg))
1312 (x (nth 4 arg)))
1313 (simplify
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)
1318 ((mplus)
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)
1323 ((mplus)
1324 ((mtimes)
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))
1335 (a (nth 2 arg))
1336 (x (nth 3 arg)))
1337 (simplify
1338 `((mequal) (($ultraspherical) ((mplus) 1 ,n) ,a ,x)
1339 ((mtimes) ((mexpt) ((mplus) 1 ,n) -1)
1340 ((mplus)
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))
1349 (x (nth 2 arg)))
1350 (simplify
1351 `((mequal ) ((,fn) ((mplus ) 1 ,n) ,x)
1352 ((mplus )
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))
1359 (x (nth 2 arg))
1360 (z (if (eq fn '$legendre_q)
1361 `((mtimes) -1 ((%kron_delta) ,n 0)) 0)))
1362 (simplify
1363 `((mequal) ((,fn) ((mplus) 1 ,n) ,x)
1364 ((mplus)
1365 ((mtimes) ((mexpt) ((mplus) 1 ,n) -1)
1366 ((mplus)
1367 ((mtimes) ((mtimes) -1 ,n)
1368 ((,fn) ((mplus) -1 ,n) ,x))
1369 ((mtimes) ((mplus) 1 ((mtimes) 2 ,n))
1370 ((,fn) ,n ,x) ,x)))
1371 ,z)))))
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))
1376 (m (nth 2 arg))
1377 (x (nth 3 arg)))
1378 (simplify
1379 `((mequal) ((,fn) ((mplus) 1 ,n) ,m ,x)
1380 ((mtimes)
1381 ((mexpt) ((mplus) 1 ((mtimes) -1 ,m) ,n) -1)
1382 ((mplus)
1383 ((mtimes)
1384 ((mplus) ((mtimes) -1 ,m)
1385 ((mtimes) -1 ,n))
1386 ((,fn) ((mplus) -1 ,n) ,m ,x))
1387 ((mtimes) ((mplus) 1 ((mtimes) 2 ,n))
1388 ((,fn) ,n ,m ,x) ,x)))))))
1390 ((eq fn '$laguerre)
1391 (check-arg-length fn 2 (- (length arg) 1))
1392 (let ((n (nth 1 arg))
1393 (x (nth 2 arg)))
1394 (simplify
1395 `((mequal ) (($laguerre ) ((mplus ) 1 ,n) ,x)
1396 ((mtimes ) ((mexpt ) ((mplus ) 1 ,n) -1)
1397 ((mplus )
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))
1405 (a (nth 2 arg))
1406 (x (nth 3 arg)))
1407 (simplify
1408 `((mequal) (($gen_laguerre) ((mplus) 1 ,n) ,a ,x)
1409 ((mtimes) ((mexpt ) ((mplus) 1 ,n) -1)
1410 ((mplus)
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)))))))))
1416 ((eq fn '$hermite)
1417 (check-arg-length fn 2 (- (length arg) 1))
1418 (let ((n (nth 1 arg))
1419 (x (nth 2 arg)))
1420 (simplify
1421 `((mequal) (($hermite) ((mplus) 1 ,n) ,x)
1422 ((mplus)
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)
1428 :test 'eq)
1429 (check-arg-length fn 2 (- (length arg) 1))
1430 (let ((n (nth 1 arg))
1431 (x (nth 2 arg)))
1432 (simplify
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))
1458 (b (nth 3 arg))
1459 (x (nth 4 arg)))
1460 (simplify
1461 `((mlist)
1462 ((mtimes) ((mexpt) ((mplus) 1 ((mtimes) -1 ,x)) ,a)
1463 ((mexpt) ((mplus ) 1 ,x) ,b))
1464 -1 1))))
1466 ((eq fn '$ultraspherical)
1467 (check-arg-length fn 3 (- (length arg) 1))
1468 (let ((a (nth 2 arg))
1469 (x (nth 3 arg)))
1470 (simplify
1471 `((mlist)
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)))
1478 (simplify
1479 `((mlist)
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)))
1486 (simplify
1487 `((mlist)
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))
1493 `((mlist) 1 -1 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))
1500 `((mlist) 1 -1 1))
1502 ((eq fn '$laguerre)
1503 (check-arg-length fn 2 (- (length arg) 1))
1504 (let ((x (nth 2 arg)))
1505 (simplify
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))
1511 (x (nth 3 arg)))
1512 (simplify
1513 `((mlist)
1514 ((mtimes) ((mexpt) ,x ,a)
1515 ((mexpt) $%e ((mtimes) -1 ,x))) 0 $inf))))
1517 ((eq fn '$hermite)
1518 (check-arg-length fn 2 (- (length arg) 1))
1519 (let ((x (nth 2 arg)))
1520 (simplify
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))))