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 var
))
21 (defvar *debug-hyp
* nil
)
23 (defmvar $prefer_whittaker nil
)
25 ;; When T give result in terms of gamma_incomplete and not gamma_incomplete_lower
26 (defmvar $prefer_gamma_incomplete nil
)
28 ;; When NIL do not automatically expand polynomials as a result
29 (defmvar $expand_polynomials t
)
32 (:execute
:compile-toplevel
)
33 (defmacro simp
(x) `(simplifya ,x
()))
35 ;; The macro MABS has been renamed to HYP-MABS in order to
36 ;; avoid conflict with the Maxima symbol MABS. The other
37 ;; M* macros defined here should probably be similarly renamed
38 ;; for consistency. jfa 03/27/2002
40 (defmacro hyp-mabs
(x) `(simp `((mabs) ,,x
)))
42 (defmacro msqrt
(x) `(m^t
,x
1//2))
44 (defmacro mexpt
(x) `(m^t
'$%e
,x
))
46 (defmacro mlog
(x) `(simp `((%log
) ,,x
)))
48 (defmacro msin
(x) `(simp `((%sin
) ,,x
)))
50 (defmacro mcos
(x) `(simp `((%cos
) ,,x
)))
52 (defmacro masin
(x) `(simp `((%asin
) ,,x
)))
54 (defmacro matan
(x) `(simp `((%atan
) ,,x
)))
56 (defmacro mgamma
(x) `(simp `((%gamma
) ,,x
)))
58 (defmacro mbinom
(x y
) `(simp `((%binomial
) ,,x
,,y
)))
60 (defmacro merf
(x) `(simp `((%erf
) ,,x
)))
62 (defmacro =1//2 (x) `(alike1 ,x
1//2))
65 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
67 ;;; Functions moved from hypgeo.lisp to this place.
68 ;;; These functions are no longer used in hypgeo.lisp.
72 (simplifya (list '(%gamma
) expr
) nil
))
82 ;; Test if X is a number, either Lisp number or a maxima rational.
87 (eq (caar (simplifya x nil
)) 'rat
))))
89 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
91 (defun hyp-integerp (x)
92 ;; In this file, maxima-integerp was used in many places. But it
93 ;; seems that this code expects maxima-integerp to return T when it
94 ;; is really an integer, not something that was declared an integer.
95 ;; But I'm not really sure if this is true everywhere, but it is
96 ;; true in some places.
98 ;; Thus, we replace all calls to maxima-integerp with hyp-integerp,
99 ;; which, for now, returns T only when the arg is an integer.
100 ;; Should we do something more?
101 (and (maxima-integerp x
) (integerp x
)))
103 ;; Main entry point for simplification of hypergeometric functions.
105 ;; F(a1,a2,a3,...;b1,b2,b3;z)
107 ;; L1 is a (maxima) list of an's, L2 is a (maxima) list of bn's.
108 (defmfun $hgfred
(arg-l1 arg-l2 arg
)
111 (eq (caar a
) 'mlist
))))
112 (unless (arg-ok arg-l1
)
113 (merror (intl:gettext
"hgfred: first argument must be a list; found: ~:M") arg-l1
))
114 (unless (arg-ok arg-l2
)
115 (merror (intl:gettext
"hgfred: second argument must be a list; found: ~:M") arg-l2
)))
117 ;; Do we really want $radexpand set to '$all? This is probably a
118 ;; bad idea in general, but we'll leave this in for now until we can
119 ;; verify find all of the code that does or does not need this and
120 ;; until we can verify all of the test cases are correct.
121 (let (;;($radexpand '$all)
124 (*checkcoefsignlist
* nil
))
125 (hgfsimp-exec (cdr arg-l1
) (cdr arg-l2
) arg
)))
127 (defun hgfsimp-exec (arg-l1 arg-l2 arg
)
128 (let* ((l1 (copy-tree arg-l1
))
129 (l2 (copy-tree arg-l2
))
130 ($exponentialize nil
)
131 (res (hgfsimp l1 l2 arg
)))
132 ;; I think hgfsimp returns FAIL and UNDEF for cases where it
133 ;; couldn't reduce the function.
134 (cond ((eq res
'fail
)
141 (defun hgfsimp (arg-l1 arg-l2 var
)
142 (prog (resimp listcmdiff
)
143 (setq arg-l1
(macsimp arg-l1
)
144 arg-l2
(macsimp arg-l2
)
145 resimp
(simpg arg-l1 arg-l2
))
146 (cond ((not (eq (and (consp resimp
) (car resimp
)) 'fail
))
148 ((and (not (zerop1 var
)) ; Do not call splitfpq for a zero argument
150 (intdiffl1l2 (cadr resimp
) (caddr resimp
))))
151 (return (splitpfq listcmdiff
155 (return (dispatch-spec-simp (cadr resimp
)
158 (defun macsimp (expr)
159 (mapcar #'(lambda (index) ($expand index
)) expr
))
161 ;; Simplify the parameters. If L1 and L2 have common elements, remove
162 ;; them from both L1 and L2.
163 (defun simpg (arg-l1 arg-l2
)
164 (let ((il (zl-intersection arg-l1 arg-l2
)))
166 (simpg-exec arg-l1 arg-l2
))
168 (simpg-exec (del il arg-l1
)
174 (del (cdr a
) (delete (car a
) b
:count
1 :test
#'equal
)))))
176 ;; Handle the simple cases where the result is either a polynomial, or
177 ;; is undefined because we divide by zero.
178 (defun simpg-exec (arg-l1 arg-l2
)
180 (cond ((zerop-in-l arg-l1
)
181 ;; A zero in the first index means the series terminates
182 ;; after the first term, so the result is always 1.
184 ((setq n
(hyp-negp-in-l arg-l1
))
185 ;; A negative integer in the first series means we have a
187 (create-poly arg-l1 arg-l2 n
))
188 ((and (or (zerop-in-l arg-l2
)
189 (hyp-negp-in-l arg-l2
))
190 (every #'mnump arg-l1
)
191 (every #'mnump arg-l2
))
192 ;; A zero or negative number in the second index means we
193 ;; eventually divide by zero, so we're undefined. But only
194 ;; do this if both indices contain numbers. See Bug
195 ;; 1858964 for discussion.
198 ;; We failed so more complicated stuff needs to be done.
199 (append (list 'fail
) (list arg-l1
) (list arg-l2
))))))
201 (defun intdiffl1l2 (arg-l1 arg-l2
)
205 (intdiff arg-l1 arg-l2
))))
207 ;; For each element x in arg-l1 and y in arg-l2, compute d = x - y.
208 ;; Find the smallest such non-negative integer d and return (list x
210 (defun intdiff (arg-l1 arg-l2
)
212 ;; Compute all possible differences between elements in arg-l1 and
213 ;; arg-l2. Only save the ones where the difference is a positive
217 (let ((diff (sub x y
)))
219 (push (list x diff
) result
)))))
220 ;; Find the smallest one and return it.
221 (let ((min (first result
)))
222 (dolist (x (rest result
))
223 (when (< (second x
) (second min
))
227 ;; Create the appropriate polynomial for the hypergeometric function.
228 (defun create-poly (arg-l1 arg-l2 n
)
229 (let ((len1 (length arg-l1
))
230 (len2 (length arg-l2
)))
231 ;; n is the smallest (in magnitude) negative integer in L1. To
232 ;; make everything come out right, we need to make sure this value
233 ;; is first in L1. This is ok, the definition of the
234 ;; hypergeometric function does not depend on the order of values
236 (setf arg-l1
(cons n
(remove n arg-l1
:count
1)))
237 (cond ((and (equal len1
2)
239 (2f1polys arg-l1 arg-l2 n
))
246 (t (create-any-poly arg-l1 arg-l2
(mul -
1 n
))))))
248 (defun 1f1polys (arg-l2 n
)
249 (let* ((c (car arg-l2
))
251 (fact1 (mul (power 2 n
)
252 (take '(mfactorial) n
)
254 ;; For all of the polynomials here, I think it's ok to
255 ;; replace sqrt(z^2) with z because when everything is
256 ;; expanded out they evaluate to exactly the same thing. So
257 ;; $radexpand $all is ok here.
258 (fact2 (let (($radexpand
'$all
))
259 (mul (power 2 '((rat simp
) 1 2))
260 (power var
'((rat simp
) 1 2))))))
261 (cond ((alike1 c
'((rat simp
) 1 2))
263 ;; hermite(2*n,x) = (-1)^n*(2*n)!/n!*M(-n,1/2,x^2)
266 ;; M(-n,1/2,x) = n!/(2*n)!*(-1)^n*hermite(2*n,sqrt(x))
268 ;; But hermite(m,x) = 2^(m/2)*He(sqrt(2)*sqrt(x)), so
270 ;; M(-n,1/2,x) = (-1)^n*n!*2^n/(2*n)!*He(2*n,sqrt(2)*sqrt(x))
272 (inv (take '(mfactorial) (add n n
)))
273 (hermpol (add n n
) fact2
)))
274 ((alike1 c
'((rat simp
) 3 2))
276 ;; hermite(2*n+1,x) = (-1)^n*(2*n+1)!/n!*M(-n,3/2,x^2)*2*x
279 ;; M(-n,3/2,x) = n!/(2*n+1)!*(-1)^n*hermite(2*n+1,sqrt(x))/2/sqrt(x)
281 ;; and in terms of He, we get
283 ;; 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))
285 (inv (power 2 '((rat simp
) 1 2)))
286 (inv (take '(mfactorial) (add n n
1)))
287 (hermpol (add n n
1) fact2
)
288 ;; Similarly, $radexpand here is ok to convert sqrt(z^2) to z.
289 (let (($radexpand
'$all
))
290 (inv (power var
'((rat simp
) 1 2))))))
291 ((alike1 c
(neg (add n n
)))
294 (inv (take '(%binomial
) (add n n
) n
))
295 (lagpol n
(sub c
1) var
)))
299 ;; gen_laguerre(n,alpha,x) =
300 ;; binomial(n+alpha,n)*hgfred([-n],[alpha+1],x);
302 ;; Or hgfred([-n],[alpha],x) =
303 ;; gen_laguerre(n,alpha-1,x)/binomial(n+alpha-1,n)
305 ;; But 1/binomial(n+alpha-1,n) = n!*(alpha-1)!/(n+alpha-1)!
306 ;; = n! / (alpha * (alpha + 1) * ... * (alpha + n - 1)
307 ;; = n! / poch(alpha, n)
311 ;; However, if c is not a number leave the result in terms
312 ;; of gamma functions. I (rtoy) think this makes for a
313 ;; simpler result, especially if n is rather large. If the
314 ;; user really wants the answer expanded out, makefact and
315 ;; minfactorial will do that.
316 (if (and (integerp n
)
319 (and (minusp c
) (> c
(- n
))))
320 (merror (intl:gettext
"hgfred: 1F1(~M; ~M; ~M) not defined.")
322 (mul (take '(mfactorial) n
)
323 (inv (take '($pochhammer
) c n
))
324 (lagpol n
(sub c
1) var
)))
325 (let (($gamma_expand t
)) ; Expand Gamma function
326 (mul (take '(mfactorial) n
)
328 (inv (take '(%gamma
) (add c n
)))
329 (lagpol n
(sub c
1) var
))))))))
331 ;; Hermite polynomial. Note: The Hermite polynomial used here is the
332 ;; He polynomial, defined as (A&S 22.5.18, 22.5.19)
334 ;; He(n,x) = 2^(-n/2)*H(n,x/sqrt(2))
338 ;; H(n,x) = 2^(n/2)*He(x*sqrt(2))
340 ;; We want to use H, as used in specfun, so we need to convert it.
341 (defun hermpol (n arg
)
342 (let ((fact (inv (power 2 (div n
2))))
343 (x (mul arg
(inv (power 2 '((rat simp
) 1 2))))))
345 (if (and $expand_polynomials
(integerp n
))
346 (mfuncall '$hermite n x
)
347 (list '($hermite simp
) n x
)))))
349 ;; Generalized Laguerre polynomial
350 (defun lagpol (n a arg
)
351 (if (and (numberp a
) (zerop a
))
352 (if (and $expand_polynomials
(integerp n
))
353 (mfuncall '$laguerre n arg
)
354 (list '($laguerre simp
) n arg
))
355 (if (and $expand_polynomials
(integerp n
))
356 (mfuncall '$gen_laguerre n a arg
)
357 (list '($gen_laguerre simp
) n a arg
))))
359 (defun 2f0polys (arg-l1 n
)
360 (let ((a (car arg-l1
))
362 (when (alike1 (sub b a
) '((rat simp
) -
1 2))
364 (cond ((alike1 (sub b a
) '((rat simp
) 1 2))
365 ;; 2F0(-n,-n+1/2,z) or 2F0(-n-1/2,-n,z)
366 (interhermpol n a b var
))
369 (let ((x (mul -
1 (inv var
)))
371 (mul (take '(mfactorial) order
)
372 (inv (power x order
))
373 (inv (power -
1 order
))
374 (lagpol order
(mul -
1 (add b order
)) x
)))))))
376 ;; Compute 2F0(-n,-n+1/2;z) and 2F0(-n-1/2,-n;z) in terms of Hermite
379 ;; Ok. I couldn't find any references giving expressions for this, so
380 ;; here's a quick derivation.
382 ;; 2F0(-n,-n+1/2;z) = sum(pochhammer(-n,k)*pochhammer(-n+1/2,k)*z^k/k!, k, 0, n)
384 ;; It's easy to show pochhammer(-n,k) = (-1)^k*n!/(n-k)!
385 ;; Also, it's straightforward but tedious to show that
386 ;; pochhammer(-n+1/2,k) = (-1)^k*(2*n)!*(n-k)!/2^(2*k)/n!/(2*n-2*k)!
389 ;; 2F0 = (2*n)!*sum(z^k/2^(2*k)/k!/(2*n-2*k)!)
391 ;; Compare this to the expression for He(2*n,x) (A&S 22.3.11):
393 ;; He(2*n,x) = (2*n)! * x^(2*n) * sum((-1)^k*x^(-2*k)/2^k/k!/(2*n-2*k)!)
397 ;; 2F0(-n,-n+1/2;z) = y^n * He(2*n,y)
399 ;; where y = sqrt(-2/x)
401 ;; For 2F0(-n-1/2,-n;z) = sum(pochhammer(-n,k)*pochhammer(-n-1/2,k)*z^k/k!)
404 ;; pochhammer(-n-1/2,k) = pochhammer(-(n+1)+1/2,k)
407 ;; So 2F0 = (2*n+1)!*sum(z^k/z^(2*k)/k!/(2*n+1-2*k)!)
411 ;; 2F0(-n-1/2,-n;z) = y^(2*n+1) * He(2*n+1,y)
414 (defun interhermpol (n a b x
)
415 (let ((arg (power (div 2 (mul -
1 x
)) '((rat simp
) 1 2)))
416 (order (cond ((alike1 a n
)
419 (sub 1 (add n n
))))))
420 ;; 2F0(-n,-n+1/2;z) = y^(-2*n)*He(2*n,y)
421 ;; 2F0(-n-1/2,-n;z) = y^(-(2*n+1))*He(2*n+1,y)
423 ;; where y = sqrt(-2/var);
424 (mul (power arg
(mul -
1 order
))
425 (hermpol order arg
))))
427 ;; F(n,b;c;z), where n is a negative integer (number or symbolic).
428 ;; The order of the arguments must be checked by the calling routine.
429 (defun 2f1polys (arg-l1 arg-l2 n
)
431 ;; Since F(a,b;c;z) = F(b,a;c;z), make sure L1 has the negative
432 ;; integer first, so we have F(-n,d;c;z)
433 ;; Remark: 2f1polys is only called from create-poly. create-poly calls
434 ;; 2f1polys with the correct order of arg-l1. This test is not necessary.
435 ; (cond ((not (alike1 (car arg-l1) n))
436 ; (setq arg-l1 (reverse arg-l1))))
439 ;; The argument of the hypergeometric function is a number.
440 ;; Avoid the following check which does not work for this case.
441 (setq v
(div (add (cadr arg-l1
) n
) 2)))
443 ;; Check if (b+n)/2 is free of the argument.
444 ;; At this point of the code there is no check of the return value
445 ;; of vfvp. When nil we have no solution and the result is wrong.
446 (setq l
(vfvp (div (add (cadr arg-l1
) n
) 2)))
447 (setq v
(cdr (assoc 'v l
:test
#'equal
)))))
449 (cond ((and (or (not (integerp n
))
450 (not $expand_polynomials
))
451 ;; Assuming we have F(-n,b;c;z), then v is (b+n)/2.
452 ;; See if it can be a Legendre function.
453 ;; We call legpol-core because we know that arg-l1 has the
454 ;; correct order. This avoids questions about the parameter b
455 ;; from legpol-core, because legpol calls legpol-core with
456 ;; both order of arguments.
457 (setq lgf
(legpol-core (car arg-l1
)
461 ((and (or (not (integerp n
))
462 (not $expand_polynomials
))
463 (alike1 (sub (car arg-l2
) v
) '((rat simp
) 1 2)))
465 ;; F(-n, n+2*a; a+1/2; x) = n!*gegen(n, a, 1-2*x)/pochhammer(2*a,n)
467 ;; So v = a, and (car arg-l2) = a + 1/2.
470 (t (mul (take '(mfactorial) (mul -
1 n
))
471 (inv (take '($pochhammer
)
476 (sub 1 (mul 2 *par
*))))))
479 ;; F(-n, n + a + 1 + b; a + 1; x)
480 ;; = n!*jacobi_p(n,a,b,1-2*x)/pochhammer(a+1,n)
481 (return (mul (take '(mfactorial) (mul -
1 n
))
482 (inv (take '($pochhammer
) (car arg-l2
) (mul -
1 n
)))
484 (add (car arg-l2
) -
1)
485 (sub (mul 2 v
) (car arg-l2
))
486 (sub 1 (mul 2 *par
*)))))))))
489 (defun jacobpol (n a b x
)
490 (if (and $expand_polynomials
(integerp n
))
491 (mfuncall '$jacobi_p n a b x
)
492 (list '($jacobi_p simp
) n a b x
)))
494 ;; Gegenbauer (Ultraspherical) polynomial. We use ultraspherical to
496 (defun gegenpol (n v x
)
497 (cond ((equal v
0) (tchebypol n x
))
499 (if (and $expand_polynomials
(integerp n
))
500 (mfuncall '$ultraspherical n v x
)
501 `(($ultraspherical simp
) ,n
,v
,x
)))))
503 ;; Legendre polynomial
504 (defun legenpol (n x
)
505 (if (and $expand_polynomials
(integerp n
))
506 (mfuncall '$legendre_p n x
)
507 `(($legendre_p simp
) ,n
,x
)))
509 ;; Chebyshev polynomial
510 (defun tchebypol (n x
)
511 (if (and $expand_polynomials
(integerp n
))
512 (mfuncall '$chebyshev_t n x
)
513 `(($chebyshev_t simp
) ,n
,x
)))
515 ;; Expand the hypergeometric function as a polynomial. No real checks
516 ;; are made to ensure the hypergeometric function reduces to a
518 (defmfun $hgfpoly
(arg-l1 arg-l2 arg
)
521 (n (hyp-negp-in-l (cdr arg-l1
))))
522 (create-any-poly (cdr arg-l1
) (cdr arg-l2
) (- n
))))
524 (defun create-any-poly (arg-l1 arg-l2 n
)
525 (prog (result exp prodnum proden
)
526 (setq result
1 prodnum
1 proden
1 exp
1)
528 (cond ((zerop n
) (return result
)))
529 (setq prodnum
(mul prodnum
(mull arg-l1
))
530 proden
(mul proden
(mull arg-l2
)))
536 (inv (factorial exp
)))))
539 arg-l1
(incr1 arg-l1
)
540 arg-l2
(incr1 arg-l2
))
543 ;; Compute the product of the elements of the list L.
545 (reduce #'mul l
:initial-value
1))
547 ;; Add 1 to each element of the list L
549 (mapcar #'(lambda (x) (add x
1)) l
))
551 ;; Figure out the orders of generalized hypergeometric function we
552 ;; have and call the right simplifier.
553 (defun dispatch-spec-simp (arg-l1 arg-l2
)
554 (let ((len1 (length arg-l1
))
555 (len2 (length arg-l2
)))
556 (cond ((and (< len1
2)
558 ;; pFq where p and q < 2.
559 (simp2>f
<2 arg-l1 arg-l2 len1 len2
))
563 (simp2f1 arg-l1 arg-l2
))
567 (cond ((and (maxima-integerp (car arg-l1
))
568 (member ($sign
(car arg-l1
)) '($neg $nz
)))
569 ;; 2F0(-n,b; ; z), n a positive integer
570 (2f0polys arg-l1
(car arg-l1
)))
571 ((and (maxima-integerp (cadr arg-l1
))
572 (member ($sign
(cadr arg-l1
)) '($neg $nz
)))
573 ;; 2F0(a,-n; ; z), n a positive integer
574 (2f0polys (reverse arg-l1
) (cadr arg-l1
)))
576 (fpqform arg-l1 arg-l2 var
))))
580 (simp1f2 arg-l1 arg-l2
))
583 (simp2f2 arg-l1 arg-l2
))
585 ;; We don't have simplifiers for any other hypergeometric
587 (fpqform arg-l1 arg-l2 var
)))))
589 ;; Handle the cases where the number of indices is less than 2.
590 (defun simp2>f
<2 (arg-l1 arg-l2 len1 len2
)
591 (cond ((and (zerop len1
) (zerop len2
))
592 ;; hgfred([],[],z) = e^z
594 ((and (zerop len1
) (equal len2
1))
597 ;; hgfred([],[b],0) = 1
602 ;; The hypergeometric series is then
604 ;; 1+sum(z^k/k!/[b*(b+1)*...(b+k-1)], k, 1, inf)
606 ;; = 1+sum(z^k/k!*gamma(b)/gamma(b+k), k, 1, inf)
607 ;; = sum(z^k/k!*gamma(b)/gamma(b+k), k, 0, inf)
608 ;; = gamma(b)*sum(z^k/k!/gamma(b+k), k, 0, inf)
610 ;; Note that bessel_i(b,z) has the series
612 ;; (z/2)^(b)*sum((z^2/4)^k/k!/gamma(b+k+1), k, 0, inf)
614 ;; bessel_i(b-1,2*sqrt(z))
615 ;; = (sqrt(z))^(b-1)*sum(z^k/k!/gamma(b+k),k,0,inf)
616 ;; = z^((b-1)/2)*hgfred([],[b],z)/gamma(b)
618 ;; So this hypergeometric series is a Bessel I function:
620 ;; hgfred([],[b],z) = bessel_i(b-1,2*sqrt(z))*z^((1-b)/2)*gamma(b)
621 (bestrig (car arg-l2
) var
))))
623 ;; hgfred([a],[],z) = 1 + sum(binomial(a+k,k)*z^k) = 1/(1-z)^a
624 (power (sub 1 var
) (mul -
1 (car arg-l1
))))
626 ;; The general case of 1F1, the confluent hypergeomtric function.
627 (confl arg-l1 arg-l2 var
))))
631 ;; bessel_i(a-1,2*sqrt(x))*gamma(a)*x^((1-a)/2)
637 ;; bessel_j(a-1,2*sqrt(x))*gamma(a)*x^((1-a)/2)
641 ;; If a is half of an odd integer and small enough, the Bessel
642 ;; functions are expanded in terms of trig or hyperbolic functions.
645 ;; I think it's ok to have $radexpand $all here so that sqrt(z^2) is
647 (let (($radexpand
'$all
))
649 ;; gamma(b)*(-x)^((1-b)/2)*bessel_j(b-1,2*sqrt(-x))
650 (sratsimp (mul (power (neg x
) (div (sub 1 b
) 2))
654 (mul 2 (power (neg x
) '((rat simp
) 1 2))))))
655 ;; gamma(b)*(x)^((1-b)/2)*bessel_i(b-1,2*sqrt(x))
656 (sratsimp (mul (power x
(div (sub 1 b
) 2))
660 (mul 2 (power x
'((rat simp
) 1 2)))))))))
662 ;; Kummer's transformation. A&S 13.1.27
664 ;; M(a,b,z) = e^z*M(b-a,b,-z)
665 (defun kummer (arg-l1 arg-l2
)
666 (mul (list '(mexpt) '$%e var
)
667 (confl (list (sub (car arg-l2
) (car arg-l1
)))
668 arg-l2
(mul -
1 var
))))
670 ;; Return non-NIL if any element of the list L is zero.
672 (defun zerop-in-l (l)
674 (and (numberp x
) (zerop x
)))
677 ;; If the list L contains a negative integer, return the most positive
678 ;; of the negative integers. Otherwise return NIL.
679 (defun hyp-negp-in-l (l)
682 (when (and (integerp x
) (minusp x
))
684 (setf max-neg
(max max-neg x
))
688 ;; Compute the intersection of L1 and L2, possibly destructively
689 ;; modifying L2. Perserves duplications in L1.
690 (defun zl-intersection (arg-l1 arg-l2
)
691 (cond ((null arg-l1
) nil
)
692 ((member (car arg-l1
) arg-l2
:test
#'equal
)
694 (zl-intersection (cdr arg-l1
)
695 (delete (car arg-l1
) arg-l2
:count
1 :test
#'equal
))))
696 (t (zl-intersection (cdr arg-l1
) arg-l2
))))
698 ;; Whittaker M function. A&S 13.1.32 defines it as
700 ;; %m[k,u](z) = exp(-z/2)*z^(u+1/2)*M(1/2+u-k,1+2*u,z)
702 ;; where M is the confluent hypergeometric function.
703 (defun whitfun (k m var
)
704 (list '(mqapply) (list '($%m array
) k m
) var
))
706 (defun simp1f2 (arg-l1 arg-l2
)
707 "Simplify 1F2([a], [b,c], var). ARG-L1 is the list [a], and ARG-L2 is
708 the list [b, c]. The dependent variable is the (special variable)
710 (let ((a (car arg-l1
)))
711 (destructuring-bind (b c
)
714 ((and (alike1 a
1//2)
717 ;; Sine integral, expintegral_si(z) = z*%f[1,2]([1/2],[3/2,3/2], -z^2/4)
718 ;; (See http://functions.wolfram.com/06.37.26.0001.01)
719 ;; Subst z = 2*sqrt(-y) into the above to get
721 ;; expintegral_si(2*sqrt(-y)) = 2*%f[1,2]([1/2],[3/2,3/2], y)*sqrt(-y)
723 ;; Hence %f[1,2]([1/2],[3/2,3/2], y) = expintegral_si(2*sqrt(-y))/2/sqrt(-y)
724 (div (ftake '%expintegral_si
(mul 2 (power (neg var
) 1//2)))
725 (mul 2 (power (neg var
) 1//2))))
727 (fpqform arg-l1 arg-l2 var
))))))
729 (defun simp2f2 (arg-l1 arg-l2
)
730 (destructuring-bind (a1 a2
)
732 (destructuring-bind (b1 b2
)
740 ;; expintegral_ci(z) = -z^2/4*%f[2,2]([1,1],[2,2], -z^2/4) + log(z) + %gamma
741 ;; (See http://functions.wolfram.com/06.38.26.0001.01)
743 ;; Subst z = 2*sqrt(-y) into the above to get
745 ;; expintegral_ci(2*sqrt(-y)) =
746 ;; %f[2,2]([1,1],[2,2],y)*y + log(2*sqrt(-y)) + %gamma
750 ;; %f[2,2]([1,1],[2,2],y) =
751 ;; (expintegral_ci(2*sqrt(-y)) - log(2*sqrt(-y)) - %gamma)/y
753 ;; But see also http://functions.wolfram.com/06.35.26.0001.01
754 ;; which shows that expintegral_ei can be written in terms of
755 ;; %f[2,2]([1,1],[2,2],z) too. Not sure how to choose
756 ;; between the two representations.
757 (div (sub (ftake '%expintegral_ci
(mul 2 (power (neg var
) 1//2)))
758 (add (ftake '%log
(mul 2 (power (neg var
) 1//2)))
762 (fpqform arg-l1 arg-l2 var
))))))
764 (defvar $trace2f1 nil
765 "Enables simple tracing of simp2f1 so you can see how it decides
766 what approach to use to simplify hypergeometric functions")
768 (defun simp2f1 (arg-l1 arg-l2
)
770 (setq a
(car arg-l1
) b
(cadr arg-l1
) c
(car arg-l2
))
774 (return (add var
1))))
777 (format t
"Tracing SIMP2F1~%")
778 (format t
" Test a or b negative integer ...~%"))
780 ;; Look if a or b is a symbolic negative integer. The routine
781 ;; 2f1polys handles this case.
782 (cond ((and (maxima-integerp a
) (member ($sign a
) '($neg $nz
)))
783 (return (2f1polys arg-l1 arg-l2 a
))))
784 (cond ((and (maxima-integerp b
) (member ($sign b
) '($neg $nz
)))
785 (return (2f1polys (list b a
) arg-l2 b
))))
788 (format t
" Test F(1,1,2)...~%"))
790 (cond ((and (alike1 a
1)
793 ;; F(1,1;2;z) = -log(1-z)/z, A&S 15.1.3
796 (return (mul (inv (mul -
1 var
))
797 (take '(%log
) (add 1 (mul -
1 var
)))))))
800 (format t
" Test c = 1/2 or c = 3/2...~%"))
802 (cond ((or (alike1 c
'((rat simp
) 3 2))
803 (alike1 c
'((rat simp
) 1 2)))
804 ;; F(a,b; 3/2; z) or F(a,b;1/2;z)
805 (cond ((setq lgf
(trig-log (list a b
) (list c
)))
807 (format t
" Yes: trig-log~%"))
811 (format t
" Test |a-b|=1/2...~%"))
813 (cond ((or (alike1 (sub a b
) '((rat simp
) 1 2))
814 (alike1 (sub b a
) '((rat simp
) 1 2)))
815 ;; F(a,b;c;z) where |a-b|=1/2
816 (cond ((setq lgf
(hyp-cos a b c
))
818 (format t
" Yes: hyp-cos~%"))
822 (format t
" Test a,b are integers, c is a numerical integer...~%"))
824 (cond ((and (maxima-integerp a
)
827 ;; F(a,b;c;z) when a, and b are integers (or are declared
828 ;; to be integers) and c is a integral number.
829 (setf lgf
(simpr2f1 (list a b
) (list c
)))
830 (unless (symbolp lgf
) ; Should be more specific! (DK 01/2010)
832 (format t
" Yes: simpr2f1~%"))
836 (format t
" Test a+b and c+1/2 are numerical integers...~%"))
838 (cond ((and (hyp-integerp (add c
(inv 2)))
839 (hyp-integerp (add a b
)))
840 ;; F(a,b;c;z) where a+b is an integer and c+1/2 is an
843 (format t
" Yes: step4~%"))
844 (return (step4 a b c
))))
847 (format t
" Test a-b+1/2 is a numerical integer...~%"))
849 (cond ((hyp-integerp (add (sub a b
) (inv 2)))
850 ;; F(a,b;c,z) where a-b+1/2 is an integer
851 (cond ((setq lgf
(step7 a b c
))
854 (format t
" Yes: step7~%"))
858 (when (and (hyp-integerp (add c
1//2))
859 (or (and (hyp-integerp (add a
1//2))
861 (and (hyp-integerp (add b
1//2))
864 (format t
" Test for atanh: a+1/2, b, and c+1/2 are integers~%"))
865 (return (hyp-atanh a b c
)))
867 (when (hyp-integerp (add c
1//2))
869 (format t
" Test for atanh: c+1/2 is an integer~%"))
870 (cond ((and (hyp-integerp (add a
1//2))
873 (format t
" atanh with integers a+1/2 and b ~%"))
874 (return (hyp-atanh a b c
)))
875 ((and (hyp-integerp (add b
1//2))
878 (format t
" atanh with integers a and b+1/2 ~%"))
879 (return (hyp-atanh b a c
)))))
882 (format t
" Test for Legendre function...~%"))
884 (cond ((setq lgf
(legfun a b c
))
886 ;; LEGFUN returned something interesting, so we're done.
888 (format t
" Yes: case 1~%"))
890 ;; LEGFUN didn't return anything, so try it with the args
891 ;; reversed, since F(a,b;c;z) is F(b,a;c;z).
892 (setf lgf
(legfun b a c
))
895 (format t
" Yes: case 2~%"))
899 (format t
"'simp2f1-will-continue-in~%"))
900 (return (fpqform arg-l1 arg-l2 var
))))
902 ;; As best as I (rtoy) can tell, step7 is meant to handle an extension
903 ;; of hyp-cos, which handles |a-b|=1/2 and either a+b-1/2 = c or
906 ;; Based on the code, step7 wants a = s + m/n and c = 2*s+k/l. For
907 ;; hyp-cos, we want c-2*a to be a integer. Or k/l-2*m/n is an
912 (prog (l m n k mn kl sym sym1 r
)
913 ;; Write a = sym + mn, c = sym1 + kl
919 ;; We only handle the case where 2*sym = sym1.
920 (cond ((not (equal (mul sym
2) sym1
))
922 (setq kl
(cdras 'c l
))
923 ;; a-b+1/2 is an integer.
925 r
(sub (add (inv 2) (cdras 'c l
)) mn
)
930 ;; We have a = m*s+m/n, c = 2*m*s+k/l.
931 (cond ((equal (* 2 l
) n
)
932 (cond ((hyp-integerp (/ (- k m
) n
))
933 (return (hyp-algv k l m n a b c
))))))
934 (cond ((hyp-integerp (/ k
(* 2 l
)))
935 (cond ((hyp-integerp (/ m n
))
936 (return (hyp-algv k l m n a b c
)))
938 ((hyp-integerp (/ m n
))
940 ((hyp-integerp (/ (- (* k n
) (* 2 l m
)) (* 2 l n
)))
941 (return (hyp-algv k l m n a b c
))))
944 (defun getxy (k l m n
)
948 (cond ((hyp-integerp (setq x
(truncate (+ y
952 (return (list x y
))))
956 (defun hyp-algv (k l m n a b c
)
959 (setq xy
(getxy k l m n
)
962 (cond ((< x
0)(go out
)))
964 (cond ((< (add a-b x
(inv 2)) 0)
965 (return (f88 x y a b c fun
)))
966 (t (return (f87 x y a c fun
)))))
968 (cond ((< (add a-b x
(inv 2)) 0)
969 (return (f90 x y a c fun
)))
970 (t (return (f89 x y a c fun
))))))
973 (cond ((< (- (add a-b
(inv 2)) w
) 0)
974 (return (f92 x y a c fun
)))
975 (t (return (f91 x y a c fun
))))))
977 (defun f87 (x y a c fun
)
979 (inv (mul (factf c y
)
980 (factf (sub (add c y
) (add a x
)) (- x y
))
981 (factf (sub (add c y
) (add a x
(inv 2)))
982 (sub (add a x
(inv 2)) (add a
(inv 2))))))
983 (power 'ell
(sub 1 c
))
984 (power (sub 1 'ell
)(sub (add y c
) (add a
(inv 2))))
985 ($diff
(mul (power 'ell
(add a x
))
986 (power (sub 1 'ell
)(mul -
1 a
))
987 ($diff
(mul (power 'ell
(sub (add (inv 2) x
) y
))
988 ($diff
(mul (power 'ell
(sub (add c y
) 1))
999 (defun f88 (x y a b c fun
)
1001 (inv (mul (factf c y
)
1002 (factf (sub (add c y
) (add a x
)) (- x y
))
1003 (factf (add a
(inv 2) x
)
1004 (sub b
(sub x
(sub a
(inv 2)))))))
1005 (power 'ell
(sub 1 c
))
1006 (power (sub 1 'ell
)(sub (add y c
) (add a
(inv 2))))
1007 ($diff
(mul (power 'ell
(add a x
))
1008 (power (sub 1 'ell
)(mul -
1 a
))
1009 ($diff
(mul (power 'ell
(sub c
(sub x
(sub (inv 2) (mul a
2))))))
1010 (power (sub 1 'ell
) (sub (add a x b
)(sub c y
)))
1011 ($diff
(mul (power 'ell
(sub b
1 ))
1014 'ell
(sub b
(sub a
(sub x
(inv 2)))))
1019 ;; A new version of step7.
1020 (defun step7 (a b c
)
1021 ;; To get here, we know that a-b+1/2 is an integer. To make further
1022 ;; progress, we want a+b-1/2-c to be an integer too.
1024 ;; Let a-b+1/2 = p and a+b+1/2-c = q where p and q are (numerical)
1027 ;; A&S 15.2.3 and 15.2.5 allows us to increase or decrease a. Then
1028 ;; F(a,b;c;z) can be written in terms of F(a',b;c;z) where a' = a-p
1029 ;; and a'-b = a-p-b = 1/2. Also, a'+b+1/2-c = a-p+b+1/2-c = q-p =
1030 ;; r, which is also an integer.
1032 ;; A&S 15.2.4 and 15.2.6 allows us to increase or decrese c. Thus,
1033 ;; we can write F(a',b;c;z) in terms of F(a',b;c',z) where c' =
1034 ;; c+r. Now a'-b=1/2 and a'+b+1/2-c' = a-p+b+1/2-c-r =
1035 ;; a+b+1/2-c-p-r = q-p-(q-p)=0.
1037 ;; Thus F(a',b;c';z) is exactly the form we want for hyp-cos. In
1038 ;; fact, it's A&S 15.1.14: F(a,a+1/2,;1+2a;z) =
1039 ;; 2^(2*a)*(1+sqrt(1-z))^(-2*a).
1040 (declare (special var
))
1041 (let ((q (sub (add a b
(inv 2))
1043 (unless (hyp-integerp q
)
1044 ;; Wrong form, so return NIL
1045 (return-from step7 nil
))
1046 ;; Since F(a,b;c;z) = F(b,a;c;z), we need to figure out which form
1047 ;; to use. The criterion will be the fewest number of derivatives
1049 (let* ((p1 (add (sub a b
) (inv 2)))
1051 (p2 (add (sub b a
) (inv 2)))
1054 (format t
"step 7:~%")
1055 (format t
" q, p1, r1 = ~A ~A ~A~%" q p1 r1
)
1056 (format t
" p2, r2 = ~A ~A~%" p2 r2
))
1057 (cond ((<= (+ (abs p1
) (abs r1
))
1058 (+ (abs p2
) (abs r2
)))
1061 (step7-core b a c
))))))
1063 (defun step7-core (a b c
)
1064 (let* ((p (add (sub a b
) (inv 2)))
1065 (q (sub (add a b
(inv 2))
1069 (c-prime (add 1 (mul 2 a-prime
))))
1070 ;; Ok, p and q are integers. We can compute something. There are
1071 ;; four cases to handle depending on the sign of p and r.
1073 ;; We need to differentiate some hypergeometric forms, so use 'ell
1075 (let ((fun (hyp-cos a-prime
(add a-prime
1//2) c-prime
'ell
)))
1076 ;; fun is F(a',a'+1/2;2*a'+1;z), or NIL
1079 (format t
"step7-core~%")
1080 (format t
" a,b,c = ~A ~A ~A~%" a b c
)
1081 (format t
" p,q,r = ~A ~A ~A~%" p q r
)
1082 (format t
" a', c' = ~A ~A~%" a-prime c-prime
)
1083 (format t
" F(a',a'+1/2; 1+2*a';z) =~%")
1084 (maxima-display fun
))
1085 ;; Compute the result, and substitute the actual argument into
1087 (maxima-substitute var
'ell
1090 (step-7-pp a-prime b c-prime p r
'ell fun
))
1092 (step-7-pm a-prime b c-prime p r
'ell fun
))))
1095 (step-7-mp a-prime b c-prime p r
'ell fun
))
1097 (step-7-mm a-prime b c-prime p r
'ell fun
))))))))))
1099 ;; F(a,b;c;z) in terms of F(a',b;c';z)
1101 ;; F(a'+p,b;c'-r;z) where p >= 0, r >= 0.
1102 (defun step-7-pp (a b c p r z fun
)
1103 ;; Apply A&S 15.2.4 and 15.2.3
1104 (let ((res (as-15.2
.4 a b c r z fun
)))
1105 (as-15.2
.3 a b
(sub c r
) p z res
)))
1110 ;; F(a'+p,b;c'-r;z) = F(a'+p,b;c'+r';z)
1111 (defun step-7-pm (a b c p r z fun
)
1112 ;; Apply A&S 15.2.6 and 15.2.3
1113 (let ((res (as-15.2
.6 a b c
(- r
) z fun
)))
1114 (as-15.2
.3 a b
(sub c r
) p z res
)))
1119 ;; F(a'+p,b;c'-r;z) = F(a'-p',b;c'-r;z)
1120 (defun step-7-mp (a b c p r z fun
)
1121 ;; Apply A&S 15.2.4 and 15.2.5
1122 (let ((res (as-15.2
.4 a b c r z fun
)))
1123 (as-15.2
.5 a b
(sub c r
) (- p
) z res
)))
1127 ;; Let p' = - p, r' = -r
1129 ;; F(a'+p,b;c'-r;z) = F(a'-p',b;c'+r';z)
1130 (defun step-7-mm (a b c p r z fun
)
1131 ;; Apply A&S 15.2.6 and A&S 15.2.5
1132 (let ((res (as-15.2
.6 a b c
(- r
) z fun
)))
1133 (as-15.2
.5 a b
(sub c r
) (- p
) z res
)))
1135 ;; F(a,b;c;z) when a and b are integers (or declared to be integers)
1136 ;; and c is an integral number.
1137 (defun simpr2f1 (arg-l1 arg-l2
)
1138 (destructuring-bind (a b
)
1140 (destructuring-bind (c)
1142 (let ((inl1p (hyp-integerp a
))
1143 (inl1bp (hyp-integerp b
))
1144 (inl2p (hyp-integerp c
)))
1147 (cond ((and inl1p inl1bp
)
1148 ;; a, b, c are (numerical) integers
1151 ;; a and c are integers
1154 ;; b and c are integers.
1157 ;; Can't really do anything else if c is not an integer.
1163 ((eq (caaar arg-l1
) 'rat
)
1164 ;; How do we ever get here?
1174 (cond ((and (> (car arg-l2
)(car arg-l1
))
1175 (> (car arg-l2
)(cadr arg-l1
)))
1176 (geredf (car arg-l1
)(cadr arg-l1
)(car arg-l2
)))
1177 (t (gered1 arg-l1 arg-l2
#'hgfsimp
))))
1179 (defun geredno2 (a b c
)
1180 (cond ((> c b
) (geredf b a c
))
1181 (t (gered2 a b c
))))
1183 ;; Consider F(1,1;2;z). A&S 15.1.3 says this is equal to -log(1-z)/z.
1185 ;; Apply A&S 15.2.2:
1187 ;; 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)
1191 ;; diff((1-z)^(m+ell)*F(1+ell;1+ell;2+ell;z),z,m)
1192 ;; = (-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)
1196 ;; diff((1-z)^ell*F(1+ell+m,1+ell;2+ell+m;z),z,n)
1197 ;; = 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)
1199 ;; The derivation above assumes that ell, m, and n are all
1200 ;; non-negative integers. Thus, F(a,b;c;z), where a, b, and c are
1201 ;; integers and a <= b <= c, can be written in terms of F(1,1;2;z).
1202 ;; The result also holds for b <= a <= c, of course.
1204 ;; Also note that the last two differentiations can be combined into
1205 ;; one differention since the result of the first is in exactly the
1206 ;; form required for the second. The code below does one
1209 ;; So if a = 1+ell, b = 1+ell+m, and c = 2+ell+m+n, we have ell = a-1,
1210 ;; m = b - a, and n = c - ell - m - 2 = c - b - 1.
1212 (defun derivint (a b c
)
1223 (factorial (+ n m l
1))
1226 (inv (factorial (+ n m
)))
1227 (inv (factorial (+ m l
)))
1228 (power (sub 1 psey
) (sub n l
))
1229 ($diff
(mul (power (sub 1 psey
) (+ m l
))
1230 ($diff
(mul (power psey -
1)
1232 (take '(%log
) (sub 1 psey
)))
1238 ($limit result psey var
)
1239 (maxima-substitute var psey result
)))))
1241 ;; Handle F(a, b; c; z) for certain values of a, b, and c. See the
1242 ;; comments below for these special values. The optional arg z
1243 ;; defaults to var, which is usually the argument of hgfred.
1244 (defun hyp-cos (a b c
&optional
(z var
))
1245 (let ((a1 (div (sub (add a b
) (div 1 2)) 2))
1249 (cond ((eql 0 ($ratsimp
(sub (sub (add a b
)
1257 ;; = 2^(2*a-1)*(1-z)^(-1/2)*(1+sqrt(1-z))^(1-2*a)
1259 ;; But if 1-2*a is a negative integer, let's rationalize the answer to give
1262 ;; = 2^(2*a-1)*(1-z)^(-1/2)*(1-sqrt(1-z))^(2*a-1)/z^(2*a-1)
1264 (format t
" Case a+b-1/2=c~%"))
1265 (let ((2a-1 (sub (mul a1
2) 1)))
1266 (cond ((and (integerp 2a-1
) (plusp 2a-1
))
1267 ;; 2^(2*a-1)*(1-z)^(-1/2)*(1-sqrt(1-z))^(2*a-1)/z^(2*a-1)
1269 (inv (power z1
1//2))
1270 (power (sub 1 (power z1
1//2)) 2a-1
)
1271 (inv (power z
2a-1
))))
1273 ;; 2^(2*a-1)*(1-z)^(-1/2)*(1+sqrt(1-z))^(1-2*a)
1274 (mul (power 2 (sub (mul a1
2) 1))
1275 (inv (power z1
(div 1 2)))
1280 (sub 1 (mul 2 a1
))))))))
1281 ((eql 0 ($ratsimp
(sub (add 1 (mul 2 a1
)) c
)))
1282 ;; c = 1+2*a1 = a+b+1/2
1286 ;; F(a,1/2+a;1+2*a;z) = 2^(2*a)*(1+sqrt(1-z))^(-2*a)
1288 ;; But if 2*a is a positive integer, let's rationalize the answer to give
1290 ;; F(a,1/2+a;1+2*a;z) = 2^(2*a)*(1-sqrt(1-z))^(2*a)/z^(2*a)
1292 (format t
" Case c=1+2*a=a+b+1/2~%"))
1293 (let ((2a (sub c
1)))
1294 (cond ((and (integerp 2a
) (plusp 2a
))
1295 ;; 2^(2*a)*(1-sqrt(1-z))^(2*a)/z^(2*a)
1297 (power (sub 1 (power z1
1//2))
1299 (power z
(mul -
1 2a
))))
1301 ;; 2^(2*a)*(1+sqrt(1-z))^(-2*a)
1303 (power (add 1 (power z1
1//2))
1304 (mul -
1 2a
))))))))))
1306 ;; Is A a non-negative integer?
1308 (cond ((hyp-integerp a
)
1312 ;;; The following code doesn't appear to be used at all. Comment it all out for now.
1316 (cond ((eq (quest (sub c b
)) '$negative
)
1317 (cond ((eq (quest (sub c a
)) '$negative
)
1318 (gered1 (list a b
)(list c
) #'hgfsimp
))
1319 (t (gered2 a b c
))))
1320 ((eq (quest (sub c a
)) '$negative
)(gered2 b a c
))
1321 (t (rest-degen a b c
))))
1327 (cond ((nni (setq m
(sub a
1)))
1328 (return (rest-degen-1 a b c m
))))
1329 (cond ((ni b
)(return (rest-degen-2 a b c
))))
1330 (cond ((and (nni (setq n
(sub c
1)))
1331 (nni (setq m
(sub (sub a n
) 1)))
1332 (nni (setq l
(sub b a
)))
1333 (eq (sub (sub c a
) b
)
1334 (mul -
1 (add m m n l
1))))
1335 (return (gered1 (list a b
)
1338 (return (hyp-deg b a c
))))
1345 (ni (sub (sub c a
) b
))
1346 (nni (sub (sub c a
) 1)))
1347 (return (deg299 a b c
))))
1348 (cond ((and (nni (setq n
(sub (sub c m
) 2)))
1349 (nni (setq l
(sub b c
)))
1350 (equal (sub (sub c a
) b
)
1351 (mul -
1 (add l m
1))))
1352 (return (gered1 (list a b
)
1355 (cond ((nni (setq l
(sub (sub b m
) 1)))
1356 (return (rest-degen-1a a b c m l
))))
1357 (return (hyp-deg b a c
))))
1360 (defun rest-degen-1a
1363 (cond ((and (nni (setq n
1364 (sub (sub (sub c m
) l
) 2)))
1365 (equal (sub n m
)(sub (sub c a
) b
)))
1366 (return (deg2913 a b c
))))
1367 (cond ((and (equal c
(mul -
1 n
))
1368 (equal (sub (sub c a
) b
)
1369 (mul -
1 (add m m l n
2))))
1370 (return (deg2918 a b c
))))
1371 (return (hyp-deg b a c
))))
1377 (cond ((and (ni c
)(ni (sub (sub c a
) b
)))
1378 (return (rest-degen-2a a b c
))))
1379 (cond ((and (nni (setq m
(sub c
1)))
1380 (nni (setq l
(sub a c
)))
1381 (ni (sub (sub c a
) b
)))
1382 (return (deg292 a b c
))))
1383 (return (hyp-deg b a c
))))
1386 (defun rest-degen-2a
1389 (cond ((nni (sub a c
))
1390 (return (gered1 (list a b
)
1393 (cond ((nni (sub (sub c a
) 1))
1394 (return (deg2917 a b c
))))
1395 (return (hyp-deg b a c
))))
1399 (cond ((numberp a
)(checksigntm a
))
1400 ((equal (caar a
) 'rat
)(checksigntm a
))
1403 (defun ni(a)(not (hyp-integerp a
)))
1409 (cond (fldeg (setq fldeg nil
)
1410 (return (hgfsimp (list a b
)
1414 (return (fpqform (list a b
)(list c
) var
))))
1419 (mul (power (mul -
1 var
)(mul -
1 b
))
1420 (hgfsimp (list (add b
1 (mul -
1 c
)) b
)
1421 (list (add b
1 (mul -
1 a
)))
1427 (mul (power var
(sub 1 c
))
1428 (power (sub 1 var
)(add c
(mul -
1 a
)(mul -
1 b
)))
1429 (hgfsimp (list (sub 1 a
)(sub 1 b
))
1436 (mul (power var
(sub 1 c
))
1437 (hgfsimp (list (add a
1 (mul -
1 c
))
1438 (add b
1 (mul -
1 c
)))
1445 (mul (power (mul -
1 var
)(mul -
1 a
))
1446 (hgfsimp (list a
(add a
1 (mul -
1 c
)))
1447 (list (add a
1 (mul -
1 b
)))
1453 ;; Is F(a, b; c; z) is Legendre function?
1455 ;; Lemma 29 (see ref) says F(a, b; c; z) can be reduced to a Legendre
1456 ;; function if two of the numbers 1-c, +/-(a-b), and +/- (c-a-b) are
1457 ;; equal to each other or one of them equals +/- 1/2.
1459 ;; This routine checks for each of the possibilities.
1460 (defun legfun (a b c
)
1461 (let ((1-c (sub 1 c
))
1463 (c-a-b (sub (sub c a
) b
))
1465 (cond ((alike1 a-b inv2
)
1468 (format t
"Legendre a-b = 1/2~%"))
1469 (gered1 (list a b
) (list c
) #'legf24
))
1471 ((alike1 a-b
(mul -
1 inv2
))
1474 ;; For example F(a,a+1/2;c;x)
1476 (format t
"Legendre a-b = -1/2~%"))
1477 (legf24 (list a b
) (list c
) var
))
1479 ((alike1 c-a-b
'((rat simp
) 1 2))
1481 ;; For example F(a,b;a+b+1/2;z)
1483 (format t
"Legendre c-a-b = 1/2~%"))
1484 (legf20 (list a b
) (list c
) var
))
1486 ((and (alike1 c-a-b
'((rat simp
) 3 2))
1488 (not (alike1 a -
1//2))
1489 (not (alike1 b -
1//2)))
1490 ;; c-a-b = 3/2 e.g. F(a,b;a+b+3/2;z) Reduce to
1491 ;; F(a,b;a+b+1/2) and use A&S 15.2.6. But if c = 1, we
1492 ;; don't want to reduce c to 0! Problem: The derivative of
1493 ;; assoc_legendre_p introduces a unit_step function and the
1494 ;; result looks very complicated. And this doesn't work if
1495 ;; a+1/2 or b+1/2 is zero, so skip that too.
1497 (format t
"Legendre c-a-b = 3/2~%")
1498 (mformat t
" : a = ~A~%" a
)
1499 (mformat t
" : b = ~A~%" b
)
1500 (mformat t
" : c = ~A~%" c
))
1501 (let ((psey (gensym)))
1504 (mul (power (sub 1 psey
) '((rat simp
) 3 2))
1505 (add a b
'((rat simp
) 1 2))
1506 (inv (add b
'((rat simp
) 1 2)))
1507 (inv (add a
'((rat simp
) 1 2)))
1508 ($diff
(mul (power (sub 1 psey
) '((rat simp
) -
1 2))
1510 (list (add a b
'((rat simp
) 1 2)))
1515 ((alike1 c-a-b
'((rat simp
) -
1 2))
1516 ;; c-a-b = -1/2, e.g. F(a,b; a+b-1/2; z)
1517 ;; This case is reduce to F(a,b; a+b+1/2; z) with
1518 ;; F(a,b;c;z) = (1-z)^(c-a-b)*F(c-a,c-b;c;z)
1520 (format t
"Legendre c-a-b = -1/2~%"))
1521 (gered1 (list a b
) (list c
) #'legf20
))
1524 ;; 1-c = a-b, F(a,b; b-a+1; z)
1526 (format t
"Legendre 1-c = a-b~%"))
1527 (gered1 (list a b
) (list c
) #'legf16
))
1529 ((alike1 1-c
(mul -
1 a-b
))
1530 ;; 1-c = b-a, e.g. F(a,b; a-b+1; z)
1532 (format t
"Legendre 1-c = b-a~%"))
1533 (legf16 (list a b
) (list c
) var
))
1536 ;; 1-c = c-a-b, e.g. F(a,b; (a+b+1)/2; z)
1538 (format t
"Legendre 1-c = c-a-b~%"))
1539 (gered1 (list a b
) (list c
) #'legf14
))
1541 ((alike1 1-c
(mul -
1 c-a-b
))
1544 ;; For example F(a,1-a;c;x)
1546 (format t
"Legendre 1-c = a+b-c~%"))
1547 (legf14 (list a b
) (list c
) var
))
1549 ((alike1 a-b
(mul -
1 c-a-b
))
1550 ;; a-b = a+b-c, e.g. F(a,b;2*b;z)
1552 (format t
"Legendre a-b = a+b-c~%"))
1553 (legf36 (list a b
) (list c
) var
))
1555 ((or (alike1 1-c inv2
)
1556 (alike1 1-c
(mul -
1 inv2
)))
1557 ;; 1-c = 1/2 or 1-c = -1/2
1558 ;; For example F(a,b;1/2;z) or F(a,b;3/2;z)
1560 (format t
"Legendre |1-c| = 1/2~%"))
1561 ;; At this point it does not make sense to call legpol. legpol can
1562 ;; handle only cases with a negative integer in the first argument
1563 ;; list. This has been tested already. Therefore we can not get
1564 ;; a result from legpol. For this case a special algorithm is needed.
1565 ;; At this time we return nil.
1572 (format t
"Legendre a-b = c-a-b~%"))
1573 'legendre-funct-to-be-discovered
)
1577 ;;; The following legf<n> functions correspond to formulas in Higher
1578 ;;; Transcendental Functions. See the chapter on Legendre functions,
1579 ;;; in particular the table on page 124ff,
1581 ;; Handle the case c-a-b = 1/2
1585 ;; 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)
1587 ;; See also A&S 15.4.12 and 15.4.13.
1589 ;; Let a = 1/2+n/2-m/2, b = -n/2-m/2, c = 1-m. Then, m = 1-c. And we
1590 ;; have two equivalent expressions for n:
1592 ;; n = c - 2*b - 1 or n = 2*a - c
1594 ;; The code below chooses the first solution. A&S chooses second.
1596 ;; F(a,b;c;w) = 2^(c-1)*gamma(c)*(-w)^((1-c)/2)*P(c-2*b-1,1-c,sqrt(1-w))
1599 (defun legf20 (arg-l1 arg-l2 var
)
1601 (let* (($radexpand nil
)
1604 (a (sub (sub c b
) '((rat simp
) 1 2)))
1606 (n (mul -
1 (add b b m
))))
1608 ;; n = -(2*b+1-c) = c - 1 - 2*b
1611 ;; F(a,b;a+b+1/2;x) = 2^(a+b-1/2)*gamma(a+b+1/2)*x^((1/2-a-b)/2)
1612 ;; *assoc_legendre_p(a-b-1/2,1/2-a-b,sqrt(1-x))
1613 ;; This formula is correct for all arguments x.
1614 (mul (power 2 (add a b
'((rat simp
) -
1 2)))
1615 (take '(%gamma
) (add a b
'((rat simp
) 1 2)))
1617 (div (sub '((rat simp
) 1 2) (add a b
))
1621 (power (sub 1 var
) '((rat simp
) 1 2))
1624 ;; Handle the case a-b = -1/2.
1628 ;; 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)
1630 ;; See also A&S 15.4.10 and 15.4.11.
1632 ;; Let a = -n/2-m/2, b = 1/2-n/2-m/2, c = 1-m. Then m = 1-c. Again,
1633 ;; we have 2 possible (equivalent) values for n:
1635 ;; n = -(2*a + 1 - c) or n = c-2*b
1637 ;; The code below chooses the first solution.
1639 ;; 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))
1641 ;; 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))
1643 ;; Is there a mistake in 15.4.10 and 15.4.11?
1645 (defun legf24 (arg-l1 arg-l2 var
)
1646 (let* (($radexpand nil
)
1650 ; (n (mul -1 (add a a m))) ; This is not 2*a-c
1651 (n (sub (add a a
) c
)) ; but this.
1652 (z (inv (power (sub 1 var
) (inv 2)))))
1653 ;; A&S 15.4.10, 15.4.11
1654 (cond ((eq (asksign var
) '$negative
)
1657 ;; F(a,a+1/2;c;x) = 2^(c-1)*gamma(c)(-x)^(1/2-c/2)*(1-x)^(c/2-a-1/2)
1658 ;; *assoc_legendre_p(2*a-c,1-c,1/sqrt(1-x))
1659 (mul (inv (power 2 m
))
1661 (power (mul -
1 var
) (div m
2))
1662 (power (sub 1 var
) (sub (div m -
2) a
))
1668 (mul (inv (power 2 m
))
1670 (power var
(div m
2))
1671 (power (sub 1 var
) (sub (div m -
2) a
))
1681 ;; 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))
1683 ;; See also A&S 15.4.14 and 15.4.15.
1685 ;; Let a = -n, b = -n-m, c = 1-m. Then m = 1-c. We have 2 solutions
1688 ;; n = -a or n = c-b-1.
1690 ;; The code below chooses the first solution.
1692 ;; F(a,b;c;w) = gamma(c)*w^(1/2-c/2)*(1-w)^(-a)*P(-a,1-c,(1+w)/(1-w));
1694 ;; FIXME: We don't correctly handle the branch cut here!
1695 (defun legf16 (arg-l1 arg-l2 var
)
1696 (let* (($radexpand nil
)
1702 ;; m = b - a, so b = a + m
1708 (format t
"a, c = ~A ~A~%" a c
)
1709 (format t
"b = ~A~%" b
))
1710 ;; A&S 15.4.14, 15.4.15
1711 (cond ((eq (asksign var
) '$negative
)
1714 ;; F(a,b;a-b+1,x) = gamma(a-b+1)*(1-x)^(-b)*(-x)^(b/2-a/2)
1715 ;; * assoc_legendre_p(-b,b-a,(1+x)/(1-x))
1718 (mul (take '(%gamma
) c
)
1719 (power (sub 1 var
) (mul -
1 b
))
1720 (power (mul -
1 var
) (div m
2))
1723 (mul (take '(%gamma
) c
)
1724 (power (sub 1 var
) (mul -
1 b
))
1725 (power var
(div m
2))
1726 (legen n m z
'$p
))))))
1728 ;; Handle the case 1-c = a+b-c.
1730 ;; See, for example, A&S 8.1.2 (which
1731 ;; might have a bug?) or
1732 ;; http://functions.wolfram.com/HypergeometricFunctions/LegendreP2General/26/01/02/
1736 ;; P(n,m,z) = (z+1)^(m/2)*(z-1)^(-m/2)/gamma(1-m)*F(-n,1+n;1-m;(1-z)/2)
1738 ;; See also A&S 8.1.2, 15.4.16, 15.4.17
1740 ;; Let a=-n, b = 1+n, c = 1-m. Then m = 1-c and n has 2 solutions:
1742 ;; n = -a or n = b - 1.
1744 ;; The code belows chooses the first solution.
1746 ;; 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)
1747 (defun legf14 (arg-l1 arg-l2 var
)
1748 ;; Set $radexpand to NIL, because we don't want (-z)^x getting
1749 ;; expanded to (-1)^x*z^x because that's wrong for this.
1750 (let* (($radexpand nil
)
1756 (z (sub 1 (mul 2 var
))))
1758 (format t
"~&legf14~%"))
1759 ;; A&S 15.4.16, 15.4.17
1760 (cond ((not (alike1 (add a b
) 1))
1761 ;; I think 15.4.16 and 15.4.17 require the form
1762 ;; F(a,1-a;c;x). That is, a+b = 1. If we don't have it
1765 ((and (eq (asksign var
) '$positive
)
1766 (eq (asksign (sub 1 var
)) '$positive
))
1768 (format t
" A&S 15.4.17~%"))
1771 ;; F(a,1-a;c;x) = gamma(c)*x^(1/2-c/2)*(1-x)^(c/2-1/2)*
1772 ;; assoc_legendre_p(-a,1-c,1-2*x)
1776 (power var
(div (sub 1 c
) 2))
1777 (power (sub 1 var
) (div (sub c
1) 2))
1782 ;; F(a,1-a;c;z) = gamma(c)*(-z)^(1/2-c/2)*(1-z)^(c/2-1/2)*
1783 ;; assoc_legendre_p(-a,1-c,1-2*z)
1785 (format t
" A&S 15.4.17~%"))
1787 (power (mul -
1 var
) (div (sub 1 c
) 2))
1788 (power (sub 1 var
) (div (sub c
1) 2))
1789 (legen n m z
'$p
))))))
1791 ;; Handle a-b = a+b-c
1795 ;; exp(-%i*m*%pi)*Q(n,m,z) =
1796 ;; 2^n*gamma(1+n)*gamma(1+n+m)*(z+1)^(m/2-n-1)*(z-1)^(-m/2)/gamma(2+2*n)
1797 ;; * hgfred([1+n-m,1+n],[2+2*n],2/(1+z))
1799 ;; Let a = 1+n-m, b = 1+n, c = 2+2*n. then n = b-1 and m = b - a.
1800 ;; (There are other solutions.)
1802 ;; F(a,b;c;z) = 2*gamma(2*b)/gamma(b)/gamma(2*b-a)*w^(-b)*(1-w)^((b-a)/2)
1803 ;; *Q(b-1,b-a,2/w-1)*exp(-%i*%pi*(b-a))
1805 (defun legf36 (arg-l1 arg-l2 var
)
1806 (declare (ignore arg-l2
))
1807 (let* ((a (car arg-l1
))
1811 ;;z (div (sub 2 var) var)
1812 (z (sub (div 2 var
) 1)))
1813 (mul (inv (power 2 n
))
1814 (inv (gm (add 1 n
)))
1815 (inv (gm (add 1 n m
)))
1816 (inv (power (add z
1)
1820 (inv (power (sub z
1) (div m -
2)))
1822 (power '$%e
(mul -
1 '$%i m
'$%pi
))
1823 (legen n m z
'$q
))))
1825 (defun legen (n m x pq
)
1826 ;; A&S 8.2.1: P(-n-1,m,z) = P(n,m,z)
1827 (let ((n (if (or (member ($sign n
) '($neg $nz
)) ; negative sign or
1828 (mminusp n
)) ; negative form like -n-1
1832 (list (if (eq pq
'$q
)
1834 '($legendre_p simp
))
1837 (list (if (eq pq
'$q
)
1838 '($assoc_legendre_q simp
)
1839 '($assoc_legendre_p simp
))
1842 (defun legpol-core (a b c
)
1843 ;; I think for this to be correct, we need a to be a negative integer.
1844 (unless (and (eq '$yes
(ask-integerp a
))
1845 (eq (asksign a
) '$negative
))
1846 (return-from legpol-core nil
))
1847 (let* ((l (vfvp (div (add b a
) 2)))
1848 (v (cdr (assoc 'v l
:test
#'equal
))))
1851 ((and (alike1 v
'((rat simp
) 1 2))
1854 ;; P(n,x) = F(-n,n+1;1;(1-x)/2)
1855 (legenpol (mul -
1 a
)
1856 (sub 1 (mul 2 var
))))
1858 ((and (alike1 c
'((rat simp
) 1 2))
1859 (alike1 (add b a
) '((rat simp
) 1 2)))
1860 ;; c = 1/2, a+b = 1/2
1863 ;; P(2*n,x) = (-1)^n*(2*n)!/2^(2*n)/(n!)^2*F(-n,n+1/2;1/2;x^2)
1865 ;; F(-n,n+1/2;1/2;x^2) = P(2*n,x)*(-1)^n*(n!)^2/(2*n)!*2^(2*n)
1867 (let ((n (mul -
1 a
)))
1869 (power (gm (add n
1)) 2)
1870 (inv (gm (add 1 (mul 2 n
))))
1873 (power var
(div 1 2))))))
1875 ((and (alike1 c
'((rat simp
) 3 2))
1876 (alike1 (add b a
) '((rat simp
) 3 2)))
1877 ;; c = 3/2, a+b = 3/2
1880 ;; 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
1882 ;; 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
1884 (let ((n (mul -
1 a
)))
1886 (power (gm (add 1 n
)) 2)
1887 (inv (gm (add 2 (mul 2 n
))))
1889 (legenpol (add 1 (mul 2 n
))
1890 (power var
(div 1 2)))
1891 (inv (power var
(div 1 2))))))
1893 ((and (zerp (sub b a
))
1894 (zerp (sub c
(add a b
))))
1898 ;; P(n,x) = binomial(2*n,n)*((x-1)/2)^n*F(-n,-n;-2*n;2/(1-x))
1900 ;; F(-n,-n;-2*n;x) = P(n,1-2/x)/binomial(2*n,n)(-1/x)^(-n)
1901 (mul (power (gm (add 1 (mul -
1 a
))) 2)
1902 (inv (gm (add 1 (mul -
2 a
))))
1903 (power (mul -
1 var
) (mul -
1 a
))
1904 (legenpol (mul -
1 a
)
1905 (add 1 (div -
2 var
)))))
1906 ((and (alike1 (sub a b
) '((rat simp
) 1 2))
1907 (alike1 (sub c
(mul 2 b
)) '((rat simp
) 1 2)))
1908 ;; a - b = 1/2, c - 2*b = 1/2
1911 ;; P(n,x) = binomial(2*n,n)*(x/2)^n*F(-n/2,(1-n)/2;1/2-n;1/x^2)
1913 ;; F(-n/2,(1-n)/2;1/2-n,1/x^2) = P(n,x)/binomial(2*n,n)*(x/2)^(-n)
1914 (mul (power (gm (add 1 (mul -
2 b
))) 2)
1915 (inv (gm (add 1 (mul -
4 b
))))
1916 (power (mul 2 (power var
(div 1 2))) (mul -
2 b
))
1917 (legenpol (mul -
2 b
)
1918 (power var
(div -
1 2)))))
1919 ((and (alike1 (sub b a
) '((rat simp
) 1 2))
1920 (alike1 (sub c
(mul 2 a
)) '((rat simp
) 1 2)))
1921 ;; b - a = 1/2, c + 2*a = 1/2
1924 ;; P(n,x) = binomial(2*n,n)*(x/2)^n*F(-n/2,(1-n)/2;1/2-n;1/x^2)
1926 ;; F(-n/2,(1-n)/2;1/2-n,1/x^2) = P(n,x)/binomial(2*n,n)*(x/2)^(-n)
1927 (mul (power (gm (add 1 (mul -
2 a
))) 2)
1928 (inv (gm (add 1 (mul -
4 a
))))
1929 (power (mul 2 (power var
(div 1 2))) (mul -
2 a
))
1930 (legenpol (mul -
2 a
)
1931 (power var
(div -
1 2)))))
1935 (defun legpol (a b c
)
1936 ;; See if F(a,b;c;z) is a Legendre polynomial. If not, try
1938 (or (legpol-core a b c
)
1939 (legpol-core b a c
)))
1943 ;; F(a,b;c;z) = (1-z)^(c-a-b)*F(c-a,c-b;c;z)
1944 (defun gered1 (arg-l1 arg-l2 simpflg
)
1945 (destructuring-bind (a b
)
1947 (destructuring-bind (c)
1949 (mul (power (sub 1 var
)
1961 ;; F(a,b;c;z) = (1-z)^(-a)*F(a,c-b;c;z/(z-1))
1962 (defun gered2 (a b c
)
1963 (mul (power (sub 1 var
) (mul -
1 a
))
1964 (hgfsimp (list a
(sub c b
))
1966 (div var
(sub var
1)))))
1970 ;; F(a,b;c;z) = A*z^(-a)*F(a,a-c+1;a+b-c+1;1-1/z)
1971 ;; + B*(1-z)^(c-a-b)*z^(a-c)*F(c-a,1-a;c-a-b+1,1-1/z)
1973 ;; where A = gamma(c)*gamma(c-a-b)/gamma(c-a)/gamma(c-b)
1974 ;; B = gamma(c)*gamma(a+b-c)/gamma(a)/gamma(b)
1976 (defun geredf (a b c
)
1977 (let (($gamma_expand t
))
1978 (add (div (mul (take '(%gamma
) c
)
1979 (take '(%gamma
) (add c
(mul -
1 a
) (mul -
1 b
)))
1980 (power var
(mul -
1 a
))
1981 ($hgfred
`((mlist) ,a
,(add a
1 (mul -
1 c
)))
1982 `((mlist) ,(add a b
(mul -
1 c
) 1))
1983 (sub 1 (div 1 var
))))
1984 (mul (take '(%gamma
) (sub c a
))
1985 (take '(%gamma
) (sub c b
))))
1986 (div (mul (take '(%gamma
) c
)
1987 (take '(%gamma
) (add a b
(mul -
1 c
)))
1989 (add c
(mul -
1 a
) (mul -
1 b
)))
1990 (power var
(sub a c
))
1991 ($hgfred
`((mlist) ,(sub c a
) ,(sub 1 a
))
1992 `((mlist) ,(add c
(mul -
1 a
) (mul -
1 b
) 1))
1993 (sub 1 (div 1 var
))))
1994 (mul (take '(%gamma
) a
) (take '(%gamma
) b
))))))
1996 (defun trig-log (arg-l1 arg-l2
)
1997 (cond ((equal (simplifya (car arg-l2
) nil
) '((rat simp
) 3 2))
2000 (format t
" trig-log: Test c=3/2~%"))
2001 (trig-log-3 arg-l1 arg-l2
))
2002 ((equal (simplifya (car arg-l2
) nil
) '((rat simp
) 1 2))
2005 (format t
" trig-log: Test c=1/2~%"))
2006 (trig-log-1 arg-l1 arg-l2
))
2009 (defun trig-log-3 (arg-l1 arg-l2
)
2010 (cond ((and (or (equal (car arg-l1
) 1) (equal (cadr arg-l1
) 1))
2011 (or (equal (car arg-l1
) (div 1 2))
2012 (equal (cadr arg-l1
) (div 1 2))))
2013 ;; (a = 1 or b = 1) and (a = 1/2 or b = 1/2)
2015 (format t
" Case a or b is 1 and the other is 1/2~%"))
2016 (trig-log-3-exec arg-l1 arg-l2
))
2017 ((and (equal (car arg-l1
) (cadr arg-l1
))
2018 (or (equal 1 (car arg-l1
))
2019 (equal (div 1 2) (car arg-l1
))))
2020 ;; a = b and (a = 1 or a = 1/2)
2022 (format t
" Case a=b and a is 1 or 1/2~%"))
2023 (trig-log-3a-exec arg-l1 arg-l2
))
2024 ((or (equal (add (car arg-l1
) (cadr arg-l1
)) 1)
2025 (equal (add (car arg-l1
) (cadr arg-l1
)) 2))
2026 ;; a + b = 1 or a + b = 2
2028 (format t
" Case a+b is 1 or 2~%"))
2029 (trig-sin arg-l1 arg-l2
))
2030 ((or (equal (sub (car arg-l1
) (cadr arg-l1
)) (div 1 2))
2031 (equal (sub (cadr arg-l1
) (car arg-l1
)) (div 1 2)))
2032 ;; a - b = 1/2 or b - a = 1/2
2034 (format t
" Case a-b=1/2 or b-a=1/2~%"))
2035 (trig-3 arg-l1 arg-l2
))
2038 (defun trig-3 (arg-l1 arg-l2
)
2039 (declare (ignore arg-l2
))
2042 ;; F(a,a+1/2,3/2,z^2) =
2043 ;; ((1+z)^(1-2*a) - (1-z)^(1-2*a))/2/z/(1-2*a)
2044 (let* (($radexpand
'$all
)
2046 (sub (add (car arg-l1
)
2049 (z (power var
(div 1 2))))
2053 (sub (power (add 1 z
) a
)
2054 (power (sub 1 z
) a
)))))
2056 (defun trig-sin (arg-l1 arg-l2
)
2057 (declare (ignore arg-l2
))
2058 ;; A&S 15.1.15, 15.1.16
2059 (destructuring-bind (a b
)
2061 ;; I think it's ok to convert sqrt(z^2) to z here, so $radexpand
2063 (let (($radexpand
'$all
)
2065 (cond ((equal (add a b
) 1)
2068 ;; F(a,1-a;3/2;sin(z)^2) =
2070 ;; sin((2*a-1)*z)/(2*a-1)/sin(z)
2071 (mul (inv (mul (mul -
1 (sub a b
))
2072 (msin (masin (msqrt var
)))))
2075 (masin (msqrt var
))))))
2076 ((equal (add a b
) 2)
2079 ;; F(a, 2-a; 3/2; sin(z)^2) =
2081 ;; sin((2*a-2)*z)/(a-1)/sin(2*z)
2082 (mul (msin (mul (setq z1
2096 ;;Generates atan if arg positive else log
2097 (defun trig-log-3-exec (arg-l1 arg-l2
)
2098 (declare (ignore arg-l1 arg-l2
))
2099 ;; See A&S 15.1.4 and 15.1.5
2101 ;; F(a,b;3/2;z) where a = 1/2 and b = 1 (or vice versa).
2103 ;; I think it's ok to convert sqrt(z^2) to z here, so $radexpand is
2105 (let (($radexpand
'$all
))
2106 (cond ((equal (checksigntm var
) '$positive
)
2109 ;; F(1/2,1;3/2,z^2) =
2111 ;; log((1+z)/(1-z))/z/2
2113 ;; This is the same as atanh(z)/z. Should we return that
2114 ;; instead? This would make this match what hyp-atanh
2116 (let ((z (power var
(div 1 2))))
2119 (mlog (div (add 1 z
)
2121 ((equal (checksigntm var
) '$negative
)
2124 ;; F(1/2,1;3/2,z^2) =
2126 (let ((z (power (mul -
1 var
)
2131 (defun trig-log-3a-exec (arg-l1 arg-l2
)
2132 ;; See A&S 15.1.6 and 15.1.7
2134 ;; F(a,b;3/2,z) where a = b and a = 1/2 or a = 1.
2136 ;; I think it's ok to convert sqrt(z^2) to z here, so $radexpand is
2138 (let ((a (first arg-l1
))
2140 (cond ((equal (checksigntm var
) '$positive
)
2143 ;; F(1/2,1/2; 3/2; z^2) = sqrt(1-z^2)*F(1,1;3/2;z^2) =
2145 (let ((z (power var
(div 1 2))))
2147 (div (trig-log-3a-exec (list (div 1 2) (div 1 2)) arg-l2
)
2148 (power (sub 1 (power z
2)) (div 1 2)))
2149 (div (masin z
) z
))))
2150 ((equal (checksigntm var
) '$negative
)
2153 ;; F(1/2,1/2; 3/2; -z^2) = sqrt(1+z^2)*F(1,1,3/2; -z^2) =
2154 ;;log(z + sqrt(1+z^2))/z
2155 (let* ((z (power (mul -
1 var
)
2157 (1+z^
2 (add 1 (power z
2))))
2159 (div (trig-log-3a-exec (list (div 1 2) (div 1 2))
2163 (div (mlog (add z
(power 1+z^
2
2168 (defun trig-log-1 (arg-l1 arg-l2
) ;; 2F1's with C = 1/2
2169 (declare (ignore arg-l2
))
2170 ;; 15.1.17, 11, 18, 12, 9, and 19
2172 ;; I think it's ok to convert sqrt(z^2) to z here, so $radexpand is
2174 (let (($radexpand
'$all
)
2175 x z $exponentialize a b
)
2176 (setq a
(car arg-l1
) b
(cadr arg-l1
))
2177 (cond ((=0 (m+t a b
))
2180 (cond ((equal (checksigntm var
) '$positive
)
2182 ;; F(-a,a;1/2;sin(z)^2) = cos(2*a*z)
2183 (trig-log-1-pos a var
))
2184 ((equal (checksigntm var
) '$negative
)
2186 ;; F(-a,a;1/2;-z^2) = 1/2*((sqrt(1+z^2)+z)^(2*a)
2187 ;; +(sqrt(1+z^2)-z)^(2*a))
2189 (trig-log-1-neg a b var
))
2191 ((equal (m+t a b
) 1.
)
2193 (cond ((equal (checksigntm var
) '$positive
)
2195 ;; F(a,1-a;1/2;sin(z)^2) = cos((2*a-1)*z)/cos(z)
2196 (m//t
(mcos (m*t
(m-t a b
) (setq z
(masin (msqrt var
)))))
2198 ((equal (checksigntm var
) '$negative
)
2200 ;; F(a,1-a;1/2;-z^2) = 1/2*(1+z^2)^(-1/2)*
2201 ;; {[(sqrt(1+z^2)+z]^(2*a-1)
2202 ;; +[sqrt(1+z^2)-z]^(2*a-1)}
2203 (m*t
1//2 (m//t
(setq x
(msqrt (m-t 1. var
))))
2204 (m+t
(m^t
(m+t x
(setq z
(msqrt (m-t var
))))
2206 (m^t
(m-t x z
) b
))))
2208 ((=1//2 (hyp-mabs (m-t b a
)))
2209 ;; F(a, a+1/2; 1/2; z)
2210 (cond ((equal (checksigntm var
) '$positive
)
2212 ;; F(a,1/2+a;1/2;z^2) = ((1+z)^(-2*a)+(1-z)^(-2*a))/2
2214 (m+t
(m^t
(m1+t
(setq z
(msqrt var
)))
2215 (setq b
(m-t 1//2 (m+t a b
))))
2216 (m^t
(m-t 1. z
) b
))))
2217 ((equal (checksigntm var
) '$negative
)
2219 ;; F(a,1/2+a;1/2;-tan(z)^2) = cos(z)^(2*a)*cos(2*a*z)
2220 (m*t
(m^t
(mcos (setq z
(matan (msqrt (m-t var
)))))
2221 (setq b
(m+t a b -
1//2)))
2226 (defun trig-log-1-pos (a z
)
2227 ;; I think it's ok to convert sqrt(z^2) to z here, so $radexpand is
2229 (let (($radexpand
'$all
))
2230 (mcos (m*t
2. a
(masin (msqrt z
))))))
2232 (defun trig-log-1-neg (a b v
)
2233 ;; Look to see a is of the form m*s+c where m and c
2234 ;; are numbers. If m is positive, swap a and b.
2235 ;; Basically we want F(-a,a;1/2;-z^2) =
2236 ;; F(a,-a;1/2;-z^2), as they should be.
2237 (let* (($radexpand
'$all
)
2239 (m (cdras 'm match
))
2240 (s (cdras 's match
))
2242 (if (and m
(eq (checksigntm m
) '$positive
))
2245 (if (eq (checksigntm a
) '$negative
)
2248 (x (msqrt (m-t 1. v
)))
2249 (z (msqrt (m-t v
))))
2252 (setq b
(m*t
2. b
)))
2253 (m^t
(m-t x z
) b
)))))
2255 ;; Pattern match for m*s+c where a is a number, x is symbolic, and c
2259 '((mplus) ((coeffpt) (m $numberp
) (s nonnump
))
2260 ((coeffpp) (c $numberp
)))))
2262 ;; List L contains two elements first the numerator parameter that
2263 ;;exceeds the denumerator one and is called "C", second
2264 ;;the difference of the two parameters which is called "M".
2267 (defun diffintprop-gen-exec (l l1 l2
)
2268 (prog (c m poly constfact
)
2271 l1
(delete c l1
:count
1 :test
#'equal
)
2273 l2
(delete c l2
:count
1 :test equal
)
2274 poly
($expand
(constrpoly c m
'avgoustis
))
2275 constfact
(createconstfact c m
))
2276 (return (yanmult constfact
2277 (diffintprop-exec poly l1 l2
)))))
2279 (defun constrpoly (c m k
)
2280 (cond ((zerop m
) 1.
)
2281 (t (mul (add c k
(1- m
)) (constrpoly c
(1- m
) k
)))))
2283 (defun createconstfact (c m
)
2284 (cond ((zerop m
) 1.
)
2285 (t (mul (inv (add c
(1- m
)))
2286 (createconstfact c
(1- m
))))))
2288 (defun diffintprop-exec (poly l1 l2
)
2289 (distrdiffintprop (createcoefpowlist-exec poly
) l1 l2
))
2291 (defun distrdiffintprop (l l1 l2
)
2293 (t (add (yanmult ($factor
(cadar l
))
2294 (diffintprop (caar l
) l1 l2
))
2295 (distrdiffintprop (cdr l
) l1 l2
)))))
2297 (defun diffintprop (pow l1 l2
)
2298 (cond ((zerop pow
) (hgfsimp l1 l2 var
))
2300 (yanmult (mul (div (multpl l1
) (multpl l2
)) var
)
2301 (hgfsimp (incr1 l1
) (incr1 l2
) var
)))
2302 (t (searchaddserieslist pow l1 l2
))))
2304 (defun searchaddserieslist (pow l1 l2
)
2306 (cond ((setq series
(searchserieslistp serieslist pow
))
2307 (return (eval series
))))
2316 '(yanmult (mul (div (multpl l1
) (multpl l2
))
2318 (diffintproprecurse (1- pow
)
2321 (return (eval res
))))
2323 (defun diffintproprecurse (pow l1 l2
)
2326 ($expand
(power (add 'avgoustis
1.
) pow
)))
2327 (return (diffintprop-exec poly l1 l2
))))
2330 (cond ((null l
) 1.
) (t (mul (car l
) (multpl (cdr l
))))))
2332 (defun createcoefpowlist-exec (poly)
2334 (setq conster
(consterminit poly
'avgoustis
)
2335 hp
($hipow poly
'avgoustis
))
2336 (return (append (list (list 0. conster
))
2337 (createcoefpowlist poly hp
)))))
2339 (defun createcoefpowlist (poly hp
)
2340 (cond ((equal hp
1.
)
2341 (list (list 1.
($coeff poly
'avgoustis
))))
2342 (t (append (createcoefpowlist poly
(1- hp
))
2348 (defun consterminit (fun var
)
2349 (cond ((eq (caar fun
) 'mplus
) (consterm (cdr fun
) var
))
2350 (t (cond ((freevar fun
) fun
) (t 0.
)))))
2352 (defun searchserieslistp (serieslist pow
)
2353 (cond ((null serieslist
) nil
)
2354 ((equal (caar serieslist
) pow
) (cadar serieslist
))
2355 (t (searchserieslistp (cdr serieslist
) pow
))))
2357 (defun yanmult (a b
)
2358 (cond ((eq (caar b
) 'mplus
) (yanmul a
(cdr b
)))
2363 (t (add (mul a
(car b
)) (yanmul a
(cdr b
))))))
2367 (defun freevarpar (exp)
2368 (cond ((freevar exp
) (freepar exp
))
2371 ;; Why is this needed?
2374 (defun freepar (exp)
2376 (not (eq exp
*par
*)))
2377 (t (and (freepar (car exp
))
2378 (freepar (cdr exp
))))))
2380 ;; Confluent hypergeometric function.
2383 (defun confl (arg-l1 arg-l2 var
)
2384 (let* ((a (car arg-l1
))
2393 (not (integerp a
)) ; Do not handle an integer or
2394 (not (integerp (add a a
)))) ; an half integral value
2395 ;; F(a;1;z) = laguerre(-a,z)
2396 (lagpol (neg a
) 0 var
))
2398 ((and (maxima-integerp a
)
2399 (member ($sign a
) '($neg nz
)))
2400 ;; F(-n; c; z) and n a positive integer
2401 (1f1polys (list c
) a
))
2403 ((alike1 c
(add a a
))
2407 ;; F(n+1;2*n+1;2*z) =
2408 ;; gamma(3/2+n)*exp(z)*(z/2)^(-n-1/2)*bessel_i(n+1/2,z).
2413 ;; gamma(n+1/2)*exp(z/2)*(z/4)^(-n-3/2)*bessel_i(n-1/2,z/2);
2415 ;; If n is a negative integer, we use laguerre polynomial.
2416 (if (and (maxima-integerp a
)
2417 (eq (asksign a
) '$negative
))
2418 ;; We have already checked a negative integer. This algorithm
2419 ;; is now present in 1f1polys and at this place never called.
2422 (inv (take '(%binomial
) (add n n
) n
))
2423 (lagpol n
(sub c
1) var
)))
2424 (let ((z (div var
2)))
2426 (bestrig (add a
'((rat simp
) 1 2))
2427 (div (mul z z
) 4))))))
2429 ((and (integerp (setq n
(sub (add a a
) c
)))
2432 (not (integerp (add a a
))))
2433 ;; F(a,2*a-n,z) and a not an integer or a half integral value
2435 (format t
"~&Case 1F1(a,2*a-n,x):")
2436 (format t
"~& ; a = ~A~%" a
)
2437 (format t
"~& ; c = ~A~%" c
)
2438 (format t
"~& : n = ~A~%" n
))
2440 (mul (take '(%gamma
) (sub a
(add n
'((rat simp
) 1 2))))
2442 (sub (add '((rat simp
) 1 2) n
) a
))
2443 (power '$%e
(div var
2))
2444 (let ((index (gensym)))
2446 (mul (power -
1 index
)
2447 (take '($pochhammer
) (- n
) index
)
2448 (take '($pochhammer
) (add a a
(* -
2 n
) -
1) index
)
2449 (add a index
(- n
) '((rat simp
) -
1 2))
2450 (inv (take '($pochhammer
) (sub (add a a
) n
) index
))
2451 (inv (take '(mfactorial) index
))
2453 (add a index
(- n
) '((rat simp
) -
1 2))
2457 ((and (integerp (setq n
(sub c
(add a a
))))
2460 (not (integerp (add a a
))))
2461 ;; F(a,2*a+n,z) and a not an integer or a half integral value
2463 (format t
"~&Case 1F1(a,2*a+n,x):")
2464 (format t
"~& ; a = ~A~%" a
)
2465 (format t
"~& ; c = ~A~%" c
)
2466 (format t
"~& : n = ~A~%" n
))
2468 (mul (take '(%gamma
) (sub a
'((rat simp
) 1 2)))
2470 (sub '((rat simp
) 1 2) a
))
2471 (power '$%e
(div var
2))
2472 (let ((index (gensym)))
2474 (mul (take '($pochhammer
) (- n
) index
)
2475 (take '($pochhammer
) (add a a -
1) index
)
2476 (add a index
'((rat simp
) -
1 2))
2477 (inv (take '($pochhammer
) (add a a n
) index
))
2478 (inv (take '(mfactorial) index
))
2480 (add a index
'((rat simp
) -
1 2))
2484 ((and (integerp (setq n
(sub a
'((rat simp
) 1 2))))
2492 (format t
"~&Case 1F1(n+1/2,m,x):")
2493 (format t
"~& ; a = ~A~%" a
)
2494 (format t
"~& ; c = ~A~%" c
)
2495 (format t
"~& : n = ~A~%" n
)
2496 (format t
"~& : m = ~A~%" m
))
2498 (mul (power 2 (- 1 m
))
2499 (power '$%e
(div var
2))
2502 (inv (take '($pochhammer
) '((rat simp
) 1 2) (- m
1)))
2503 (inv (take '($pochhammer
) '((rat simp
) 1 2) n
))
2504 (let ((index1 (gensumindex)))
2506 (mul (power 2 (neg index1
))
2507 (power (neg var
) index1
)
2508 (inv (take '(mfactorial) index1
))
2509 (mfuncall '$gen_laguerre
2511 (sub index1
'((rat simp
) 1 2))
2513 (let ((index2 (gensumindex)))
2515 (mul (power -
1 index2
)
2516 (power 2 (neg index2
))
2520 (let ((index3 (gensumindex)))
2522 (mul (take '(%binomial
) index2 index3
)
2524 (sub index2
(mul 2 index3
))
2526 index3
0 index2 t
)))
2527 index2
0 (add index1 m -
1) t
)))
2530 ((and (integerp (setq n
(sub a
'((rat simp
) 1 2))))
2539 (format t
"~&Case 1F1(1/2-n,m,x):")
2540 (format t
"~& ; a = ~A~%" a
)
2541 (format t
"~& ; c = ~A~%" c
)
2542 (format t
"~& : n = ~A~%" n
)
2543 (format t
"~& : m = ~A~%" m
))
2547 (power '$%e
(div var
2))
2549 (inv (take '($pochhammer
) '((rat simp
) 1 2) (- m
1)))
2550 (inv (take '($pochhammer
) (sub m
'((rat simp
) 1 2)) n
))
2551 (let ((index1 (gensumindex)))
2553 (mul (power 2 (neg index1
))
2555 (take '(%binomial
) n index1
)
2556 (take '($pochhammer
)
2557 (sub '((rat simp
) 3 2) (+ m n
))
2559 (let ((index2 (gensumindex)))
2561 (mul (power '((rat simp
) -
1 2) index2
)
2565 (let ((index3 (gensumindex)))
2567 (mul (take '(%binomial
) index2 index3
)
2569 (sub index2
(mul 2 index3
))
2571 index3
0 index2 t
)))
2572 index2
0 (add index1 m -
1) t
)))
2575 ((not (hyp-integerp a-c
))
2576 (cond ((hyp-integerp a
)
2577 (kummer arg-l1 arg-l2
))
2581 ;; %m[k,u](z) = exp(-z/2)*z^(u+1/2)*M(1/2+u-k,1+2*u,z)
2585 ;; M(a,c,z) = exp(z/2)*z^(-c/2)*%m[c/2-a,c/2-1/2](z)
2587 ;; But if we apply Kummer's transformation, we can also
2588 ;; derive the expression
2590 ;; %m[k,u](z) = exp(z/2)*z^(u+1/2)*M(1/2+u+k,1+2*u,-z)
2594 ;; M(a,c,z) = exp(-z/2)*(-z)^(-c/2)*%m[a-c/2,c/2-1/2](-z)
2596 ;; For Laplace transforms it might be more beneficial to
2597 ;; return this second form instead.
2598 (let* ((m (div (sub c
1) 2))
2599 (k (add '((rat simp
) 1 2) m
(mul -
1 a
))))
2600 (mul (power var
(mul -
1 (add '((rat simp
) 1 2) m
)))
2601 (power '$%e
(div var
2))
2602 (whitfun k m var
))))
2604 (fpqform arg-l1 arg-l2 var
))))
2606 (sratsimp (erfgammared a c var
)))
2608 (kummer arg-l1 arg-l2
)))))
2611 ;; M(1/2,3/2,-z^2) = sqrt(%pi)*erf(z)/2/sqrt(z)
2613 ;; So M(1/2,3/2,z) = sqrt(%pi)*erf(sqrt(-z))/2/sqrt(-z)
2614 ;; = sqrt(%pi)*erf(%i*sqrt(z))/2/(%i*sqrt(z))
2615 (defun hyprederf (x)
2616 (let ((x (mul '$%i
(power x
'((rat simp
) 1 2 )))))
2617 (mul (power '$%pi
'((rat simp
) 1 2))
2622 ;; M(a,c,z), where a-c is a negative integer.
2623 (defun erfgammared (a c z
)
2624 (cond ((and (nump a
) (nump c
))
2625 (erfgamnumred a c z
))
2626 (t (gammareds a c z
))))
2628 ;; I (rtoy) think this is what the function above is doing, but I'm
2629 ;; not sure. Plus, I think it's wrong.
2631 ;; For hgfred([n],[2+n],-z), the above returns
2633 ;; 2*n*(n+1)*z^(-n-1)*(gamma_incomplete_lower(n,z)*z-gamma_incomplete_lower(n+1,z))
2635 ;; But from A&S 13.4.3
2637 ;; -M(n,2+n,z) - n*M(n+1,n+2,z) + (n+1)*M(n,n+1,z) = 0
2639 ;; so M(n,2+n,z) = (n+1)*M(n,n+1,z)-n*M(n+1,n+2,z)
2641 ;; And M(n,n+1,-z) = n*z^(-n)*gamma_incomplete_lower(n,z)
2645 ;; 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)
2646 ;; = n*(n+1)*z^(-n-1)*(gamma_incomplete_lower(n,z)*n-gamma_incomplete_lower(n+1,z))
2648 ;; So the version above is off by a factor of 2. But I think it's more than that.
2649 ;; Using A&S 13.4.3 again,
2651 ;; M(n,n+3,-z) = [n*M(n+1,n+3,-z) - (n+2)*M(n,n+2,-z)]/(-2);
2653 ;; The version above doesn't produce anything like this equation would
2654 ;; produce, given the value of M(n,n+2,-z) derived above.
2655 (defun gammareds (a c z
)
2656 ;; M(a,c,z) where a-c is a negative integer.
2657 (let ((diff (sub c a
)))
2659 ;; We have M(a,a+1,z).
2663 ;; Apply Kummer's tranformation to get the form M(a-1,a,z)
2665 ;; (I don't think we ever get here, but just in case, we leave it.)
2667 (kummer (list a
) (list c
))))
2669 ;; We have M(a, a+n, z)
2672 ;; (1+a-b)*M(a,b,z) - a*M(a+1,b,z)+(b-1)*M(a,b-1,z) = 0
2676 ;; M(a,b,z) = [a*M(a+1,b,z) - (b-1)*M(a,b-1,z)]/(1+a-b);
2678 ;; Thus, the difference between b and a is reduced, until
2679 ;; b-a=1, which we handle above.
2681 (gammareds (add 1 a
) c z
))
2683 (gammareds a
(sub c
1) z
)))
2684 (inv (sub (add 1 a
) c
)))))))
2687 ;; gamma_incomplete_lower(a,x) = x^a/a*M(a,1+a,-x)
2688 ;; = x^a/a*exp(-x)*M(1,1+a,x)
2690 ;; where gamma_incomplete_lower(a,x) is the lower incomplete gamma function.
2692 ;; M(a,1+a,x) = a*(-x)^(-a)*gamma_incomplete_lower(a,-x)
2693 (defun hypredincgm (a z
)
2694 (let ((-z (mul -
1 z
)))
2695 (if (not $prefer_gamma_incomplete
)
2697 (power -z
(mul -
1 a
))
2698 (take '(%gamma_incomplete_lower
) a -z
))
2700 (power -z
(mul -
1 a
))
2701 (sub (take '(%gamma
) a
)
2702 (take '(%gamma_incomplete
) a -z
))))))
2704 ;; M(a,c,z), when a and c are numbers, and a-c is a negative integer
2705 (defun erfgamnumred (a c z
)
2706 (cond ((hyp-integerp (sub c
'((rat simp
) 1 2)))
2708 (t (gammareds a c z
))))
2710 ;; M(a,c,z) when a and c are numbers and c-1/2 is an integer and a-c
2711 ;; is a negative integer. Thus, we have M(p+1/2, q+1/2,z)
2712 (defun erfred (a c z
)
2714 (setq n
(sub a
'((rat simp
) 1 2))
2715 m
(sub c
'((rat simp
) 3 2)))
2718 ;; a - c < 0 so n - m - 1 < 0
2719 (cond ((not (or (> n m
) (minusp n
)))
2721 (return (thno33 n m z
)))
2722 ((and (minusp n
) (minusp m
))
2724 (return (thno35 (mul -
1 n
) (mul -
1 m
) z
)))
2725 ((and (minusp n
) (plusp m
))
2727 (return (thno34 (mul -
1 n
) m z
)))
2730 (return (gammareds (add n
'((rat simp
) 1 2))
2731 (add m
'((rat simp
) 3 2))
2734 ;; Compute M(n+1/2, m+3/2, z) with 0 <= n <= m.
2736 ;; I (rtoy) think this is what this routine is doing. (I'm guessing
2737 ;; that thno33 means theorem number 33 from Yannis Avgoustis' thesis.)
2739 ;; I don't have his thesis, but I see there are similar ways to derive
2740 ;; the result we want.
2743 ;; Use Kummer's transformation (A&S ) to get
2745 ;; M(n+1/2,m+3/2,z) = exp(z)*M(m-n+1,m+3/2,-z)
2747 ;; From A&S, we have
2749 ;; 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)
2751 ;; Apply Kummer's transformation again:
2753 ;; M(1,n+3/2,z) = exp(z)*M(n+1/2,n+3/2,-z)
2755 ;; Apply the differentiation formula again:
2757 ;; 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)
2759 ;; And we know that M(1/2,3/2,z) can be expressed in terms of erf.
2763 ;; Since n <= m, apply the differentiation formula:
2765 ;; 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)
2767 ;; Apply Kummer's transformation:
2769 ;; M(1/2,m-n+3/2,z) = exp(z)*M(m-n+1,m-n+3/2,z)
2771 ;; Apply the differentiation formula again:
2773 ;; 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)
2775 ;; I think this routine uses Method 2.
2776 (defun thno33 (n m x
)
2777 ;; 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)
2778 ;; M(1/2,m-n+3/2,z) = exp(z)*M(m-n+1,m-n+3/2,-z)
2779 ;; 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)
2780 ;; diff(M(1,3/2,z),z,m-n) = (-1)^(m-n)*diff(M(1,3/2,-z),z,m-n)
2781 ;; M(1,3/2,-z) = exp(-z)*M(1/2,3/2,z)
2782 (let* ((yannis (gensym))
2784 ;; poch(m-n+3/2,n)/poch(1/2,n)
2785 (factor1 (div (take '($pochhammer
) (add m-n
'((rat simp
) 3 2)) n
)
2786 (take '($pochhammer
) '((rat simp
) 1 2) n
)))
2787 ;; poch(3/2,m-n)/poch(1,m-n)
2788 (factor2 (div (take '($pochhammer
) '((rat simp
) 3 2) m-n
)
2789 (take '($pochhammer
) 1 m-n
)))
2790 ;; M(1,3/2,-z) = exp(-z)*M(1/2,3/2,z)
2791 (hgferf (mul (power '$%e
(mul -
1 yannis
))
2792 (hyprederf yannis
)))
2793 ;; diff(M(1,3/2,z),z,m-n)
2794 (diff1 (meval (list '($diff
) hgferf yannis m-n
)))
2795 ;; exp(z)*M(m-n+1,m-n+3/2,-z)
2796 (kummer (mul (power '$%e yannis
) diff1
))
2797 ;; diff(M(1/2,m-n+3/2,z),z,n)
2798 (diff2 (meval (list '($diff
) kummer yannis n
))))
2799 ;; Multiply all the terms together.
2800 (sratsimp (mul (power -
1 m-n
)
2803 (maxima-substitute x yannis diff2
)))))
2805 ;; M(n+1/2,m+3/2,z), with n < 0 and m > 0
2807 ;; Let's write it more explicitly as M(-n+1/2,m+3/2,z) with n > 0 and
2810 ;; First, use Kummer's transformation to get
2812 ;; M(-n+1/2,m+3/2,z) = exp(z)*M(m+n+1,m+3/2,-z)
2816 ;; 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)
2820 ;; diff(M(1,3/2,z),z,m) = poch(1,m)/poch(3/2,m)*M(m+1,m+3/2,z)
2822 ;; Thus, we can compute M(-n+1/2,m+3/2,z) from M(1,3/2,z).
2824 ;; The second formula above can be derived easily by multiplying the
2825 ;; series for M(m+1,m+3/2,z) by z^(n+m) and differentiating n times.
2827 (defun thno34 (n m x
)
2828 (let ((yannis (gensym)))
2834 (div (mul (take '($pochhammer
) '((rat simp
) 3 2) m
)
2835 (power '$%e yannis
))
2836 (mul (take '($pochhammer
) 1 m
)
2837 (take '($pochhammer
) (1+ m
) n
)
2839 (meval (list '($diff
)
2840 (mul (power yannis
(+ m n
))
2841 (meval (list '($diff
)
2850 ;; M(n+1/2,m+3/2,z), with n < 0 and m < 0
2852 ;; Write it more explicitly as M(-n+1/2,-m+3/2,z) with n > 0 and m >
2857 ;; 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).
2859 ;; Apply Kummer's transformation:
2861 ;; M(-n+1/2,3/2,z) = exp(z) * M(n+1,3/2,-z)
2865 ;; diff(z^n*M(1,3/2,z),z,n) = n!*M(n+1,3/2,z)
2867 ;; So we can express M(-n+1/2,-m+3/2,z) in terms of M(1,3/2,z).
2869 ;; The first formula above follows from the more general formula
2871 ;; diff(z^(b-1)*M(a,b,z),z,n) = poch(b-n,n)*z^(b-n-1)*M(a,b-n,z)
2873 ;; The last formula follows from the general result
2875 ;; diff(z^(a+n-1)*M(a,b,z),z,n) = poch(a,n)*z^(a-1)*M(a+n,b,z)
2877 ;; Both of these are easily derived by using the series for M and
2880 (defun thno35 (n m x
)
2881 (let ((yannis (gensym)))
2886 (mul (div (power yannis
(sub m
'((rat simp
) 1 2)))
2887 (mul (power -
1 (* -
1 m
))
2888 (take '($pochhammer
) 1 n
)
2889 (take '($pochhammer
) '((rat simp
) -
1 2) m
)))
2890 (meval (list '($diff
)
2891 (mul (power yannis
'((rat simp
) 1 2))
2893 (meval (list '($diff
)
2903 ;; Pochhammer symbol. fctrl(a,n) = a*(a+1)*(a+2)*...*(a+n-1).
2905 ;; N must be a positive integer!
2907 ;; FIXME: This appears to be identical to factf below.
2915 (fctrl a
(1- n
))))))
2920 (m2 exp
'(v freevarpar
)))
2923 (defun fpqform (arg-l1 arg-l2 arg
)
2925 (list '($%f simp array
) (length arg-l1
)(length arg-l2
))
2926 (append (list '(mlist simp
)) arg-l1
)
2927 (append (list '(mlist simp
)) arg-l2
)
2930 ;; Consider pFq([a_k]; [c_j]; z). If a_k = c_j + m for some k and j
2931 ;; and m >= 0, we can express pFq in terms of (p-1)F(q-1).
2933 ;; Here is a derivation for F(a,b;c;z), but it generalizes to the
2934 ;; generalized hypergeometric very easily.
2938 ;; diff(z^(a+n-1)*F(a,b;c;z), z, n) = poch(a,n)*z^(a-1)*F(a+n,b;c;z)
2940 ;; F(a+n,b;c;z) = diff(z^(a+n-1)*F(a,b;c;z), z, n)/poch(a,n)/z^(a-1)
2943 ;; So this expresses F(a+n,b;c;z) in terms of F(a,b;c;z). Let a = c +
2944 ;; n. This therefore gives F(c+n,b;c;z) in terms of F(c,b;c;z) =
2945 ;; 1F0(b;;z), which we know.
2947 ;; For simplicity, we will write F(z) for F(a,b;c;z).
2952 ;; diff(z^x*F(z),z,n) = sum binomial(n,k)*diff(z^x,z,n-k)*diff(F(z),z,k)
2955 ;; But diff(z^x,z,n-k) = x*(x-1)*...*(x-n+k+1)*z^(x-n+k)
2956 ;; = poch(x-n+k+1,n-k)*z^(x-n+k)
2960 ;; z^(-a+1)/poch(a,n)*diff(z^(a+n-1),z,n-k)
2961 ;; = poch(a+n-1-n+k+1,n-k)/poch(a,n)*z^(a+n-1-n+k)*z^(-a+1)
2962 ;; = poch(a+k,n-k)/poch(a,n)*z^k
2965 ;; Combining these we have
2968 ;; F(a+n,b;c;z) = sum z^k/poch(a,k)*binomial(n,k)*diff(F(a,b;c;z),z,k)
2971 ;; Since a = c, we have
2974 ;; F(a+n,b;a;z) = sum z^k/poch(a,k)*binomial(n,k)*diff(F(a,b;a;z),z,k)
2977 ;; But F(a,b;a;z) = F(b;;z) and it's easy to see that A&S 15.2.2 can
2978 ;; be specialized to this case to give
2980 ;; diff(F(b;;z),z,k) = poch(b,k)*F(b+k;;z)
2982 ;; Finally, combining all of these, we have
2985 ;; F(a+n,b;c;z) = sum z^k/poch(a,k)*binomial(n,k)*poch(b,k)*F(b+k;;z)
2988 ;; Thus, F(a+n,b;c;z) is expressed in terms of 1F0(b+k;;z), as desired.
2989 (defun splitpfq (l arg-l1 arg-l2
)
2990 (destructuring-bind (a1 k
)
2997 (arg-l1 (delete a1 arg-l1
:count
1 :test
#'equal
))
2998 (arg-l2 (delete b1 arg-l2
:count
1 :test
#'equal
)))
2999 (loop for count from
0 upto k
3002 (format t
"splitpfg term:~%")
3003 (maxima-display (mul (combin k count
)
3004 (div prodnum proden
)
3007 (mtell "F(~:M, ~:M)~%"
3008 (cons '(mlist) arg-l1
)
3009 (cons '(mlist) arg-l2
)))
3010 (setq result
(add result
3011 (mul (combin k count
)
3012 (div prodnum proden
)
3015 (hgfsimp arg-l1 arg-l2 var
))))
3016 (setq prod-b
(mul prod-b
(add b1 count
)))
3017 (setq prodnum
(mul prodnum
(mull arg-l1
))
3018 proden
(mul proden
(mull arg-l2
)))
3019 (setq arg-l1
(incr1 arg-l1
))
3020 (setq arg-l2
(incr1 arg-l2
)))
3023 ;; binomial(k,count)
3024 (defun combin (k count
)
3026 (mul (factorial count
)
3027 (factorial (sub k count
)))))
3030 ;; We have something like F(s+m,-s+n;c;z)
3031 ;; Rewrite it like F(a'+d,-a';c;z) where a'=s-n=-b and d=m+n.
3034 (let* ((sym (cdras 'f
(s+c a
)))
3035 (sign (cdras 'm
(m2 sym
'((mtimes) ((coefft) (m $numberp
)) ((coefft) (s nonnump
)))))))
3036 (when (and sign
(minusp sign
))
3038 (list nil
(mul -
1 b
) (add a b
))))
3041 ;;Algor. 2F1-RL from thesis:step 4:dispatch on a+m,-a+n,1/2+l cases
3042 (defun step4 (a b c
)
3043 ;; F(a,b;c;z) where a+b is an integer and c+1/2 is an integer. If a
3044 ;; and b are not integers themselves, we can derive the result from
3045 ;; F(a1,-a1;1/2;z). However, if a and b are integers, we can't use
3046 ;; that because F(a1,-a1;1/2;z) is a polynomial. We need to derive
3047 ;; the result from F(1,1;3/2;z).
3048 (if (and (hyp-integerp a
)
3053 (defun step4-a (a b c
)
3054 (let* ((alglist (algii a b
))
3055 (aprime (cadr alglist
))
3058 ($ratsimpexpons $true
)
3060 ;; At this point, we have F(a'+m,-a';1/2+n;z) where m and n are
3062 (cond ((hyp-integerp (add aprime
(inv 2)))
3063 ;; Ok. We have a problem if aprime + 1/2 is an integer.
3064 ;; We can't always use the algorithm below because we have
3065 ;; F(1/2,-1/2;1/2;z) which is 1F0(-1/2;;z) so the
3066 ;; derivation isn't quite right. Also, sometimes we'll end
3067 ;; up with a division by zero.
3069 ;; Thus, We need to do something else. So, use A&S 15.3.3
3070 ;; to change the problem:
3072 ;; F(a,b;c;z) = (1-z)^(c-a-b)*F(c-a, c-b; c; z)
3076 ;; 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)
3078 ;; Recall that a' + 1/2 is an integer. Thus we have
3079 ;; F(<int>,<int>,1/2+n;z), which we know how to handle in
3081 (gered1 (list a b
) (list c
) #'hgfsimp
))
3084 (cond ((equal (checksigntm var
) '$positive
)
3085 (trig-log-1-pos aprime
'ell
))
3086 ((equal (checksigntm var
) '$negative
)
3087 (trig-log-1-neg (mul -
1 aprime
) aprime
'ell
)))))
3088 ;; Ok, this uses F(a,-a;1/2;z). Since there are 2 possible
3089 ;; representations (A&S 15.1.11 and 15.1.17), we check the sign
3090 ;; of the var (as done in trig-log-1) to select which form we
3091 ;; want to use. The original didn't and seemed to want to use
3092 ;; the negative form.
3094 ;; With this change, F(a,-a;3/2;z) matches what A&S 15.2.6 would
3095 ;; produce starting from F(a,-a;1/2;z), assuming z < 0.
3101 ;; F(a,b;c;z), where a and b are (positive) integers and c = 1/2+l.
3102 ;; This can be computed from F(1,1;3/2;z).
3104 ;; Assume a < b, without loss of generality.
3106 ;; F(m,n;3/2+L;z), m < n.
3108 ;; We start from F(1,1;3/2;z). Use A&S 15.2.3, differentiating m
3109 ;; times to get F(m,1;3/2;z). Swap a and b to get F(m,1;3/2;z) =
3110 ;; F(1,m;3/2;z) and use A&S 15.2.3 again to get F(n,m;3/2;z) by
3111 ;; differentiating n times. Finally, if L < 0, use A&S 15.2.4.
3112 ;; Otherwise use A&S 15.2.6.
3114 ;; I (rtoy) can't think of any way to do this with less than 3
3115 ;; differentiations.
3117 ;; Note that if d = (n-m)/2 is not an integer, we can write F(m,n;c;z)
3118 ;; as F(-d+u,d+u;c;z) where u = (n+m)/2. In this case, we could use
3119 ;; step4-a to compute the result.
3122 ;; Transform F(a,b;c;z) to F(a+n,b;c;z), given F(a,b;c;z)
3123 (defun as-15.2
.3 (a bb cx n arg fun
)
3124 (declare (ignore bb cx
))
3127 ;; F(a+n,b;c;z) = z^(1-a)/poch(a,n)*diff(z^(a+n-1)*fun,z,n)
3128 (mul (inv (factf a n
))
3129 (power arg
(sub 1 a
))
3130 ($diff
(mul (power arg
(sub (add a n
) 1))
3134 ;; Transform F(a,b;c;z) to F(a,b;c-n;z), given F(a,b;c;z)
3135 (defun as-15.2
.4 (axax bb c n arg fun
)
3136 (declare (ignore axax bb
))
3139 ;; F(a,b;c-n;z) = 1/poch(c-n,n)/z^(c-n-1)*diff(z^(c-1)*fun,z,n)
3140 (mul (inv (factf (sub c n
) n
))
3141 (inv (power arg
(sub (sub c n
) 1)))
3142 ($diff
(mul (power arg
(sub c
1))
3146 ;; Transform F(a,b;c;z) to F(a-n,b;c;z), given F(a,b;c;z)
3147 (defun as-15.2
.5 (a b c n arg fun
)
3149 ;; F(a-n,b;c;z) = 1/poch(c-a,n)*z^(1+a-c)*(1-z)^(c+n-a-b)
3150 ;; *diff(z^(c-a+n-1)*(1-z)^(a+b-c)*F(a,b;c;z),z,n)
3152 (mul (inv (factf (sub c a
) n
))
3153 (power arg
(sub (add a
1) c
))
3155 (sub (add c n
) (add a b
)))
3156 ($diff
(mul (power arg
(sub (add c n
)
3163 ;; Transform F(a,b;c;z) to F(a,b;c+n;z), given F(a,b;c;z)
3164 (defun as-15.2
.6 (a b c n arg fun
)
3166 ;; F(a,b;c+n;z) = poch(c,n)/poch(c-a,n)/poch(c-b,n)*(1-z)^(c+n-a-b)
3167 ;; *diff((1-z)^(a+b-c)*fun,z,n)
3170 (inv (factf (sub c a
) n
))
3171 (inv (factf (sub c b
) n
))
3172 (inv (power (sub 1 arg
) (sub (add a b
)
3174 ($diff
(mul (power (sub 1 arg
) (sub (add a b
) c
))
3178 ;; Transform F(a,b;c;z) to F(a+n, b; c+n; z)
3179 (defun as-15.2
.7 (a b c n arg fun
)
3181 ;; F(a+n,b;c+n;z) = (-1)^n*poch(c,n)/poch(a,n)/poch(c-b,n)*(1-z)^(1-a)
3182 ;; *diff((1-z)^(a+n-1)*fun, z, n)
3187 (inv (factf (sub c b
) n
))
3188 (power (sub 1 arg
) (sub 1 a
))
3189 ($diff
(mul (power (sub 1 arg
) (sub (add a n
) 1))
3193 ;; Transform F(a,b;c;z) to F(a-n, b; c-n; z)
3194 (defun as-15.2
.8 (axax b c n arg fun
)
3196 ;; F(a-n,b;c-n;z) = 1/poch(c-n,n)/(z^(c-n-1)*(1-z)^(b-c))
3197 ;; *diff(z^(c-1)*(1-z^(b-c+n)*fun, z, n))
3198 (declare (ignore axax
))
3200 (mul (inv (factf (sub c n
) n
))
3201 (inv (mul (power arg
(sub (sub c n
) 1))
3202 (power (sub 1 arg
) (sub b c
))))
3203 ($diff
(mul (power arg
(sub c
1))
3204 (power (sub 1 arg
) (add (sub b c
) n
))
3208 ;; Transform F(a,b;c;z) to F(a+n,b+n;c+n;z)
3209 (defun as-15.2
.2 (a b c n arg fun
)
3211 ;; F(a+n,b+n; c+n;z) = poch(c,n)/poch(a,n)/poch(b,n)
3219 ;; Transform F(a,b;c;z) to F(a-n,b-n;c-n;z)
3220 (defun as-15.2
.9 (a b c n arg fun
)
3222 ;; F(a-n,b-n; c-n;z) = 1/poch(c-n,n)/(z^(c-n-1)*(1-z)^(a+b-c-n))
3223 ;; *diff(z^(c-1)*(1-z)^(a+b-c)*fun, z, n)
3225 (mul (inv (factf (sub c n
) n
))
3226 (inv (mul (power arg
(sub (sub c n
) 1))
3227 (power (sub 1 arg
) (sub (add a b
)
3229 ($diff
(mul (power arg
(sub c
1))
3230 (power (sub 1 arg
) (sub (add a b
) c
))
3234 (defun step4-int (a b c
)
3237 (let* ((s (gensym (symbol-name '#:step4-var-
)))
3241 (res (cond ((eq (checksigntm var
) '$negative
)
3243 ;; -%i*log(%i*sqrt(zn)+sqrt(1-zn))/(sqrt(1-zn)*sqrt(zn))
3245 (let ((root1-z (power (sub 1 s
) (inv 2)))
3246 (rootz (power s
(inv 2))))
3248 (mlog (add (mul '$%i rootz
)
3253 ;; F(1,1;3/2;z) = asin(sqrt(zp))/(sqrt(1-zp)*sqrt(zp))
3255 (let ((root1-z (power (sub 1 s
) (inv 2)))
3256 (rootz (power s
(inv 2))))
3260 ;; Start with res = F(1,1;3/2;z). Compute F(m,1;3/2;z)
3261 (setf res
(as-15.2
.3 1 1 3//2 m s res
))
3262 ;; We now have res = C*F(m,1;3/2;z). Compute F(m,n;3/2;z)
3263 (setf res
(as-15.2
.3 1 a
3//2 n s res
))
3264 ;; We now have res = C*F(m,n;3/2;z). Now compute F(m,n;3/2+ell;z):
3267 (as-15.2
.4 a b
3//2 (- ell
) s res
))
3269 (as-15.2
.6 a b
3//2 ell s res
)))))))
3271 ;;Pattern match for s(ymbolic) + c(onstant) in parameter
3273 (m2 exp
'((mplus) ((coeffpt)(f nonnump
)) ((coeffpp)(c $numberp
)))))
3276 (cond ((not ($numberp z
)) t
)
3279 ;;Algor. III from thesis:determines which Differ. Formula to use
3280 (defun algiii (fun m n aprime
)
3283 (cond ((and (nni m
) (nni n
))
3285 (f81 fun m n aprime
))
3287 (f85 fun mm nn aprime
))))
3288 ((and (hyp-negp n
) (hyp-negp m
))
3289 (cond ((> (abs m
) (abs n
))
3290 (f86 fun mm nn aprime
))
3292 (f82 fun mm nn aprime
))))
3293 ((and (hyp-negp m
) (nni n
))
3294 (f83 fun mm nn aprime
))
3296 (f84 fun mm nn aprime
)))))
3298 ;; Factorial function:x*(x+1)*(x+2)...(x+n-1)
3300 ;; FIXME: This appears to be identical to fctrl above
3303 (t (mul x
(factf (add x
1) (sub n
1))))))
3305 ;;Formula #85 from Yannis thesis:finds by differentiating F[2,1](a,b,c,z)
3306 ;; given F[2,1](a+m,b,c+n,z) where b=-a and c=1/2, n,m integers
3308 ;; Like F81, except m > n.
3310 ;; F(a+m,-a;c+n;z), m > n, c = 1/2, m and n are non-negative integers
3313 ;; 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)
3316 ;; diff((1-z)^(a+m-1)*F(a+m-n,-a;1/2;z),z,n)
3317 ;; = (-1)^n*poch(a+m-n,n)*poch(1/2+a,n)/poch(1/2,n)*(1-z)^(a+m-n-1)
3318 ;; * F(a+m,-a;1/2+n;z)
3320 (defun f85 (fun m n a
)
3321 (mul (factf (inv 2) n
)
3323 (inv (factf (sub (add a m
)
3326 (inv (factf (sub (inv 2)
3329 (inv (factf a
(- m n
)))
3330 (power (sub 1 'ell
) (sub (sub (add 1 n
) m
) a
))
3331 ($diff
(mul (power (sub 1 'ell
) (sub (add a m
) 1))
3332 (power 'ell
(sub 1 a
))
3333 ($diff
(mul (power 'ell
(sub (add a m -
1) n
))
3338 ;;Used to find negative things that are not integers,eg RAT's
3340 (cond ((equal (asksign x
) '$negative
)
3344 ;; F(a,-a+m; c+n; z) where m,n are non-negative integers, m < n, c = 1/2.
3347 ;; diff((1-z)^(a+b-c)*F(a,b;c;z),z,n)
3348 ;; = poch(c-a,n)*poch(c-b,n)/poch(c,n)*(1-z)^(a+b-c-n)*F(a,b;c+n;z)
3351 ;; diff((1-z)^(a+m-1))*F(a,b;c;z),z,m)
3352 ;; = (-1)^m*poch(a,m)*poch(c-b,m)/poch(c,m)*(1-z)^(a-1)*F(a+m,b;c+m;z)
3354 ;; Rewrite F(a,-a+m; c+n;z) as F(-a+m, a; c+n; z). Then apply 15.2.6
3355 ;; to F(-a,a;1/2;z), differentiating n-m times:
3357 ;; diff((1-z)^(-1/2)*F(-a,a;1/2;z),z,n-m)
3358 ;; = 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)
3360 ;; Multiply this result by (1-z)^(n-a-1/2) and apply 15.2.7, differentiating m times:
3362 ;; diff((1-z)^(m-a-1)*F(-a,a;1/2+n-m;z),z,m)
3363 ;; = (-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)
3365 ;; Which gives F(-a+m,a;1/2+n;z), which is what we wanted.
3366 (defun f81 (fun m n a
)
3367 (mul (factf (add (inv 2) (- n m
)) m
)
3368 (factf (inv 2) (- n m
))
3371 (inv (factf (add (inv 2) n
(sub a m
)) m
))
3372 (inv (factf (sub (inv 2) a
) (- n m
)))
3373 (inv (factf (add (inv 2) a
) (- n m
)))
3374 (power (sub 1 'ell
) (sub 1 a
))
3375 ($diff
(mul (power (sub 1 'ell
) (add a n
(inv -
2)))
3376 ($diff
(mul (power (sub 1 'ell
) (inv -
2))
3381 ;; Like f86, but |n|>=|m|
3383 ;; F(a-m,-a;1/2-n;z) where n >= m >0
3386 ;; diff(z^(c-1)*F(a,b;c;z),z,n)
3387 ;; = poch(c-n,n)*z^(c-n-1)*F(a;b;c-n;z)
3390 ;; diff(z^(c-1)*(1-z)^(b-c+n)*F(a,b;c;z),z,n)
3391 ;; = poch(c-n,n)*z^(c-n-1)*(1-z)^(b-c)*F(a-n,b;c-n;z)
3395 ;; diff(z^(-1/2)*F(a,-a;1/2;z),z,n-m)
3396 ;; = poch(1/2-n+m,n-m)*z^(m-n-1/2)*F(a,-a;1/2-n+m;z)
3398 ;; diff(z^(m-n-1/2)*(1-z)^(n-a-1/2)*F(a,-a;1/2-n+m;z),z,m)
3399 ;; = poch(1/2-n,m)*z^(-1/2-n)*(1-z)^(n-m-a-1/2)*F(a-m,-a;1/2-n;z)
3403 ;; G(z) = z^(m-n-1/2)*F(a,-a;1/2-n+m;z)
3404 ;; = 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)
3406 ;; F(a-m,-a;1/2-n;z)
3407 ;; = 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)
3408 (defun f82 (fun m n a
)
3409 (mul (inv (factf (sub (inv 2) n
) m
))
3410 (inv (factf (sub (add (inv 2) m
) n
) (- n m
)))
3411 (power 'ell
(add n
(inv 2)))
3412 (power (sub 1 'ell
) (sub (add m
(inv 2) a
) n
))
3413 ($diff
(mul (power (sub 1 'ell
)
3414 (sub (sub n a
) (inv 2)))
3415 ($diff
(mul (power 'ell
(inv -
2)) fun
)
3421 ;; F(a+m,-a;1/2+n;z) with m,n integers and m < 0, n >= 0
3423 ;; Write this more clearly as F(a-m,-a;1/2+n;z), m > 0, n >= 0
3424 ;; or equivalently F(a-m,-a;c+n;z)
3427 ;; diff((1-z)^(-1/2)*F(a,-a;1/2;z),z,n)
3428 ;; = poch((1/2+a,n)*poch(1/2-a,n)/poch(1/2,n)*(1-z)^(-1/2-n)
3429 ;; * F(a,-a;1/2+n;z)
3432 ;; diff(z^(n+m-a-1/2)*(1-z)^(-1/2-n)*F(a,-a;1/2+n;z),z,m)
3433 ;; = 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)
3434 ;; = 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)
3436 ;; (1-z)^(-1/2-n)*F(a,-a;1/2+n;z)
3437 ;; = 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)
3439 ;; F(a-m,-a;1/2+n;z)
3440 ;; = (1-z)^(n+m+1/2)*z^(a-n+1/2)/poch(1/2+n-a,m)
3441 ;; *diff(z^(n+m-a-1/2)*(1-z)^(-1/2-n)*F(a,-a;1/2+n;z),z,m)
3442 (defun f83 (fun m n a
)
3443 (mul (factf (inv 2) n
)
3444 (inv (factf (sub (inv 2) a
) n
))
3445 (inv (factf (sub (add n
(inv 2)) a
) m
))
3446 (inv (factf (add (inv 2) a
) n
))
3447 (power (sub 1 'ell
) (add m n
(inv 2)))
3448 (power 'ell
(add (sub a n
) (inv 2)))
3449 ($diff
(mul (power 'ell
(sub (sub (+ m n
) a
) (inv 2)))
3450 ($diff
(mul (power (sub 1 'ell
)
3458 ;; The last case F(a+m,-a;c+n;z), m,n integers, m >= 0, n < 0
3460 ;; F(a+m,-a;1/2-n;z)
3463 ;; 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)
3466 ;; diff(z^(a+m-1)*F(a,b;c;z),z,m) = poch(a,n)*z^(a-1)*F(a+n,b;c;z)
3470 ;; 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)
3472 ;; 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)
3473 (defun f84 (fun m n a
)
3474 (mul (inv (mul (factf a m
)
3475 (factf (sub (inv 2) n
) n
)))
3476 (power 'ell
(sub 1 a
))
3477 ($diff
(mul (power 'ell
(sub (add a m n
) (inv 2)))
3478 ($diff
(mul (power 'ell
(inv -
2)) fun
)
3484 ;; Like f82, but |n|<|m|
3486 ;; F(a-m,-a;1/2-n;z), 0 < n < m
3489 ;; diff(z^(c-a+n-1)*(1-z)^(a+b-c)*F(a,b;c;z),z,n)
3490 ;; = poch(c-a,n)*z^(c-a-1)*(1-z)^(a+b-c-n)*F(a-n,b;c;z)
3493 ;; diff(z^(c-1)*(1-z)^(b-c+n)*F(a,b;c;z),z,n)
3494 ;; = poch(c-n,n)*z^(c-n-1)*(1-z)^(b-c)*F(a-n,b;c-n;z)
3498 ;; diff(z^(-a+m-n-1/2)*(1-z)^(-1/2)*F(a,-a;1/2;z),z,m-n)
3499 ;; = poch(1/2-a,m-n)*z^(-a-1/2)*(1-z)^(-1/2-m+n)*F(a-m+n,-a;1/2;z)
3501 ;; diff(z^(-1/2)*(1-z)^(-a-1/2+n)*F(a-m+n,-a;1/2;z),z,n)
3502 ;; = poch(1/2-n,n)*z^(-n-1/2)*(1-z)^(-a-1/2)*F(a-m,-a;1/2-n;z)
3504 ;; G(z) = z^(-a-1/2)*(1-z)^(-1/2-m+n)*F(a-m+n,-a;1/2;z)
3505 ;; = 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)
3507 ;; F(a-m,-a;1/2-n;z)
3508 ;; = z^(n+1/2)*(1-z)^(a+1/2)/poch(1/2-n,n)
3509 ;; *diff(z^(-1/2)*(1-z)^(-a-1/2+n)*F(a-m+n,-a;1/2;z),z,n)
3510 ;; = z^(n+1/2)*(1-z)^(a+1/2)/poch(1/2-n,n)
3511 ;; *diff(z^a*(1-z)^(m-a)*G(z),z,n)
3513 (defun f86 (fun m n a
)
3514 (mul (inv (mul (factf (sub (inv 2) n
) n
)
3515 (factf (sub (inv 2) a
) (- m n
))))
3516 (power 'ell
(add n
(inv 2)))
3517 (power (sub 1 'ell
) (add (inv 2) a
))
3518 ($diff
(mul (power 'ell a
)
3519 (power (sub 1 'ell
) (sub m a
))
3520 ($diff
(mul (power 'ell
3521 (sub (sub (sub m n
) (inv 2)) a
))
3528 ;; F(-1/2+n, 1+m; 1/2+l; z)
3529 (defun hyp-atanh (a b c
)
3530 ;; We start with F(-1/2,1;1/2;z) = 1-sqrt(z)*atanh(sqrt(z)). We can
3531 ;; derive the remaining forms by differentiating this enough times.
3533 ;; FIXME: Do we need to assume z > 0? We do that anyway, here.
3534 (let* ((s (gensym (symbol-name '#:hyp-atanh-
)))
3538 (res (sub 1 (mul (power s
'((rat simp
) 1 2))
3539 (take '(%atanh
) (power s
'((rat simp
) 1 2))))))
3543 ;; The total number of derivates we compute is n + m + ell. We
3544 ;; should do something to reduce the number of derivatives.
3547 (format t
"a ,b ,c = ~a ~a ~a~%" a b c
)
3548 (format t
"n, m, ell = ~a ~a ~a~%" n m ell
)
3549 (format t
"init a b c = ~a ~a ~a~%" new-a new-b new-c
))
3550 (cond ((alike1 (sub n ell
) 0)
3551 ;; n = ell so we can use A&S 15.2.7 or A&S 15.2.8
3553 (setf res
(as-15.2
.7 new-a new-b new-c n s res
)))
3555 (setf res
(as-15.2
.8 new-a new-b new-c
(- n
) s res
))))
3556 (setf new-a
(add new-a n
))
3557 (setf new-c
(add new-c n
)))
3559 ;; Adjust ell and then n. (Does order matter?)
3561 (setf res
(as-15.2
.6 new-a new-b new-c ell s res
))
3562 (setf new-c
(add new-c ell
)))
3564 (setf res
(as-15.2
.4 new-a new-b new-c
(- ell
) s res
))
3565 (setf new-c
(add new-c ell
))))
3568 (maxima-display res
)
3569 (format t
"new a b c = ~a ~a ~a~%" new-a new-b new-c
))
3572 (setf res
(as-15.2
.3 new-a new-b new-c n s res
))
3573 (setf new-a
(add new-a n
)))
3576 (setf res
(as-15.2
.5 new-a new-b new-c
(- n
) s res
))
3577 (setf new-a
(add new-a n
))))))
3580 (format t
"new a b c = ~a ~a ~a~%" new-a new-b new-c
)
3581 (maxima-display res
))
3582 ;; Finally adjust m by swapping the a and b parameters, since the
3583 ;; hypergeometric function is symmetric in a and b.
3585 (setf res
(as-15.2
.3 new-b new-a new-c m s res
))
3586 (setf new-b
(add new-b m
)))
3588 (setf res
(as-15.2
.5 new-b new-a new-c
(- m
) s res
))
3589 (setf new-b
(add new-b m
))))
3592 (format t
"new a b c = ~a ~a ~a~%" new-a new-b new-c
)
3593 (maxima-display res
))
3594 ;; Substitute the argument into the expression and simplify the result.
3595 (sratsimp (maxima-substitute var s res
))))
3599 (declare-top (unspecial var
))