2 ;;; ** (c) Copyright 1979 Massachusetts Institute of Technology **
8 ;;; Definite integration using the generalized hypergeometric functions
9 ;;; Avgoustis, Ioannis Dimitrios
10 ;;; Thesis. 1977. M.S.--Massachusetts Institute of Technology. Dept.
11 ;;; of Electrical Engineering and Computer Science
12 ;;; http://dspace.mit.edu/handle/1721.1/16269
14 ;;; Avgoustis, I. D., Symbolic Laplace Transforms of Special Functions,
15 ;;; Proceedings of the 1977 MACSYMA Users' Conference, pp 21-41
19 (declare-top (special $true $false
))
21 (declare-top (special var
*par
* checkcoefsignlist $exponentialize
24 (defvar *debug-hyp
* nil
)
26 (defmvar $prefer_whittaker nil
)
28 ;; When T give result in terms of gamma_incomplete and not gamma_incomplete_lower
29 (defmvar $prefer_gamma_incomplete nil
)
31 ;; When NIL do not automatically expand polynomials as a result
32 (defmvar $expand_polynomials t
)
36 #-gcl
(:execute
:compile-toplevel
)
37 (defmacro simp
(x) `(simplifya ,x
()))
39 ;; The macro MABS has been renamed to HYP-MABS in order to
40 ;; avoid conflict with the Maxima symbol MABS. The other
41 ;; M* macros defined here should probably be similarly renamed
42 ;; for consistency. jfa 03/27/2002
44 (defmacro hyp-mabs
(x) `(simp `((mabs) ,,x
)))
46 (defmacro msqrt
(x) `(m^t
,x
1//2))
48 (defmacro mexpt
(x) `(m^t
'$%e
,x
))
50 (defmacro mlog
(x) `(simp `((%log
) ,,x
)))
52 (defmacro msin
(x) `(simp `((%sin
) ,,x
)))
54 (defmacro mcos
(x) `(simp `((%cos
) ,,x
)))
56 (defmacro masin
(x) `(simp `((%asin
) ,,x
)))
58 (defmacro matan
(x) `(simp `((%atan
) ,,x
)))
60 (defmacro mgamma
(x) `(simp `((%gamma
) ,,x
)))
62 (defmacro mbinom
(x y
) `(simp `((%binomial
) ,,x
,,y
)))
64 (defmacro merf
(x) `(simp `((%erf
) ,,x
)))
66 (defmacro =1//2 (x) `(alike1 ,x
1//2))
69 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
71 ;;; Functions moved from hypgeo.lisp to this place.
72 ;;; These functions are no longer used in hypgeo.lisp.
76 (simplifya (list '(%gamma
) expr
) nil
))
86 ;; Test if X is a number, either Lisp number or a maxima rational.
91 (eq (caar (simplifya x nil
)) 'rat
))))
93 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
95 (defun hyp-integerp (x)
96 ;; In this file, maxima-integerp was used in many places. But it
97 ;; seems that this code expects maxima-integerp to return T when it
98 ;; is really an integer, not something that was declared an integer.
99 ;; But I'm not really sure if this is true everywhere, but it is
100 ;; true in some places.
102 ;; Thus, we replace all calls to maxima-integerp with hyp-integerp,
103 ;; which, for now, returns T only when the arg is an integer.
104 ;; Should we do something more?
105 (and (maxima-integerp x
) (integerp x
)))
107 ;; Main entry point for simplification of hypergeometric functions.
109 ;; F(a1,a2,a3,...;b1,b2,b3;z)
111 ;; L1 is a (maxima) list of an's, L2 is a (maxima) list of bn's.
112 (defmfun $hgfred
(arg-l1 arg-l2 arg
)
115 (eq (caar a
) 'mlist
))))
116 (unless (arg-ok arg-l1
)
117 (merror (intl:gettext
"hgfred: first argument must be a list; found: ~:M") arg-l1
))
118 (unless (arg-ok arg-l2
)
119 (merror (intl:gettext
"hgfred: second argument must be a list; found: ~:M") arg-l2
)))
121 ;; Do we really want $radexpand set to '$all? This is probably a
122 ;; bad idea in general, but we'll leave this in for now until we can
123 ;; verify find all of the code that does or does not need this and
124 ;; until we can verify all of the test cases are correct.
125 (let (;;($radexpand '$all)
128 (checkcoefsignlist nil
))
129 (hgfsimp-exec (cdr arg-l1
) (cdr arg-l2
) arg
)))
131 (defun hgfsimp-exec (arg-l1 arg-l2 arg
)
132 (let* ((l1 (copy-tree arg-l1
))
133 (l2 (copy-tree arg-l2
))
134 ($exponentialize nil
)
135 (res (hgfsimp l1 l2 arg
)))
136 ;; I think hgfsimp returns FAIL and UNDEF for cases where it
137 ;; couldn't reduce the function.
138 (cond ((eq res
'fail
)
145 (defun hgfsimp (arg-l1 arg-l2 var
)
146 (prog (resimp listcmdiff
)
147 (setq arg-l1
(macsimp arg-l1
)
148 arg-l2
(macsimp arg-l2
)
149 resimp
(simpg arg-l1 arg-l2
))
150 (cond ((not (eq (and (consp resimp
) (car resimp
)) 'fail
))
152 ((and (not (zerop1 var
)) ; Do not call splitfpq for a zero argument
154 (intdiffl1l2 (cadr resimp
) (caddr resimp
))))
155 (return (splitpfq listcmdiff
159 (return (dispatch-spec-simp (cadr resimp
)
162 (defun macsimp (expr)
163 (mapcar #'(lambda (index) ($expand index
)) expr
))
165 ;; Simplify the parameters. If L1 and L2 have common elements, remove
166 ;; them from both L1 and L2.
167 (defun simpg (arg-l1 arg-l2
)
168 (let ((il (zl-intersection arg-l1 arg-l2
)))
170 (simpg-exec arg-l1 arg-l2
))
172 (simpg-exec (del il arg-l1
)
178 (del (cdr a
) (delete (car a
) b
:count
1 :test
#'equal
)))))
180 ;; Handle the simple cases where the result is either a polynomial, or
181 ;; is undefined because we divide by zero.
182 (defun simpg-exec (arg-l1 arg-l2
)
184 (cond ((zerop-in-l arg-l1
)
185 ;; A zero in the first index means the series terminates
186 ;; after the first term, so the result is always 1.
188 ((setq n
(hyp-negp-in-l arg-l1
))
189 ;; A negative integer in the first series means we have a
191 (create-poly arg-l1 arg-l2 n
))
192 ((and (or (zerop-in-l arg-l2
)
193 (hyp-negp-in-l arg-l2
))
194 (every #'mnump arg-l1
)
195 (every #'mnump arg-l2
))
196 ;; A zero or negative number in the second index means we
197 ;; eventually divide by zero, so we're undefined. But only
198 ;; do this if both indices contain numbers. See Bug
199 ;; 1858964 for discussion.
202 ;; We failed so more complicated stuff needs to be done.
203 (append (list 'fail
) (list arg-l1
) (list arg-l2
))))))
205 (defun intdiffl1l2 (arg-l1 arg-l2
)
209 (intdiff arg-l1 arg-l2
))))
211 ;; For each element x in arg-l1 and y in arg-l2, compute d = x - y.
212 ;; Find the smallest such non-negative integer d and return (list x
214 (defun intdiff (arg-l1 arg-l2
)
216 ;; Compute all possible differences between elements in arg-l1 and
217 ;; arg-l2. Only save the ones where the difference is a positive
221 (let ((diff (sub x y
)))
223 (push (list x diff
) result
)))))
224 ;; Find the smallest one and return it.
225 (let ((min (first result
)))
226 (dolist (x (rest result
))
227 (when (< (second x
) (second min
))
231 ;; Create the appropriate polynomial for the hypergeometric function.
232 (defun create-poly (arg-l1 arg-l2 n
)
233 (let ((len1 (length arg-l1
))
234 (len2 (length arg-l2
)))
235 ;; n is the smallest (in magnitude) negative integer in L1. To
236 ;; make everything come out right, we need to make sure this value
237 ;; is first in L1. This is ok, the definition of the
238 ;; hypergeometric function does not depend on the order of values
240 (setf arg-l1
(cons n
(remove n arg-l1
:count
1)))
241 (cond ((and (equal len1
2)
243 (2f1polys arg-l1 arg-l2 n
))
250 (t (create-any-poly arg-l1 arg-l2
(mul -
1 n
))))))
252 (defun 1f1polys (arg-l2 n
)
253 (let* ((c (car arg-l2
))
255 (fact1 (mul (power 2 n
)
256 (take '(mfactorial) n
)
258 ;; For all of the polynomials here, I think it's ok to
259 ;; replace sqrt(z^2) with z because when everything is
260 ;; expanded out they evaluate to exactly the same thing. So
261 ;; $radexpand $all is ok here.
262 (fact2 (let (($radexpand
'$all
))
263 (mul (power 2 '((rat simp
) 1 2))
264 (power var
'((rat simp
) 1 2))))))
265 (cond ((alike1 c
'((rat simp
) 1 2))
267 ;; hermite(2*n,x) = (-1)^n*(2*n)!/n!*M(-n,1/2,x^2)
270 ;; M(-n,1/2,x) = n!/(2*n)!*(-1)^n*hermite(2*n,sqrt(x))
272 ;; But hermite(m,x) = 2^(m/2)*He(sqrt(2)*sqrt(x)), so
274 ;; M(-n,1/2,x) = (-1)^n*n!*2^n/(2*n)!*He(2*n,sqrt(2)*sqrt(x))
276 (inv (take '(mfactorial) (add n n
)))
277 (hermpol (add n n
) fact2
)))
278 ((alike1 c
'((rat simp
) 3 2))
280 ;; hermite(2*n+1,x) = (-1)^n*(2*n+1)!/n!*M(-n,3/2,x^2)*2*x
283 ;; M(-n,3/2,x) = n!/(2*n+1)!*(-1)^n*hermite(2*n+1,sqrt(x))/2/sqrt(x)
285 ;; and in terms of He, we get
287 ;; M(-n,3/2,x) = (-1)^n*n!*2^(n-1/2)/(2*n+1)!/sqrt(x)*He(2*n+1,sqrt(2)*sqrt(x))
289 (inv (power 2 '((rat simp
) 1 2)))
290 (inv (take '(mfactorial) (add n n
1)))
291 (hermpol (add n n
1) fact2
)
292 ;; Similarly, $radexpand here is ok to convert sqrt(z^2) to z.
293 (let (($radexpand
'$all
))
294 (inv (power var
'((rat simp
) 1 2))))))
295 ((alike1 c
(neg (add n n
)))
298 (inv (take '(%binomial
) (add n n
) n
))
299 (lagpol n
(sub c
1) var
)))
303 ;; gen_laguerre(n,alpha,x) =
304 ;; binomial(n+alpha,n)*hgfred([-n],[alpha+1],x);
306 ;; Or hgfred([-n],[alpha],x) =
307 ;; gen_laguerre(n,alpha-1,x)/binomial(n+alpha-1,n)
309 ;; But 1/binomial(n+alpha-1,n) = n!*(alpha-1)!/(n+alpha-1)!
310 ;; = n! / (alpha * (alpha + 1) * ... * (alpha + n - 1)
311 ;; = n! / poch(alpha, n)
315 ;; However, if c is not a number leave the result in terms
316 ;; of gamma functions. I (rtoy) think this makes for a
317 ;; simpler result, especially if n is rather large. If the
318 ;; user really wants the answer expanded out, makefact and
319 ;; minfactorial will do that.
320 (if (and (integerp n
)
323 (and (minusp c
) (> c
(- n
))))
324 (merror (intl:gettext
"hgfred: 1F1(~M; ~M; ~M) not defined.")
326 (mul (take '(mfactorial) n
)
327 (inv (take '($pochhammer
) c n
))
328 (lagpol n
(sub c
1) var
)))
329 (let (($gamma_expand t
)) ; Expand Gamma function
330 (mul (take '(mfactorial) n
)
332 (inv (take '(%gamma
) (add c n
)))
333 (lagpol n
(sub c
1) var
))))))))
335 ;; Hermite polynomial. Note: The Hermite polynomial used here is the
336 ;; He polynomial, defined as (A&S 22.5.18, 22.5.19)
338 ;; He(n,x) = 2^(-n/2)*H(n,x/sqrt(2))
342 ;; H(n,x) = 2^(n/2)*He(x*sqrt(2))
344 ;; We want to use H, as used in specfun, so we need to convert it.
345 (defun hermpol (n arg
)
346 (let ((fact (inv (power 2 (div n
2))))
347 (x (mul arg
(inv (power 2 '((rat simp
) 1 2))))))
349 (if (and $expand_polynomials
(integerp n
))
350 (mfuncall '$hermite n x
)
351 (list '($hermite simp
) n x
)))))
353 ;; Generalized Laguerre polynomial
354 (defun lagpol (n a arg
)
355 (if (and (numberp a
) (zerop a
))
356 (if (and $expand_polynomials
(integerp n
))
357 (mfuncall '$laguerre n arg
)
358 (list '($laguerre simp
) n arg
))
359 (if (and $expand_polynomials
(integerp n
))
360 (mfuncall '$gen_laguerre n a arg
)
361 (list '($gen_laguerre simp
) n a arg
))))
363 (defun 2f0polys (arg-l1 n
)
364 (let ((a (car arg-l1
))
366 (when (alike1 (sub b a
) '((rat simp
) -
1 2))
368 (cond ((alike1 (sub b a
) '((rat simp
) 1 2))
369 ;; 2F0(-n,-n+1/2,z) or 2F0(-n-1/2,-n,z)
370 (interhermpol n a b var
))
373 (let ((x (mul -
1 (inv var
)))
375 (mul (take '(mfactorial) order
)
376 (inv (power x order
))
377 (inv (power -
1 order
))
378 (lagpol order
(mul -
1 (add b order
)) x
)))))))
380 ;; Compute 2F0(-n,-n+1/2;z) and 2F0(-n-1/2,-n;z) in terms of Hermite
383 ;; Ok. I couldn't find any references giving expressions for this, so
384 ;; here's a quick derivation.
386 ;; 2F0(-n,-n+1/2;z) = sum(pochhammer(-n,k)*pochhammer(-n+1/2,k)*z^k/k!, k, 0, n)
388 ;; It's easy to show pochhammer(-n,k) = (-1)^k*n!/(n-k)!
389 ;; Also, it's straightforward but tedious to show that
390 ;; pochhammer(-n+1/2,k) = (-1)^k*(2*n)!*(n-k)!/2^(2*k)/n!/(2*n-2*k)!
393 ;; 2F0 = (2*n)!*sum(z^k/2^(2*k)/k!/(2*n-2*k)!)
395 ;; Compare this to the expression for He(2*n,x) (A&S 22.3.11):
397 ;; He(2*n,x) = (2*n)! * x^(2*n) * sum((-1)^k*x^(-2*k)/2^k/k!/(2*n-2*k)!)
401 ;; 2F0(-n,-n+1/2;z) = y^n * He(2*n,y)
403 ;; where y = sqrt(-2/x)
405 ;; For 2F0(-n-1/2,-n;z) = sum(pochhammer(-n,k)*pochhammer(-n-1/2,k)*z^k/k!)
408 ;; pochhammer(-n-1/2,k) = pochhammer(-(n+1)+1/2,k)
411 ;; So 2F0 = (2*n+1)!*sum(z^k/z^(2*k)/k!/(2*n+1-2*k)!)
415 ;; 2F0(-n-1/2,-n;z) = y^(2*n+1) * He(2*n+1,y)
418 (defun interhermpol (n a b x
)
419 (let ((arg (power (div 2 (mul -
1 x
)) '((rat simp
) 1 2)))
420 (order (cond ((alike1 a n
)
423 (sub 1 (add n n
))))))
424 ;; 2F0(-n,-n+1/2;z) = y^(-2*n)*He(2*n,y)
425 ;; 2F0(-n-1/2,-n;z) = y^(-(2*n+1))*He(2*n+1,y)
427 ;; where y = sqrt(-2/var);
428 (mul (power arg
(mul -
1 order
))
429 (hermpol order arg
))))
431 ;; F(n,b;c;z), where n is a negative integer (number or symbolic).
432 ;; The order of the arguments must be checked by the calling routine.
433 (defun 2f1polys (arg-l1 arg-l2 n
)
435 ;; Since F(a,b;c;z) = F(b,a;c;z), make sure L1 has the negative
436 ;; integer first, so we have F(-n,d;c;z)
437 ;; Remark: 2f1polys is only called from create-poly. create-poly calls
438 ;; 2f1polys with the correct order of arg-l1. This test is not necessary.
439 ; (cond ((not (alike1 (car arg-l1) n))
440 ; (setq arg-l1 (reverse arg-l1))))
443 ;; The argument of the hypergeometric function is a number.
444 ;; Avoid the following check which does not work for this case.
445 (setq v
(div (add (cadr arg-l1
) n
) 2)))
447 ;; Check if (b+n)/2 is free of the argument.
448 ;; At this point of the code there is no check of the return value
449 ;; of vfvp. When nil we have no solution and the result is wrong.
450 (setq l
(vfvp (div (add (cadr arg-l1
) n
) 2)))
451 (setq v
(cdr (assoc 'v l
:test
#'equal
)))))
453 (cond ((and (or (not (integerp n
))
454 (not $expand_polynomials
))
455 ;; Assuming we have F(-n,b;c;z), then v is (b+n)/2.
456 ;; See if it can be a Legendre function.
457 ;; We call legpol-core because we know that arg-l1 has the
458 ;; correct order. This avoids questions about the parameter b
459 ;; from legpol-core, because legpol calls legpol-core with
460 ;; both order of arguments.
461 (setq lgf
(legpol-core (car arg-l1
)
465 ((and (or (not (integerp n
))
466 (not $expand_polynomials
))
467 (alike1 (sub (car arg-l2
) v
) '((rat simp
) 1 2)))
469 ;; F(-n, n+2*a; a+1/2; x) = n!*gegen(n, a, 1-2*x)/pochhammer(2*a,n)
471 ;; So v = a, and (car arg-l2) = a + 1/2.
474 (t (mul (take '(mfactorial) (mul -
1 n
))
475 (inv (take '($pochhammer
)
480 (sub 1 (mul 2 *par
*))))))
483 ;; F(-n, n + a + 1 + b; a + 1; x)
484 ;; = n!*jacobi_p(n,a,b,1-2*x)/pochhammer(a+1,n)
485 (return (mul (take '(mfactorial) (mul -
1 n
))
486 (inv (take '($pochhammer
) (car arg-l2
) (mul -
1 n
)))
488 (add (car arg-l2
) -
1)
489 (sub (mul 2 v
) (car arg-l2
))
490 (sub 1 (mul 2 *par
*)))))))))
493 (defun jacobpol (n a b x
)
494 (if (and $expand_polynomials
(integerp n
))
495 (mfuncall '$jacobi_p n a b x
)
496 (list '($jacobi_p simp
) n a b x
)))
498 ;; Gegenbauer (Ultraspherical) polynomial. We use ultraspherical to
500 (defun gegenpol (n v x
)
501 (cond ((equal v
0) (tchebypol n x
))
503 (if (and $expand_polynomials
(integerp n
))
504 (mfuncall '$ultraspherical n v x
)
505 `(($ultraspherical simp
) ,n
,v
,x
)))))
507 ;; Legendre polynomial
508 (defun legenpol (n x
)
509 (if (and $expand_polynomials
(integerp n
))
510 (mfuncall '$legendre_p n x
)
511 `(($legendre_p simp
) ,n
,x
)))
513 ;; Chebyshev polynomial
514 (defun tchebypol (n x
)
515 (if (and $expand_polynomials
(integerp n
))
516 (mfuncall '$chebyshev_t n x
)
517 `(($chebyshev_t simp
) ,n
,x
)))
519 ;; Expand the hypergeometric function as a polynomial. No real checks
520 ;; are made to ensure the hypergeometric function reduces to a
522 (defmfun $hgfpoly
(arg-l1 arg-l2 arg
)
525 (n (hyp-negp-in-l (cdr arg-l1
))))
526 (create-any-poly (cdr arg-l1
) (cdr arg-l2
) (- n
))))
528 (defun create-any-poly (arg-l1 arg-l2 n
)
529 (prog (result exp prodnum proden
)
530 (setq result
1 prodnum
1 proden
1 exp
1)
532 (cond ((zerop n
) (return result
)))
533 (setq prodnum
(mul prodnum
(mull arg-l1
))
534 proden
(mul proden
(mull arg-l2
)))
540 (inv (factorial exp
)))))
543 arg-l1
(incr1 arg-l1
)
544 arg-l2
(incr1 arg-l2
))
547 ;; Compute the product of the elements of the list L.
549 (reduce #'mul l
:initial-value
1))
551 ;; Add 1 to each element of the list L
553 (mapcar #'(lambda (x) (add x
1)) l
))
555 ;; Figure out the orders of generalized hypergeometric function we
556 ;; have and call the right simplifier.
557 (defun dispatch-spec-simp (arg-l1 arg-l2
)
558 (let ((len1 (length arg-l1
))
559 (len2 (length arg-l2
)))
560 (cond ((and (< len1
2)
562 ;; pFq where p and q < 2.
563 (simp2>f
<2 arg-l1 arg-l2 len1 len2
))
567 (simp2f1 arg-l1 arg-l2
))
571 (cond ((and (maxima-integerp (car arg-l1
))
572 (member ($sign
(car arg-l1
)) '($neg $nz
)))
573 ;; 2F0(-n,b; ; z), n a positive integer
574 (2f0polys arg-l1
(car arg-l1
)))
575 ((and (maxima-integerp (cadr arg-l1
))
576 (member ($sign
(cadr arg-l1
)) '($neg $nz
)))
577 ;; 2F0(a,-n; ; z), n a positive integer
578 (2f0polys (reverse arg-l1
) (cadr arg-l1
)))
580 (fpqform arg-l1 arg-l2 var
))))
582 ;; We don't have simplifiers for any other hypergeometric
584 (fpqform arg-l1 arg-l2 var
)))))
586 ;; Handle the cases where the number of indices is less than 2.
587 (defun simp2>f
<2 (arg-l1 arg-l2 len1 len2
)
588 (cond ((and (zerop len1
) (zerop len2
))
589 ;; hgfred([],[],z) = e^z
591 ((and (zerop len1
) (equal len2
1))
594 ;; hgfred([],[b],0) = 1
599 ;; The hypergeometric series is then
601 ;; 1+sum(z^k/k!/[b*(b+1)*...(b+k-1)], k, 1, inf)
603 ;; = 1+sum(z^k/k!*gamma(b)/gamma(b+k), k, 1, inf)
604 ;; = sum(z^k/k!*gamma(b)/gamma(b+k), k, 0, inf)
605 ;; = gamma(b)*sum(z^k/k!/gamma(b+k), k, 0, inf)
607 ;; Note that bessel_i(b,z) has the series
609 ;; (z/2)^(b)*sum((z^2/4)^k/k!/gamma(b+k+1), k, 0, inf)
611 ;; bessel_i(b-1,2*sqrt(z))
612 ;; = (sqrt(z))^(b-1)*sum(z^k/k!/gamma(b+k),k,0,inf)
613 ;; = z^((b-1)/2)*hgfred([],[b],z)/gamma(b)
615 ;; So this hypergeometric series is a Bessel I function:
617 ;; hgfred([],[b],z) = bessel_i(b-1,2*sqrt(z))*z^((1-b)/2)*gamma(b)
618 (bestrig (car arg-l2
) var
))))
620 ;; hgfred([a],[],z) = 1 + sum(binomial(a+k,k)*z^k) = 1/(1-z)^a
621 (power (sub 1 var
) (mul -
1 (car arg-l1
))))
623 ;; The general case of 1F1, the confluent hypergeomtric function.
624 (confl arg-l1 arg-l2 var
))))
628 ;; bessel_i(a-1,2*sqrt(x))*gamma(a)*x^((1-a)/2)
634 ;; bessel_j(a-1,2*sqrt(x))*gamma(a)*x^((1-a)/2)
638 ;; If a is half of an odd integer and small enough, the Bessel
639 ;; functions are expanded in terms of trig or hyperbolic functions.
642 ;; I think it's ok to have $radexpand $all here so that sqrt(z^2) is
644 (let (($radexpand
'$all
))
646 ;; gamma(b)*(-x)^((1-b)/2)*bessel_j(b-1,2*sqrt(-x))
647 (sratsimp (mul (power (neg x
) (div (sub 1 b
) 2))
651 (mul 2 (power (neg x
) '((rat simp
) 1 2))))))
652 ;; gamma(b)*(x)^((1-b)/2)*bessel_i(b-1,2*sqrt(x))
653 (sratsimp (mul (power x
(div (sub 1 b
) 2))
657 (mul 2 (power x
'((rat simp
) 1 2)))))))))
659 ;; Kummer's transformation. A&S 13.1.27
661 ;; M(a,b,z) = e^z*M(b-a,b,-z)
662 (defun kummer (arg-l1 arg-l2
)
663 (mul (list '(mexpt) '$%e var
)
664 (confl (list (sub (car arg-l2
) (car arg-l1
)))
665 arg-l2
(mul -
1 var
))))
667 ;; Return non-NIL if any element of the list L is zero.
669 (defun zerop-in-l (l)
671 (and (numberp x
) (zerop x
)))
674 ;; If the list L contains a negative integer, return the most positive
675 ;; of the negative integers. Otherwise return NIL.
676 (defun hyp-negp-in-l (l)
679 (when (and (integerp x
) (minusp x
))
681 (setf max-neg
(max max-neg x
))
685 ;; Compute the intersection of L1 and L2, possibly destructively
686 ;; modifying L2. Perserves duplications in L1.
687 (defun zl-intersection (arg-l1 arg-l2
)
688 (cond ((null arg-l1
) nil
)
689 ((member (car arg-l1
) arg-l2
:test
#'equal
)
691 (zl-intersection (cdr arg-l1
)
692 (delete (car arg-l1
) arg-l2
:count
1 :test
#'equal
))))
693 (t (zl-intersection (cdr arg-l1
) arg-l2
))))
695 ;; Whittaker M function. A&S 13.1.32 defines it as
697 ;; %m[k,u](z) = exp(-z/2)*z^(u+1/2)*M(1/2+u-k,1+2*u,z)
699 ;; where M is the confluent hypergeometric function.
700 (defun whitfun (k m var
)
701 (list '(mqapply) (list '($%m array
) k m
) var
))
703 (defvar $trace2f1 nil
704 "Enables simple tracing of simp2f1 so you can see how it decides
705 what approach to use to simplify hypergeometric functions")
707 (defun simp2f1 (arg-l1 arg-l2
)
709 (setq a
(car arg-l1
) b
(cadr arg-l1
) c
(car arg-l2
))
713 (return (add var
1))))
716 (format t
"Tracing SIMP2F1~%")
717 (format t
" Test a or b negative integer ...~%"))
719 ;; Look if a or b is a symbolic negative integer. The routine
720 ;; 2f1polys handles this case.
721 (cond ((and (maxima-integerp a
) (member ($sign a
) '($neg $nz
)))
722 (return (2f1polys arg-l1 arg-l2 a
))))
723 (cond ((and (maxima-integerp b
) (member ($sign b
) '($neg $nz
)))
724 (return (2f1polys (list b a
) arg-l2 b
))))
727 (format t
" Test F(1,1,2)...~%"))
729 (cond ((and (alike1 a
1)
732 ;; F(1,1;2;z) = -log(1-z)/z, A&S 15.1.3
735 (return (mul (inv (mul -
1 var
))
736 (take '(%log
) (add 1 (mul -
1 var
)))))))
739 (format t
" Test c = 1/2 or c = 3/2...~%"))
741 (cond ((or (alike1 c
'((rat simp
) 3 2))
742 (alike1 c
'((rat simp
) 1 2)))
743 ;; F(a,b; 3/2; z) or F(a,b;1/2;z)
744 (cond ((setq lgf
(trig-log (list a b
) (list c
)))
746 (format t
" Yes: trig-log~%"))
750 (format t
" Test |a-b|=1/2...~%"))
752 (cond ((or (alike1 (sub a b
) '((rat simp
) 1 2))
753 (alike1 (sub b a
) '((rat simp
) 1 2)))
754 ;; F(a,b;c;z) where |a-b|=1/2
755 (cond ((setq lgf
(hyp-cos a b c
))
757 (format t
" Yes: hyp-cos~%"))
761 (format t
" Test a,b are integers, c is a numerical integer...~%"))
763 (cond ((and (maxima-integerp a
)
766 ;; F(a,b;c;z) when a, and b are integers (or are declared
767 ;; to be integers) and c is a integral number.
768 (setf lgf
(simpr2f1 (list a b
) (list c
)))
769 (unless (symbolp lgf
) ; Should be more specific! (DK 01/2010)
771 (format t
" Yes: simpr2f1~%"))
775 (format t
" Test a+b and c+1/2 are numerical integers...~%"))
777 (cond ((and (hyp-integerp (add c
(inv 2)))
778 (hyp-integerp (add a b
)))
779 ;; F(a,b;c;z) where a+b is an integer and c+1/2 is an
782 (format t
" Yes: step4~%"))
783 (return (step4 a b c
))))
786 (format t
" Test a-b+1/2 is a numerical integer...~%"))
788 (cond ((hyp-integerp (add (sub a b
) (inv 2)))
789 ;; F(a,b;c,z) where a-b+1/2 is an integer
790 (cond ((setq lgf
(step7 a b c
))
793 (format t
" Yes: step7~%"))
797 (when (and (hyp-integerp (add c
1//2))
798 (or (and (hyp-integerp (add a
1//2))
800 (and (hyp-integerp (add b
1//2))
803 (format t
" Test for atanh: a+1/2, b, and c+1/2 are integers~%"))
804 (return (hyp-atanh a b c
)))
806 (when (hyp-integerp (add c
1//2))
808 (format t
" Test for atanh: c+1/2 is an integer~%"))
809 (cond ((and (hyp-integerp (add a
1//2))
812 (format t
" atanh with integers a+1/2 and b ~%"))
813 (return (hyp-atanh a b c
)))
814 ((and (hyp-integerp (add b
1//2))
817 (format t
" atanh with integers a and b+1/2 ~%"))
818 (return (hyp-atanh b a c
)))))
821 (format t
" Test for Legendre function...~%"))
823 (cond ((setq lgf
(legfun a b c
))
825 ;; LEGFUN returned something interesting, so we're done.
827 (format t
" Yes: case 1~%"))
829 ;; LEGFUN didn't return anything, so try it with the args
830 ;; reversed, since F(a,b;c;z) is F(b,a;c;z).
831 (setf lgf
(legfun b a c
))
834 (format t
" Yes: case 2~%"))
838 (format t
"'simp2f1-will-continue-in~%"))
839 (return (fpqform arg-l1 arg-l2 var
))))
841 ;; As best as I (rtoy) can tell, step7 is meant to handle an extension
842 ;; of hyp-cos, which handles |a-b|=1/2 and either a+b-1/2 = c or
845 ;; Based on the code, step7 wants a = s + m/n and c = 2*s+k/l. For
846 ;; hyp-cos, we want c-2*a to be a integer. Or k/l-2*m/n is an
851 (prog (l m n k mn kl sym sym1 r
)
852 ;; Write a = sym + mn, c = sym1 + kl
858 ;; We only handle the case where 2*sym = sym1.
859 (cond ((not (equal (mul sym
2) sym1
))
861 (setq kl
(cdras 'c l
))
862 ;; a-b+1/2 is an integer.
864 r
(sub (add (inv 2) (cdras 'c l
)) mn
)
869 ;; We have a = m*s+m/n, c = 2*m*s+k/l.
870 (cond ((equal (* 2 l
) n
)
871 (cond ((hyp-integerp (/ (- k m
) n
))
872 (return (hyp-algv k l m n a b c
))))))
873 (cond ((hyp-integerp (/ k
(* 2 l
)))
874 (cond ((hyp-integerp (/ m n
))
875 (return (hyp-algv k l m n a b c
)))
877 ((hyp-integerp (/ m n
))
879 ((hyp-integerp (/ (- (* k n
) (* 2 l m
)) (* 2 l n
)))
880 (return (hyp-algv k l m n a b c
))))
883 (defun getxy (k l m n
)
887 (cond ((hyp-integerp (setq x
(truncate (+ y
891 (return (list x y
))))
895 (defun hyp-algv (k l m n a b c
)
898 (setq xy
(getxy k l m n
)
901 (cond ((< x
0)(go out
)))
903 (cond ((< (add a-b x
(inv 2)) 0)
904 (return (f88 x y a b c fun
)))
905 (t (return (f87 x y a c fun
)))))
907 (cond ((< (add a-b x
(inv 2)) 0)
908 (return (f90 x y a c fun
)))
909 (t (return (f89 x y a c fun
))))))
912 (cond ((< (- (add a-b
(inv 2)) w
) 0)
913 (return (f92 x y a c fun
)))
914 (t (return (f91 x y a c fun
))))))
916 (defun f87 (x y a c fun
)
918 (inv (mul (factf c y
)
919 (factf (sub (add c y
) (add a x
)) (- x y
))
920 (factf (sub (add c y
) (add a x
(inv 2)))
921 (sub (add a x
(inv 2)) (add a
(inv 2))))))
922 (power 'ell
(sub 1 c
))
923 (power (sub 1 'ell
)(sub (add y c
) (add a
(inv 2))))
924 ($diff
(mul (power 'ell
(add a x
))
925 (power (sub 1 'ell
)(mul -
1 a
))
926 ($diff
(mul (power 'ell
(sub (add (inv 2) x
) y
))
927 ($diff
(mul (power 'ell
(sub (add c y
) 1))
938 (defun f88 (x y a b c fun
)
940 (inv (mul (factf c y
)
941 (factf (sub (add c y
) (add a x
)) (- x y
))
942 (factf (add a
(inv 2) x
)
943 (sub b
(sub x
(sub a
(inv 2)))))))
944 (power 'ell
(sub 1 c
))
945 (power (sub 1 'ell
)(sub (add y c
) (add a
(inv 2))))
946 ($diff
(mul (power 'ell
(add a x
))
947 (power (sub 1 'ell
)(mul -
1 a
))
948 ($diff
(mul (power 'ell
(sub c
(sub x
(sub (inv 2) (mul a
2))))))
949 (power (sub 1 'ell
) (sub (add a x b
)(sub c y
)))
950 ($diff
(mul (power 'ell
(sub b
1 ))
953 'ell
(sub b
(sub a
(sub x
(inv 2)))))
958 ;; A new version of step7.
960 ;; To get here, we know that a-b+1/2 is an integer. To make further
961 ;; progress, we want a+b-1/2-c to be an integer too.
963 ;; Let a-b+1/2 = p and a+b+1/2-c = q where p and q are (numerical)
966 ;; A&S 15.2.3 and 15.2.5 allows us to increase or decrease a. Then
967 ;; F(a,b;c;z) can be written in terms of F(a',b;c;z) where a' = a-p
968 ;; and a'-b = a-p-b = 1/2. Also, a'+b+1/2-c = a-p+b+1/2-c = q-p =
969 ;; r, which is also an integer.
971 ;; A&S 15.2.4 and 15.2.6 allows us to increase or decrese c. Thus,
972 ;; we can write F(a',b;c;z) in terms of F(a',b;c',z) where c' =
973 ;; c+r. Now a'-b=1/2 and a'+b+1/2-c' = a-p+b+1/2-c-r =
974 ;; a+b+1/2-c-p-r = q-p-(q-p)=0.
976 ;; Thus F(a',b;c';z) is exactly the form we want for hyp-cos. In
977 ;; fact, it's A&S 15.1.14: F(a,a+1/2,;1+2a;z) =
978 ;; 2^(2*a)*(1+sqrt(1-z))^(-2*a).
979 (declare (special var
))
980 (let ((q (sub (add a b
(inv 2))
982 (unless (hyp-integerp q
)
983 ;; Wrong form, so return NIL
984 (return-from step7 nil
))
985 ;; Since F(a,b;c;z) = F(b,a;c;z), we need to figure out which form
986 ;; to use. The criterion will be the fewest number of derivatives
988 (let* ((p1 (add (sub a b
) (inv 2)))
990 (p2 (add (sub b a
) (inv 2)))
993 (format t
"step 7:~%")
994 (format t
" q, p1, r1 = ~A ~A ~A~%" q p1 r1
)
995 (format t
" p2, r2 = ~A ~A~%" p2 r2
))
996 (cond ((<= (+ (abs p1
) (abs r1
))
997 (+ (abs p2
) (abs r2
)))
1000 (step7-core b a c
))))))
1002 (defun step7-core (a b c
)
1003 (let* ((p (add (sub a b
) (inv 2)))
1004 (q (sub (add a b
(inv 2))
1008 (c-prime (add 1 (mul 2 a-prime
))))
1009 ;; Ok, p and q are integers. We can compute something. There are
1010 ;; four cases to handle depending on the sign of p and r.
1012 ;; We need to differentiate some hypergeometric forms, so use 'ell
1014 (let ((fun (hyp-cos a-prime
(add a-prime
1//2) c-prime
'ell
)))
1015 ;; fun is F(a',a'+1/2;2*a'+1;z), or NIL
1018 (format t
"step7-core~%")
1019 (format t
" a,b,c = ~A ~A ~A~%" a b c
)
1020 (format t
" p,q,r = ~A ~A ~A~%" p q r
)
1021 (format t
" a', c' = ~A ~A~%" a-prime c-prime
)
1022 (format t
" F(a',a'+1/2; 1+2*a';z) =~%")
1023 (maxima-display fun
))
1024 ;; Compute the result, and substitute the actual argument into
1026 (maxima-substitute var
'ell
1029 (step-7-pp a-prime b c-prime p r
'ell fun
))
1031 (step-7-pm a-prime b c-prime p r
'ell fun
))))
1034 (step-7-mp a-prime b c-prime p r
'ell fun
))
1036 (step-7-mm a-prime b c-prime p r
'ell fun
))))))))))
1038 ;; F(a,b;c;z) in terms of F(a',b;c';z)
1040 ;; F(a'+p,b;c'-r;z) where p >= 0, r >= 0.
1041 (defun step-7-pp (a b c p r z fun
)
1042 ;; Apply A&S 15.2.4 and 15.2.3
1043 (let ((res (as-15.2
.4 a b c r z fun
)))
1044 (as-15.2
.3 a b
(sub c r
) p z res
)))
1049 ;; F(a'+p,b;c'-r;z) = F(a'+p,b;c'+r';z)
1050 (defun step-7-pm (a b c p r z fun
)
1051 ;; Apply A&S 15.2.6 and 15.2.3
1052 (let ((res (as-15.2
.6 a b c
(- r
) z fun
)))
1053 (as-15.2
.3 a b
(sub c r
) p z res
)))
1058 ;; F(a'+p,b;c'-r;z) = F(a'-p',b;c'-r;z)
1059 (defun step-7-mp (a b c p r z fun
)
1060 ;; Apply A&S 15.2.4 and 15.2.5
1061 (let ((res (as-15.2
.4 a b c r z fun
)))
1062 (as-15.2
.5 a b
(sub c r
) (- p
) z res
)))
1066 ;; Let p' = - p, r' = -r
1068 ;; F(a'+p,b;c'-r;z) = F(a'-p',b;c'+r';z)
1069 (defun step-7-mm (a b c p r z fun
)
1070 ;; Apply A&S 15.2.6 and A&S 15.2.5
1071 (let ((res (as-15.2
.6 a b c
(- r
) z fun
)))
1072 (as-15.2
.5 a b
(sub c r
) (- p
) z res
)))
1074 ;; F(a,b;c;z) when a and b are integers (or declared to be integers)
1075 ;; and c is an integral number.
1076 (defun simpr2f1 (arg-l1 arg-l2
)
1077 (destructuring-bind (a b
)
1079 (destructuring-bind (c)
1081 (let ((inl1p (hyp-integerp a
))
1082 (inl1bp (hyp-integerp b
))
1083 (inl2p (hyp-integerp c
)))
1086 (cond ((and inl1p inl1bp
)
1087 ;; a, b, c are (numerical) integers
1090 ;; a and c are integers
1093 ;; b and c are integers.
1096 ;; Can't really do anything else if c is not an integer.
1102 ((eq (caaar arg-l1
) 'rat
)
1103 ;; How do we ever get here?
1113 (cond ((and (> (car arg-l2
)(car arg-l1
))
1114 (> (car arg-l2
)(cadr arg-l1
)))
1115 (geredf (car arg-l1
)(cadr arg-l1
)(car arg-l2
)))
1116 (t (gered1 arg-l1 arg-l2
#'hgfsimp
))))
1118 (defun geredno2 (a b c
)
1119 (cond ((> c b
) (geredf b a c
))
1120 (t (gered2 a b c
))))
1122 ;; Consider F(1,1;2;z). A&S 15.1.3 says this is equal to -log(1-z)/z.
1124 ;; Apply A&S 15.2.2:
1126 ;; diff(F(1,1;2;z),z,ell) = poch(1,ell)*poch(1,ell)/poch(2,ell)*F(1+ell,1+ell;2+ell;z)
1130 ;; diff((1-z)^(m+ell)*F(1+ell;1+ell;2+ell;z),z,m)
1131 ;; = (-1)^m*poch(1+ell,m)*poch(1,m)/poch(2+ell,m)*(1-z)^ell*F(1+ell+m,1+ell;2+ell+m;z)
1135 ;; diff((1-z)^ell*F(1+ell+m,1+ell;2+ell+m;z),z,n)
1136 ;; = poch(1,n)*poch(1+m,n)/poch(2+ell+m,n)*(1-z)^(ell-n)*F(1+ell+m,1+ell;2+ell+m+n;z)
1138 ;; The derivation above assumes that ell, m, and n are all
1139 ;; non-negative integers. Thus, F(a,b;c;z), where a, b, and c are
1140 ;; integers and a <= b <= c, can be written in terms of F(1,1;2;z).
1141 ;; The result also holds for b <= a <= c, of course.
1143 ;; Also note that the last two differentiations can be combined into
1144 ;; one differention since the result of the first is in exactly the
1145 ;; form required for the second. The code below does one
1148 ;; So if a = 1+ell, b = 1+ell+m, and c = 2+ell+m+n, we have ell = a-1,
1149 ;; m = b - a, and n = c - ell - m - 2 = c - b - 1.
1151 (defun derivint (a b c
)
1162 (factorial (+ n m l
1))
1165 (inv (factorial (+ n m
)))
1166 (inv (factorial (+ m l
)))
1167 (power (sub 1 psey
) (sub n l
))
1168 ($diff
(mul (power (sub 1 psey
) (+ m l
))
1169 ($diff
(mul (power psey -
1)
1171 (take '(%log
) (sub 1 psey
)))
1177 ($limit result psey var
)
1178 (maxima-substitute var psey result
)))))
1180 ;; Handle F(a, b; c; z) for certain values of a, b, and c. See the
1181 ;; comments below for these special values. The optional arg z
1182 ;; defaults to var, which is usually the argument of hgfred.
1183 (defun hyp-cos (a b c
&optional
(z var
))
1184 (let ((a1 (div (sub (add a b
) (div 1 2)) 2))
1188 (cond ((eql 0 ($ratsimp
(sub (sub (add a b
)
1196 ;; = 2^(2*a-1)*(1-z)^(-1/2)*(1+sqrt(1-z))^(1-2*a)
1198 ;; But if 1-2*a is a negative integer, let's rationalize the answer to give
1201 ;; = 2^(2*a-1)*(1-z)^(-1/2)*(1-sqrt(1-z))^(2*a-1)/z^(2*a-1)
1203 (format t
" Case a+b-1/2=c~%"))
1204 (let ((2a-1 (sub (mul a1
2) 1)))
1205 (cond ((and (integerp 2a-1
) (plusp 2a-1
))
1206 ;; 2^(2*a-1)*(1-z)^(-1/2)*(1-sqrt(1-z))^(2*a-1)/z^(2*a-1)
1208 (inv (power z1
1//2))
1209 (power (sub 1 (power z1
1//2)) 2a-1
)
1210 (inv (power z
2a-1
))))
1212 ;; 2^(2*a-1)*(1-z)^(-1/2)*(1+sqrt(1-z))^(1-2*a)
1213 (mul (power 2 (sub (mul a1
2) 1))
1214 (inv (power z1
(div 1 2)))
1219 (sub 1 (mul 2 a1
))))))))
1220 ((eql 0 ($ratsimp
(sub (add 1 (mul 2 a1
)) c
)))
1221 ;; c = 1+2*a1 = a+b+1/2
1225 ;; F(a,1/2+a;1+2*a;z) = 2^(2*a)*(1+sqrt(1-z))^(-2*a)
1227 ;; But if 2*a is a positive integer, let's rationalize the answer to give
1229 ;; F(a,1/2+a;1+2*a;z) = 2^(2*a)*(1-sqrt(1-z))^(2*a)/z^(2*a)
1231 (format t
" Case c=1+2*a=a+b+1/2~%"))
1232 (let ((2a (sub c
1)))
1233 (cond ((and (integerp 2a
) (plusp 2a
))
1234 ;; 2^(2*a)*(1-sqrt(1-z))^(2*a)/z^(2*a)
1236 (power (sub 1 (power z1
1//2))
1238 (power z
(mul -
1 2a
))))
1240 ;; 2^(2*a)*(1+sqrt(1-z))^(-2*a)
1242 (power (add 1 (power z1
1//2))
1243 (mul -
1 2a
))))))))))
1245 ;; Is A a non-negative integer?
1247 (cond ((hyp-integerp a
)
1251 ;;; The following code doesn't appear to be used at all. Comment it all out for now.
1255 (cond ((eq (quest (sub c b
)) '$negative
)
1256 (cond ((eq (quest (sub c a
)) '$negative
)
1257 (gered1 (list a b
)(list c
) #'hgfsimp
))
1258 (t (gered2 a b c
))))
1259 ((eq (quest (sub c a
)) '$negative
)(gered2 b a c
))
1260 (t (rest-degen a b c
))))
1266 (cond ((nni (setq m
(sub a
1)))
1267 (return (rest-degen-1 a b c m
))))
1268 (cond ((ni b
)(return (rest-degen-2 a b c
))))
1269 (cond ((and (nni (setq n
(sub c
1)))
1270 (nni (setq m
(sub (sub a n
) 1)))
1271 (nni (setq l
(sub b a
)))
1272 (eq (sub (sub c a
) b
)
1273 (mul -
1 (add m m n l
1))))
1274 (return (gered1 (list a b
)
1277 (return (hyp-deg b a c
))))
1284 (ni (sub (sub c a
) b
))
1285 (nni (sub (sub c a
) 1)))
1286 (return (deg299 a b c
))))
1287 (cond ((and (nni (setq n
(sub (sub c m
) 2)))
1288 (nni (setq l
(sub b c
)))
1289 (equal (sub (sub c a
) b
)
1290 (mul -
1 (add l m
1))))
1291 (return (gered1 (list a b
)
1294 (cond ((nni (setq l
(sub (sub b m
) 1)))
1295 (return (rest-degen-1a a b c m l
))))
1296 (return (hyp-deg b a c
))))
1299 (defun rest-degen-1a
1302 (cond ((and (nni (setq n
1303 (sub (sub (sub c m
) l
) 2)))
1304 (equal (sub n m
)(sub (sub c a
) b
)))
1305 (return (deg2913 a b c
))))
1306 (cond ((and (equal c
(mul -
1 n
))
1307 (equal (sub (sub c a
) b
)
1308 (mul -
1 (add m m l n
2))))
1309 (return (deg2918 a b c
))))
1310 (return (hyp-deg b a c
))))
1316 (cond ((and (ni c
)(ni (sub (sub c a
) b
)))
1317 (return (rest-degen-2a a b c
))))
1318 (cond ((and (nni (setq m
(sub c
1)))
1319 (nni (setq l
(sub a c
)))
1320 (ni (sub (sub c a
) b
)))
1321 (return (deg292 a b c
))))
1322 (return (hyp-deg b a c
))))
1325 (defun rest-degen-2a
1328 (cond ((nni (sub a c
))
1329 (return (gered1 (list a b
)
1332 (cond ((nni (sub (sub c a
) 1))
1333 (return (deg2917 a b c
))))
1334 (return (hyp-deg b a c
))))
1338 (cond ((numberp a
)(checksigntm a
))
1339 ((equal (caar a
) 'rat
)(checksigntm a
))
1342 (defun ni(a)(not (hyp-integerp a
)))
1348 (cond (fldeg (setq fldeg nil
)
1349 (return (hgfsimp (list a b
)
1353 (return (fpqform (list a b
)(list c
) var
))))
1358 (mul (power (mul -
1 var
)(mul -
1 b
))
1359 (hgfsimp (list (add b
1 (mul -
1 c
)) b
)
1360 (list (add b
1 (mul -
1 a
)))
1366 (mul (power var
(sub 1 c
))
1367 (power (sub 1 var
)(add c
(mul -
1 a
)(mul -
1 b
)))
1368 (hgfsimp (list (sub 1 a
)(sub 1 b
))
1375 (mul (power var
(sub 1 c
))
1376 (hgfsimp (list (add a
1 (mul -
1 c
))
1377 (add b
1 (mul -
1 c
)))
1384 (mul (power (mul -
1 var
)(mul -
1 a
))
1385 (hgfsimp (list a
(add a
1 (mul -
1 c
)))
1386 (list (add a
1 (mul -
1 b
)))
1392 ;; Is F(a, b; c; z) is Legendre function?
1394 ;; Lemma 29 (see ref) says F(a, b; c; z) can be reduced to a Legendre
1395 ;; function if two of the numbers 1-c, +/-(a-b), and +/- (c-a-b) are
1396 ;; equal to each other or one of them equals +/- 1/2.
1398 ;; This routine checks for each of the possibilities.
1399 (defun legfun (a b c
)
1400 (let ((1-c (sub 1 c
))
1402 (c-a-b (sub (sub c a
) b
))
1404 (cond ((alike1 a-b inv2
)
1407 (format t
"Legendre a-b = 1/2~%"))
1408 (gered1 (list a b
) (list c
) #'legf24
))
1410 ((alike1 a-b
(mul -
1 inv2
))
1413 ;; For example F(a,a+1/2;c;x)
1415 (format t
"Legendre a-b = -1/2~%"))
1416 (legf24 (list a b
) (list c
) var
))
1418 ((alike1 c-a-b
'((rat simp
) 1 2))
1420 ;; For example F(a,b;a+b+1/2;z)
1422 (format t
"Legendre c-a-b = 1/2~%"))
1423 (legf20 (list a b
) (list c
) var
))
1425 ((and (alike1 c-a-b
'((rat simp
) 3 2))
1427 (not (alike1 a -
1//2))
1428 (not (alike1 b -
1//2)))
1429 ;; c-a-b = 3/2 e.g. F(a,b;a+b+3/2;z) Reduce to
1430 ;; F(a,b;a+b+1/2) and use A&S 15.2.6. But if c = 1, we
1431 ;; don't want to reduce c to 0! Problem: The derivative of
1432 ;; assoc_legendre_p introduces a unit_step function and the
1433 ;; result looks very complicated. And this doesn't work if
1434 ;; a+1/2 or b+1/2 is zero, so skip that too.
1436 (format t
"Legendre c-a-b = 3/2~%")
1437 (mformat t
" : a = ~A~%" a
)
1438 (mformat t
" : b = ~A~%" b
)
1439 (mformat t
" : c = ~A~%" c
))
1440 (let ((psey (gensym)))
1443 (mul (power (sub 1 psey
) '((rat simp
) 3 2))
1444 (add a b
'((rat simp
) 1 2))
1445 (inv (add b
'((rat simp
) 1 2)))
1446 (inv (add a
'((rat simp
) 1 2)))
1447 ($diff
(mul (power (sub 1 psey
) '((rat simp
) -
1 2))
1449 (list (add a b
'((rat simp
) 1 2)))
1454 ((alike1 c-a-b
'((rat simp
) -
1 2))
1455 ;; c-a-b = -1/2, e.g. F(a,b; a+b-1/2; z)
1456 ;; This case is reduce to F(a,b; a+b+1/2; z) with
1457 ;; F(a,b;c;z) = (1-z)^(c-a-b)*F(c-a,c-b;c;z)
1459 (format t
"Legendre c-a-b = -1/2~%"))
1460 (gered1 (list a b
) (list c
) #'legf20
))
1463 ;; 1-c = a-b, F(a,b; b-a+1; z)
1465 (format t
"Legendre 1-c = a-b~%"))
1466 (gered1 (list a b
) (list c
) #'legf16
))
1468 ((alike1 1-c
(mul -
1 a-b
))
1469 ;; 1-c = b-a, e.g. F(a,b; a-b+1; z)
1471 (format t
"Legendre 1-c = b-a~%"))
1472 (legf16 (list a b
) (list c
) var
))
1475 ;; 1-c = c-a-b, e.g. F(a,b; (a+b+1)/2; z)
1477 (format t
"Legendre 1-c = c-a-b~%"))
1478 (gered1 (list a b
) (list c
) #'legf14
))
1480 ((alike1 1-c
(mul -
1 c-a-b
))
1483 ;; For example F(a,1-a;c;x)
1485 (format t
"Legendre 1-c = a+b-c~%"))
1486 (legf14 (list a b
) (list c
) var
))
1488 ((alike1 a-b
(mul -
1 c-a-b
))
1489 ;; a-b = a+b-c, e.g. F(a,b;2*b;z)
1491 (format t
"Legendre a-b = a+b-c~%"))
1492 (legf36 (list a b
) (list c
) var
))
1494 ((or (alike1 1-c inv2
)
1495 (alike1 1-c
(mul -
1 inv2
)))
1496 ;; 1-c = 1/2 or 1-c = -1/2
1497 ;; For example F(a,b;1/2;z) or F(a,b;3/2;z)
1499 (format t
"Legendre |1-c| = 1/2~%"))
1500 ;; At this point it does not make sense to call legpol. legpol can
1501 ;; handle only cases with a negative integer in the first argument
1502 ;; list. This has been tested already. Therefore we can not get
1503 ;; a result from legpol. For this case a special algorithm is needed.
1504 ;; At this time we return nil.
1511 (format t
"Legendre a-b = c-a-b~%"))
1512 'legendre-funct-to-be-discovered
)
1516 ;;; The following legf<n> functions correspond to formulas in Higher
1517 ;;; Transcendental Functions. See the chapter on Legendre functions,
1518 ;;; in particular the table on page 124ff,
1520 ;; Handle the case c-a-b = 1/2
1524 ;; P(n,m,z) = 2^m*(z^2-1)^(-m/2)/gamma(1-m)*F(1/2+n/2-m/2, -n/2-m/2; 1-m; 1-z^2)
1526 ;; See also A&S 15.4.12 and 15.4.13.
1528 ;; Let a = 1/2+n/2-m/2, b = -n/2-m/2, c = 1-m. Then, m = 1-c. And we
1529 ;; have two equivalent expressions for n:
1531 ;; n = c - 2*b - 1 or n = 2*a - c
1533 ;; The code below chooses the first solution. A&S chooses second.
1535 ;; F(a,b;c;w) = 2^(c-1)*gamma(c)*(-w)^((1-c)/2)*P(c-2*b-1,1-c,sqrt(1-w))
1538 (defun legf20 (arg-l1 arg-l2 var
)
1540 (let* (($radexpand nil
)
1543 (a (sub (sub c b
) '((rat simp
) 1 2)))
1545 (n (mul -
1 (add b b m
))))
1547 ;; n = -(2*b+1-c) = c - 1 - 2*b
1550 ;; F(a,b;a+b+1/2;x) = 2^(a+b-1/2)*gamma(a+b+1/2)*x^((1/2-a-b)/2)
1551 ;; *assoc_legendre_p(a-b-1/2,1/2-a-b,sqrt(1-x))
1552 ;; This formula is correct for all arguments x.
1553 (mul (power 2 (add a b
'((rat simp
) -
1 2)))
1554 (take '(%gamma
) (add a b
'((rat simp
) 1 2)))
1556 (div (sub '((rat simp
) 1 2) (add a b
))
1560 (power (sub 1 var
) '((rat simp
) 1 2))
1563 ;; Handle the case a-b = -1/2.
1567 ;; P(n,m,z) = 2^m*(z^2-1)^(-m/2)*z^(n+m)/gamma(1-m)*F(-n/2-m/2,1/2-n/2-m/2;1-m;1-1/z^2)
1569 ;; See also A&S 15.4.10 and 15.4.11.
1571 ;; Let a = -n/2-m/2, b = 1/2-n/2-m/2, c = 1-m. Then m = 1-c. Again,
1572 ;; we have 2 possible (equivalent) values for n:
1574 ;; n = -(2*a + 1 - c) or n = c-2*b
1576 ;; The code below chooses the first solution.
1578 ;; F(a,b;c;w) = 2^(c-1)*w^(1/2-c/2)*(1-w)^(c/2-a-1/2)*P(c-2*a-1,1-c,1/sqrt(1-w))
1580 ;; F(a,b;c;w) = 2^(c-1)*w^(1/2-c/2)*(1-w)^(c/2-b)*P(c-2*b,1-c,sqrt(1-w))
1582 ;; Is there a mistake in 15.4.10 and 15.4.11?
1584 (defun legf24 (arg-l1 arg-l2 var
)
1585 (let* (($radexpand nil
)
1589 ; (n (mul -1 (add a a m))) ; This is not 2*a-c
1590 (n (sub (add a a
) c
)) ; but this.
1591 (z (inv (power (sub 1 var
) (inv 2)))))
1592 ;; A&S 15.4.10, 15.4.11
1593 (cond ((eq (asksign var
) '$negative
)
1596 ;; F(a,a+1/2;c;x) = 2^(c-1)*gamma(c)(-x)^(1/2-c/2)*(1-x)^(c/2-a-1/2)
1597 ;; *assoc_legendre_p(2*a-c,1-c,1/sqrt(1-x))
1598 (mul (inv (power 2 m
))
1600 (power (mul -
1 var
) (div m
2))
1601 (power (sub 1 var
) (sub (div m -
2) a
))
1607 (mul (inv (power 2 m
))
1609 (power var
(div m
2))
1610 (power (sub 1 var
) (sub (div m -
2) a
))
1620 ;; P(n,m,z) = 2^(-n)*(z+1)^(m/2+n)*(z-1)^(-m/2)/gamma(1-m)*F(-n,-n-m;1-m;(z-1)/(z+1))
1622 ;; See also A&S 15.4.14 and 15.4.15.
1624 ;; Let a = -n, b = -n-m, c = 1-m. Then m = 1-c. We have 2 solutions
1627 ;; n = -a or n = c-b-1.
1629 ;; The code below chooses the first solution.
1631 ;; F(a,b;c;w) = gamma(c)*w^(1/2-c/2)*(1-w)^(-a)*P(-a,1-c,(1+w)/(1-w));
1633 ;; FIXME: We don't correctly handle the branch cut here!
1634 (defun legf16 (arg-l1 arg-l2 var
)
1635 (let* (($radexpand nil
)
1641 ;; m = b - a, so b = a + m
1647 (format t
"a, c = ~A ~A~%" a c
)
1648 (format t
"b = ~A~%" b
))
1649 ;; A&S 15.4.14, 15.4.15
1650 (cond ((eq (asksign var
) '$negative
)
1653 ;; F(a,b;a-b+1,x) = gamma(a-b+1)*(1-x)^(-b)*(-x)^(b/2-a/2)
1654 ;; * assoc_legendre_p(-b,b-a,(1+x)/(1-x))
1657 (mul (take '(%gamma
) c
)
1658 (power (sub 1 var
) (mul -
1 b
))
1659 (power (mul -
1 var
) (div m
2))
1662 (mul (take '(%gamma
) c
)
1663 (power (sub 1 var
) (mul -
1 b
))
1664 (power var
(div m
2))
1665 (legen n m z
'$p
))))))
1667 ;; Handle the case 1-c = a+b-c.
1669 ;; See, for example, A&S 8.1.2 (which
1670 ;; might have a bug?) or
1671 ;; http://functions.wolfram.com/HypergeometricFunctions/LegendreP2General/26/01/02/
1675 ;; P(n,m,z) = (z+1)^(m/2)*(z-1)^(-m/2)/gamma(1-m)*F(-n,1+n;1-m;(1-z)/2)
1677 ;; See also A&S 8.1.2, 15.4.16, 15.4.17
1679 ;; Let a=-n, b = 1+n, c = 1-m. Then m = 1-c and n has 2 solutions:
1681 ;; n = -a or n = b - 1.
1683 ;; The code belows chooses the first solution.
1685 ;; F(a,b;c;w) = gamma(c)*(-w)^(1/2-c/2)*(1-w)^(c/2-1/2)*P(-a,1-c,1-2*w)
1686 (defun legf14 (arg-l1 arg-l2 var
)
1687 ;; Set $radexpand to NIL, because we don't want (-z)^x getting
1688 ;; expanded to (-1)^x*z^x because that's wrong for this.
1689 (let* (($radexpand nil
)
1695 (z (sub 1 (mul 2 var
))))
1697 (format t
"~&legf14~%"))
1698 ;; A&S 15.4.16, 15.4.17
1699 (cond ((not (alike1 (add a b
) 1))
1700 ;; I think 15.4.16 and 15.4.17 require the form
1701 ;; F(a,1-a;c;x). That is, a+b = 1. If we don't have it
1704 ((and (eq (asksign var
) '$positive
)
1705 (eq (asksign (sub 1 var
)) '$positive
))
1707 (format t
" A&S 15.4.17~%"))
1710 ;; F(a,1-a;c;x) = gamma(c)*x^(1/2-c/2)*(1-x)^(c/2-1/2)*
1711 ;; assoc_legendre_p(-a,1-c,1-2*x)
1715 (power var
(div (sub 1 c
) 2))
1716 (power (sub 1 var
) (div (sub c
1) 2))
1721 ;; F(a,1-a;c;z) = gamma(c)*(-z)^(1/2-c/2)*(1-z)^(c/2-1/2)*
1722 ;; assoc_legendre_p(-a,1-c,1-2*z)
1724 (format t
" A&S 15.4.17~%"))
1726 (power (mul -
1 var
) (div (sub 1 c
) 2))
1727 (power (sub 1 var
) (div (sub c
1) 2))
1728 (legen n m z
'$p
))))))
1730 ;; Handle a-b = a+b-c
1734 ;; exp(-%i*m*%pi)*Q(n,m,z) =
1735 ;; 2^n*gamma(1+n)*gamma(1+n+m)*(z+1)^(m/2-n-1)*(z-1)^(-m/2)/gamma(2+2*n)
1736 ;; * hgfred([1+n-m,1+n],[2+2*n],2/(1+z))
1738 ;; Let a = 1+n-m, b = 1+n, c = 2+2*n. then n = b-1 and m = b - a.
1739 ;; (There are other solutions.)
1741 ;; F(a,b;c;z) = 2*gamma(2*b)/gamma(b)/gamma(2*b-a)*w^(-b)*(1-w)^((b-a)/2)
1742 ;; *Q(b-1,b-a,2/w-1)*exp(-%i*%pi*(b-a))
1744 (defun legf36 (arg-l1 arg-l2 var
)
1745 (declare (ignore arg-l2
))
1746 (let* ((a (car arg-l1
))
1750 ;;z (div (sub 2 var) var)
1751 (z (sub (div 2 var
) 1)))
1752 (mul (inv (power 2 n
))
1753 (inv (gm (add 1 n
)))
1754 (inv (gm (add 1 n m
)))
1755 (inv (power (add z
1)
1759 (inv (power (sub z
1) (div m -
2)))
1761 (power '$%e
(mul -
1 '$%i m
'$%pi
))
1762 (legen n m z
'$q
))))
1764 (defun legen (n m x pq
)
1765 ;; A&S 8.2.1: P(-n-1,m,z) = P(n,m,z)
1766 (let ((n (if (or (member ($sign n
) '($neg $nz
)) ; negative sign or
1767 (mminusp n
)) ; negative form like -n-1
1771 (list (if (eq pq
'$q
)
1773 '($legendre_p simp
))
1776 (list (if (eq pq
'$q
)
1777 '($assoc_legendre_q simp
)
1778 '($assoc_legendre_p simp
))
1781 (defun legpol-core (a b c
)
1782 ;; I think for this to be correct, we need a to be a negative integer.
1783 (unless (and (eq '$yes
(ask-integerp a
))
1784 (eq (asksign a
) '$negative
))
1785 (return-from legpol-core nil
))
1786 (let* ((l (vfvp (div (add b a
) 2)))
1787 (v (cdr (assoc 'v l
:test
#'equal
))))
1790 ((and (alike1 v
'((rat simp
) 1 2))
1793 ;; P(n,x) = F(-n,n+1;1;(1-x)/2)
1794 (legenpol (mul -
1 a
)
1795 (sub 1 (mul 2 var
))))
1797 ((and (alike1 c
'((rat simp
) 1 2))
1798 (alike1 (add b a
) '((rat simp
) 1 2)))
1799 ;; c = 1/2, a+b = 1/2
1802 ;; P(2*n,x) = (-1)^n*(2*n)!/2^(2*n)/(n!)^2*F(-n,n+1/2;1/2;x^2)
1804 ;; F(-n,n+1/2;1/2;x^2) = P(2*n,x)*(-1)^n*(n!)^2/(2*n)!*2^(2*n)
1806 (let ((n (mul -
1 a
)))
1808 (power (gm (add n
1)) 2)
1809 (inv (gm (add 1 (mul 2 n
))))
1812 (power var
(div 1 2))))))
1814 ((and (alike1 c
'((rat simp
) 3 2))
1815 (alike1 (add b a
) '((rat simp
) 3 2)))
1816 ;; c = 3/2, a+b = 3/2
1819 ;; P(2*n+1,x) = (-1)^n*(2*n+1)!/2^(2*n)/(n!)^2*F(-n,n+3/2;3/2;x^2)*x
1821 ;; F(-n,n+3/2;3/2;x^2) = P(2*n+1,x)*(-1)^n*(n!)^2/(2*n+1)!*2^(2*n)/x
1823 (let ((n (mul -
1 a
)))
1825 (power (gm (add 1 n
)) 2)
1826 (inv (gm (add 2 (mul 2 n
))))
1828 (legenpol (add 1 (mul 2 n
))
1829 (power var
(div 1 2)))
1830 (inv (power var
(div 1 2))))))
1832 ((and (zerp (sub b a
))
1833 (zerp (sub c
(add a b
))))
1837 ;; P(n,x) = binomial(2*n,n)*((x-1)/2)^n*F(-n,-n;-2*n;2/(1-x))
1839 ;; F(-n,-n;-2*n;x) = P(n,1-2/x)/binomial(2*n,n)(-1/x)^(-n)
1840 (mul (power (gm (add 1 (mul -
1 a
))) 2)
1841 (inv (gm (add 1 (mul -
2 a
))))
1842 (power (mul -
1 var
) (mul -
1 a
))
1843 (legenpol (mul -
1 a
)
1844 (add 1 (div -
2 var
)))))
1845 ((and (alike1 (sub a b
) '((rat simp
) 1 2))
1846 (alike1 (sub c
(mul 2 b
)) '((rat simp
) 1 2)))
1847 ;; a - b = 1/2, c - 2*b = 1/2
1850 ;; P(n,x) = binomial(2*n,n)*(x/2)^n*F(-n/2,(1-n)/2;1/2-n;1/x^2)
1852 ;; F(-n/2,(1-n)/2;1/2-n,1/x^2) = P(n,x)/binomial(2*n,n)*(x/2)^(-n)
1853 (mul (power (gm (add 1 (mul -
2 b
))) 2)
1854 (inv (gm (add 1 (mul -
4 b
))))
1855 (power (mul 2 (power var
(div 1 2))) (mul -
2 b
))
1856 (legenpol (mul -
2 b
)
1857 (power var
(div -
1 2)))))
1858 ((and (alike1 (sub b a
) '((rat simp
) 1 2))
1859 (alike1 (sub c
(mul 2 a
)) '((rat simp
) 1 2)))
1860 ;; b - a = 1/2, c + 2*a = 1/2
1863 ;; P(n,x) = binomial(2*n,n)*(x/2)^n*F(-n/2,(1-n)/2;1/2-n;1/x^2)
1865 ;; F(-n/2,(1-n)/2;1/2-n,1/x^2) = P(n,x)/binomial(2*n,n)*(x/2)^(-n)
1866 (mul (power (gm (add 1 (mul -
2 a
))) 2)
1867 (inv (gm (add 1 (mul -
4 a
))))
1868 (power (mul 2 (power var
(div 1 2))) (mul -
2 a
))
1869 (legenpol (mul -
2 a
)
1870 (power var
(div -
1 2)))))
1874 (defun legpol (a b c
)
1875 ;; See if F(a,b;c;z) is a Legendre polynomial. If not, try
1877 (or (legpol-core a b c
)
1878 (legpol-core b a c
)))
1882 ;; F(a,b;c;z) = (1-z)^(c-a-b)*F(c-a,c-b;c;z)
1883 (defun gered1 (arg-l1 arg-l2 simpflg
)
1884 (destructuring-bind (a b
)
1886 (destructuring-bind (c)
1888 (mul (power (sub 1 var
)
1900 ;; F(a,b;c;z) = (1-z)^(-a)*F(a,c-b;c;z/(z-1))
1901 (defun gered2 (a b c
)
1902 (mul (power (sub 1 var
) (mul -
1 a
))
1903 (hgfsimp (list a
(sub c b
))
1905 (div var
(sub var
1)))))
1909 ;; F(a,b;c;z) = A*z^(-a)*F(a,a-c+1;a+b-c+1;1-1/z)
1910 ;; + B*(1-z)^(c-a-b)*z^(a-c)*F(c-a,1-a;c-a-b+1,1-1/z)
1912 ;; where A = gamma(c)*gamma(c-a-b)/gamma(c-a)/gamma(c-b)
1913 ;; B = gamma(c)*gamma(a+b-c)/gamma(a)/gamma(b)
1915 (defun geredf (a b c
)
1916 (let (($gamma_expand t
))
1917 (add (div (mul (take '(%gamma
) c
)
1918 (take '(%gamma
) (add c
(mul -
1 a
) (mul -
1 b
)))
1919 (power var
(mul -
1 a
))
1920 ($hgfred
`((mlist) ,a
,(add a
1 (mul -
1 c
)))
1921 `((mlist) ,(add a b
(mul -
1 c
) 1))
1922 (sub 1 (div 1 var
))))
1923 (mul (take '(%gamma
) (sub c a
))
1924 (take '(%gamma
) (sub c b
))))
1925 (div (mul (take '(%gamma
) c
)
1926 (take '(%gamma
) (add a b
(mul -
1 c
)))
1928 (add c
(mul -
1 a
) (mul -
1 b
)))
1929 (power var
(sub a c
))
1930 ($hgfred
`((mlist) ,(sub c a
) ,(sub 1 a
))
1931 `((mlist) ,(add c
(mul -
1 a
) (mul -
1 b
) 1))
1932 (sub 1 (div 1 var
))))
1933 (mul (take '(%gamma
) a
) (take '(%gamma
) b
))))))
1935 (defun trig-log (arg-l1 arg-l2
)
1936 (cond ((equal (simplifya (car arg-l2
) nil
) '((rat simp
) 3 2))
1939 (format t
" trig-log: Test c=3/2~%"))
1940 (trig-log-3 arg-l1 arg-l2
))
1941 ((equal (simplifya (car arg-l2
) nil
) '((rat simp
) 1 2))
1944 (format t
" trig-log: Test c=1/2~%"))
1945 (trig-log-1 arg-l1 arg-l2
))
1948 (defun trig-log-3 (arg-l1 arg-l2
)
1949 (cond ((and (or (equal (car arg-l1
) 1) (equal (cadr arg-l1
) 1))
1950 (or (equal (car arg-l1
) (div 1 2))
1951 (equal (cadr arg-l1
) (div 1 2))))
1952 ;; (a = 1 or b = 1) and (a = 1/2 or b = 1/2)
1954 (format t
" Case a or b is 1 and the other is 1/2~%"))
1955 (trig-log-3-exec arg-l1 arg-l2
))
1956 ((and (equal (car arg-l1
) (cadr arg-l1
))
1957 (or (equal 1 (car arg-l1
))
1958 (equal (div 1 2) (car arg-l1
))))
1959 ;; a = b and (a = 1 or a = 1/2)
1961 (format t
" Case a=b and a is 1 or 1/2~%"))
1962 (trig-log-3a-exec arg-l1 arg-l2
))
1963 ((or (equal (add (car arg-l1
) (cadr arg-l1
)) 1)
1964 (equal (add (car arg-l1
) (cadr arg-l1
)) 2))
1965 ;; a + b = 1 or a + b = 2
1967 (format t
" Case a+b is 1 or 2~%"))
1968 (trig-sin arg-l1 arg-l2
))
1969 ((or (equal (sub (car arg-l1
) (cadr arg-l1
)) (div 1 2))
1970 (equal (sub (cadr arg-l1
) (car arg-l1
)) (div 1 2)))
1971 ;; a - b = 1/2 or b - a = 1/2
1973 (format t
" Case a-b=1/2 or b-a=1/2~%"))
1974 (trig-3 arg-l1 arg-l2
))
1977 (defun trig-3 (arg-l1 arg-l2
)
1978 (declare (ignore arg-l2
))
1981 ;; F(a,a+1/2,3/2,z^2) =
1982 ;; ((1+z)^(1-2*a) - (1-z)^(1-2*a))/2/z/(1-2*a)
1983 (let* (($radexpand
'$all
)
1985 (sub (add (car arg-l1
)
1988 (z (power var
(div 1 2))))
1992 (sub (power (add 1 z
) a
)
1993 (power (sub 1 z
) a
)))))
1995 (defun trig-sin (arg-l1 arg-l2
)
1996 (declare (ignore arg-l2
))
1997 ;; A&S 15.1.15, 15.1.16
1998 (destructuring-bind (a b
)
2000 ;; I think it's ok to convert sqrt(z^2) to z here, so $radexpand
2002 (let (($radexpand
'$all
)
2004 (cond ((equal (add a b
) 1)
2007 ;; F(a,1-a;3/2;sin(z)^2) =
2009 ;; sin((2*a-1)*z)/(2*a-1)/sin(z)
2010 (mul (inv (mul (mul -
1 (sub a b
))
2011 (msin (masin (msqrt var
)))))
2014 (masin (msqrt var
))))))
2015 ((equal (add a b
) 2)
2018 ;; F(a, 2-a; 3/2; sin(z)^2) =
2020 ;; sin((2*a-2)*z)/(a-1)/sin(2*z)
2021 (mul (msin (mul (setq z1
2035 ;;Generates atan if arg positive else log
2036 (defun trig-log-3-exec (arg-l1 arg-l2
)
2037 (declare (ignore arg-l1 arg-l2
))
2038 ;; See A&S 15.1.4 and 15.1.5
2040 ;; F(a,b;3/2;z) where a = 1/2 and b = 1 (or vice versa).
2042 ;; I think it's ok to convert sqrt(z^2) to z here, so $radexpand is
2044 (let (($radexpand
'$all
))
2045 (cond ((equal (checksigntm var
) '$positive
)
2048 ;; F(1/2,1;3/2,z^2) =
2050 ;; log((1+z)/(1-z))/z/2
2052 ;; This is the same as atanh(z)/z. Should we return that
2053 ;; instead? This would make this match what hyp-atanh
2055 (let ((z (power var
(div 1 2))))
2058 (mlog (div (add 1 z
)
2060 ((equal (checksigntm var
) '$negative
)
2063 ;; F(1/2,1;3/2,z^2) =
2065 (let ((z (power (mul -
1 var
)
2070 (defun trig-log-3a-exec (arg-l1 arg-l2
)
2071 ;; See A&S 15.1.6 and 15.1.7
2073 ;; F(a,b;3/2,z) where a = b and a = 1/2 or a = 1.
2075 ;; I think it's ok to convert sqrt(z^2) to z here, so $radexpand is
2077 (let ((a (first arg-l1
))
2079 (cond ((equal (checksigntm var
) '$positive
)
2082 ;; F(1/2,1/2; 3/2; z^2) = sqrt(1-z^2)*F(1,1;3/2;z^2) =
2084 (let ((z (power var
(div 1 2))))
2086 (div (trig-log-3a-exec (list (div 1 2) (div 1 2)) arg-l2
)
2087 (power (sub 1 (power z
2)) (div 1 2)))
2088 (div (masin z
) z
))))
2089 ((equal (checksigntm var
) '$negative
)
2092 ;; F(1/2,1/2; 3/2; -z^2) = sqrt(1+z^2)*F(1,1,3/2; -z^2) =
2093 ;;log(z + sqrt(1+z^2))/z
2094 (let* ((z (power (mul -
1 var
)
2096 (1+z^
2 (add 1 (power z
2))))
2098 (div (trig-log-3a-exec (list (div 1 2) (div 1 2))
2102 (div (mlog (add z
(power 1+z^
2
2107 (defun trig-log-1 (arg-l1 arg-l2
) ;; 2F1's with C = 1/2
2108 (declare (ignore arg-l2
))
2109 ;; 15.1.17, 11, 18, 12, 9, and 19
2111 ;; I think it's ok to convert sqrt(z^2) to z here, so $radexpand is
2113 (let (($radexpand
'$all
)
2114 x z $exponentialize a b
)
2115 (setq a
(car arg-l1
) b
(cadr arg-l1
))
2116 (cond ((=0 (m+t a b
))
2119 (cond ((equal (checksigntm var
) '$positive
)
2121 ;; F(-a,a;1/2;sin(z)^2) = cos(2*a*z)
2122 (trig-log-1-pos a var
))
2123 ((equal (checksigntm var
) '$negative
)
2125 ;; F(-a,a;1/2;-z^2) = 1/2*((sqrt(1+z^2)+z)^(2*a)
2126 ;; +(sqrt(1+z^2)-z)^(2*a))
2128 (trig-log-1-neg a b var
))
2130 ((equal (m+t a b
) 1.
)
2132 (cond ((equal (checksigntm var
) '$positive
)
2134 ;; F(a,1-a;1/2;sin(z)^2) = cos((2*a-1)*z)/cos(z)
2135 (m//t
(mcos (m*t
(m-t a b
) (setq z
(masin (msqrt var
)))))
2137 ((equal (checksigntm var
) '$negative
)
2139 ;; F(a,1-a;1/2;-z^2) = 1/2*(1+z^2)^(-1/2)*
2140 ;; {[(sqrt(1+z^2)+z]^(2*a-1)
2141 ;; +[sqrt(1+z^2)-z]^(2*a-1)}
2142 (m*t
1//2 (m//t
(setq x
(msqrt (m-t 1. var
))))
2143 (m+t
(m^t
(m+t x
(setq z
(msqrt (m-t var
))))
2145 (m^t
(m-t x z
) b
))))
2147 ((=1//2 (hyp-mabs (m-t b a
)))
2148 ;; F(a, a+1/2; 1/2; z)
2149 (cond ((equal (checksigntm var
) '$positive
)
2151 ;; F(a,1/2+a;1/2;z^2) = ((1+z)^(-2*a)+(1-z)^(-2*a))/2
2153 (m+t
(m^t
(m1+t
(setq z
(msqrt var
)))
2154 (setq b
(m-t 1//2 (m+t a b
))))
2155 (m^t
(m-t 1. z
) b
))))
2156 ((equal (checksigntm var
) '$negative
)
2158 ;; F(a,1/2+a;1/2;-tan(z)^2) = cos(z)^(2*a)*cos(2*a*z)
2159 (m*t
(m^t
(mcos (setq z
(matan (msqrt (m-t var
)))))
2160 (setq b
(m+t a b -
1//2)))
2165 (defun trig-log-1-pos (a z
)
2166 ;; I think it's ok to convert sqrt(z^2) to z here, so $radexpand is
2168 (let (($radexpand
'$all
))
2169 (mcos (m*t
2. a
(masin (msqrt z
))))))
2171 (defun trig-log-1-neg (a b v
)
2172 ;; Look to see a is of the form m*s+c where m and c
2173 ;; are numbers. If m is positive, swap a and b.
2174 ;; Basically we want F(-a,a;1/2;-z^2) =
2175 ;; F(a,-a;1/2;-z^2), as they should be.
2176 (let* (($radexpand
'$all
)
2178 (m (cdras 'm match
))
2179 (s (cdras 's match
))
2181 (if (and m
(eq (checksigntm m
) '$positive
))
2184 (if (eq (checksigntm a
) '$negative
)
2187 (x (msqrt (m-t 1. v
)))
2188 (z (msqrt (m-t v
))))
2191 (setq b
(m*t
2. b
)))
2192 (m^t
(m-t x z
) b
)))))
2194 ;; Pattern match for m*s+c where a is a number, x is symbolic, and c
2198 '((mplus) ((coeffpt) (m $numberp
) (s nonnump
))
2199 ((coeffpp) (c $numberp
)))))
2201 ;; List L contains two elements first the numerator parameter that
2202 ;;exceeds the denumerator one and is called "C", second
2203 ;;the difference of the two parameters which is called "M".
2206 (defun diffintprop-gen-exec (l l1 l2
)
2207 (prog (c m poly constfact
)
2210 l1
(delete c l1
:count
1 :test
#'equal
)
2212 l2
(delete c l2
:count
1 :test equal
)
2213 poly
($expand
(constrpoly c m
'avgoustis
))
2214 constfact
(createconstfact c m
))
2215 (return (yanmult constfact
2216 (diffintprop-exec poly l1 l2
)))))
2218 (defun constrpoly (c m k
)
2219 (cond ((zerop m
) 1.
)
2220 (t (mul (add c k
(1- m
)) (constrpoly c
(1- m
) k
)))))
2222 (defun createconstfact (c m
)
2223 (cond ((zerop m
) 1.
)
2224 (t (mul (inv (add c
(1- m
)))
2225 (createconstfact c
(1- m
))))))
2227 (defun diffintprop-exec (poly l1 l2
)
2228 (distrdiffintprop (createcoefpowlist-exec poly
) l1 l2
))
2230 (defun distrdiffintprop (l l1 l2
)
2232 (t (add (yanmult ($factor
(cadar l
))
2233 (diffintprop (caar l
) l1 l2
))
2234 (distrdiffintprop (cdr l
) l1 l2
)))))
2236 (defun diffintprop (pow l1 l2
)
2237 (cond ((zerop pow
) (hgfsimp l1 l2 var
))
2239 (yanmult (mul (div (multpl l1
) (multpl l2
)) var
)
2240 (hgfsimp (incr1 l1
) (incr1 l2
) var
)))
2241 (t (searchaddserieslist pow l1 l2
))))
2243 (defun searchaddserieslist (pow l1 l2
)
2245 (cond ((setq series
(searchserieslistp serieslist pow
))
2246 (return (eval series
))))
2255 '(yanmult (mul (div (multpl l1
) (multpl l2
))
2257 (diffintproprecurse (1- pow
)
2260 (return (eval res
))))
2262 (defun diffintproprecurse (pow l1 l2
)
2265 ($expand
(power (add 'avgoustis
1.
) pow
)))
2266 (return (diffintprop-exec poly l1 l2
))))
2269 (cond ((null l
) 1.
) (t (mul (car l
) (multpl (cdr l
))))))
2271 (defun createcoefpowlist-exec (poly)
2273 (setq conster
(consterminit poly
'avgoustis
)
2274 hp
($hipow poly
'avgoustis
))
2275 (return (append (list (list 0. conster
))
2276 (createcoefpowlist poly hp
)))))
2278 (defun createcoefpowlist (poly hp
)
2279 (cond ((equal hp
1.
)
2280 (list (list 1.
($coeff poly
'avgoustis
))))
2281 (t (append (createcoefpowlist poly
(1- hp
))
2287 (defun consterminit (fun var
)
2288 (cond ((eq (caar fun
) 'mplus
) (consterm (cdr fun
) var
))
2289 (t (cond ((freevar fun
) fun
) (t 0.
)))))
2291 (defun searchserieslistp (serieslist pow
)
2292 (cond ((null serieslist
) nil
)
2293 ((equal (caar serieslist
) pow
) (cadar serieslist
))
2294 (t (searchserieslistp (cdr serieslist
) pow
))))
2296 (defun yanmult (a b
)
2297 (cond ((eq (caar b
) 'mplus
) (yanmul a
(cdr b
)))
2302 (t (add (mul a
(car b
)) (yanmul a
(cdr b
))))))
2306 (defun freevarpar (exp)
2307 (cond ((freevar exp
) (freepar exp
))
2310 ;; Why is this needed?
2313 (defun freepar (exp)
2315 (not (eq exp
*par
*)))
2316 (t (and (freepar (car exp
))
2317 (freepar (cdr exp
))))))
2319 ;; Confluent hypergeometric function.
2322 (defun confl (arg-l1 arg-l2 var
)
2323 (let* ((a (car arg-l1
))
2332 (not (integerp a
)) ; Do not handle an integer or
2333 (not (integerp (add a a
)))) ; an half integral value
2334 ;; F(a;1;z) = laguerre(-a,z)
2335 (lagpol (neg a
) 0 var
))
2337 ((and (maxima-integerp a
)
2338 (member ($sign a
) '($neg nz
)))
2339 ;; F(-n; c; z) and n a positive integer
2340 (1f1polys (list c
) a
))
2342 ((alike1 c
(add a a
))
2346 ;; F(n+1;2*n+1;2*z) =
2347 ;; gamma(3/2+n)*exp(z)*(z/2)^(-n-1/2)*bessel_i(n+1/2,z).
2352 ;; gamma(n+1/2)*exp(z/2)*(z/4)^(-n-3/2)*bessel_i(n-1/2,z/2);
2354 ;; If n is a negative integer, we use laguerre polynomial.
2355 (if (and (maxima-integerp a
)
2356 (eq (asksign a
) '$negative
))
2357 ;; We have already checked a negative integer. This algorithm
2358 ;; is now present in 1f1polys and at this place never called.
2361 (inv (take '(%binomial
) (add n n
) n
))
2362 (lagpol n
(sub c
1) var
)))
2363 (let ((z (div var
2)))
2365 (bestrig (add a
'((rat simp
) 1 2))
2366 (div (mul z z
) 4))))))
2368 ((and (integerp (setq n
(sub (add a a
) c
)))
2371 (not (integerp (add a a
))))
2372 ;; F(a,2*a-n,z) and a not an integer or a half integral value
2374 (format t
"~&Case 1F1(a,2*a-n,x):")
2375 (format t
"~& ; a = ~A~%" a
)
2376 (format t
"~& ; c = ~A~%" c
)
2377 (format t
"~& : n = ~A~%" n
))
2379 (mul (take '(%gamma
) (sub a
(add n
'((rat simp
) 1 2))))
2381 (sub (add '((rat simp
) 1 2) n
) a
))
2382 (power '$%e
(div var
2))
2383 (let ((index (gensym)))
2385 (mul (power -
1 index
)
2386 (take '($pochhammer
) (- n
) index
)
2387 (take '($pochhammer
) (add a a
(* -
2 n
) -
1) index
)
2388 (add a index
(- n
) '((rat simp
) -
1 2))
2389 (inv (take '($pochhammer
) (sub (add a a
) n
) index
))
2390 (inv (take '(mfactorial) index
))
2392 (add a index
(- n
) '((rat simp
) -
1 2))
2396 ((and (integerp (setq n
(sub c
(add a a
))))
2399 (not (integerp (add a a
))))
2400 ;; F(a,2*a+n,z) and a not an integer or a half integral value
2402 (format t
"~&Case 1F1(a,2*a+n,x):")
2403 (format t
"~& ; a = ~A~%" a
)
2404 (format t
"~& ; c = ~A~%" c
)
2405 (format t
"~& : n = ~A~%" n
))
2407 (mul (take '(%gamma
) (sub a
'((rat simp
) 1 2)))
2409 (sub '((rat simp
) 1 2) a
))
2410 (power '$%e
(div var
2))
2411 (let ((index (gensym)))
2413 (mul (take '($pochhammer
) (- n
) index
)
2414 (take '($pochhammer
) (add a a -
1) index
)
2415 (add a index
'((rat simp
) -
1 2))
2416 (inv (take '($pochhammer
) (add a a n
) index
))
2417 (inv (take '(mfactorial) index
))
2419 (add a index
'((rat simp
) -
1 2))
2423 ((and (integerp (setq n
(sub a
'((rat simp
) 1 2))))
2431 (format t
"~&Case 1F1(n+1/2,m,x):")
2432 (format t
"~& ; a = ~A~%" a
)
2433 (format t
"~& ; c = ~A~%" c
)
2434 (format t
"~& : n = ~A~%" n
)
2435 (format t
"~& : m = ~A~%" m
))
2437 (mul (power 2 (- 1 m
))
2438 (power '$%e
(div var
2))
2441 (inv (take '($pochhammer
) '((rat simp
) 1 2) (- m
1)))
2442 (inv (take '($pochhammer
) '((rat simp
) 1 2) n
))
2443 (let ((index1 (gensumindex)))
2445 (mul (power 2 (neg index1
))
2446 (power (neg var
) index1
)
2447 (inv (take '(mfactorial) index1
))
2448 (mfuncall '$gen_laguerre
2450 (sub index1
'((rat simp
) 1 2))
2452 (let ((index2 (gensumindex)))
2454 (mul (power -
1 index2
)
2455 (power 2 (neg index2
))
2459 (let ((index3 (gensumindex)))
2461 (mul (take '(%binomial
) index2 index3
)
2463 (sub index2
(mul 2 index3
))
2465 index3
0 index2 t
)))
2466 index2
0 (add index1 m -
1) t
)))
2469 ((and (integerp (setq n
(sub a
'((rat simp
) 1 2))))
2478 (format t
"~&Case 1F1(1/2-n,m,x):")
2479 (format t
"~& ; a = ~A~%" a
)
2480 (format t
"~& ; c = ~A~%" c
)
2481 (format t
"~& : n = ~A~%" n
)
2482 (format t
"~& : m = ~A~%" m
))
2486 (power '$%e
(div var
2))
2488 (inv (take '($pochhammer
) '((rat simp
) 1 2) (- m
1)))
2489 (inv (take '($pochhammer
) (sub m
'((rat simp
) 1 2)) n
))
2490 (let ((index1 (gensumindex)))
2492 (mul (power 2 (neg index1
))
2494 (take '(%binomial
) n index1
)
2495 (take '($pochhammer
)
2496 (sub '((rat simp
) 3 2) (+ m n
))
2498 (let ((index2 (gensumindex)))
2500 (mul (power '((rat simp
) -
1 2) index2
)
2504 (let ((index3 (gensumindex)))
2506 (mul (take '(%binomial
) index2 index3
)
2508 (sub index2
(mul 2 index3
))
2510 index3
0 index2 t
)))
2511 index2
0 (add index1 m -
1) t
)))
2514 ((not (hyp-integerp a-c
))
2515 (cond ((hyp-integerp a
)
2516 (kummer arg-l1 arg-l2
))
2520 ;; %m[k,u](z) = exp(-z/2)*z^(u+1/2)*M(1/2+u-k,1+2*u,z)
2524 ;; M(a,c,z) = exp(z/2)*z^(-c/2)*%m[c/2-a,c/2-1/2](z)
2526 ;; But if we apply Kummer's transformation, we can also
2527 ;; derive the expression
2529 ;; %m[k,u](z) = exp(z/2)*z^(u+1/2)*M(1/2+u+k,1+2*u,-z)
2533 ;; M(a,c,z) = exp(-z/2)*(-z)^(-c/2)*%m[a-c/2,c/2-1/2](-z)
2535 ;; For Laplace transforms it might be more beneficial to
2536 ;; return this second form instead.
2537 (let* ((m (div (sub c
1) 2))
2538 (k (add '((rat simp
) 1 2) m
(mul -
1 a
))))
2539 (mul (power var
(mul -
1 (add '((rat simp
) 1 2) m
)))
2540 (power '$%e
(div var
2))
2541 (whitfun k m var
))))
2543 (fpqform arg-l1 arg-l2 var
))))
2545 (sratsimp (erfgammared a c var
)))
2547 (kummer arg-l1 arg-l2
)))))
2550 ;; M(1/2,3/2,-z^2) = sqrt(%pi)*erf(z)/2/sqrt(z)
2552 ;; So M(1/2,3/2,z) = sqrt(%pi)*erf(sqrt(-z))/2/sqrt(-z)
2553 ;; = sqrt(%pi)*erf(%i*sqrt(z))/2/(%i*sqrt(z))
2554 (defun hyprederf (x)
2555 (let ((x (mul '$%i
(power x
'((rat simp
) 1 2 )))))
2556 (mul (power '$%pi
'((rat simp
) 1 2))
2561 ;; M(a,c,z), where a-c is a negative integer.
2562 (defun erfgammared (a c z
)
2563 (cond ((and (nump a
) (nump c
))
2564 (erfgamnumred a c z
))
2565 (t (gammareds a c z
))))
2567 ;; I (rtoy) think this is what the function above is doing, but I'm
2568 ;; not sure. Plus, I think it's wrong.
2570 ;; For hgfred([n],[2+n],-z), the above returns
2572 ;; 2*n*(n+1)*z^(-n-1)*(gamma_incomplete_lower(n,z)*z-gamma_incomplete_lower(n+1,z))
2574 ;; But from A&S 13.4.3
2576 ;; -M(n,2+n,z) - n*M(n+1,n+2,z) + (n+1)*M(n,n+1,z) = 0
2578 ;; so M(n,2+n,z) = (n+1)*M(n,n+1,z)-n*M(n+1,n+2,z)
2580 ;; And M(n,n+1,-z) = n*z^(-n)*gamma_incomplete_lower(n,z)
2584 ;; M(n,2+n,z) = (n+1)*n*z^(-n)*gamma_incomplete_lower(n,z) - n*(n+1)*z^(-n-1)*gamma_incomplete_lower(n+1,z)
2585 ;; = n*(n+1)*z^(-n-1)*(gamma_incomplete_lower(n,z)*n-gamma_incomplete_lower(n+1,z))
2587 ;; So the version above is off by a factor of 2. But I think it's more than that.
2588 ;; Using A&S 13.4.3 again,
2590 ;; M(n,n+3,-z) = [n*M(n+1,n+3,-z) - (n+2)*M(n,n+2,-z)]/(-2);
2592 ;; The version above doesn't produce anything like this equation would
2593 ;; produce, given the value of M(n,n+2,-z) derived above.
2594 (defun gammareds (a c z
)
2595 ;; M(a,c,z) where a-c is a negative integer.
2596 (let ((diff (sub c a
)))
2598 ;; We have M(a,a+1,z).
2602 ;; Apply Kummer's tranformation to get the form M(a-1,a,z)
2604 ;; (I don't think we ever get here, but just in case, we leave it.)
2606 (kummer (list a
) (list c
))))
2608 ;; We have M(a, a+n, z)
2611 ;; (1+a-b)*M(a,b,z) - a*M(a+1,b,z)+(b-1)*M(a,b-1,z) = 0
2615 ;; M(a,b,z) = [a*M(a+1,b,z) - (b-1)*M(a,b-1,z)]/(1+a-b);
2617 ;; Thus, the difference between b and a is reduced, until
2618 ;; b-a=1, which we handle above.
2620 (gammareds (add 1 a
) c z
))
2622 (gammareds a
(sub c
1) z
)))
2623 (inv (sub (add 1 a
) c
)))))))
2626 ;; gamma_incomplete_lower(a,x) = x^a/a*M(a,1+a,-x)
2627 ;; = x^a/a*exp(-x)*M(1,1+a,x)
2629 ;; where gamma_incomplete_lower(a,x) is the lower incomplete gamma function.
2631 ;; M(a,1+a,x) = a*(-x)^(-a)*gamma_incomplete_lower(a,-x)
2632 (defun hypredincgm (a z
)
2633 (let ((-z (mul -
1 z
)))
2634 (if (not $prefer_gamma_incomplete
)
2636 (power -z
(mul -
1 a
))
2637 (take '(%gamma_incomplete_lower
) a -z
))
2639 (power -z
(mul -
1 a
))
2640 (sub (take '(%gamma
) a
)
2641 (take '(%gamma_incomplete
) a -z
))))))
2643 ;; M(a,c,z), when a and c are numbers, and a-c is a negative integer
2644 (defun erfgamnumred (a c z
)
2645 (cond ((hyp-integerp (sub c
'((rat simp
) 1 2)))
2647 (t (gammareds a c z
))))
2649 ;; M(a,c,z) when a and c are numbers and c-1/2 is an integer and a-c
2650 ;; is a negative integer. Thus, we have M(p+1/2, q+1/2,z)
2651 (defun erfred (a c z
)
2653 (setq n
(sub a
'((rat simp
) 1 2))
2654 m
(sub c
'((rat simp
) 3 2)))
2657 ;; a - c < 0 so n - m - 1 < 0
2658 (cond ((not (or (> n m
) (minusp n
)))
2660 (return (thno33 n m z
)))
2661 ((and (minusp n
) (minusp m
))
2663 (return (thno35 (mul -
1 n
) (mul -
1 m
) z
)))
2664 ((and (minusp n
) (plusp m
))
2666 (return (thno34 (mul -
1 n
) m z
)))
2669 (return (gammareds (add n
'((rat simp
) 1 2))
2670 (add m
'((rat simp
) 3 2))
2673 ;; Compute M(n+1/2, m+3/2, z) with 0 <= n <= m.
2675 ;; I (rtoy) think this is what this routine is doing. (I'm guessing
2676 ;; that thno33 means theorem number 33 from Yannis Avgoustis' thesis.)
2678 ;; I don't have his thesis, but I see there are similar ways to derive
2679 ;; the result we want.
2682 ;; Use Kummer's transformation (A&S ) to get
2684 ;; M(n+1/2,m+3/2,z) = exp(z)*M(m-n+1,m+3/2,-z)
2686 ;; From A&S, we have
2688 ;; diff(M(1,n+3/2,z),z,m-n) = poch(1,m-n)/poch(n+3/2,m-n)*M(m-n+1,m+3/2,z)
2690 ;; Apply Kummer's transformation again:
2692 ;; M(1,n+3/2,z) = exp(z)*M(n+1/2,n+3/2,-z)
2694 ;; Apply the differentiation formula again:
2696 ;; diff(M(1/2,3/2,z),z,n) = poch(1/2,n)/poch(3/2,n)*M(n+1/2,n+3/2,z)
2698 ;; And we know that M(1/2,3/2,z) can be expressed in terms of erf.
2702 ;; Since n <= m, apply the differentiation formula:
2704 ;; diff(M(1/2,m-n+3/2,z),z,n) = poch(1/2,n)/poch(m-n+3/2,n)*M(n+1/2,m+3/2,z)
2706 ;; Apply Kummer's transformation:
2708 ;; M(1/2,m-n+3/2,z) = exp(z)*M(m-n+1,m-n+3/2,z)
2710 ;; Apply the differentiation formula again:
2712 ;; diff(M(1,3/2,z),z,m-n) = poch(1,m-n)/poch(3/2,m-n)*M(m-n+1,m-n+3/2,z)
2714 ;; I think this routine uses Method 2.
2715 (defun thno33 (n m x
)
2716 ;; M(n+1/2,m+3/2,z) = diff(M(1/2,m-n+3/2,z),z,n)*poch(m-n+3/2,n)/poch(1/2,n)
2717 ;; M(1/2,m-n+3/2,z) = exp(z)*M(m-n+1,m-n+3/2,-z)
2718 ;; M(m-n+1,m-n+3/2,z) = diff(M(1,3/2,z),z,m-n)*poch(3/2,m-n)/poch(1,m-n)
2719 ;; diff(M(1,3/2,z),z,m-n) = (-1)^(m-n)*diff(M(1,3/2,-z),z,m-n)
2720 ;; M(1,3/2,-z) = exp(-z)*M(1/2,3/2,z)
2721 (let* ((yannis (gensym))
2723 ;; poch(m-n+3/2,n)/poch(1/2,n)
2724 (factor1 (div (take '($pochhammer
) (add m-n
'((rat simp
) 3 2)) n
)
2725 (take '($pochhammer
) '((rat simp
) 1 2) n
)))
2726 ;; poch(3/2,m-n)/poch(1,m-n)
2727 (factor2 (div (take '($pochhammer
) '((rat simp
) 3 2) m-n
)
2728 (take '($pochhammer
) 1 m-n
)))
2729 ;; M(1,3/2,-z) = exp(-z)*M(1/2,3/2,z)
2730 (hgferf (mul (power '$%e
(mul -
1 yannis
))
2731 (hyprederf yannis
)))
2732 ;; diff(M(1,3/2,z),z,m-n)
2733 (diff1 (meval (list '($diff
) hgferf yannis m-n
)))
2734 ;; exp(z)*M(m-n+1,m-n+3/2,-z)
2735 (kummer (mul (power '$%e yannis
) diff1
))
2736 ;; diff(M(1/2,m-n+3/2,z),z,n)
2737 (diff2 (meval (list '($diff
) kummer yannis n
))))
2738 ;; Multiply all the terms together.
2739 (sratsimp (mul (power -
1 m-n
)
2742 (maxima-substitute x yannis diff2
)))))
2744 ;; M(n+1/2,m+3/2,z), with n < 0 and m > 0
2746 ;; Let's write it more explicitly as M(-n+1/2,m+3/2,z) with n > 0 and
2749 ;; First, use Kummer's transformation to get
2751 ;; M(-n+1/2,m+3/2,z) = exp(z)*M(m+n+1,m+3/2,-z)
2755 ;; diff(z^(n+m)*M(m+1,m+3/2,z),z,n) = poch(m+1,n)*z^m*M(m+n+1,m+3/2,z)
2759 ;; diff(M(1,3/2,z),z,m) = poch(1,m)/poch(3/2,m)*M(m+1,m+3/2,z)
2761 ;; Thus, we can compute M(-n+1/2,m+3/2,z) from M(1,3/2,z).
2763 ;; The second formula above can be derived easily by multiplying the
2764 ;; series for M(m+1,m+3/2,z) by z^(n+m) and differentiating n times.
2766 (defun thno34 (n m x
)
2767 (let ((yannis (gensym)))
2773 (div (mul (take '($pochhammer
) '((rat simp
) 3 2) m
)
2774 (power '$%e yannis
))
2775 (mul (take '($pochhammer
) 1 m
)
2776 (take '($pochhammer
) (1+ m
) n
)
2778 (meval (list '($diff
)
2779 (mul (power yannis
(+ m n
))
2780 (meval (list '($diff
)
2789 ;; M(n+1/2,m+3/2,z), with n < 0 and m < 0
2791 ;; Write it more explicitly as M(-n+1/2,-m+3/2,z) with n > 0 and m >
2796 ;; diff(sqrt(z)*M(-n+1/2,3/2,z),z,m) = poch(3/2-m,m)*M(-n+1/2,-m+3/2,z).
2798 ;; Apply Kummer's transformation:
2800 ;; M(-n+1/2,3/2,z) = exp(z) * M(n+1,3/2,-z)
2804 ;; diff(z^n*M(1,3/2,z),z,n) = n!*M(n+1,3/2,z)
2806 ;; So we can express M(-n+1/2,-m+3/2,z) in terms of M(1,3/2,z).
2808 ;; The first formula above follows from the more general formula
2810 ;; diff(z^(b-1)*M(a,b,z),z,n) = poch(b-n,n)*z^(b-n-1)*M(a,b-n,z)
2812 ;; The last formula follows from the general result
2814 ;; diff(z^(a+n-1)*M(a,b,z),z,n) = poch(a,n)*z^(a-1)*M(a+n,b,z)
2816 ;; Both of these are easily derived by using the series for M and
2819 (defun thno35 (n m x
)
2820 (let ((yannis (gensym)))
2825 (mul (div (power yannis
(sub m
'((rat simp
) 1 2)))
2826 (mul (power -
1 (* -
1 m
))
2827 (take '($pochhammer
) 1 n
)
2828 (take '($pochhammer
) '((rat simp
) -
1 2) m
)))
2829 (meval (list '($diff
)
2830 (mul (power yannis
'((rat simp
) 1 2))
2832 (meval (list '($diff
)
2842 ;; Pochhammer symbol. fctrl(a,n) = a*(a+1)*(a+2)*...*(a+n-1).
2844 ;; N must be a positive integer!
2846 ;; FIXME: This appears to be identical to factf below.
2854 (fctrl a
(1- n
))))))
2859 (m2 exp
'(v freevarpar
)))
2862 (defun fpqform (arg-l1 arg-l2 arg
)
2864 (list '($%f simp array
) (length arg-l1
)(length arg-l2
))
2865 (append (list '(mlist simp
)) arg-l1
)
2866 (append (list '(mlist simp
)) arg-l2
)
2869 ;; Consider pFq([a_k]; [c_j]; z). If a_k = c_j + m for some k and j
2870 ;; and m >= 0, we can express pFq in terms of (p-1)F(q-1).
2872 ;; Here is a derivation for F(a,b;c;z), but it generalizes to the
2873 ;; generalized hypergeometric very easily.
2877 ;; diff(z^(a+n-1)*F(a,b;c;z), z, n) = poch(a,n)*z^(a-1)*F(a+n,b;c;z)
2879 ;; F(a+n,b;c;z) = diff(z^(a+n-1)*F(a,b;c;z), z, n)/poch(a,n)/z^(a-1)
2882 ;; So this expresses F(a+n,b;c;z) in terms of F(a,b;c;z). Let a = c +
2883 ;; n. This therefore gives F(c+n,b;c;z) in terms of F(c,b;c;z) =
2884 ;; 1F0(b;;z), which we know.
2886 ;; For simplicity, we will write F(z) for F(a,b;c;z).
2891 ;; diff(z^x*F(z),z,n) = sum binomial(n,k)*diff(z^x,z,n-k)*diff(F(z),z,k)
2894 ;; But diff(z^x,z,n-k) = x*(x-1)*...*(x-n+k+1)*z^(x-n+k)
2895 ;; = poch(x-n+k+1,n-k)*z^(x-n+k)
2899 ;; z^(-a+1)/poch(a,n)*diff(z^(a+n-1),z,n-k)
2900 ;; = poch(a+n-1-n+k+1,n-k)/poch(a,n)*z^(a+n-1-n+k)*z^(-a+1)
2901 ;; = poch(a+k,n-k)/poch(a,n)*z^k
2904 ;; Combining these we have
2907 ;; F(a+n,b;c;z) = sum z^k/poch(a,k)*binomial(n,k)*diff(F(a,b;c;z),z,k)
2910 ;; Since a = c, we have
2913 ;; F(a+n,b;a;z) = sum z^k/poch(a,k)*binomial(n,k)*diff(F(a,b;a;z),z,k)
2916 ;; But F(a,b;a;z) = F(b;;z) and it's easy to see that A&S 15.2.2 can
2917 ;; be specialized to this case to give
2919 ;; diff(F(b;;z),z,k) = poch(b,k)*F(b+k;;z)
2921 ;; Finally, combining all of these, we have
2924 ;; F(a+n,b;c;z) = sum z^k/poch(a,k)*binomial(n,k)*poch(b,k)*F(b+k;;z)
2927 ;; Thus, F(a+n,b;c;z) is expressed in terms of 1F0(b+k;;z), as desired.
2928 (defun splitpfq (l arg-l1 arg-l2
)
2929 (destructuring-bind (a1 k
)
2936 (arg-l1 (delete a1 arg-l1
:count
1 :test
#'equal
))
2937 (arg-l2 (delete b1 arg-l2
:count
1 :test
#'equal
)))
2938 (loop for count from
0 upto k
2941 (format t
"splitpfg term:~%")
2942 (maxima-display (mul (combin k count
)
2943 (div prodnum proden
)
2946 (mtell "F(~:M, ~:M)~%"
2947 (cons '(mlist) arg-l1
)
2948 (cons '(mlist) arg-l2
)))
2949 (setq result
(add result
2950 (mul (combin k count
)
2951 (div prodnum proden
)
2954 (hgfsimp arg-l1 arg-l2 var
))))
2955 (setq prod-b
(mul prod-b
(add b1 count
)))
2956 (setq prodnum
(mul prodnum
(mull arg-l1
))
2957 proden
(mul proden
(mull arg-l2
)))
2958 (setq arg-l1
(incr1 arg-l1
))
2959 (setq arg-l2
(incr1 arg-l2
)))
2962 ;; binomial(k,count)
2963 (defun combin (k count
)
2965 (mul (factorial count
)
2966 (factorial (sub k count
)))))
2969 ;; We have something like F(s+m,-s+n;c;z)
2970 ;; Rewrite it like F(a'+d,-a';c;z) where a'=s-n=-b and d=m+n.
2973 (let* ((sym (cdras 'f
(s+c a
)))
2974 (sign (cdras 'm
(m2 sym
'((mtimes) ((coefft) (m $numberp
)) ((coefft) (s nonnump
)))))))
2975 (when (and sign
(minusp sign
))
2977 (list nil
(mul -
1 b
) (add a b
))))
2980 ;;Algor. 2F1-RL from thesis:step 4:dispatch on a+m,-a+n,1/2+l cases
2981 (defun step4 (a b c
)
2982 ;; F(a,b;c;z) where a+b is an integer and c+1/2 is an integer. If a
2983 ;; and b are not integers themselves, we can derive the result from
2984 ;; F(a1,-a1;1/2;z). However, if a and b are integers, we can't use
2985 ;; that because F(a1,-a1;1/2;z) is a polynomial. We need to derive
2986 ;; the result from F(1,1;3/2;z).
2987 (if (and (hyp-integerp a
)
2992 (defun step4-a (a b c
)
2993 (let* ((alglist (algii a b
))
2994 (aprime (cadr alglist
))
2997 ($ratsimpexpons $true
)
2999 ;; At this point, we have F(a'+m,-a';1/2+n;z) where m and n are
3001 (cond ((hyp-integerp (add aprime
(inv 2)))
3002 ;; Ok. We have a problem if aprime + 1/2 is an integer.
3003 ;; We can't always use the algorithm below because we have
3004 ;; F(1/2,-1/2;1/2;z) which is 1F0(-1/2;;z) so the
3005 ;; derivation isn't quite right. Also, sometimes we'll end
3006 ;; up with a division by zero.
3008 ;; Thus, We need to do something else. So, use A&S 15.3.3
3009 ;; to change the problem:
3011 ;; F(a,b;c;z) = (1-z)^(c-a-b)*F(c-a, c-b; c; z)
3015 ;; F('a+m,-a';1/2+n;z) = (1-z)^(1/2+n-m)*F(1/2+n-a'-m,1/2+n+a';1/2+n;z)
3017 ;; Recall that a' + 1/2 is an integer. Thus we have
3018 ;; F(<int>,<int>,1/2+n;z), which we know how to handle in
3020 (gered1 (list a b
) (list c
) #'hgfsimp
))
3023 (cond ((equal (checksigntm var
) '$positive
)
3024 (trig-log-1-pos aprime
'ell
))
3025 ((equal (checksigntm var
) '$negative
)
3026 (trig-log-1-neg (mul -
1 aprime
) aprime
'ell
)))))
3027 ;; Ok, this uses F(a,-a;1/2;z). Since there are 2 possible
3028 ;; representations (A&S 15.1.11 and 15.1.17), we check the sign
3029 ;; of the var (as done in trig-log-1) to select which form we
3030 ;; want to use. The original didn't and seemed to want to use
3031 ;; the negative form.
3033 ;; With this change, F(a,-a;3/2;z) matches what A&S 15.2.6 would
3034 ;; produce starting from F(a,-a;1/2;z), assuming z < 0.
3040 ;; F(a,b;c;z), where a and b are (positive) integers and c = 1/2+l.
3041 ;; This can be computed from F(1,1;3/2;z).
3043 ;; Assume a < b, without loss of generality.
3045 ;; F(m,n;3/2+L;z), m < n.
3047 ;; We start from F(1,1;3/2;z). Use A&S 15.2.3, differentiating m
3048 ;; times to get F(m,1;3/2;z). Swap a and b to get F(m,1;3/2;z) =
3049 ;; F(1,m;3/2;z) and use A&S 15.2.3 again to get F(n,m;3/2;z) by
3050 ;; differentiating n times. Finally, if L < 0, use A&S 15.2.4.
3051 ;; Otherwise use A&S 15.2.6.
3053 ;; I (rtoy) can't think of any way to do this with less than 3
3054 ;; differentiations.
3056 ;; Note that if d = (n-m)/2 is not an integer, we can write F(m,n;c;z)
3057 ;; as F(-d+u,d+u;c;z) where u = (n+m)/2. In this case, we could use
3058 ;; step4-a to compute the result.
3061 ;; Transform F(a,b;c;z) to F(a+n,b;c;z), given F(a,b;c;z)
3062 (defun as-15.2
.3 (a bb cx n arg fun
)
3063 (declare (ignore bb cx
))
3066 ;; F(a+n,b;c;z) = z^(1-a)/poch(a,n)*diff(z^(a+n-1)*fun,z,n)
3067 (mul (inv (factf a n
))
3068 (power arg
(sub 1 a
))
3069 ($diff
(mul (power arg
(sub (add a n
) 1))
3073 ;; Transform F(a,b;c;z) to F(a,b;c-n;z), given F(a,b;c;z)
3074 (defun as-15.2
.4 (axax bb c n arg fun
)
3075 (declare (ignore axax bb
))
3078 ;; F(a,b;c-n;z) = 1/poch(c-n,n)/z^(c-n-1)*diff(z^(c-1)*fun,z,n)
3079 (mul (inv (factf (sub c n
) n
))
3080 (inv (power arg
(sub (sub c n
) 1)))
3081 ($diff
(mul (power arg
(sub c
1))
3085 ;; Transform F(a,b;c;z) to F(a-n,b;c;z), given F(a,b;c;z)
3086 (defun as-15.2
.5 (a b c n arg fun
)
3088 ;; F(a-n,b;c;z) = 1/poch(c-a,n)*z^(1+a-c)*(1-z)^(c+n-a-b)
3089 ;; *diff(z^(c-a+n-1)*(1-z)^(a+b-c)*F(a,b;c;z),z,n)
3091 (mul (inv (factf (sub c a
) n
))
3092 (power arg
(sub (add a
1) c
))
3094 (sub (add c n
) (add a b
)))
3095 ($diff
(mul (power arg
(sub (add c n
)
3102 ;; Transform F(a,b;c;z) to F(a,b;c+n;z), given F(a,b;c;z)
3103 (defun as-15.2
.6 (a b c n arg fun
)
3105 ;; F(a,b;c+n;z) = poch(c,n)/poch(c-a,n)/poch(c-b,n)*(1-z)^(c+n-a-b)
3106 ;; *diff((1-z)^(a+b-c)*fun,z,n)
3109 (inv (factf (sub c a
) n
))
3110 (inv (factf (sub c b
) n
))
3111 (inv (power (sub 1 arg
) (sub (add a b
)
3113 ($diff
(mul (power (sub 1 arg
) (sub (add a b
) c
))
3117 ;; Transform F(a,b;c;z) to F(a+n, b; c+n; z)
3118 (defun as-15.2
.7 (a b c n arg fun
)
3120 ;; F(a+n,b;c+n;z) = (-1)^n*poch(c,n)/poch(a,n)/poch(c-b,n)*(1-z)^(1-a)
3121 ;; *diff((1-z)^(a+n-1)*fun, z, n)
3126 (inv (factf (sub c b
) n
))
3127 (power (sub 1 arg
) (sub 1 a
))
3128 ($diff
(mul (power (sub 1 arg
) (sub (add a n
) 1))
3132 ;; Transform F(a,b;c;z) to F(a-n, b; c-n; z)
3133 (defun as-15.2
.8 (axax b c n arg fun
)
3135 ;; F(a-n,b;c-n;z) = 1/poch(c-n,n)/(z^(c-n-1)*(1-z)^(b-c))
3136 ;; *diff(z^(c-1)*(1-z^(b-c+n)*fun, z, n))
3137 (declare (ignore axax
))
3139 (mul (inv (factf (sub c n
) n
))
3140 (inv (mul (power arg
(sub (sub c n
) 1))
3141 (power (sub 1 arg
) (sub b c
))))
3142 ($diff
(mul (power arg
(sub c
1))
3143 (power (sub 1 arg
) (add (sub b c
) n
))
3147 ;; Transform F(a,b;c;z) to F(a+n,b+n;c+n;z)
3148 (defun as-15.2
.2 (a b c n arg fun
)
3150 ;; F(a+n,b+n; c+n;z) = poch(c,n)/poch(a,n)/poch(b,n)
3158 ;; Transform F(a,b;c;z) to F(a-n,b-n;c-n;z)
3159 (defun as-15.2
.9 (a b c n arg fun
)
3161 ;; F(a-n,b-n; c-n;z) = 1/poch(c-n,n)/(z^(c-n-1)*(1-z)^(a+b-c-n))
3162 ;; *diff(z^(c-1)*(1-z)^(a+b-c)*fun, z, n)
3164 (mul (inv (factf (sub c n
) n
))
3165 (inv (mul (power arg
(sub (sub c n
) 1))
3166 (power (sub 1 arg
) (sub (add a b
)
3168 ($diff
(mul (power arg
(sub c
1))
3169 (power (sub 1 arg
) (sub (add a b
) c
))
3173 (defun step4-int (a b c
)
3176 (let* ((s (gensym (symbol-name '#:step4-var-
)))
3180 (res (cond ((eq (checksigntm var
) '$negative
)
3182 ;; -%i*log(%i*sqrt(zn)+sqrt(1-zn))/(sqrt(1-zn)*sqrt(zn))
3184 (let ((root1-z (power (sub 1 s
) (inv 2)))
3185 (rootz (power s
(inv 2))))
3187 (mlog (add (mul '$%i rootz
)
3192 ;; F(1,1;3/2;z) = asin(sqrt(zp))/(sqrt(1-zp)*sqrt(zp))
3194 (let ((root1-z (power (sub 1 s
) (inv 2)))
3195 (rootz (power s
(inv 2))))
3199 ;; Start with res = F(1,1;3/2;z). Compute F(m,1;3/2;z)
3200 (setf res
(as-15.2
.3 1 1 3//2 m s res
))
3201 ;; We now have res = C*F(m,1;3/2;z). Compute F(m,n;3/2;z)
3202 (setf res
(as-15.2
.3 1 a
3//2 n s res
))
3203 ;; We now have res = C*F(m,n;3/2;z). Now compute F(m,n;3/2+ell;z):
3206 (as-15.2
.4 a b
3//2 (- ell
) s res
))
3208 (as-15.2
.6 a b
3//2 ell s res
)))))))
3210 ;;Pattern match for s(ymbolic) + c(onstant) in parameter
3212 (m2 exp
'((mplus) ((coeffpt)(f nonnump
)) ((coeffpp)(c $numberp
)))))
3215 (cond ((not ($numberp z
)) t
)
3218 ;;Algor. III from thesis:determines which Differ. Formula to use
3219 (defun algiii (fun m n aprime
)
3222 (cond ((and (nni m
) (nni n
))
3224 (f81 fun m n aprime
))
3226 (f85 fun mm nn aprime
))))
3227 ((and (hyp-negp n
) (hyp-negp m
))
3228 (cond ((> (abs m
) (abs n
))
3229 (f86 fun mm nn aprime
))
3231 (f82 fun mm nn aprime
))))
3232 ((and (hyp-negp m
) (nni n
))
3233 (f83 fun mm nn aprime
))
3235 (f84 fun mm nn aprime
)))))
3237 ;; Factorial function:x*(x+1)*(x+2)...(x+n-1)
3239 ;; FIXME: This appears to be identical to fctrl above
3242 (t (mul x
(factf (add x
1) (sub n
1))))))
3244 ;;Formula #85 from Yannis thesis:finds by differentiating F[2,1](a,b,c,z)
3245 ;; given F[2,1](a+m,b,c+n,z) where b=-a and c=1/2, n,m integers
3247 ;; Like F81, except m > n.
3249 ;; F(a+m,-a;c+n;z), m > n, c = 1/2, m and n are non-negative integers
3252 ;; diff(z^(a+m-n-1)*F(a,-a;1/2;z),z,m-n) = poch(a,m-n)*z^(a-1)*F(a+m-n,-a;1/2;z)
3255 ;; diff((1-z)^(a+m-1)*F(a+m-n,-a;1/2;z),z,n)
3256 ;; = (-1)^n*poch(a+m-n,n)*poch(1/2+a,n)/poch(1/2,n)*(1-z)^(a+m-n-1)
3257 ;; * F(a+m,-a;1/2+n;z)
3259 (defun f85 (fun m n a
)
3260 (mul (factf (inv 2) n
)
3262 (inv (factf (sub (add a m
)
3265 (inv (factf (sub (inv 2)
3268 (inv (factf a
(- m n
)))
3269 (power (sub 1 'ell
) (sub (sub (add 1 n
) m
) a
))
3270 ($diff
(mul (power (sub 1 'ell
) (sub (add a m
) 1))
3271 (power 'ell
(sub 1 a
))
3272 ($diff
(mul (power 'ell
(sub (add a m -
1) n
))
3277 ;;Used to find negative things that are not integers,eg RAT's
3279 (cond ((equal (asksign x
) '$negative
)
3283 ;; F(a,-a+m; c+n; z) where m,n are non-negative integers, m < n, c = 1/2.
3286 ;; diff((1-z)^(a+b-c)*F(a,b;c;z),z,n)
3287 ;; = poch(c-a,n)*poch(c-b,n)/poch(c,n)*(1-z)^(a+b-c-n)*F(a,b;c+n;z)
3290 ;; diff((1-z)^(a+m-1))*F(a,b;c;z),z,m)
3291 ;; = (-1)^m*poch(a,m)*poch(c-b,m)/poch(c,m)*(1-z)^(a-1)*F(a+m,b;c+m;z)
3293 ;; Rewrite F(a,-a+m; c+n;z) as F(-a+m, a; c+n; z). Then apply 15.2.6
3294 ;; to F(-a,a;1/2;z), differentiating n-m times:
3296 ;; diff((1-z)^(-1/2)*F(-a,a;1/2;z),z,n-m)
3297 ;; = poch(1/2+a,n-m)*poch(1/2-a,n-m)/poch(1/2,n-m)*(1-z)^(-1/2-n+m)*F(-a,a;1/2+n-m;z)
3299 ;; Multiply this result by (1-z)^(n-a-1/2) and apply 15.2.7, differentiating m times:
3301 ;; diff((1-z)^(m-a-1)*F(-a,a;1/2+n-m;z),z,m)
3302 ;; = (-1)^m*poch(-a,m)*poch(1/2+n-m-a,m)/poch(1/2+n-m)*(1-z)^(-a-1)*F(-a+m,a;1/2+n;z)
3304 ;; Which gives F(-a+m,a;1/2+n;z), which is what we wanted.
3305 (defun f81 (fun m n a
)
3306 (mul (factf (add (inv 2) (- n m
)) m
)
3307 (factf (inv 2) (- n m
))
3310 (inv (factf (add (inv 2) n
(sub a m
)) m
))
3311 (inv (factf (sub (inv 2) a
) (- n m
)))
3312 (inv (factf (add (inv 2) a
) (- n m
)))
3313 (power (sub 1 'ell
) (sub 1 a
))
3314 ($diff
(mul (power (sub 1 'ell
) (add a n
(inv -
2)))
3315 ($diff
(mul (power (sub 1 'ell
) (inv -
2))
3320 ;; Like f86, but |n|>=|m|
3322 ;; F(a-m,-a;1/2-n;z) where n >= m >0
3325 ;; diff(z^(c-1)*F(a,b;c;z),z,n)
3326 ;; = poch(c-n,n)*z^(c-n-1)*F(a;b;c-n;z)
3329 ;; diff(z^(c-1)*(1-z)^(b-c+n)*F(a,b;c;z),z,n)
3330 ;; = poch(c-n,n)*z^(c-n-1)*(1-z)^(b-c)*F(a-n,b;c-n;z)
3334 ;; diff(z^(-1/2)*F(a,-a;1/2;z),z,n-m)
3335 ;; = poch(1/2-n+m,n-m)*z^(m-n-1/2)*F(a,-a;1/2-n+m;z)
3337 ;; diff(z^(m-n-1/2)*(1-z)^(n-a-1/2)*F(a,-a;1/2-n+m;z),z,m)
3338 ;; = poch(1/2-n,m)*z^(-1/2-n)*(1-z)^(n-m-a-1/2)*F(a-m,-a;1/2-n;z)
3342 ;; G(z) = z^(m-n-1/2)*F(a,-a;1/2-n+m;z)
3343 ;; = z^(n-m+1/2)/poch(1/2-n+m,n-m)*diff(z^(-1/2)*F(a,-a;1/2;z),z,n-m)
3345 ;; F(a-m,-a;1/2-n;z)
3346 ;; = z^(n+1/2)*(1-z)^(m+a-1/2-n)/poch(1/2-n,m)*diff((1-z)^(n-a-1/2)*G(z),z,m)
3347 (defun f82 (fun m n a
)
3348 (mul (inv (factf (sub (inv 2) n
) m
))
3349 (inv (factf (sub (add (inv 2) m
) n
) (- n m
)))
3350 (power 'ell
(add n
(inv 2)))
3351 (power (sub 1 'ell
) (sub (add m
(inv 2) a
) n
))
3352 ($diff
(mul (power (sub 1 'ell
)
3353 (sub (sub n a
) (inv 2)))
3354 ($diff
(mul (power 'ell
(inv -
2)) fun
)
3360 ;; F(a+m,-a;1/2+n;z) with m,n integers and m < 0, n >= 0
3362 ;; Write this more clearly as F(a-m,-a;1/2+n;z), m > 0, n >= 0
3363 ;; or equivalently F(a-m,-a;c+n;z)
3366 ;; diff((1-z)^(-1/2)*F(a,-a;1/2;z),z,n)
3367 ;; = poch((1/2+a,n)*poch(1/2-a,n)/poch(1/2,n)*(1-z)^(-1/2-n)
3368 ;; * F(a,-a;1/2+n;z)
3371 ;; diff(z^(n+m-a-1/2)*(1-z)^(-1/2-n)*F(a,-a;1/2+n;z),z,m)
3372 ;; = poch(1/2+n-a,m)*z^(1/2+n-a-1)*(1-z)^(-1/2-n-m)*F(a-m,-a;1/2+n;z)
3373 ;; = poch(1/2+n-a,m)*z^(n-a-1/2)*(1-z)^(-1/2-n-m)*F(a-m,-a;1/2+n;z)
3375 ;; (1-z)^(-1/2-n)*F(a,-a;1/2+n;z)
3376 ;; = poch(1/2,n)/poch(1/2-a,n)/poch(1/2+a,n)*diff((1-z)^(-1/2)*F(a,-a;1/2;z),z,n)
3378 ;; F(a-m,-a;1/2+n;z)
3379 ;; = (1-z)^(n+m+1/2)*z^(a-n+1/2)/poch(1/2+n-a,m)
3380 ;; *diff(z^(n+m-a-1/2)*(1-z)^(-1/2-n)*F(a,-a;1/2+n;z),z,m)
3381 (defun f83 (fun m n a
)
3382 (mul (factf (inv 2) n
)
3383 (inv (factf (sub (inv 2) a
) n
))
3384 (inv (factf (sub (add n
(inv 2)) a
) m
))
3385 (inv (factf (add (inv 2) a
) n
))
3386 (power (sub 1 'ell
) (add m n
(inv 2)))
3387 (power 'ell
(add (sub a n
) (inv 2)))
3388 ($diff
(mul (power 'ell
(sub (sub (+ m n
) a
) (inv 2)))
3389 ($diff
(mul (power (sub 1 'ell
)
3397 ;; The last case F(a+m,-a;c+n;z), m,n integers, m >= 0, n < 0
3399 ;; F(a+m,-a;1/2-n;z)
3402 ;; diff(z^(c-1)*F(a,b;c;z),z,n) = poch(c-n,n)*z^(c-n-1)*F(a,b;c-n;z)
3405 ;; diff(z^(a+m-1)*F(a,b;c;z),z,m) = poch(a,n)*z^(a-1)*F(a+n,b;c;z)
3409 ;; diff(z^(-1/2)*F(a,-a;1/2;z),z,n) = poch(1/2-n,n)*z^(-n-1/2)*F(a,-a;1/2-n;z)
3411 ;; diff(z^(a+m-1)*F(a,-a;1/2-n;z),z,m) = poch(a,m)*z^(a-1)*F(a+m,-a;1/2-n;z)
3412 (defun f84 (fun m n a
)
3413 (mul (inv (mul (factf a m
)
3414 (factf (sub (inv 2) n
) n
)))
3415 (power 'ell
(sub 1 a
))
3416 ($diff
(mul (power 'ell
(sub (add a m n
) (inv 2)))
3417 ($diff
(mul (power 'ell
(inv -
2)) fun
)
3423 ;; Like f82, but |n|<|m|
3425 ;; F(a-m,-a;1/2-n;z), 0 < n < m
3428 ;; diff(z^(c-a+n-1)*(1-z)^(a+b-c)*F(a,b;c;z),z,n)
3429 ;; = poch(c-a,n)*z^(c-a-1)*(1-z)^(a+b-c-n)*F(a-n,b;c;z)
3432 ;; diff(z^(c-1)*(1-z)^(b-c+n)*F(a,b;c;z),z,n)
3433 ;; = poch(c-n,n)*z^(c-n-1)*(1-z)^(b-c)*F(a-n,b;c-n;z)
3437 ;; diff(z^(-a+m-n-1/2)*(1-z)^(-1/2)*F(a,-a;1/2;z),z,m-n)
3438 ;; = poch(1/2-a,m-n)*z^(-a-1/2)*(1-z)^(-1/2-m+n)*F(a-m+n,-a;1/2;z)
3440 ;; diff(z^(-1/2)*(1-z)^(-a-1/2+n)*F(a-m+n,-a;1/2;z),z,n)
3441 ;; = poch(1/2-n,n)*z^(-n-1/2)*(1-z)^(-a-1/2)*F(a-m,-a;1/2-n;z)
3443 ;; G(z) = z^(-a-1/2)*(1-z)^(-1/2-m+n)*F(a-m+n,-a;1/2;z)
3444 ;; = 1/poch(1/2-a,m-n)*diff(z^(-a+m-n-1/2)*(1-z)^(-1/2)*F(a,-a;1/2;z),z,m-n)
3446 ;; F(a-m,-a;1/2-n;z)
3447 ;; = z^(n+1/2)*(1-z)^(a+1/2)/poch(1/2-n,n)
3448 ;; *diff(z^(-1/2)*(1-z)^(-a-1/2+n)*F(a-m+n,-a;1/2;z),z,n)
3449 ;; = z^(n+1/2)*(1-z)^(a+1/2)/poch(1/2-n,n)
3450 ;; *diff(z^a*(1-z)^(m-a)*G(z),z,n)
3452 (defun f86 (fun m n a
)
3453 (mul (inv (mul (factf (sub (inv 2) n
) n
)
3454 (factf (sub (inv 2) a
) (- m n
))))
3455 (power 'ell
(add n
(inv 2)))
3456 (power (sub 1 'ell
) (add (inv 2) a
))
3457 ($diff
(mul (power 'ell a
)
3458 (power (sub 1 'ell
) (sub m a
))
3459 ($diff
(mul (power 'ell
3460 (sub (sub (sub m n
) (inv 2)) a
))
3467 ;; F(-1/2+n, 1+m; 1/2+l; z)
3468 (defun hyp-atanh (a b c
)
3469 ;; We start with F(-1/2,1;1/2;z) = 1-sqrt(z)*atanh(sqrt(z)). We can
3470 ;; derive the remaining forms by differentiating this enough times.
3472 ;; FIXME: Do we need to assume z > 0? We do that anyway, here.
3473 (let* ((s (gensym (symbol-name '#:hyp-atanh-
)))
3477 (res (sub 1 (mul (power s
'((rat simp
) 1 2))
3478 (take '(%atanh
) (power s
'((rat simp
) 1 2))))))
3482 ;; The total number of derivates we compute is n + m + ell. We
3483 ;; should do something to reduce the number of derivatives.
3486 (format t
"a ,b ,c = ~a ~a ~a~%" a b c
)
3487 (format t
"n, m, ell = ~a ~a ~a~%" n m ell
)
3488 (format t
"init a b c = ~a ~a ~a~%" new-a new-b new-c
))
3489 (cond ((alike1 (sub n ell
) 0)
3490 ;; n = ell so we can use A&S 15.2.7 or A&S 15.2.8
3492 (setf res
(as-15.2
.7 new-a new-b new-c n s res
)))
3494 (setf res
(as-15.2
.8 new-a new-b new-c
(- n
) s res
))))
3495 (setf new-a
(add new-a n
))
3496 (setf new-c
(add new-c n
)))
3498 ;; Adjust ell and then n. (Does order matter?)
3500 (setf res
(as-15.2
.6 new-a new-b new-c ell s res
))
3501 (setf new-c
(add new-c ell
)))
3503 (setf res
(as-15.2
.4 new-a new-b new-c
(- ell
) s res
))
3504 (setf new-c
(add new-c ell
))))
3507 (maxima-display res
)
3508 (format t
"new a b c = ~a ~a ~a~%" new-a new-b new-c
))
3511 (setf res
(as-15.2
.3 new-a new-b new-c n s res
))
3512 (setf new-a
(add new-a n
)))
3515 (setf res
(as-15.2
.5 new-a new-b new-c
(- n
) s res
))
3516 (setf new-a
(add new-a n
))))))
3519 (format t
"new a b c = ~a ~a ~a~%" new-a new-b new-c
)
3520 (maxima-display res
))
3521 ;; Finally adjust m by swapping the a and b parameters, since the
3522 ;; hypergeometric function is symmetric in a and b.
3524 (setf res
(as-15.2
.3 new-b new-a new-c m s res
))
3525 (setf new-b
(add new-b m
)))
3527 (setf res
(as-15.2
.5 new-b new-a new-c
(- m
) s res
))
3528 (setf new-b
(add new-b m
))))
3531 (format t
"new a b c = ~a ~a ~a~%" new-a new-b new-c
)
3532 (maxima-display res
))
3533 ;; Substitute the argument into the expression and simplify the result.
3534 (sratsimp (maxima-substitute var s res
))))
3538 #-gcl
(:compile-toplevel
)
3539 (declare-top (unspecial var
*par
* checkcoefsignlist
))