In orthopoly, avoid use of strings in pretty printer output;
[maxima.git] / share / orthopoly / orthopoly.lisp
blobd98d2cab90420450ccea907cbb80ca8db53dabc9
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) ((mprogn) ,@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 (:load-toplevel :execute)
259 (let (($context '$global) (context '$global))
260 (meval '(($declare) $pochhammer $complex))))
262 (defmvar $pochhammer_max_index 100)
264 ;; This disallows noninteger assignments to $pochhammer_max_index.
266 (setf (get '$pochhammer_max_index 'assign)
267 #'(lambda (a b)
268 (declare (ignore a))
269 (if (not (and (atom b) (integerp b)))
270 (progn
271 (mtell "The value of `pochhammer_max_index' must be an integer.~%")
272 'munbindp))))
274 (defun $pochhammer (x n)
275 (take '($pochhammer) x n))
277 (in-package #:bigfloat)
279 ;; Numerical evaluation of pochhammer using the bigfloat package.
280 (defun pochhammer (x n)
281 (let ((acc 1))
282 (if (< n 0) (/ 1 (pochhammer (+ x n) (- n)))
283 (dotimes (k n acc)
284 (setq acc (* acc (+ k x)))))))
286 (in-package :maxima)
288 (defun simp-pochhammer (e y z)
289 (declare (ignore y))
290 (let ((x) (n) (return-a-rat))
291 (twoargcheck e)
292 (setq return-a-rat ($ratp (second e)))
293 (setq x (simplifya (specrepcheck (second e)) z))
294 (setq n (simplifya (specrepcheck (third e)) z))
296 (cond ((or ($taylorp (second e)) ($taylorp (third e)))
297 (setq x (simplifya (second e) z))
298 (setq n (simplifya (third e) z))
299 ($taylor (div (take '(%gamma) (add x n)) (take '(%gamma) x))))
301 ((eql n 0) 1)
303 ;; pochhammer(1,n) = n! (factorial is faster than bigfloat::pochhammer.)
304 ((eql x 1) (take '(mfactorial) n))
306 ;; pure numeric evaluation--use numeric package.
307 ((and (integerp n) (complex-number-p x '$numberp))
308 (maxima::to (bigfloat::pochhammer (bigfloat::to x) (bigfloat::to n))))
310 ((and (integerp (mul 2 n)) (integerp (mul 2 x)) (> (mul 2 n) 0) (> (mul 2 x) 0))
311 (div (take '(%gamma) (add x n)) (take '(%gamma) x)))
313 ;; Use a reflection identity when (great (neg n) n) is true. Specifically,
314 ;; use pochhammer(x,-n) * pochhammer(x-n,n) = 1; thus pochhammer(x,n) = 1/pochhammer(x+n,-n).
315 ((great (neg n) n)
316 (div 1 (take '($pochhammer) (add x n) (neg n))))
318 ;; Expand when n is an integer and either abs(n) < $expop or abs(n) < $pochhammer_max_index.
319 ;; Let's give $expand a bit of early help.
320 ((and (integerp n) (or (< (abs n) $expop) (< (abs n) $pochhammer_max_index)))
321 (if (< n 0) (div 1 (take '($pochhammer) (add x n) (neg n)))
322 (let ((acc 1))
323 (if (or (< (abs n) $expop) return-a-rat) (setq acc ($rat acc) x ($rat x)))
324 (dotimes (k n (if return-a-rat acc ($ratdisrep acc)))
325 (setq acc (mul acc (add k x)))))))
327 ;; return a nounform.
328 (t `(($pochhammer simp) ,x ,n)))))
330 (putprop '$pochhammer
331 '((x n)
332 ((mtimes) (($pochhammer) x n)
333 ((mplus) ((mtimes) -1 ((mqapply) (($psi array) 0) x))
334 ((mqapply) (($psi array) 0) ((mplus) n x))))
335 ((mtimes) (($pochhammer) x n)
336 ((mqapply) (($psi array) 0) ((mplus) n x))))
337 'grad)
339 (defprop $pochhammer tex-pochhammer tex)
341 (defun tex-pochhammer (x l r)
342 (setq x (mapcar #'(lambda (s) (tex s nil nil nil nil)) (cdr x)))
343 (append l
344 `("\\left(")
345 (nth 0 x)
346 `("\\right)_{")
347 (nth 1 x)
348 `("}")
351 (setf (get '$pochhammer 'dimension) 'dimension-pochhammer)
353 (defun dimension-pochhammer (form result)
354 (setq form `(( ((mprogn) ,(nth 1 form)) simp array) ,(nth 2 form)))
355 (dimension-array form result))
357 ;; pochhammer-quotient(a b x n) returns pochhammer( a,n) / pochhammer(b,n).
358 ;; Only when one of a, b, or x is a floating point number and the others are
359 ;; numeric types (that is when (use-float a b x) evaluates to true) does this
360 ;; function differ from using (div ($pochhammer a n) ($pochhammer b n)).
361 ;; In the floating point case, we arrange the operations to reduce rounding
362 ;; errors and to reduce over and underflows.
364 (defun pochhammer-quotient (a b x n)
365 (cond ((mminusp n)
366 (pochhammer-quotient b a x (neg n)))
367 ((and (integerp n) (use-float a b x))
368 (let ((acc 1.0))
369 (setq a (maxima-to-lisp-complex-float a))
370 (setq b (maxima-to-lisp-complex-float b))
371 (dotimes (i n (lisp-float-to-maxima-float acc))
372 (setq acc (* acc (/ (+ i a) (+ i b)))))))
373 (t (div ($pochhammer a n) ($pochhammer b n)))))
375 (defun use-hypergeo (n x)
376 (declare (ignore x))
377 (or (and (integerp n) (> n -1))
378 ($featurep n '$integer)))
379 ; (and ($featurep n '$integer) ($ratnump x))))
381 ;; See A&S 22.5.42, page 779. For integer n, use the identity
382 ;; binomial(n+a,n) = pochhammer(a+1,n)/pochhammer(1,n)
384 ;; The condition number of the pochhammer function is
385 ;; |1 + x/(x+1) + x/(x+2) + ... + x/(x+n-1)| <= n.
386 ;; The relative floating point error in computing the pochhammer
387 ;; function is bounded by 3 n eps. Putting these errors together,
388 ;; the error in d is bounded 4 n |d| eps.
390 ;; We're looking at (d +|- 4 n eps |d|)(f +|- e) = d (f +|- (e + 4 n eps |f|)) +
391 ;; O(eps^2).
393 (defun $jacobi_p (n a b x)
394 (cond ((use-hypergeo n x)
395 (let ((f) (d) (e))
396 ;(setq d (div ($pochhammer (add a 1) n) ($pochhammer 1 n)))
397 (setq d (pochhammer-quotient (add a 1) 1 x n))
398 (multiple-value-setq (f e)
399 ($hypergeo21 (mul -1 n) (add n a b 1) (add a 1)
400 (div (add 1 (mul -1 x)) 2) n))
401 (setq e (if e (+ e (* 4 n (abs f) +flonum-epsilon+)) nil))
402 (orthopoly-return-handler d f e)))
403 (t `(($jacobi_p simp) ,n ,a ,b ,x))))
405 (putprop '$jacobi_p
406 '((n a b x)
407 ((unk) "$first" "$jacobi_p")
408 ((unk) "$second" "$jacobi_p")
409 ((unk) "$third" "$jacobi_p")
411 ((mtimes)
412 ((mexpt) ((mplus ) a b ((mtimes ) 2 n)) -1)
413 ((mplus)
414 ((mtimes) 2
415 (($unit_step) n)
416 ((mplus) a n) ((mplus) b n)
417 (($jacobi_p) ((mplus) -1 n) a b x))
418 ((mtimes) n (($jacobi_p) n a b x)
419 ((mplus) a ((mtimes ) -1 b)
420 ((mtimes)
421 ((mplus) ((mtimes) -1 a) ((mtimes ) -1 b)
422 ((mtimes) -2 n)) x))))
423 ((mexpt) ((mplus) 1 ((mtimes) -1 ((mexpt ) x 2))) -1)))
424 'grad)
426 (defprop $jacobi_p tex-jacobi-poly tex)
428 (defun tex-jacobi-poly (x l r)
429 (tex-sub-and-super-scripted-function "P" `(0) nil `(1 2) t 3 x l r))
431 (setf (get '$jacobi_p 'dimension) 'dimension-jacobi-p)
433 (defun dimension-jacobi-p (form result)
434 (dimension-function
435 (dimension-sub-and-super-scripted-function '|$p| `(1) `(2 3) t 4 form)
436 result))
438 ;; See A&S 22.5.46, page 779.
440 (defun $ultraspherical (n a x)
441 (cond ((use-hypergeo n x)
442 (let ((f) (d) (e))
443 ;(setq d (div ($pochhammer (mul 2 a) n) ($pochhammer 1 n)))
444 (setq d (pochhammer-quotient (mul 2 a) 1 x n))
445 (multiple-value-setq (f e)
446 ($hypergeo21 (mul -1 n) (add n (mul 2 a)) (add a (div 1 2))
447 (div (add 1 (mul -1 x)) 2) n))
448 (setq e (if e (+ e (* 4 n (abs f) +flonum-epsilon+)) nil))
449 (orthopoly-return-handler d f e)))
450 (t `(($ultraspherical simp) ,n ,a ,x))))
452 (putprop '$ultraspherical
453 '((n a x)
454 ((unk) "$first" "$ultraspherical")
455 ((unk) "$second" "$ultrapsherical")
456 ((mtimes)
457 ((mplus)
458 ((mtimes)
459 (($unit_step) n)
460 ((mplus) -1 ((mtimes) 2 a) n)
461 (($ultraspherical) ((mplus) -1 n) a x))
462 ((mtimes) -1 n (($ultraspherical) n a x) x))
463 ((mexpt) ((mplus) 1 ((mtimes) -1 ((mexpt) x 2))) -1)))
464 'grad)
466 (defprop $ultraspherical tex-ultraspherical tex)
468 (defun tex-ultraspherical (x l r)
469 (tex-sub-and-super-scripted-function "C" `(0) nil `(1) t 2 x l r))
471 (setf (get '$ultraspherical 'dimension) 'dimension-ultraspherical)
473 (defun dimension-ultraspherical (form result)
474 (dimension-function
475 (dimension-sub-and-super-scripted-function '|$c| `(1) `(2) t 3 form)
476 result))
478 (defun $chebyshev_t (n x)
479 (cond ((use-hypergeo n x)
480 (let ((f) (e))
481 (multiple-value-setq (f e)
482 ($hypergeo21 (mul -1 n) n (rat 1 2) (div (add 1 (mul -1 x)) 2) n))
483 (orthopoly-return-handler 1 f e)))
484 (t `(($chebyshev_t simp) ,n ,x))))
486 (putprop '$chebyshev_t
487 '((n x)
488 ((unk) "$first" "$chebyshev_t")
489 ((mtimes)
490 ((mplus)
491 ((mtimes) n (($chebyshev_t) ((mplus ) -1 n) x))
492 ((mtimes ) -1 n (($chebyshev_t) n x) x))
493 ((mexpt) ((mplus ) 1 ((mtimes) -1 ((mexpt) x 2))) -1)))
494 'grad)
496 (defprop $chebyshev_t tex-chebyshev-t tex)
498 (defun tex-chebyshev-t (x l r)
499 (tex-sub-and-super-scripted-function "T" `(0) nil nil nil 1 x l r))
501 (setf (get '$chebyshev_t 'dimension) 'dimension-chebyshev-t)
503 (defun dimension-chebyshev-t (form result)
504 (dimension-function
505 (dimension-sub-and-super-scripted-function '|$t| `(1) nil nil 2 form)
506 result))
508 ;; See A & S 22.5.48, page 779.
510 (defun $chebyshev_u (n x)
511 (cond ((use-hypergeo n x)
512 (let ((f) (d) (e))
513 (setq d (add 1 n))
514 (multiple-value-setq (f e)
515 ($hypergeo21 (mul -1 n) (add 2 n) (rat 3 2)
516 (div (add 1 (mul -1 x)) 2) n))
517 (orthopoly-return-handler d f e)))
518 (t `(($chebyshev_u simp) ,n ,x))))
520 (putprop '$chebyshev_u
521 '((n x)
522 ((unk) "$first" "$chebyshev_u")
523 ((mtimes)
524 ((mplus)
525 ((mtimes)
526 (($unit_step) n)
527 ((mplus) 1 n) (($chebyshev_u) ((mplus) -1 n) x))
528 ((mtimes) -1 n (($chebyshev_u) n x) x))
529 ((mexpt) ((mplus ) 1 ((mtimes) -1 ((mexpt) x 2))) -1)))
530 'grad)
532 (defprop $chebyshev_u tex-chebyshev-u tex)
534 (defun tex-chebyshev-u (x l r)
535 (tex-sub-and-super-scripted-function "U" `(0) nil nil nil 1 x l r))
537 (setf (get '$chebyshev_u 'dimension) 'dimension-chebyshev-u)
539 (defun dimension-chebyshev-u (form result)
540 (dimension-function
541 (dimension-sub-and-super-scripted-function '|$u| `(1) nil nil 2 form)
542 result))
544 ;; See A&S 8.2.1 page 333 and 22.5.35 page 779. We evaluate the legendre
545 ;; polynomials as jacobi_p(n,0,0,x). Eat less exercise more.
547 (defun $legendre_p (n x)
548 (cond ((use-hypergeo n x)
549 (if (and (integerp n) (< n 0))
550 ($legendre_p (- (abs n) 1) x)
551 ($jacobi_p n 0 0 x)))
552 (t `(($legendre_p simp) ,n ,x))))
554 (putprop '$legendre_p
555 '((n x)
556 ((unk) "$first" "$legendre_p")
557 ((mtimes)
558 ((mplus)
559 ((mtimes) n (($legendre_p) ((mplus) -1 n) x))
560 ((mtimes) -1 n (($legendre_p) n x) x))
561 ((mexpt) ((mplus) 1 ((mtimes) -1 ((mexpt) x 2))) -1)))
562 'grad)
564 (defprop $legendre_p tex-legendre-p tex)
566 (defun tex-legendre-p (x l r)
567 (tex-sub-and-super-scripted-function "P" `(0) nil nil nil 1 x l r))
569 (setf (get '$legendre_p 'dimension) 'dimension-legendre-p)
571 (defun dimension-legendre-p (form result)
572 (dimension-function
573 (dimension-sub-and-super-scripted-function '|$p| `(1) nil nil 2 form)
574 result))
576 (defun $legendre_q (n x)
577 (if (and (integerp n) (> n -1))
578 ($assoc_legendre_q n 0 x)
579 `(($legendre_q simp) ,n ,x)))
581 (putprop '$legendre_q
582 '((n x)
583 ((unk) "$first" "$legendre_p")
584 ((mplus)
585 ((mtimes) -1 ((%kron_delta) 0 n)
586 ((mexpt) ((mplus) -1 ((mexpt) x 2)) -1))
587 ((mtimes)
588 ((mplus)
589 ((mtimes) n (($legendre_q) ((mplus) -1 n) x))
590 ((mtimes) -1 n (($legendre_q) n x) x))
591 ((mexpt) ((mplus) 1 ((mtimes) -1 ((mexpt) x 2))) -1))))
592 'grad)
594 (defprop $legendre_q tex-legendre-q tex)
596 (defun tex-legendre-q (x l r)
597 (tex-sub-and-super-scripted-function "Q" `(0) nil nil nil 1 x l r))
599 (setf (get '$legendre_q 'dimension) 'dimension-legendre-q)
601 (defun dimension-legendre-q (form result)
602 (dimension-function
603 (dimension-sub-and-super-scripted-function '|$q| `(1) nil nil 2 form)
604 result))
606 ;; See A & S 8.6.7 and 8.2.6 pages 333 and 334. I chose the
607 ;; definition that is real valued on (-1,1).
609 ;; For negative m, A & S 8.2.6 page 333 and G & R 8.706 page 1000
610 ;; disagree; the factor of exp(i m pi) in A & S 8.1.6 suggests to me that
611 ;; A & S 8.2.6 is bogus. As further evidence, Macsyma 2.4 seems to
612 ;; agree with G & R 8.706. I'll use G & R.
614 ;; Return assoc_legendre(0,m,x). This isn't a user-level function; we
615 ;; don't check that m is a positive integer.
617 (defun q0m (m x)
618 (cond ((< m 0)
619 (merror "function q0m called with negative order. File a bug report"))
620 ((= m 0)
621 (div (simplify `((%log) ,(div (add 1 x) (sub 1 x)))) 2))
623 (mul (factorial (- m 1)) `((rat simp) 1 2)
624 (if (oddp m) -1 1) (power (sub 1 (mult x x)) (div m 2))
625 (add
626 (mul (if (oddp m) 1 -1) (power (add 1 x) (neg m)))
627 (power (sub 1 x) (neg m)))))))
629 ;; Return assoc_legendre(1,m,x). This isn't a user-level function; we
630 ;; don't check that m is a positive integer; we don't check that m is
631 ;; a positive integer.
633 (defun q1m (m x)
634 (cond ((< m 0)
635 (merror "function q1m called with negative order. File a bug report"))
636 ((= m 0)
637 (sub (mul x (q0m 0 x)) 1))
638 ((= m 1)
639 (mul -1 (power (sub 1 (mult x x)) `((rat simp) 1 2))
640 (sub (q0m 0 x) (div x (sub (mul x x) 1)))))
642 (mul (if (oddp m) -1 1) (power (sub 1 (mult x x)) (div m 2))
643 (add
644 (mul (factorial (- m 2)) `((rat simp) 1 2) (if (oddp m) -1 1)
645 (sub (power (add x 1) (sub 1 m))
646 (power (sub x 1) (sub 1 m))))
648 (mul (factorial (- m 1)) `((rat simp) 1 2) (if (oddp m) -1 1)
649 (add (power (add x 1) (neg m))
650 (power (sub x 1) (neg m)))))))))
652 ;; Return assoc_legendre_q(n,n,x). I don't have a reference that gives
653 ;; a formula for assoc_legendre_q(n,n,x). To figure one out, I used
654 ;; A&S 8.2.1 and a formula for assoc_legendre_p(n,n,x).
656 ;; After finishing the while loop, q = int(1/(1-t^2)^(n+1),t,0,x).
658 (defun assoc-legendre-q-nn (n x)
659 (let ((q)
660 (z (sub 1 (mul x x)))
661 (i 1))
662 (setq q (div (simplify `((%log) ,(div (add 1 x) (sub 1 x)))) 2))
663 (while (<= i n)
664 (setq q (add (mul (sub 1 `((rat) 1 ,(* 2 i))) q)
665 (div x (mul 2 i (power z i)))))
666 (incf i))
667 (mul (expt -2 n) (factorial n) (power z (div n 2)) q)))
669 ;; Use degree recursion to find the assoc_legendre_q function.
670 ;; See A&S 8.5.3. When i = m in the while loop, we have a special
671 ;; case. For negative order, see A&S 8.2.6.
673 (defun $assoc_legendre_q (n m x)
674 (cond ((and (integerp n) (> n -1) (integerp m) (<= (abs m) n))
675 (cond ((< m 0)
676 (mul (div (factorial (+ n m)) (factorial (- n m)))
677 ($assoc_legendre_q n (- m) x)))
679 (if (not (or (floatp x) ($bfloatp x))) (setq x ($rat x)))
680 (let* ((q0 (q0m m x))
681 (q1 (if (= n 0) q0 (q1m m x)))
682 (q) (i 2)
683 (use-rat (or ($ratp x) (floatp x) ($bfloatp x))))
685 (while (<= i n)
686 (setq q (if (= i m) (assoc-legendre-q-nn i x)
687 (div (sub (mul (- (* 2 i) 1) x q1)
688 (mul (+ i -1 m) q0)) (- i m))))
689 (setq q0 q1)
690 (setq q1 q)
691 (incf i))
692 (if use-rat q1 ($ratsimp q1))))))
693 (t `(($assoc_legendre_q simp) ,n ,m ,x))))
695 ;; See G & R, 8.733, page 1005 first equation.
697 (putprop '$assoc_legendre_q
698 '((n m x)
699 ((unk) "$first" "$assoc_legendre_q")
700 ((unk) "$second" "$assoc_legendre_q")
702 ((mplus)
703 ((mtimes)
704 ((mplus)
705 ((mtimes) -1 ((mplus) 1 ((mtimes) -1 m) n)
706 (($assoc_legendre_q ) ((mplus ) 1 n) m x))
707 ((mtimes) ((mplus) 1 n)
708 (($assoc_legendre_q ) n m x) x))
709 ((mexpt) ((mplus) 1 ((mtimes) -1 ((mexpt) x 2))) -1))))
710 'grad)
712 (defprop $assoc_legendre_q tex-assoc-legendre-q tex)
714 (defun tex-assoc-legendre-q (x l r)
715 (tex-sub-and-super-scripted-function "Q" `(0) nil `(1) nil 2 x l r))
717 (setf (get '$assoc_legendre_q 'dimension) 'dimension-assoc-legendre-q)
719 (defun dimension-assoc-legendre-q (form result)
720 (dimension-function
721 (dimension-sub-and-super-scripted-function '|$q| `(1) `(2) nil 3 form)
722 result))
724 ;; See A & S 22.5.37 page 779, A & S 8.6.6 (second equation) page 334, and
725 ;; A & S 8.2.5 page 333. For n < 0, see A&S 8.2.1 page 333.
727 (defun $assoc_legendre_p (n m x)
728 (let ((f) (d) (dx 0))
729 (cond ((and (integerp n) (integerp m))
730 (cond ((< n 0)
731 (setq f ($assoc_legendre_p (- (abs n) 1) m x))
732 (setq d 1)
733 (setq dx 1))
734 ((> (abs m) n)
735 (setq f 0)
736 (setq d 1))
737 ((< m 0)
738 (setq f ($assoc_legendre_p n (neg m) x))
739 ;; Adding a factor (-1)^m to the transformation to get the
740 ;; expected results for odd negative integers. DK 09/2009
741 (setq d (mul (power -1 m)
742 (div (factorial (+ n m)) (factorial (- n m)))))
743 (setq dx 1))
745 (cond ((eql m 0)
746 (setq d 1))
748 (setq d (simplify
749 `((%genfact) ,(- (* 2 m) 1) ,(- m 1) 2)))
750 (setq d (mul d (if (oddp m) -1 1)))
751 (setq d (mul d (power (sub 1 (mul x x)) (div m 2))))))
752 (setq dx 4)
753 (setq f
754 ($ultraspherical (- n m) (add m (rat 1 2)) x)))))
756 (setq d 1)
757 (setq f `(($assoc_legendre_p simp) ,n ,m ,x))))
758 (interval-mult d f (* +flonum-epsilon+ dx))))
761 ;; For the derivative of the associated legendre p function, see
762 ;; A & S 8.5.4 page 334.
764 (putprop `$assoc_legendre_p
765 '((n m x)
766 ((unk) "$first" "$assoc_legendre_p")
767 ((unk) "$second" "$assoc_legendre_p")
768 ((mtimes simp)
769 ((mplus simp)
770 ((mtimes simp) -1 ((mplus simp) m n) (($unit_step) n)
771 (($assoc_legendre_p simp) ((mplus simp) -1 n) m x))
772 ((mtimes simp) n (($assoc_legendre_p simp) n m x) x))
773 ((mexpt simp) ((mplus simp) -1 ((mexpt simp) x 2)) -1)))
774 'grad)
776 (defprop $assoc_legendre_p tex-assoc-legendre-p tex)
778 (defun tex-assoc-legendre-p (x l r)
779 (tex-sub-and-super-scripted-function "P" `(0) nil `(1) nil 2 x l r))
781 (setf (get '$assoc_legendre_p 'dimension) 'dimension-assoc-legendre-p)
783 (defun dimension-assoc-legendre-p (form result)
784 (dimension-function
785 (dimension-sub-and-super-scripted-function '|$p| `(1) `(2) nil 3 form)
786 result))
788 ;; See A&S 22.5.55 and 22.5.56, page 780.
790 (defun $hermite (n x)
791 (cond ((integerp n)
792 (let ((f) (d) (e))
793 (cond (($evenp n)
794 (setq n (/ n 2))
795 (multiple-value-setq (f e)
796 ($hypergeo11 (- n) (rat 1 2) (mul x x) n))
797 (setq d (mul (if (oddp n) -1 1) (factorial (* 2 n))
798 (div 1 (factorial n)))))
799 (($oddp n)
800 (setq n (/ (- n 1) 2))
801 (multiple-value-setq (f e)
802 ($hypergeo11 (- n) (rat 3 2) (mul x x) n))
803 (setq d (mul (if (oddp n) -1 1) (factorial (+ 1 (* 2 n))) 2 x
804 (div 1 (factorial n))))))
805 (orthopoly-return-handler d f e)))
806 (t `(($hermite) ,n ,x))))
808 (putprop '$hermite
809 '((n x)
810 ((unk) "$first" "$hermite")
811 ((mtimes) 2 n (($hermite) ((mplus) -1 n) x)))
812 'grad)
814 (defprop $hermite tex-hermite tex)
816 (defun tex-hermite (x l r)
817 (tex-sub-and-super-scripted-function "H" `(0) nil nil nil 1 x l r))
819 (setf (get '$hermite 'dimension) 'dimension-hermite)
821 (defun dimension-hermite (form result)
822 (dimension-function
823 (dimension-sub-and-super-scripted-function '|$h| `(1) nil nil 2 form)
824 result))
826 ;; See A & S 22.5.54, page 780. For integer n, use the identity
827 ;; binomial(n+a,n) = pochhammer(a+1,n)/pochhammer(1,n)
829 (defun $gen_laguerre (n a x)
830 (cond ((use-hypergeo n x)
831 (let ((f) (d) (e))
832 ;(setq d (div ($pochhammer (add a 1) n) ($pochhammer 1 n)))
833 (setq d (pochhammer-quotient (add a 1) 1 x n))
834 (multiple-value-setq (f e)
835 ($hypergeo11 (mul -1 n) (add 1 a) x n))
836 (setq e (if e (+ e (* 4 (abs f) +flonum-epsilon+ n)) nil))
837 (orthopoly-return-handler d f e)))
839 `(($gen_laguerre) ,n ,a ,x))))
841 (putprop '$gen_laguerre
842 '((n a x)
843 ((unk) "$first" "$gen_laguerre")
844 ((unk) "$second" "$gen_laguerre")
845 ((mtimes)
846 ((mplus)
847 ((mtimes) -1 ((mplus) a n)
848 (($unit_step) n) (($gen_laguerre) ((mplus) -1 n) a x))
849 ((mtimes) n (($gen_laguerre) n a x)))
850 ((mexpt) x -1)))
851 'grad)
853 (defprop $gen_laguerre tex-gen-laguerre tex)
855 (defun tex-gen-laguerre (x l r)
856 (tex-sub-and-super-scripted-function "L" `(0) nil `(1) t 1 x l r))
858 (setf (get '$gen_laguerre 'dimension) 'dimension-gen-laguerre)
860 (defun dimension-gen-laguerre (form result)
861 (dimension-function
862 (dimension-sub-and-super-scripted-function '|$l| `(1) `(2) t 3 form)
863 result))
865 ;; See A & S 22.5.16, page 778.
867 (defun $laguerre (n x)
868 (cond ((use-hypergeo n x)
869 (let ((f) (e))
870 (multiple-value-setq (f e) ($hypergeo11 (mul -1 n) 1 x n))
871 (orthopoly-return-handler 1 f e)))
873 `(($laguerre) ,n ,x))))
875 (putprop '$laguerre
876 '((n x)
877 ((unk) "$first" "$laguerre")
878 ((mtimes)
879 ((mplus)
880 ((mtimes) -1 n (($laguerre) ((mplus) -1 n) x))
881 ((mtimes) n (($laguerre) n x)))
882 ((mexpt) x -1)))
883 'grad)
885 (defprop $laguerre tex-laguerre tex)
887 (defun tex-laguerre (x l r)
888 (tex-sub-and-super-scripted-function "L" `(0) nil nil nil 1 x l r))
890 (setf (get '$laguerre 'dimension) 'dimension-laguerre)
892 (defun dimension-laguerre (form result)
893 (dimension-function
894 (dimension-sub-and-super-scripted-function '|$l| `(1) nil nil 2 form)
895 result))
897 (defun $spherical_hankel1 (n x)
898 (let ((f) (d) (e))
899 (cond ((and (integerp n) (< n 0))
900 (setq d (mul '$%i (if (oddp n) 1 -1)))
901 (multiple-value-setq (f e)
902 ($spherical_hankel1 (add -1 (mul -1 n)) x))
903 (orthopoly-return-handler d f e))
904 ((use-hypergeo n x)
905 (multiple-value-setq (f e)
906 ($hypergeo11 (mul -1 n) (mul -2 n) (mul -2 '$%i x) n))
907 (setq d (mul '$%i (if (= 0 n) 1
908 (simplify `((%genfact) ,(add (mul 2 n) -1)
909 ,(add n (rat -1 2)) 2)))
910 (power '$%e (mul '$%i x)) (div -1 (power x (add 1 n)))))
911 (orthopoly-return-handler d f e))
913 `(($spherical_hankel1) ,n ,x)))))
915 (putprop '$spherical_hankel1
916 '((n x)
917 ((unk) "$first" "$spherical_hankel1")
918 ((mplus simp) (($spherical_hankel1) ((mplus) -1 n) x)
919 ((mtimes simp) -1 ((mplus) 1 n)
920 (($spherical_hankel1) n x) ((mexpt) x -1))))
921 'grad)
923 (defprop $spherical_hankel1 tex-spherical-hankel-1 tex)
925 (defun tex-spherical-hankel-1 (x l r)
926 (tex-sub-and-super-scripted-function "h^{(1)}" `(0) nil nil nil 1 x l r))
928 (setf (get '$spherical_hankel1 'dimension) 'dimension-spherical-hankel-1)
930 (defun dimension-spherical-hankel-1 (form result)
931 (let ((form1 `((mexpt) (($\h simp array) ,(nth 1 form))
932 (1))))
933 (dimension-function `((,form1 simp) ,(nth 2 form)) result)))
935 ;; See A & S 10.1.36.
937 (defun $spherical_hankel2 (n x)
938 (cond ((integerp n)
939 (setq x (mul x (power '$%e (mul '$%i '$%pi (add (mul 2 n) 1)))))
940 (let ((f))
941 (setq f ($spherical_hankel1 n x))
942 (if (oddp n) (interval-mult -1 f) f)))
943 (t `(($spherical_hankel2) ,n ,x))))
945 (putprop '$spherical_hankel2
946 '((n x)
947 ((unk) "$first" "$spherical_hankel2")
948 ((mplus simp) (($spherical_hankel2) ((mplus) -1 n) x)
949 ((mtimes simp) -1 ((mplus) 1 n)
950 (($spherical_hankel2) n x) ((mexpt) x -1))))
951 'grad)
953 (defprop $spherical_hankel2 tex-spherical-hankel-2 tex)
955 (defun tex-spherical-hankel-2 (x l r)
956 (tex-sub-and-super-scripted-function "h^{(2)}" `(0) nil nil nil 1 x l r))
958 (setf (get '$spherical_hankel2 'dimension) 'dimension-spherical-hankel-2)
960 (defun dimension-spherical-hankel-2 (form result)
961 (let ((form1 `((mexpt) (($\h simp array) ,(nth 1 form)) (2))))
962 (dimension-function `((,form1 simp) ,(nth 2 form)) result)))
964 ;;---------------------------------------------------------------------
965 ;; The spherical_bessel functions use the functions p-fun and q-fun.
966 ;; See A&S 10.1.8 and 10.1.9 page 437.
968 (defun p-fun (n x)
969 (let ((s 1) (w 1)
970 (n1 (floor (/ n 2)))
971 (x2 (mul x x)) (m2))
972 (dotimes (m n1 s)
973 (setq m2 (* 2 m))
974 (setq w (div (mul w (div
975 (* -1 (+ n m2 2) (+ n m2 1)
976 (- n m2) (- n (+ m2 1)))
977 (* 4 (+ m2 1) (+ m2 2))))
978 x2))
979 (setq s (add s w)))))
981 (defun q-fun (n x)
982 (let ((s (if (= 0 n) 0 1))
983 (w 1) (m2) (x2 (mul x x))
984 (n1 (floor (/ (- n 1) 2))))
985 (dotimes (m n1 (div (mul n (+ n 1) s) (mul 2 x)))
986 (setq m2 (* 2 m))
987 (setq w (div (mul w (div
988 (* -1 (+ n m2 3) (+ n m2 2)
989 (- n (+ m2 1)) (- n (+ m2 2)))
990 (* 4 (+ m2 3) (+ m2 2))))
991 x2))
992 (setq s (add s w)))))
995 ;; See A&S 10.1.8 page 437 and A&S 10.1.15 page 439. When the order
996 ;; is an integer and x is a float or a bigfloat, use the slatec code
997 ;; for numerical evaluation. Yes, we coerce bigfloats to floats and
998 ;; return a float.
1000 ;; For numerical evaluation, we do our own analytic continuation -- otherwise
1001 ;; we get factors exp(%i n %pi / 2) that should evaluate to 1,%i,-1,-%, but
1002 ;; numerically have "fuzz" in their values. The fuzz can cause the spherical
1003 ;; bessel functions to have nonzero (but small) imaginary values on the
1004 ;; negative real axis. See A&S 10.1.34
1006 (defun $spherical_bessel_j (n x)
1007 (cond ((and (eq '$zero (csign ($ratdisrep x)))
1008 (or (integerp n) ($featurep n '$integer)))
1009 `((%kron_delta) 0 ,n))
1011 ((and (use-float x) (integerp n))
1012 (let ((d 1) (xr) (xi) (z))
1013 (setq x ($rectform ($float x)))
1014 (setq xr ($realpart x))
1015 (setq xi ($imagpart x))
1016 (setq z (complex xr xi))
1017 (cond ((< xr 0.0)
1018 (setq d (if (oddp n) -1 1))
1019 (setq x (mul -1 x))
1020 (setq z (* -1 z))))
1021 (setq n (+ 0.5 ($float n)))
1022 (setq d (* d (sqrt (/ pi (* 2 z)))))
1023 (setq d (lisp-float-to-maxima-float d))
1024 ($expand (mul ($rectform d) ($bessel_j n x)))))
1026 ((and (integerp n) (> n -1))
1027 (let ((xt (sub x (div (mul n '$%pi) 2))))
1028 (div (add
1029 (mul (p-fun n x) (simplify `((%sin) ,xt)))
1030 (mul (q-fun n x) (simplify `((%cos) ,xt)))) x)))
1032 ((integerp n)
1033 (mul (if (oddp n) -1 1) ($spherical_bessel_y (- (+ n 1)) x)))
1036 `(($spherical_bessel_j) ,n ,x))))
1038 (putprop '$spherical_bessel_j
1039 '((n x)
1040 ((unk) "$first" "$spherical_bessel_j")
1041 ((mtimes) ((mexpt) ((mplus) 1 ((mtimes) 2 n)) -1)
1042 ((mplus)
1043 ((mtimes) n (($spherical_bessel_j) ((mplus) -1 n) x))
1044 ((mtimes) -1 ((mplus) 1 n)
1045 (($spherical_bessel_j) ((mplus) 1 n) x)))))
1046 'grad)
1048 (defprop $spherical_bessel_j tex-spherical-bessel-j tex)
1050 (defun tex-spherical-bessel-j (x l r)
1051 (tex-sub-and-super-scripted-function "j^{(2)}" `(0) nil nil nil 1 x l r))
1053 (setf (get '$spherical_bessel_j 'dimension) 'dimension-spherical-bessel-j)
1055 (defun dimension-spherical-bessel-j (form result)
1056 (let ((form1 `(($\j simp array) ,(nth 1 form))))
1057 (dimension-function `((,form1 simp) ,(nth 2 form)) result)))
1059 ;; For analytic continuation, see A&S 10.1.35.
1061 (defun $spherical_bessel_y (n x)
1062 (cond ((and (use-float x) (integerp n))
1063 (let ((d 1) (xr) (xi) (z))
1064 (setq x ($rectform ($float x)))
1065 (setq xr ($realpart x))
1066 (setq xi ($imagpart x))
1067 (setq z (complex xr xi))
1068 (cond ((< xr 0.0)
1069 (setq d (if (oddp n) 1 -1))
1070 (setq x (mul -1 x))
1071 (setq z (* -1 z))))
1072 (setq n (+ 0.5 ($float n)))
1073 (setq d (* d (sqrt (/ pi (* 2 z)))))
1074 (setq d (lisp-float-to-maxima-float d))
1075 ($expand (mul ($rectform d) ($bessel_y n x)))))
1077 ((and (integerp n) (> n -1))
1078 (let ((xt (add x (div (mul n '$%pi) 2))))
1079 (mul (if (oddp n) 1 -1)
1080 (div (sub
1081 (mul (p-fun n x) (simplify `((%cos) ,xt)))
1082 (mul (q-fun n x) (simplify `((%sin) ,xt)))) x))))
1084 ((integerp n)
1085 (mul (if (oddp n) 1 -1) ($spherical_bessel_j (- (+ n 1)) x)))
1086 (t `(($spherical_bessel_y) ,n ,x))))
1088 (putprop '$spherical_bessel_y
1089 '((n x)
1090 ((unk) "$first" "$spherical_bessel_y")
1091 ((mtimes) ((mexpt) ((mplus) 1 ((mtimes) 2 n)) -1)
1092 ((mplus)
1093 ((mtimes) n (($spherical_bessel_y) ((mplus) -1 n) x))
1094 ((mtimes) -1 ((mplus) 1 n)
1095 (($spherical_bessel_y) ((mplus) 1 n) x)))))
1096 'grad)
1098 (defprop $spherical_bessel_y tex-spherical-bessel-y tex)
1100 (defun tex-spherical-bessel-y (x l r)
1101 (tex-sub-and-super-scripted-function "y^{(2)}" `(0) nil nil nil 1 x l r))
1103 (setf (get '$spherical_bessel_y 'dimension) 'dimension-spherical-bessel-y)
1105 (defun dimension-spherical-bessel-y (form result)
1106 (let ((form1 `(($\y simp array) ,(nth 1 form))))
1107 (dimension-function `((,form1 simp) ,(nth 2 form)) result)))
1109 ;; Compute P_n^m(cos(theta)). See Merzbacher, 9.59 page 184
1110 ;; and 9.64 page 185, and A & S 22.5.37 page 779. This function
1111 ;; lacks error checking; it should only be called by spherical_harmonic.
1113 ;; We need to be careful -- for the spherical harmonics we can't use
1114 ;; assoc_legendre_p(n,m,cos(x)). If we did, we'd get factors
1115 ;; (1 - cos^2(x))^(m/2) that simplify to |sin(x)|^m but we want them
1116 ;; to simplify to sin^m(x). Oh my!
1118 (defun assoc-leg-cos (n m x)
1119 (interval-mult
1120 (if (= m 0) 1 (mul (take '(%genfact) (sub (mul 2 m) 1) (sub m (div 1 2)) 2) (power (take '(%sin) x) m)))
1121 ($ultraspherical (sub n m) (add m (div 1 2)) (take '(%cos) x))))
1123 (defun $spherical_harmonic (n m th p)
1124 (cond ((and (integerp n) (integerp m) (> n -1))
1125 (cond ((> (abs m) n)
1127 ((< m 0)
1128 (interval-mult (if (oddp m) -1 1)
1129 ($spherical_harmonic n (- m) th (mul -1 p))))
1131 (interval-mult
1132 (mul ($exp (mul '$%i m p))
1133 (power (div (* (+ (* 2 n) 1) (factorial (- n m)))
1134 (mul '$%pi (* 4 (factorial (+ n m)))))
1135 `((rat) 1 2)))
1136 (assoc-leg-cos n m th)))))
1138 `(($spherical_harmonic) ,n ,m ,th ,p))))
1140 (defprop $spherical_harmonic tex-spherical-harmonic tex)
1142 (defun tex-spherical-harmonic (x l r)
1143 (tex-sub-and-super-scripted-function "Y" `(0) nil `(1) nil 2 x l r))
1145 (setf (get '$spherical_harmonic 'dimension) 'dimension-spherical-harmonic)
1147 (defun dimension-spherical-harmonic (form result)
1148 (dimension-function
1149 (dimension-sub-and-super-scripted-function '|$y| `(1) `(2) nil 3 form)
1150 result))
1152 (putprop '$spherical_harmonic
1153 '((n m theta phi)
1154 ((unk) "$first" "$spherical_harmonic")
1155 ((unk) "$second" "$spherical_harmonic")
1156 ((mplus)
1157 ((mtimes) ((rat ) -1 2)
1158 ((mexpt)
1159 ((mtimes) ((mplus) ((mtimes) -1 m) n)
1160 ((mplus) 1 m n))
1161 ((rat) 1 2))
1162 (($spherical_harmonic) n ((mplus) 1 m) theta phi)
1163 ((mexpt) $%e ((mtimes) -1 $%i phi)))
1164 ((mtimes) ((rat) 1 2)
1165 ((mexpt)
1166 ((mtimes) ((mplus) 1 ((mtimes) -1 m) n)
1167 ((mplus) m n))
1168 ((rat) 1 2))
1169 (($spherical_harmonic) n ((mplus) -1 m) theta phi)
1170 ((mexpt) $%e ((mtimes) $%i phi))))
1172 ((mtimes) $%i m (($spherical_harmonic) n m theta phi)))
1173 'grad)
1175 (defun maxima-float-to-lisp-float (y)
1176 (let* ((x ($rectform y))
1177 (xr ($realpart x))
1178 (xi ($imagpart x)))
1179 (cond ((or (floatp xr) (floatp xi))
1180 (setq xr (float xr)
1181 xi (float xi))
1182 (if (= 0.0 xi) xr (complex xr xi)))
1184 y))))
1186 (defun lisp-float-to-maxima-float (x)
1187 (if (complexp x)
1188 (add (realpart x) (mul '$%i (imagpart x)))
1191 (defun hypergeo11-float (n b x)
1192 (let ((f0) (fm1) (f) (i 0) (k) (dk) (ak) (bk) (err)
1193 (as (make-array (- 1 n)
1194 :initial-element 0.0))
1195 (bs (make-array (- 1 n)
1196 :initial-element 0.0))
1197 (fs (make-array (- 1 n)
1198 :initial-element 0.0))
1199 (u) (u0) (um1))
1201 (setq f0 1.0)
1202 (setq fm1 0.0)
1203 (setq x (- b x))
1204 (setq n (- n))
1205 (while (< i n)
1206 (setf (aref fs i) f0)
1207 (setq dk (+ b i))
1208 (setq ak (/ (+ (* 2 i) x) dk))
1209 (setq bk (/ (- i) dk))
1210 (setq f (+ (* ak f0) (* bk fm1)))
1211 (setf (aref as i) ak)
1212 (setf (aref bs i) bk)
1213 (setq fm1 f0)
1214 (setq f0 f)
1215 (incf i))
1216 (setf (aref fs i) f0)
1217 (setq i 1)
1218 (setq err 1.0)
1219 (setq u0 1.0)
1220 (setq um1 0.0)
1221 (while (< i n)
1222 (setq k (- n i))
1223 (setq u (+ (* (aref as k) u0)
1224 (* (aref bs (+ 1 k)) um1)))
1225 (setq um1 u0)
1226 (setq u0 u)
1227 (setq err (+ err (abs (* u0 (aref fs k)))))
1228 (incf i))
1229 (values f0 (* 12 +flonum-epsilon+ err))))
1231 (defun hypergeo21-float (n b c x)
1232 (let ((f0) (fm1) (f) (i 0) (k) (dk) (ak) (bk) (err)
1233 (as (make-array (- 1 n)
1234 :initial-element 0.0))
1235 (bs (make-array (- 1 n)
1236 :initial-element 0.0))
1237 (fs (make-array (- 1 n)
1238 :initial-element 0.0))
1239 (u) (u0) (um1))
1241 (setq f0 1.0)
1242 (setq fm1 0.0)
1243 (setq n (- n))
1244 (while (< i n)
1245 (setf (aref fs i) f0)
1246 (setq dk (+ c i))
1247 (setq ak (/ (+ (* 2 i) c (- (* x (+ b i)))) dk))
1248 (setq bk (/ (* i (- x 1)) dk))
1249 (setq f (+ (* ak f0) (* bk fm1)))
1250 (setf (aref as i) ak)
1251 (setf (aref bs i) bk)
1252 (setq fm1 f0)
1253 (setq f0 f)
1254 (incf i))
1255 (setf (aref fs i) f0)
1256 (setq i 1)
1257 (setq err 1.0)
1258 (setq u0 1.0)
1259 (setq um1 0.0)
1260 (while (< i n)
1261 (setq k (- n i))
1262 (setq u (+ (* (aref as k) u0)
1263 (* (aref bs (+ 1 k)) um1)))
1264 (setq um1 u0)
1265 (setq u0 u)
1266 (incf i)
1267 (setq err (+ err (abs (* (aref fs k) u0)))))
1268 (values f0 (* 12 +flonum-epsilon+ err))))
1270 ;; For recursion relations, see A & S 22.7 page 782.
1272 ;; legendre_p(n+1,x) = ((2*n+1)*legendre_p(n,x)*x-n*legendre_p(n-1,x))/(n+1)
1274 ;; jacobi_p(n+1,a,b,x) = (((2*n+a+b+1)*(a^2-b^2) +
1275 ;; x*pochhammer(2*n+a+b,3)) * jacobi_p(n,a,b,x) -
1276 ;; 2*(n+a)*(n+b)*(2*n+a+b+2)*jacobi_p(n-1,a,b,x))/(2*(n+1)*(n+a+b+1)*(2*n+a+b))
1278 ;; ultraspherical(n+1,a,x) = (2*(n+a)*x * ultraspherical(n,a,x) -
1279 ;; (n+2*a-1)*ultraspherical(n-1,a,x))/(n+1)
1281 ;; chebyshev_t(n+1,x) = 2*x*chebyshev_t(n,x) -chebyshev_t(n-1,x)
1283 ;; chebyshev_u(n+1,x) = 2*x*chebyshev_u(n,x) -chebyshev_u(n-1,x)
1285 ;; laguerre(n+1,x) = (((2*n+1) - x)*laguerre(n,x) -(n)*laguerre(n-1,x))/(n+1)
1287 ;; gen_laguerre(n+1,a,x) = (((2*n+a+1) - x)*gen_laguerre(n,a,x)
1288 ;; -(n+a)*gen_laguerre(n-1,a,x))/(n+1)
1290 ;; hermite(n+1,x) = 2*x*hermite(n,x) -2*n*hermite(n-1,x)
1292 ;; See G & R 8.733.2; A & S 22.7.11 might be wrong -- or maybe I need
1293 ;; reading glasses.
1295 ;; (2*n+1)*x*assoc_legendre_p(n,m,x) = (n-m+1)*assoc_legendre_p(n+1,m,x)
1296 ;; + (n+m)*assoc_legendre_p(n-1,m,x)
1298 ;; For the half-integer bessel functions, See A & S 10.1.19
1300 ;; fn(n-1,x) + fn(n+1,x) = (2*n+1)*fn(n,x)/x;
1302 (defun check-arg-length (fn n m)
1303 (if (not (= n m))
1304 (merror "Function ~:M needs ~:M arguments, instead it received ~:M"
1305 fn n m)))
1307 (defun $orthopoly_recur (fn arg)
1308 (if (not ($listp arg))
1309 (merror "The second argument to orthopoly_recur must be a list"))
1310 (cond ((eq fn '$jacobi_p)
1311 (check-arg-length fn 4 (- (length arg) 1))
1312 (let ((n (nth 1 arg))
1313 (a (nth 2 arg))
1314 (b (nth 3 arg))
1315 (x (nth 4 arg)))
1316 (simplify
1317 `((mequal) (($jacobi_p ) ((mplus) 1 ,n) ,a ,b ,x)
1318 ((mtimes) ((rat) 1 2) ((mexpt) ((mplus) 1 ,n) -1)
1319 ((mexpt) ((mplus) 1 ,a ,b ,n) -1)
1320 ((mexpt) ((mplus) ,a ,b ((mtimes) 2 ,n)) -1)
1321 ((mplus)
1322 ((mtimes) -2 ((mplus) ,a ,n) ((mplus) ,b ,n)
1323 ((mplus) 2 ,a ,b ((mtimes) 2 ,n))
1324 (($jacobi_p ) ((mplus) -1 ,n) ,a ,b ,x))
1325 ((mtimes) (($jacobi_p ) ,n ,a ,b ,x)
1326 ((mplus)
1327 ((mtimes)
1328 ((mplus) ((mexpt) ,a 2)
1329 ((mtimes) -1 ((mexpt) ,b 2)))
1330 ((mplus) 1 ,a ,b ((mtimes) 2 ,n)))
1331 ((mtimes) ((mplus) ,a ,b ((mtimes) 2 ,n))
1332 ((mplus) 1 ,a ,b ((mtimes) 2 ,n))
1333 ((mplus) 2 ,a ,b ((mtimes) 2 ,n)) ,x)))))))))
1335 ((eq fn '$ultraspherical)
1336 (check-arg-length fn 3 (- (length arg) 1))
1337 (let ((n (nth 1 arg))
1338 (a (nth 2 arg))
1339 (x (nth 3 arg)))
1340 (simplify
1341 `((mequal) (($ultraspherical) ((mplus) 1 ,n) ,a ,x)
1342 ((mtimes) ((mexpt) ((mplus) 1 ,n) -1)
1343 ((mplus)
1344 ((mtimes) -1 ((mplus) -1 ((mtimes) 2 ,a) ,n)
1345 (($ultraspherical) ((mplus) -1 ,n) ,a ,x))
1346 ((mtimes) 2 ((mplus) ,a ,n)
1347 (($ultraspherical) ,n ,a ,x) ,x)))))))
1349 ((member fn `($chebyshev_t $chebyshev_u) :test 'eq)
1350 (check-arg-length fn 2 (- (length arg) 1))
1351 (let ((n (nth 1 arg))
1352 (x (nth 2 arg)))
1353 (simplify
1354 `((mequal ) ((,fn) ((mplus ) 1 ,n) ,x)
1355 ((mplus )
1356 ((mtimes ) -1 ((,fn) ((mplus ) -1 ,n) ,x))
1357 ((mtimes ) 2 ((,fn) ,n ,x) ,x))))))
1359 ((member fn '($legendre_p $legendre_q) :test 'eq)
1360 (check-arg-length fn 2 (- (length arg) 1))
1361 (let* ((n (nth 1 arg))
1362 (x (nth 2 arg))
1363 (z (if (eq fn '$legendre_q)
1364 `((mtimes) -1 ((%kron_delta) ,n 0)) 0)))
1365 (simplify
1366 `((mequal) ((,fn) ((mplus) 1 ,n) ,x)
1367 ((mplus)
1368 ((mtimes) ((mexpt) ((mplus) 1 ,n) -1)
1369 ((mplus)
1370 ((mtimes) ((mtimes) -1 ,n)
1371 ((,fn) ((mplus) -1 ,n) ,x))
1372 ((mtimes) ((mplus) 1 ((mtimes) 2 ,n))
1373 ((,fn) ,n ,x) ,x)))
1374 ,z)))))
1376 ((member fn '($assoc_legendre_p $assoc_legendre_q) :test 'eq)
1377 (check-arg-length fn 3 (- (length arg) 1))
1378 (let ((n (nth 1 arg))
1379 (m (nth 2 arg))
1380 (x (nth 3 arg)))
1381 (simplify
1382 `((mequal) ((,fn) ((mplus) 1 ,n) ,m ,x)
1383 ((mtimes)
1384 ((mexpt) ((mplus) 1 ((mtimes) -1 ,m) ,n) -1)
1385 ((mplus)
1386 ((mtimes)
1387 ((mplus) ((mtimes) -1 ,m)
1388 ((mtimes) -1 ,n))
1389 ((,fn) ((mplus) -1 ,n) ,m ,x))
1390 ((mtimes) ((mplus) 1 ((mtimes) 2 ,n))
1391 ((,fn) ,n ,m ,x) ,x)))))))
1393 ((eq fn '$laguerre)
1394 (check-arg-length fn 2 (- (length arg) 1))
1395 (let ((n (nth 1 arg))
1396 (x (nth 2 arg)))
1397 (simplify
1398 `((mequal ) (($laguerre ) ((mplus ) 1 ,n) ,x)
1399 ((mtimes ) ((mexpt ) ((mplus ) 1 ,n) -1)
1400 ((mplus )
1401 ((mtimes ) -1 ,n (($laguerre ) ((mplus ) -1 ,n) ,x))
1402 ((mtimes ) (($laguerre ) ,n ,x)
1403 ((mplus ) 1 ((mtimes ) 2 ,n) ((mtimes ) -1 ,x)))))))))
1405 ((eq fn '$gen_laguerre)
1406 (check-arg-length fn 3 (- (length arg) 1))
1407 (let ((n (nth 1 arg))
1408 (a (nth 2 arg))
1409 (x (nth 3 arg)))
1410 (simplify
1411 `((mequal) (($gen_laguerre) ((mplus) 1 ,n) ,a ,x)
1412 ((mtimes) ((mexpt ) ((mplus) 1 ,n) -1)
1413 ((mplus)
1414 ((mtimes) -1 ((mplus) ,a ,n)
1415 (($gen_laguerre) ((mplus) -1 ,n) ,a ,x))
1416 ((mtimes) (($gen_laguerre) ,n ,a ,x)
1417 ((mplus) 1 ,a ((mtimes) 2 ,n) ((mtimes ) -1 ,x)))))))))
1419 ((eq fn '$hermite)
1420 (check-arg-length fn 2 (- (length arg) 1))
1421 (let ((n (nth 1 arg))
1422 (x (nth 2 arg)))
1423 (simplify
1424 `((mequal) (($hermite) ((mplus) 1 ,n) ,x)
1425 ((mplus)
1426 ((mtimes) -2 ,n (($hermite) ((mplus) -1 ,n) ,x))
1427 ((mtimes) 2 (($hermite) ,n ,x) ,x))))))
1429 ((member fn `($spherical_bessel_j $spherical_bessel_y
1430 $spherical_hankel1 $spherical_hankel2)
1431 :test 'eq)
1432 (check-arg-length fn 2 (- (length arg) 1))
1433 (let ((n (nth 1 arg))
1434 (x (nth 2 arg)))
1435 (simplify
1436 `((mequal) ((,fn) ((mplus) 1 ,n) ,x)
1437 ((mplus) ((mtimes) -1 ((,fn ) ((mplus) -1 ,n) ,x))
1438 ((mtimes) ((,fn) ,n ,x) ((mexpt) ,x -1))
1439 ((mtimes) 2 ,n ((,fn ) ,n ,x) ((mexpt) ,x -1)))))))
1441 (t (merror "A recursion relation for ~:M isn't known to Maxima" fn))))
1443 ;; See A & S Table 22.2, page 774.
1445 (defun $orthopoly_weight (fn arg)
1446 (if (not ($listp arg))
1447 (merror "The second argument to orthopoly_weight must be a list"))
1449 (if (not (or ($symbolp (car (last arg))) ($subvarp (car (last arg)))))
1450 (merror "The last element of the second argument to orthopoly_weight must
1451 be a symbol or a subscripted symbol, instead found ~:M" (car (last arg))))
1453 (if (not (every #'(lambda (s)
1454 ($freeof (car (last arg)) s)) (butlast (cdr arg))))
1455 (merror "Only the last element of ~:M may depend on the integration
1456 variable ~:M" arg (car (last arg))))
1458 (cond ((eq fn '$jacobi_p)
1459 (check-arg-length fn 4 (- (length arg) 1))
1460 (let ((a (nth 2 arg))
1461 (b (nth 3 arg))
1462 (x (nth 4 arg)))
1463 (simplify
1464 `((mlist)
1465 ((mtimes) ((mexpt) ((mplus) 1 ((mtimes) -1 ,x)) ,a)
1466 ((mexpt) ((mplus ) 1 ,x) ,b))
1467 -1 1))))
1469 ((eq fn '$ultraspherical)
1470 (check-arg-length fn 3 (- (length arg) 1))
1471 (let ((a (nth 2 arg))
1472 (x (nth 3 arg)))
1473 (simplify
1474 `((mlist)
1475 ((mexpt) ((mplus) 1 ((mtimes) -1 ((mexpt) ,x 2)))
1476 ((mplus) ((rat) -1 2) ,a)) -1 1))))
1478 ((eq fn '$chebyshev_t)
1479 (check-arg-length fn 2 (- (length arg) 1))
1480 (let ((x (nth 2 arg)))
1481 (simplify
1482 `((mlist)
1483 ((mexpt) ((mplus) 1 ((mtimes) -1 ((mexpt) ,x 2)))
1484 ((rat) -1 2)) -1 1))))
1486 ((eq fn '$chebyshev_u)
1487 (check-arg-length fn 2 (- (length arg) 1))
1488 (let ((x (nth 2 arg)))
1489 (simplify
1490 `((mlist)
1491 ((mexpt) ((mplus) 1 ((mtimes) -1 ((mexpt) ,x 2)))
1492 ((rat) 1 2)) -1 1))))
1494 ((eq fn '$legendre_p)
1495 (check-arg-length fn 2 (- (length arg) 1))
1496 `((mlist) 1 -1 1))
1498 ; This is for a fixed order. There is also an orthogonality
1499 ; condition for fixed degree with weight function 1/(1-x^2).
1500 ; See A & S 8.14.11 and 8.14.12.
1501 ((eq fn '$assoc_legendre_p)
1502 (check-arg-length fn 3 (- (length arg) 1))
1503 `((mlist) 1 -1 1))
1505 ((eq fn '$laguerre)
1506 (check-arg-length fn 2 (- (length arg) 1))
1507 (let ((x (nth 2 arg)))
1508 (simplify
1509 `((mlist) ((mexpt) $%e ((mtimes) -1 ,x)) 0 $inf))))
1511 ((eq fn '$gen_laguerre)
1512 (check-arg-length fn 3 (- (length arg) 1))
1513 (let ((a (nth 2 arg))
1514 (x (nth 3 arg)))
1515 (simplify
1516 `((mlist)
1517 ((mtimes) ((mexpt) ,x ,a)
1518 ((mexpt) $%e ((mtimes) -1 ,x))) 0 $inf))))
1520 ((eq fn '$hermite)
1521 (check-arg-length fn 2 (- (length arg) 1))
1522 (let ((x (nth 2 arg)))
1523 (simplify
1524 `((mlist) ((mexpt) $%e ((mtimes) -1 ((mexpt) ,x 2)))
1525 ((mtimes ) -1 $inf) $inf))))
1527 (t (merror "A weight for ~:M isn't known to Maxima" fn))))