Merge branch 'master' into gcl_cleanup
[maxima/cygwin.git] / src / gamma.lisp
blobbb86c3cedce6ed90d74c0a461759a00804d444a4
1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2 ;;;
3 ;;; Double Factorial, Incomplete Gamma function, ...
4 ;;;
5 ;;; This file will be extended with further functions related to the
6 ;;; Factorial and Gamma functions.
7 ;;;
8 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
9 ;;;
10 ;;; This file contains the following Maxima User functions:
11 ;;;
12 ;;; double_factorial(z)
13 ;;;
14 ;;; gamma_incomplete(a,z)
15 ;;; gamma_incomplete_generalized(a,z1,z2)
16 ;;; gamma_incomplete_regularized(a,z)
17 ;;;
18 ;;; log_gamma(z)
19 ;;;
20 ;;; erf(z)
21 ;;; erfc(z)
22 ;;; erfi(z)
23 ;;; erf_generalized(z1,z2)
24 ;;;
25 ;;; inverse_erf(z)
26 ;;; inverse_erfc(z)
27 ;;;
28 ;;; fresnel_s(z)
29 ;;; fresnel_c(z)
30 ;;;
31 ;;; beta_incomplete(a,b,z)
32 ;;; beta_incomplete_generalized(a,b,z1,z2)
33 ;;; beta_incomplete_regularized(a,b,z)
34 ;;;
35 ;;; Maxima User variable:
36 ;;;
37 ;;; $factorial_expand - Allows argument simplificaton for expressions like
38 ;;; double_factorial(n-1) and double_factorial(2*k+n)
39 ;;; $beta_expand - Switch on further expansions for the Beta functions
40 ;;;
41 ;;; $erf_representation - When T erfc, erfi, erf_generalized, fresnel_s
42 ;;; and fresnel_c are transformed to erf.
43 ;;; $erf_%iargs - Enable simplification of Erf and Erfi for
44 ;;; imaginary arguments
45 ;;; $hypergeometric_representation
46 ;;; - Enables transformation to a Hypergeometric
47 ;;; representation for fresnel_s and fresnel_c
48 ;;;
49 ;;; Maxima User variable (not definied in this file):
50 ;;;
51 ;;; $factlim - biggest integer for numerically evaluation
52 ;;; of the Double factorial
53 ;;; $gamma_expand - Expansion of the Gamma und Incomplete Gamma
54 ;;; function for some special cases
55 ;;;
56 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
57 ;;; This library is free software; you can redistribute it and/or modify it
58 ;;; under the terms of the GNU General Public License as published by the
59 ;;; Free Software Foundation; either version 2 of the License, or (at
60 ;;; your option) any later version.
61 ;;;
62 ;;; This library is distributed in the hope that it will be useful, but
63 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
64 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
65 ;;; Library General Public License for more details.
66 ;;;
67 ;;; You should have received a copy of the GNU General Public License along
68 ;;; with this library; if not, write to the Free Software
69 ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
70 ;;;
71 ;;; Copyright (C) 2008 Dieter Kaiser
72 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
74 (in-package :maxima)
76 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
78 (defmvar $erf_representation nil
79 "When T erfc, erfi and erf_generalized are transformed to erf.")
81 (defmvar $erf_%iargs nil
82 "When T erf and erfi simplifies for an imaginary argument.")
84 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
85 ;;; The following functions test if numerical evaluation has to be done.
86 ;;; The functions should help to test for numerical evaluation more consistent
87 ;;; and without complicated conditional tests including more than one or two
88 ;;; arguments.
89 ;;;
90 ;;; The functions take a list of arguments. All arguments have to be a CL or
91 ;;; Maxima number. If all arguments are numbers we have two cases:
92 ;;; 1. $numer is T we return T. The function has to be evaluated numerically.
93 ;;; 2. One of the args is a float or a bigfloat. Evaluate numerically.
94 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
96 ;;; Test for numerically evaluation in float precision
98 (defun float-numerical-eval-p (&rest args)
99 (let ((flag nil))
100 (dolist (ll args)
101 (when (not (float-or-rational-p ll))
102 (return-from float-numerical-eval-p nil))
103 (when (floatp ll) (setq flag t)))
104 (if (or $numer flag) t nil)))
106 ;;; Test for numerically evaluation in complex float precision
108 (defun complex-float-numerical-eval-p (&rest args)
109 "Determine if ARGS consists of numerical values by determining if
110 the real and imaginary parts of each arg are nuemrical (but not
111 bigfloats). A non-NIL result is returned if at least one of args is
112 a floating-point value or if numer is true. If the result is
113 non-NIL, it is a list of the arguments reduced via COMPLEX-NUMBER-P"
114 (let (flag values)
115 (dolist (ll args)
116 (multiple-value-bind (bool rll ill)
117 (complex-number-p ll 'float-or-rational-p)
118 (unless bool
119 (return-from complex-float-numerical-eval-p nil))
120 ;; Always save the result from complex-number-p. But for backward
121 ;; compatibility, only set the flag if any item is a float.
122 (push (add rll (mul ill '$%i)) values)
123 (setf flag (or flag (or (floatp rll) (floatp ill))))))
124 (when (or $numer flag)
125 ;; Return the values in the same order as the args!
126 (nreverse values))))
128 ;;; Test for numerically evaluation in bigfloat precision
130 (defun bigfloat-numerical-eval-p (&rest args)
131 (let ((flag nil))
132 (dolist (ll args)
133 (when (not (bigfloat-or-number-p ll))
134 (return-from bigfloat-numerical-eval-p nil))
135 (when ($bfloatp ll)
136 (setq flag t)))
137 (if (or $numer flag) t nil)))
139 ;;; Test for numerically evaluation in complex bigfloat precision
141 (defun complex-bigfloat-numerical-eval-p (&rest args)
142 "Determine if ARGS consists of numerical values by determining if
143 the real and imaginary parts of each arg are nuemrical (including
144 bigfloats). A non-NIL result is returned if at least one of args is
145 a floating-point value or if numer is true. If the result is
146 non-NIL, it is a list of the arguments reduced via COMPLEX-NUMBER-P."
148 (let (flag values)
149 (dolist (ll args)
150 (multiple-value-bind (bool rll ill)
151 (complex-number-p ll 'bigfloat-or-number-p)
152 (unless bool
153 (return-from complex-bigfloat-numerical-eval-p nil))
154 ;; Always save the result from complex-number-p. But for backward
155 ;; compatibility, only set the flag if any item is a bfloat.
156 (push (add rll (mul ill '$%i)) values)
157 (when (or ($bfloatp rll) ($bfloatp ill))
158 (setf flag t))))
159 (when (or $numer flag)
160 ;; Return the values in the same order as the args!
161 (nreverse values))))
163 ;;; Test for numerical evaluation in any precision, real or complex.
164 (defun numerical-eval-p (&rest args)
165 (or (apply 'float-numerical-eval-p args)
166 (apply 'complex-float-numerical-eval-p args)
167 (apply 'bigfloat-numerical-eval-p args)
168 (apply 'complex-bigfloat-numerical-eval-p args)))
170 ;;; Check for an integer or a float or bigfloat representation. When we
171 ;;; have a float or bigfloat representation return the integer value.
173 (defun integer-representation-p (x)
174 (let ((val nil))
175 (cond ((integerp x) x)
176 ((and (floatp x) (= 0 (nth-value 1 (truncate x))))
177 (nth-value 0 (truncate x)))
178 ((and ($bfloatp x)
179 (eq ($sign (sub (setq val ($truncate x)) x)) '$zero))
180 val)
181 (t nil))))
183 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
185 ;;; The changes to the parser to connect the operator !! to double_factorial(z)
187 ;(def-mheader |$!!| (%double_factorial))
189 ;(def-led (|$!!| 160.) (op left)
190 ; (list '$expr
191 ; (mheader '$!!)
192 ; (convert left '$expr)))
194 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
196 ;;; The implementation of the function Double factorial
198 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
200 ;;; Double factorial distributes over bags
202 (defprop %double_factorial (mlist $matrix mequal) distribute_over)
204 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
206 ;;; Double factorial has mirror symmetry
208 (defprop %double_factorial t commutes-with-conjugate)
210 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
212 ;;; Differentiation of Double factorial
214 (defprop %double_factorial
215 ((z)
216 ((mtimes)
217 ((rat) 1 2)
218 ((%double_factorial) z)
219 ((mplus)
220 ((%log) 2)
221 ((mqapply)
222 (($psi array) 0)
223 ((mplus) 1 ((mtimes) ((rat) 1 2) z)))
224 ((mtimes)
225 ((rat) 1 2) $%pi
226 ((%log) ((mtimes) 2 ((mexpt) $%pi -1)))
227 ((%sin) ((mtimes) $%pi z))))))
228 grad)
230 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
232 ;;; Double factorial is a simplifying function
234 (def-simplifier double_factorial (z)
235 (cond
236 ((and (fixnump z) (> z -1) (or (minusp $factlim) (< z $factlim)))
237 ;; Positive Integer less then $factlim or $factlim is -1. Call gfact.
238 (gfact z (floor (/ z 2)) 2))
240 ((and (mnump z)
241 (eq ($sign z) '$neg)
242 (zerop1 (sub (simplify (list '(%truncate) (div z 2))) (div z 2))))
243 ;; Even negative integer or real representation. Not defined.
244 (simp-domain-error
245 (intl:gettext
246 "double_factorial: double_factorial(~:M) is undefined.") z))
248 ((or (integerp z) ; at this point odd negative integer. Evaluate.
249 (complex-float-numerical-eval-p z))
250 (cond
251 ((and (integerp z) (= z -1)) 1) ; Special cases -1 and -3
252 ((and (integerp z) (= z -3)) -1)
254 ;; Odd negative integer, float or complex float.
255 (complexify
256 (double-factorial
257 (complex ($float ($realpart z)) ($float ($imagpart z))))))))
259 ((and (not (ratnump z))
260 (complex-bigfloat-numerical-eval-p z))
261 ;; bigfloat or complex bigfloat.
262 (bfloat-double-factorial
263 (add ($bfloat ($realpart z)) (mul '$%i ($bfloat ($imagpart z))))))
265 ;; double_factorial(inf) -> inf
266 ((eq z '$inf) '$inf)
268 ((and $factorial_expand
269 (mplusp z)
270 (integerp (cadr z)))
271 (let ((k (cadr z))
272 (n (simplify (cons '(mplus) (cddr z)))))
273 (cond
274 ((= k -1)
275 ;; Special case double_factorial(n-1)
276 ;; Not sure if this simplification is useful.
277 (div (simplify (list '(mfactorial) n))
278 (simplify (list '(%double_factorial) n))))
279 ((= k (* 2 (truncate (/ k 2))))
280 ;; Special case double_factorial(2*k+n), k integer
281 (setq k (/ k 2))
282 ($factor ; we get more simple expression when factoring
283 (mul
284 (power 2 k)
285 (simplify (list '($pochhammer) (add (div n 2) 1) k))
286 (simplify (list '(%double_factorial) n)))))
288 (give-up)))))
291 (give-up))))
293 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
294 ;;; Double factorial for a complex float argument. The result is a CL complex.
295 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
297 (defun double-factorial (z)
298 (let ((pival (float pi)))
300 (expt
301 (/ 2 pival)
302 (/ (- 1 (cos (* pival z))) 4))
303 (expt 2e0 (/ z 2))
304 (gamma-lanczos (+ 1 (/ z 2))))))
306 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
307 ;;; Double factorial for a bigfloat or complex bigfloat argument
308 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
310 (defun bfloat-double-factorial (z)
311 (let* ((pival ($bfloat '$%pi))
312 (bigfloat1 ($bfloat bigfloatone))
313 (bigfloat2 (add bigfloat1 bigfloat1))
314 (bigfloat4 (add bigfloat2 bigfloat2))
315 ($ratprint nil))
316 (cmul
317 (cpower
318 (cdiv bigfloat2 pival)
319 (cdiv (sub bigfloat1
320 (simplify (list '(%cos) (cmul pival z)))) bigfloat4))
321 (cmul
322 (cpower bigfloat2 (cdiv z bigfloat2))
323 (simplify (list '(%gamma) (add bigfloat1 (cdiv z bigfloat2))))))))
325 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
327 ;;; The implementation of the Incomplete Gamma function
329 ;;; A&S 6.5.3:
331 ;;; inf
332 ;;; /
333 ;;; [ a - 1 - t
334 ;;; gamma_incomplete(a, z) = I t %e dt
335 ;;; ]
336 ;;; /
337 ;;; z
339 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
341 (defvar *debug-gamma* nil)
343 ;; TODO: This is currently called by integrate-exp-special in sin.lisp.
344 ;; Need to fix that before this can be removed.
345 (defmfun $gamma_incomplete (a z)
346 (simplify (list '(%gamma_incomplete) a z)))
348 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
351 ;;; Incomplete Gamma distributes over bags
353 (defprop %gamma_incomplete (mlist $matrix mequal) distribute_over)
355 ;;; Incomplete Gamma function has not mirror symmetry for z on the negative
356 ;;; real axis. We support a conjugate-function which test this case.
358 (defprop %gamma_incomplete conjugate-gamma-incomplete conjugate-function)
360 (defun conjugate-gamma-incomplete (args)
361 (let ((a (first args)) (z (second args)))
362 (cond ((off-negative-real-axisp z)
363 ;; Definitely not on the negative real axis for z. Mirror symmetry.
364 (simplify
365 (list
366 '(%gamma_incomplete)
367 (simplify (list '($conjugate) a))
368 (simplify (list '($conjugate) z)))))
370 ;; On the negative real axis or no information. Unsimplified.
371 (list
372 '($conjugate simp)
373 (simplify (list '(%gamma_incomplete) a z)))))))
375 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
377 ;;; Derivative of the Incomplete Gamma function
379 (putprop '%gamma_incomplete
380 `((a z)
381 ,(lambda (a z)
382 (cond ((member ($sign a) '($pos $pz))
383 ;; The derivative wrt a in terms of hypergeometric_regularized 2F2
384 ;; function and the Generalized Incomplete Gamma function
385 ;; (functions.wolfram.com), only for a>0.
386 `((mplus)
387 ((mtimes)
388 ((mexpt) ((%gamma) ,a) 2)
389 ((mexpt) ,z ,a)
390 (($hypergeometric_regularized)
391 ((mlist) ,a ,a)
392 ((mlist) ((mplus) 1 ,a) ((mplus) 1 ,a))
393 ((mtimes) -1 ,z)))
394 ((mtimes) -1
395 ((%gamma_incomplete_generalized) ,a 0 ,z)
396 ((%log) ,z))
397 ((mtimes)
398 ((%gamma) ,a)
399 ((mqapply) (($psi array) 0) ,a))))
401 ;; No derivative. Maxima generates a noun form.
402 nil)))
403 ;; The derivative wrt z
404 ((mtimes) -1
405 ((mexpt) $%e ((mtimes) -1 z))
406 ((mexpt) z ((mplus) -1 a))))
407 'grad)
409 ;;; Integral of the Incomplete Gamma function
411 (defprop %gamma_incomplete
412 ((a z)
414 ((mplus)
415 ((mtimes) -1 ((%gamma_incomplete) ((mplus) 1 a) z))
416 ((mtimes) ((%gamma_incomplete) a z) z)))
417 integral)
419 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
421 ;;; We support a simplim%function. The function is looked up in simplimit and
422 ;;; handles specific values of the function.
424 (defprop %gamma_incomplete simplim%gamma_incomplete simplim%function)
426 (defun simplim%gamma_incomplete (expr var val)
427 ;; Look for the limit of the arguments.
428 (let ((a (limit (cadr expr) var val 'think))
429 (z (limit (caddr expr) var val 'think)))
430 (cond
432 ((eq z '$infinity) ;; http://dlmf.nist.gov/8.11#i
433 (cond ((and (zerop1 ($realpart (caddr expr)))
434 (eq ($csign (m+ -1 (cadr expr))) '$neg))
436 (t (throw 'limit t))))
438 ;; Handle an argument 0 at this place.
439 ((or (zerop1 z)
440 (eq z '$zeroa)
441 (eq z '$zerob))
442 (let ((sgn ($sign ($realpart a))))
443 (cond ((zerop1 a) '$inf)
444 ((member sgn '($neg $nz)) '$infinity)
445 ;; Call the simplifier of the function.
446 (t (simplify (list '(%gamma_incomplete) a z))))))
448 ;; All other cases are handled by the simplifier of the function.
449 (simplify (list '(%gamma_incomplete) a z))))))
451 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
453 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
455 ;;; Implementation of the Lower Incomplete gamma function:
457 ;;; A&S 6.5.2
459 ;;; z
460 ;;; /
461 ;;; [ a - 1 - t
462 ;;; gamma_incomplete_lower(a, z) = I t %e dt
463 ;;; ]
464 ;;; /
465 ;;; 0
466 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
467 ;;; distribute over bags (aggregates)
469 (defprop %gamma_incomplete_lower (mlist $matrix mequal) distribute_over)
471 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
473 ;; (defprop %gamma_incomplete_lower ??? grad) WHAT TO PUT HERE ??
476 ;; Handles some special cases for the order a and simplifies it to an
477 ;; equivalent form, possibly involving erf and gamma_incomplete_lower
478 ;; to a lower order.
479 (def-simplifier gamma_incomplete_lower (a z)
480 (cond
481 ((or
482 (float-numerical-eval-p a z)
483 (complex-float-numerical-eval-p a z)
484 (bigfloat-numerical-eval-p a z)
485 (complex-bigfloat-numerical-eval-p a z))
486 (ftake '%gamma_incomplete_generalized a 0 z))
487 ((gamma-incomplete-lower-expand a z))
489 (give-up))))
491 ;; Try to express gamma_incomplete_lower(a,z) in simpler terms for
492 ;; special values of the order a. If we can't, return NIL to say so.
493 (defun gamma-incomplete-lower-expand (a z)
494 (cond ((and $gamma_expand (integerp a) (eql a 1))
495 ;; gamma_incomplete_lower(0, x) = 1-exp(x)
496 (sub 1 (m^t '$%e (neg z))))
497 ((and $gamma_expand (integerp a) (plusp a))
498 ;; gamma_incomplete_lower(a,z) can be simplified if a is a
499 ;; positive integer. Since gamma_incomplete_lower(a,z) =
500 ;; gamma(a) - gamma_incomplete(a,z), use gamma_incomplete to
501 ;; get the result.
502 (sub (simpgamma (list '(%gamma) a) 1 nil)
503 (take '(%gamma_incomplete) a z)))
504 ((and $gamma_expand (alike1 a 1//2))
505 ;; A&S 6.5.12:
507 ;; gamma_incomplete_lower(1/2,x^2) = sqrt(%pi)*erf(x)
509 ;; gamma_incomplete_lower(1/2,z) = sqrt(%pi)*erf(sqrt(x))
511 (mul (power '$%pi '((rat simp) 1 2))
512 (take '(%erf) (power z '((rat simp) 1 2)))))
513 ((and $gamma_expand (mplusp a) (integerp (cadr a)))
514 ;; gamma_incomplete_lower(a+n,z), where n is an integer
515 (let ((n (cadr a))
516 (a (simplify (cons '(mplus) (cddr a)))))
517 (cond
518 ((> n 0)
519 ;; gamma_incomplete_lower(a+n,z). See DLMF 8.8.7:
521 ;; gamma_incomplete_lower(a+n,z)
522 ;; = pochhammer(a,n)*gamma_incomplete_lower(a,z)
523 ;; -z^a*exp(-z)*sum(gamma(a+n)gamma(a+k+1)*z^k,k,0,n-1)
524 (sub
525 (mul
526 (simplify (list '($pochhammer) a n))
527 (simplify (list '(%gamma_incomplete_lower) a z)))
528 (mul
529 (power z a)
530 (power '$%e (mul -1 z))
531 (let ((gamma-a+n (simpgamma (list '(%gamma) (add a n)) 1 nil))
532 (index (gensumindex))
533 ($simpsum t))
534 (simpsum1
535 (mul
536 (div gamma-a+n
537 (simpgamma (list '(%gamma) (add a index 1)) 1 nil))
538 (power z index))
539 index 0 (add n -1))))))
540 ((< n 0)
541 (setq n (- n))
542 ;; See DLMF 8.8.8. For simplicity let g(a,z) = gamma_incomplete_lower(a,z).
544 ;; g(a,z) = gamma(a)/gamma(a-n)*g(a-n,z)
545 ;; - z^(a-1)*exp(z)*sum(gamma(a)/gamma(a-k)*z^(-k),k,0,n-1)
547 ;; Rewrite:
548 ;; g(a-n,z) = gamma(a-n)/gamma(a)*g(a,z)
549 ;; + gamma(a-n)/gamma(a)*z^(a-1)*exp(-z)
550 ;; * sum(gamma(a)/gamma(a-k)*z^(-k),k,0,n-1)
551 ;; Or
552 ;; g(a-n,z) = gamma(a-n)/gamma(a)*g(a,z)
553 ;; + z^(a-1)*exp(-z)
554 ;; * sum(gamma(a-n)/gamma(a-k)*z^(-k),k,0,n-1)
555 (let ((gamma-a-n (simpgamma (list '(%gamma) (sub a n)) 1 nil))
556 (index (gensumindex))
557 ($simpsum t))
558 (add
559 (mul
560 (div gamma-a-n
561 (list '(%gamma) a))
562 (simplify (list '(%gamma_incomplete_lower) a z)))
563 (mul
564 (power z (sub a 1))
565 (power '$%e (mul -1 z))
566 (simpsum1
567 (mul
568 (div gamma-a-n
569 (simpgamma (list '(%gamma) (sub a index)) 1 nil))
570 (power z (mul -1 index)))
571 index 0 (add n -1)))))))))
572 ((and $gamma_expand (consp a) (eq 'rat (caar a))
573 (integerp (second a))
574 (integerp (third a)))
575 ;; gamma_incomplete_lower of (numeric) rational order.
576 ;; Expand it out so that the resulting order is between 0 and
577 ;; 1.
578 (multiple-value-bind (n order)
579 (floor (/ (second a) (third a)))
580 ;; a = n + order where 0 <= order < 1.
581 (let ((rat-order (rat (numerator order) (denominator order))))
582 (cond
583 ((zerop n)
584 ;; Nothing to do if the order is already between 0 and 1
585 (list '(%gamma_incomplete_lower simp) a z))
587 ;; Use gamma_incomplete(a+n,z) above. and then substitute
588 ;; a=order. This works for n positive or negative.
589 (let* ((ord (gensym))
590 (g (simplify (list '(%gamma_incomplete_lower) (add ord n) z))))
591 ($substitute rat-order ord g)))))))
593 ;; No expansion so return nil to indicate that
594 nil)))
596 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
598 ;;; Incomplete Gamma function is a simplifying function
600 (def-simplifier gamma_incomplete (a z)
601 (let (($simpsum t)
602 (ratorder))
603 (cond
605 ;; Check for specific values
607 ((zerop1 z)
608 ;; gamma_incomplete(v,0) is gamma(v) only if the realpart(v) >
609 ;; 0. If realpart(v) <= 0, gamma_incomplete is undefined. For
610 ;; all other cases, return the noun form.
611 (let ((sgn ($sign ($realpart a))))
612 (cond ((member sgn '($neg $zero))
613 (simp-domain-error
614 (intl:gettext
615 "gamma_incomplete: gamma_incomplete(~:M,~:M) is undefined.")
616 a z))
617 ((member sgn '($pos $pz)) ($gamma a))
618 (t (give-up)))))
620 ((eq z '$inf) 0)
621 ((and (eq z '$minf)
622 (eql a 0))
623 '$infinity)
625 ;; Check for numerical evaluation in Float or Bigfloat precision
627 ((float-numerical-eval-p a z)
628 (when *debug-gamma*
629 (format t "~&SIMP-GAMMA-INCOMPLETE in float-numerical-eval-p~%"))
630 ;; a and z are Maxima numbers, at least one has a float value
631 (let ((a ($float a))
632 (z ($float z)))
633 (cond
634 ((or (= a 0.0)
635 (and (= 0 (- a (truncate a)))
636 (< a 0.0)))
637 ;; a is zero or a negative float representing an integer.
638 ;; For these cases the numerical routines of gamma-incomplete
639 ;; do not work. Call the numerical routine for the Exponential
640 ;; Integral E(n,z). The routine is called with a positive integer!.
641 (setq a (truncate a))
642 (complexify (* (expt z a) (expintegral-e (- 1 a) z))))
644 (complexify (gamma-incomplete a z))))))
646 ((complex-float-numerical-eval-p a z)
647 (when *debug-gamma*
648 (format t
649 "~&SIMP-GAMMA-INCOMPLETE in complex-float-numerical-eval-p~%"))
650 ;; a and z are Maxima numbers, at least one is a complex value and
651 ;; we have at least one float part
652 (let ((ca (complex ($float ($realpart a)) ($float ($imagpart a))))
653 (cz (complex ($float ($realpart z)) ($float ($imagpart z)))))
654 (cond
655 ((and (= (imagpart ca) 0.0)
656 (or (= (realpart ca) 0.0)
657 (and (= 0 (- (realpart ca) (truncate (realpart ca))))
658 (< (realpart ca) 0.0))))
659 ;; Call expintegral-e. See comment above.
660 (setq ca (truncate (realpart ca)))
661 (complexify (* (expt cz ca) (expintegral-e (- 1 ca) cz))))
663 (complexify (gamma-incomplete ca cz))))))
665 ((bigfloat-numerical-eval-p a z)
666 (when *debug-gamma*
667 (format t "~&SIMP-GAMMA-INCOMPLETE in bigfloat-numerical-eval-p~%"))
668 (let ((a ($bfloat a))
669 (z ($bfloat z)))
670 (cond
671 ((or (eq ($sign a) '$zero)
672 (and (eq ($sign (sub a ($truncate a))) '$zero)
673 (eq ($sign a) '$neg)))
674 ;; Call bfloat-expintegral-e. See comment above.
675 (setq a ($truncate a))
676 ($rectform (mul (power z a) (bfloat-expintegral-e (- 1 a) z))))
678 (bfloat-gamma-incomplete a z)))))
680 ((complex-bigfloat-numerical-eval-p a z)
681 (when *debug-gamma*
682 (format t
683 "~&SIMP-GAMMA-INCOMPLETE in complex-bigfloat-numerical-eval-p~%"))
684 (let ((ca (add ($bfloat ($realpart a))
685 (mul '$%i ($bfloat ($imagpart a)))))
686 (cz (add ($bfloat ($realpart z))
687 (mul '$%i ($bfloat ($imagpart z))))))
688 (cond
689 ((and (eq ($sign ($imagpart ca)) '$zero)
690 (or (eq ($sign ($realpart ca)) '$zero)
691 (and (eq ($sign (sub ($realpart ca)
692 ($truncate ($realpart ca))))
693 '$zero)
694 (eq ($sign ($realpart ca)) '$neg))))
695 ;; Call bfloat-expintegral-e. See comment above.
696 (when *debug-gamma*
697 (format t
698 "~& bigfloat-numerical-eval-p calls bfloat-expintegral-e~%"))
699 (setq a ($truncate ($realpart a)))
700 ($rectform (mul (power cz a)
701 (bfloat-expintegral-e (- 1 a) cz))))
703 (complex-bfloat-gamma-incomplete ca cz)))))
705 ;; Check for transformations and argument simplification
707 ((and $gamma_expand (integerp a))
708 ;; Integer or a symbol declared to be an integer. Expand in a series.
709 (let ((sgn ($sign a)))
710 (cond
711 ((eq sgn '$zero)
712 (add
713 (mul -1
714 (simplify (list '(%expintegral_ei) (mul -1 z))))
715 (mul
716 '((rat simp) 1 2)
717 (sub
718 (simplify (list '(%log) (mul -1 z)))
719 (simplify (list '(%log) (div -1 z)))))
720 (mul -1 (simplify (list '(%log) z)))))
721 ((member sgn '($pos $pz))
722 (mul
723 (simplify (list '(%gamma) a))
724 (power '$%e (mul -1 z))
725 (let ((index (gensumindex)))
726 (simpsum1
727 (div
728 (power z index)
729 (let (($gamma_expand nil))
730 ;; Simplify gamma, but do not expand to avoid division
731 ;; by zero.
732 (simplify (list '(%gamma) (add index 1)))))
733 index 0 (sub a 1)))))
734 ((member sgn '($neg $nz))
735 (sub
736 (mul
737 (div
738 (power -1 (add (mul -1 a) -1))
739 (simplify (list '(%gamma) (add (mul -1 a) 1))))
740 (add
741 (simplify (list '(%expintegral_ei) (mul -1 z)))
742 (mul
743 '((rat simp) -1 2)
744 (sub
745 (simplify (list '(%log) (mul -1 z)))
746 (simplify (list '(%log) (div -1 z)))))
747 (simplify (list '(%log) z))))
748 (mul
749 (power '$%e (mul -1 z))
750 (let ((index (gensumindex)))
751 (simpsum1
752 (div
753 (power z (add index a -1))
754 (simplify (list '($pochhammer) a index)))
755 index 1 (mul -1 a))))))
756 (t (give-up)))))
758 ((and $gamma_expand (setq ratorder (max-numeric-ratio-p a 2)))
759 ;; We have a half integral order and $gamma_expand is not NIL.
760 ;; We expand in a series with the Erfc function
761 (setq ratorder (- ratorder (/ 1 2)))
762 (cond
763 ((equal ratorder 0)
764 (mul
765 (power '$%pi '((rat simp) 1 2))
766 (simplify (list '(%erfc) (power z '((rat simp) 1 2))))))
767 ((> ratorder 0)
768 (sub
769 (mul
770 (simplify (list '(%gamma) a))
771 (simplify (list '(%erfc) (power z '((rat simp) 1 2)))))
772 (mul
773 (power -1 (sub ratorder 1))
774 (power '$%e (mul -1 z))
775 (power z '((rat simp) 1 2))
776 (let ((index (gensumindex)))
777 (simpsum1
778 (mul -1 ; we get more simple results
779 (simplify ; when multiplying with -1
780 (list
781 '($pochhammer)
782 (sub '((rat simp) 1 2) ratorder)
783 (sub ratorder (add index 1))))
784 (power (mul -1 z) index))
785 index 0 (sub ratorder 1))))))
786 ((< ratorder 0)
787 (setq ratorder (- ratorder))
788 (sub
789 (div
790 (mul
791 (power -1 ratorder)
792 (power '$%pi '((rat simp) 1 2))
793 (simplify (list '(%erfc) (power z '((rat simp) 1 2)))))
794 (simplify (list '($pochhammer) '((rat simp) 1 2) ratorder)))
795 (mul
796 (power z (sub '((rat simp) 1 2) ratorder))
797 (power '$%e (mul -1 z))
798 (let ((index (gensumindex)))
799 (simpsum1
800 (div
801 (power z index)
802 (simplify
803 (list
804 '($pochhammer)
805 (sub '((rat simp) 1 2) ratorder)
806 (add index 1))))
807 index 0 (sub ratorder 1))))))))
809 ((and $gamma_expand (mplusp a) (integerp (cadr a)))
810 ;; Handle gamma_incomplete(a+n, z), where n is a numerical integer
811 (let ((n (cadr a))
812 (a (simplify (cons '(mplus) (cddr a)))))
813 (cond
814 ((> n 0)
815 ;; See DLMF 8.8.9: https://dlmf.nist.gov/8.8.E9
817 ;; gamma_incomplete(a+n,z) = pochhammer(a,n)*gamma_incomplete(a,z)
818 ;; + z^a*exp(-z)*sum(gamma(a+n)/gamma(a+k+1)*z^k,k,0,n-1)
819 (add
820 (mul
821 (simplify (list '($pochhammer) a n))
822 (simplify (list '(%gamma_incomplete) a z)))
823 (mul
824 (power '$%e (mul -1 z))
825 (power z a)
826 (let ((gamma-a+n (simpgamma (list '(%gamma) (add a n)) 1 nil))
827 (index (gensumindex)))
828 (simpsum1
829 (mul
830 (div gamma-a+n
831 (simpgamma (list '(%gamma) (add a index 1)) 1 nil))
832 (power z index))
833 index 0 (add n -1))))))
834 ((< n 0)
835 (setq n (- n))
836 ;; See http://functions.wolfram.com/06.06.17.0004.01
838 ;; gamma_incomplate(a,z) =
839 ;; (-1)^n*pochhammer(1-a,n)
840 ;; *[gamma_incomplete(a-n,z)
841 ;; + z^(a-n-1)*exp(-z)*sum(z^k/pochhammer(a-n,k),k,1,n)]
843 ;; Rerarrange this in terms of gamma_incomplete(a-n,z):
845 ;; gamma_incomplete(a-n,z) =
846 ;; (-1)^n*gamma_incomplete(a,z)/pochhammer(1-a,n)
847 ;; -z^(a-n-1)*exp(-z)*sum(z^k/pochhammer(a-n,k),k,1,n)
849 ;; Change the summation index to go from k = 0 to n-1:
851 ;; z^(a-n-1)*sum(z^k/pochhammer(a-n,k),k,1,n)
852 ;; = z^(a-n-1)*sum(z^(k+1)/pochhammer(a-n,k+1),k,0,n-1)
853 ;; = z^(a-n)*sum(z^k/pochhammer(a-n,k+1),k,0,n-1)
855 ;; Thuus:
856 ;; gamma_incomplete(a-n,z) =
857 ;; (-1)^n*gamma_incomplete(a,z)/pochhammer(1-a,n)
858 ;; -z^(a-n)*sum(z^k/pochhammer(a-n,k+1),k,0,n-1)
859 (sub
860 ;; (-1)^n*gamma_incomplete(a,z)/pochhammer(1-a,n)
861 (div
862 (mul
863 (power -1 n)
864 (simplify (list '(%gamma_incomplete) a z)))
865 (simplify (list '($pochhammer) (sub 1 a) n)))
866 (mul
867 (power '$%e (mul -1 z))
868 (power z (sub a n))
869 ;; sum(z^k/pochhammer(a-n,k+1),k,0,n-1)
870 (let ((index (gensumindex)))
871 (simpsum1
872 (div
873 (power z index)
874 (simplify (list '($pochhammer) (sub a n) (add index 1))))
875 index 0 (sub n 1)))))))))
876 ((and $gamma_expand (consp a) (eq 'rat (caar a))
877 (integerp (second a))
878 (integerp (third a)))
879 ;; gamma_incomplete of (numeric) rational order. Expand it out
880 ;; so that the resulting order is between 0 and 1.
881 (multiple-value-bind (n order)
882 (floor (/ (second a) (third a)))
883 ;; a = n + order where 0 <= order < 1.
884 (let ((rat-order (rat (numerator order) (denominator order))))
885 (cond
886 ((zerop n)
887 ;; Nothing to do if the order is already between 0 and 1
888 (give-up))
890 ;; Use gamma_incomplete(a+n,z) above. and then substitute
891 ;; a=order. This works for n positive or negative.
892 (let* ((ord (gensym))
893 (g (simplify (list '(%gamma_incomplete) (add ord n) z))))
894 ($substitute rat-order ord g)))))))
896 (t (give-up)))))
898 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
899 ;;; Numerical evaluation of the Incomplete Gamma function
901 ;;; gamma-incomplete (a,z) - real and complex double float
902 ;;; bfloat-gamma-incomplete (a z) - bigfloat
903 ;;; complex-bfloat-gamma-incomplete (a z) - complex bigfloat
905 ;;; Expansion in a power series for abs(x) < R, where R is
906 ;;; *gamma-radius* + real(a) if real(a) > 0 or *gamma-radius*
907 ;;; otherwise.
909 ;;; (A&S 6.5.29):
911 ;;; inf
912 ;;; ===
913 ;;; \ gamma(a)
914 ;;; gamma(a,z) = exp(-x)*z^a * > ------------ * z^n
915 ;;; / gamma(a+1+n)
916 ;;; ===
917 ;;; n=0
919 ;;; This expansion does not work for an integer a<=0, because the Gamma function
920 ;;; in the denominator is not defined for a=0 and negative integers. For this
921 ;;; case we use the Exponential Integral E for numerically evaluation. The
922 ;;; Incomplete Gamma function and the Exponential integral are connected by
924 ;;; gamma(a,z) = z^a * expintegral_e(1-a,z)
926 ;;; When the series is not used, two forms of the continued fraction
927 ;;; are used. When z is not near the negative real axis use the
928 ;;; continued fractions (A&S 6.5.31):
930 ;;; 1 1-a 1 2-a 2
931 ;;; gamma(a,z) = exp(-z) z^a *( -- --- --- --- --- ... )
932 ;;; z+ 1+ z+ 1+ z+
934 ;;; The accuracy is controlled by *gamma-incomplete-eps* for double float
935 ;;; precision. For bigfloat precision epsilon is 10^(-$fpprec). The expansions
936 ;;; in a power series or continued fractions stops if *gamma-incomplete-maxit*
937 ;;; is exceeded and an Maxima error is thrown.
939 ;;; The above fraction does not converge on the negative real axis and
940 ;;; converges very slowly near the axis. In this case, use the
941 ;;; relationship
943 ;;; gamma(a,z) = gamma(a) - gamma_lower(a,z)
945 ;;; The continued fraction for gamma_incomplete_lower(a,z) is
946 ;;; (http://functions.wolfram.com/06.06.10.0009.01):
948 ;;; gamma_lower(a,z) = exp(-z) * z^a / cf(a,z)
950 ;;; where
952 ;;; -a*z z (a+1)*z 2*z (a+2)*z
953 ;;; cf(a,z) = a + ---- ---- ------- ---- -------
954 ;;; a+1+ a+2- a+3+ a+4- a+5+
956 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
958 (defvar *gamma-incomplete-maxit* 10000)
959 (defvar *gamma-incomplete-eps* (* 2 flonum-epsilon))
960 (defvar *gamma-incomplete-min* 1.0e-32)
962 (defvar *gamma-radius* 1.0
963 "Controls the radius within a series expansion is done.")
964 (defvar *gamma-imag* 1.0)
966 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
968 ;;; The numerical evaluation for CL float or complex values a and x
969 ;;; When the flag regularized is T, the result is divided by gamma(a) and
970 ;;; Maxima returns the numercial result for gamma_incomplete_regularized
972 (defun gamma-incomplete (a x &optional (regularized nil))
973 (setq x (+ x (cond ((complexp x) #C(0.0 0.0)) ((realp x) 0.0))))
975 (let ((factor
976 ;; Compute the factor needed to scale the series or continued
977 ;; fraction. This is x^a*exp(-x) or x^a*exp(-x)/gamma(a)
978 ;; depending on whether we want a non-regularized or
979 ;; regularized form. We want to compute the factor carefully
980 ;; to avoid unnecessary overflow if possible.
981 (cond (regularized
982 (or (try-float-computation
983 #'(lambda ()
984 ;; gammafloat is more accurate for real
985 ;; values of a.
986 (cond ((complexp a)
987 (/ (* (expt x a) (exp (- x)))
988 (gamma-lanczos a)))
990 (/ (* (expt x a) (exp (- x)))
991 (gammafloat a))))))
992 ;; Easy way failed. Use logs to compute the
993 ;; result. This loses some precision, especially
994 ;; for large values of a and/or x because we use
995 ;; many bits to hold the exponent part.
996 (exp (- (* a (log x))
998 (log-gamma-lanczos (if (complexp a)
1000 (complex a)))))))
1002 (or (try-float-computation
1003 #'(lambda ()
1004 (* (expt x a) (exp (- x)))))
1005 ;; Easy way failed, so use the log form.
1006 (exp (- (* a (log x))
1007 x)))))))
1008 (multiple-value-bind (result lower-incomplete-tail-p)
1009 (%gamma-incomplete a x)
1010 (cond (lower-incomplete-tail-p
1011 ;; %gamma-incomplete compute the lower incomplete gamma
1012 ;; function, so we need to subtract that from gamma(a),
1013 ;; more or less.
1014 (cond (regularized
1015 (- 1 (* result factor)))
1016 ((complexp a)
1017 (- (gamma-lanczos a) (* result factor)))
1019 (- (gammafloat a) (* result factor)))))
1021 ;; Continued fraction used. Just multiply by the factor
1022 ;; to get the final result.
1023 (* factor result))))))
1025 ;; Compute the key part of the gamma incomplete function using either
1026 ;; a series expression or a continued fraction expression. Two values
1027 ;; are returned: the value itself and a boolean, indicating what the
1028 ;; computed value is. If the boolean non-NIL, then the computed value
1029 ;; is the lower incomplete gamma function.
1030 (defun %gamma-incomplete (a x)
1031 (let ((gm-maxit *gamma-incomplete-maxit*)
1032 (gm-eps *gamma-incomplete-eps*)
1033 (gm-min *gamma-incomplete-min*))
1034 (when *debug-gamma*
1035 (format t "~&GAMMA-INCOMPLETE with ~A and ~A~%" a x))
1036 (cond
1037 ;; The series expansion is done for x within a circle of radius
1038 ;; R, where R = *gamma-radius*+(realpart(a)) for realpart(a) > 0
1039 ;; and R = *gamma-radisu* for realpart(a) < 0. Otherwise a
1040 ;; continued fraction is used.
1041 ((and (> (abs x) (+ *gamma-radius*
1042 (if (> (realpart a) 0.0) (realpart a) 0.0))))
1043 (cond ((and (< (realpart x) 0)
1044 (< (abs (imagpart x))
1045 (* *gamma-imag* (abs (realpart x)))))
1046 ;; For x near the negative real axis, use the
1047 ;; relationship gamma_incomplete(a,z) = gamma(a) -
1048 ;; gamma_incomplete_lower(a,z), where
1049 ;; gamma_incomplete_lower(a,z) is the lower poart of the
1050 ;; incomplete gamma function. We can evaluate that
1051 ;; using a continued fraction from
1052 ;; http://functions.wolfram.com/06.06.10.0009.01. (Note
1053 ;; that the alternative fraction,
1054 ;; http://functions.wolfram.com/06.06.10.0007.01,
1055 ;; appears to be less accurate.)
1057 ;; Also note that this appears to be valid for all
1058 ;; values of x (real or complex), but we don't want to
1059 ;; use this everywhere for gamma_incomplete. Consider
1060 ;; what happens for large real x. gamma_incomplete(a,x)
1061 ;; is small, but gamma_incomplete(a,x) = gamma(x) - cf
1062 ;; will have large roundoff errors.
1063 (when *debug-gamma*
1064 (format t "~&GAMMA-INCOMPLETE in continued fractions for lower integral~%"))
1065 (let ((a (bigfloat:to a))
1066 (x (bigfloat:to x))
1067 (bigfloat::*debug-cf-eval* *debug-gamma*)
1068 (bigfloat::*max-cf-iterations* *gamma-incomplete-maxit*))
1069 (values (/ (bigfloat::lentz #'(lambda (n)
1070 (+ n a))
1071 #'(lambda (n)
1072 (if (evenp n)
1073 (* (ash n -1) x)
1074 (- (* (+ a (ash n -1)) x))))))
1075 t)))
1077 ;; Expansion in continued fractions for gamma_incomplete.
1078 (when *debug-gamma*
1079 (format t "~&GAMMA-INCOMPLETE in continued fractions~%"))
1080 (do* ((i 1 (+ i 1))
1081 (an (- a 1.0) (* i (- a i)))
1082 (b (+ 3.0 x (- a)) (+ b 2.0))
1083 (c (/ 1.0 gm-min))
1084 (d (/ 1.0 (- b 2.0)))
1085 (h d)
1086 (del 0.0))
1087 ((> i gm-maxit)
1088 (merror (intl:gettext "gamma_incomplete: continued fractions failed for gamma_incomplete(~:M, ~:M).") a x))
1089 (setq d (+ (* an d) b))
1090 (when (< (abs d) gm-min) (setq d gm-min))
1091 (setq c (+ b (/ an c)))
1092 (when (< (abs c) gm-min) (setq c gm-min))
1093 (setq d (/ 1.0 d))
1094 (setq del (* d c))
1095 (setq h (* h del))
1096 (when (< (abs (- del 1.0)) gm-eps)
1097 ;; Return nil to indicate we used the continued fraction.
1098 (return (values h nil)))))))
1100 ;; Expansion in a series
1101 (when *debug-gamma*
1102 (format t "~&GAMMA-INCOMPLETE in series~%"))
1103 (do* ((i 1 (+ i 1))
1104 (ap a (+ ap 1.0))
1105 (del (/ 1.0 a) (* del (/ x ap)))
1106 (sum del (+ sum del)))
1107 ((> i gm-maxit)
1108 (merror (intl:gettext "gamma_incomplete: series expansion failed for gamma_incomplete(~:M, ~:M).") a x))
1109 (when (< (abs del) (* (abs sum) gm-eps))
1110 (when *debug-gamma* (format t "~&Series converged.~%"))
1111 ;; Return T to indicate we used the series and the series
1112 ;; is for the integral from 0 to x, not x to inf.
1113 (return (values sum t))))))))
1115 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1117 ;;; This function is called for a and x real
1119 (defun bfloat-gamma-incomplete (a x)
1120 (let* ((gm-maxit *gamma-incomplete-maxit*)
1121 (gm-eps (power ($bfloat 10.0) (- $fpprec)))
1122 (gm-min (mul gm-eps gm-eps))
1123 ($ratprint nil))
1124 (cond
1125 ;; The series expansion is done for x within a circle of radius
1126 ;; R, where R = *gamma-radius*+(realpart(a)) for realpart(a) > 0
1127 ;; and R = *gamma-radisu* for realpart(a) < 0. Otherwise a
1128 ;; continued fraction is used.
1129 ((eq ($sign (sub (simplify (list '(mabs) x))
1130 (add *gamma-radius*
1131 (if (eq ($sign a) '$pos) a 0.0))))
1132 '$pos)
1133 (cond
1134 ((and (eq ($sign x) '$pos))
1135 ;; Expansion in continued fractions of the Incomplete Gamma function
1136 (do* ((i 1 (+ i 1))
1137 (an (sub a 1.0) (mul i (sub a i)))
1138 (b (add 3.0 x (mul -1 a)) (add b 2.0))
1139 (c (div 1.0 gm-min))
1140 (d (div 1.0 (sub b 2.0)))
1141 (h d)
1142 (del 0.0))
1143 ((> i gm-maxit)
1144 (merror (intl:gettext "gamma_incomplete: continued fractions failed for gamma_incomplete(~:M, ~:M).") a x))
1145 (when *debug-gamma*
1146 (format t "~&in continued fractions:~%")
1147 (mformat t "~& : i = ~M~%" i)
1148 (mformat t "~& : h = ~M~%" h))
1149 (setq d (add (mul an d) b))
1150 (when (eq ($sign (sub (simplify (list '(mabs) d)) gm-min)) '$neg)
1151 (setq d gm-min))
1152 (setq c (add b (div an c)))
1153 (when (eq ($sign (sub (simplify (list '(mabs) c)) gm-min)) '$neg)
1154 (setq c gm-min))
1155 (setq d (div 1.0 d))
1156 (setq del (mul d c))
1157 (setq h (mul h del))
1158 (when (eq ($sign (sub (simplify (list '(mabs) (sub del 1.0))) gm-eps))
1159 '$neg)
1160 (return
1161 (mul h
1162 (power x a)
1163 (power ($bfloat '$%e) (mul -1 x)))))))
1165 ;; Expand to multiply everything out.
1166 ($expand
1167 ;; Expansion in continued fraction for the lower incomplete gamma.
1168 (sub (simplify (list '(%gamma) a))
1169 ;; NOTE: We want (power x a) instead of bigfloat:expt
1170 ;; because this preserves how maxima computes x^a when
1171 ;; x is negative and a is rational. For, example
1172 ;; (-8)^(1/2) is -2. bigfloat:expt returns the
1173 ;; principal value.
1174 (mul (power x a)
1175 (power ($bfloat '$%e) (mul -1 x))
1176 (let ((a (bigfloat:to a))
1177 (x (bigfloat:to x)))
1178 (to (bigfloat:/
1179 (bigfloat:lentz
1180 #'(lambda (n)
1181 (bigfloat:+ n a))
1182 #'(lambda (n)
1183 (if (evenp n)
1184 (bigfloat:* (ash n -1) x)
1185 (bigfloat:- (bigfloat:* (bigfloat:+ a (ash n -1))
1186 x))))))))))))))
1189 ;; Series expansion of the Incomplete Gamma function
1190 (do* ((i 1 (+ i 1))
1191 (ap a (add ap 1.0))
1192 (del (div 1.0 a) (mul del (div x ap)))
1193 (sum del (add sum del)))
1194 ((> i gm-maxit)
1195 (merror (intl:gettext "gamma_incomplete: series expansion failed for gamma_incomplete(~:M, ~:M).") a x))
1196 (when *debug-gamma*
1197 (format t "~&GAMMA-INCOMPLETE in series:~%")
1198 (mformat t "~& : i = ~M~%" i)
1199 (mformat t "~& : ap = ~M~%" ap)
1200 (mformat t "~& : x/ap = ~M~%" (div x ap))
1201 (mformat t "~& : del = ~M~%" del)
1202 (mformat t "~& : sum = ~M~%" sum))
1203 (when (eq ($sign (sub (simplify (list '(mabs) del))
1204 (mul (simplify (list '(mabs) sum)) gm-eps)))
1205 '$neg)
1206 (when *debug-gamma* (mformat t "~&Series converged to ~M.~%" sum))
1207 (return
1208 (sub (simplify (list '(%gamma) a))
1209 ($rectform
1210 (mul sum
1211 (power x a)
1212 (power ($bfloat '$%e) (mul -1 x))))))))))))
1214 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1216 (defun complex-bfloat-gamma-incomplete (a x)
1217 (let* ((gm-maxit *gamma-incomplete-maxit*)
1218 (gm-eps (power ($bfloat 10.0) (- $fpprec)))
1219 (gm-min (mul gm-eps gm-eps))
1220 ($ratprint nil))
1221 (when *debug-gamma*
1222 (format t "~&COMPLEX-BFLOAT-GAMMA-INCOMPLETE~%")
1223 (format t " : a = ~A~%" a)
1224 (format t " : x = ~A~%" x))
1225 (cond
1226 ;; The series expansion is done for x within a circle of radius
1227 ;; R, where R = *gamma-radius*+(realpart(a)) for realpart(a) > 0
1228 ;; and R = *gamma-radisu* for realpart(a) < 0. Otherwise a
1229 ;; continued fraction is used.
1230 ((and (eq ($sign (sub (simplify (list '(mabs) x))
1231 (add *gamma-radius*
1232 (if (eq ($sign ($realpart a)) '$pos)
1233 ($realpart a)
1234 0.0))))
1235 '$pos))
1236 (cond
1237 ((not (and (eq ($sign ($realpart x)) '$neg)
1238 (eq ($sign (sub (simplify (list '(mabs) ($imagpart x)))
1239 (simplify (list '(mabs) ($realpart x)))))
1240 '$neg)))
1241 ;; Expansion in continued fractions of the Incomplete Gamma function
1242 (when *debug-gamma*
1243 (format t "~&in continued fractions:~%"))
1244 (do* ((i 1 (+ i 1))
1245 (an (sub a 1.0) (mul i (sub a i)))
1246 (b (add 3.0 x (mul -1 a)) (add b 2.0))
1247 (c (cdiv 1.0 gm-min))
1248 (d (cdiv 1.0 (sub b 2.0)))
1249 (h d)
1250 (del 0.0))
1251 ((> i gm-maxit)
1252 (merror (intl:gettext "gamma_incomplete: continued fractions failed for gamma_incomplete(~:M, ~:M).") a x))
1253 (setq d (add (cmul an d) b))
1254 (when (eq ($sign (sub (simplify (list '(mabs) d)) gm-min)) '$neg)
1255 (setq d gm-min))
1256 (setq c (add b (cdiv an c)))
1257 (when (eq ($sign (sub (simplify (list '(mabs) c)) gm-min)) '$neg)
1258 (setq c gm-min))
1259 (setq d (cdiv 1.0 d))
1260 (setq del (cmul d c))
1261 (setq h (cmul h del))
1262 (when (eq ($sign (sub (simplify (list '(mabs) (sub del 1.0)))
1263 gm-eps))
1264 '$neg)
1265 (return
1266 ($bfloat ; force evaluation of expressions with sin or cos
1267 (cmul h
1268 (cmul
1269 (cpower x a)
1270 (cpower ($bfloat '$%e) ($bfloat (mul -1 x))))))))))
1272 ;; Expand to multiply everything out.
1273 ($expand
1274 ;; Expansion in continued fraction for the lower incomplete gamma.
1275 (sub ($rectform (simplify (list '(%gamma) a)))
1276 ;; NOTE: We want (power x a) instead of bigfloat:expt
1277 ;; because this preserves how maxima computes x^a when
1278 ;; x is negative and a is rational. For, example
1279 ;; (-8)^(1/2) is -2. bigfloat:expt returns the
1280 ;; principal value.
1281 (mul ($rectform (power x a))
1282 ($rectform (power ($bfloat '$%e) (mul -1 x)))
1283 (let ((a (bigfloat:to a))
1284 (x (bigfloat:to x)))
1285 (to (bigfloat:/
1286 (bigfloat:lentz
1287 #'(lambda (n)
1288 (bigfloat:+ n a))
1289 #'(lambda (n)
1290 (if (evenp n)
1291 (bigfloat:* (ash n -1) x)
1292 (bigfloat:- (bigfloat:* (bigfloat:+ a (ash n -1))
1293 x))))))))))))))
1295 ;; Series expansion of the Incomplete Gamma function
1296 (when *debug-gamma*
1297 (format t "~&GAMMA-INCOMPLETE in series:~%"))
1298 (do* ((i 1 (+ i 1))
1299 (ap a (add ap 1.0))
1300 (del (cdiv 1.0 a) (cmul del (cdiv x ap)))
1301 (sum del (add sum del)))
1302 ((> i gm-maxit)
1303 (merror (intl:gettext "gamma_incomplete: series expansion failed for gamma_incomplete(~:M, ~:M).") a x))
1304 (when (eq ($sign (sub (simplify (list '(mabs) del))
1305 (mul (simplify (list '(mabs) sum)) gm-eps)))
1306 '$neg)
1307 (when *debug-gamma* (format t "~&Series converged.~%"))
1308 (return
1309 ($bfloat ; force evaluation of expressions with sin or cos
1310 (sub (simplify (list '(%gamma) a))
1311 (cmul sum
1312 (cmul
1313 (cpower x a)
1314 (cpower ($bfloat '$%e) (mul -1 x)))))))))))))
1316 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1318 ;;; Implementation of the Generalized Incomplete Gamma function
1320 ;;; z2
1321 ;;; /
1322 ;;; [ a - 1 - t
1323 ;;; gamma_incomplete_generalized(a, z1, z2) = I t %e dt
1324 ;;; ]
1325 ;;; /
1326 ;;; z1
1328 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1330 ;;; Generalized Incomplete Gamma function has not mirror symmetry for z1 or z2
1331 ;;; on the negative real axis.
1332 ;;; We support a conjugate-function which test this case.
1334 (defprop %gamma_incomplete_generalized
1335 conjugate-gamma-incomplete-generalized conjugate-function)
1337 (defun conjugate-gamma-incomplete-generalized (args)
1338 (let ((a (first args)) (z1 (second args)) (z2 (third args)))
1339 (cond ((and (off-negative-real-axisp z1) (off-negative-real-axisp z2))
1340 ;; z1 and z2 definitely not on the negative real axis.
1341 ;; Mirror symmetry.
1342 (simplify
1343 (list
1344 '(%gamma_incomplete_generalized)
1345 (simplify (list '($conjugate) a))
1346 (simplify (list '($conjugate) z1))
1347 (simplify (list '($conjugate) z2)))))
1349 ;; On the negative real axis or no information. Unsimplified.
1350 (list
1351 '($conjugate simp)
1352 (simplify (list '(%gamma_incomplete_generalized) a z1 z2)))))))
1354 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1356 ;;; Generalized Incomplete Gamma distributes over bags
1358 (defprop %gamma_incomplete_generalized (mlist $matrix mequal) distribute_over)
1360 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1362 ;;; Differentiation of Generalized Incomplete Gamma function
1364 (defprop %gamma_incomplete_generalized
1365 ((a z1 z2)
1366 ;; The derivative wrt a in terms of hypergeometric_regularized 2F2 function
1367 ;; and the Generalized Incomplete Gamma function (functions.wolfram.com)
1368 ((mplus)
1369 ((mtimes)
1370 ((mexpt) ((%gamma) a) 2)
1371 ((mexpt) z1 a)
1372 (($hypergeometric_regularized)
1373 ((mlist) a a)
1374 ((mlist) ((mplus) 1 a) ((mplus) 1 a))
1375 ((mtimes) -1 z1)))
1376 ((mtimes) -1
1377 ((mexpt) ((%gamma) a) 2)
1378 ((mexpt) z2 a)
1379 (($hypergeometric_regularized)
1380 ((mlist) a a)
1381 ((mlist) ((mplus) 1 a) ((mplus) 1 a))
1382 ((mtimes) -1 z2)))
1383 ((mtimes) -1
1384 ((%gamma_incomplete_generalized) a 0 z1)
1385 ((%log) z1))
1386 ((mtimes)
1387 ((%gamma_incomplete_generalized) a 0 z2)
1388 ((%log) z2)))
1389 ;; The derivative wrt z1
1390 ((mtimes) -1
1391 ((mexpt) $%e ((mtimes) -1 z1))
1392 ((mexpt) z1 ((mplus) -1 a)))
1393 ;; The derivative wrt z2
1394 ((mtimes)
1395 ((mexpt) $%e ((mtimes) -1 z2))
1396 ((mexpt) z2 ((mplus) -1 a))))
1397 grad)
1399 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1401 ;;; Generalized Incomplete Gamma function is a simplifying function
1403 (def-simplifier gamma_incomplete_generalized (a z1 z2)
1404 (let (($simpsum t))
1405 (cond
1407 ;; Check for specific values
1409 ((zerop1 z2)
1410 (let ((sgn ($sign ($realpart a))))
1411 (cond
1412 ((member sgn '($pos $pz))
1413 (sub
1414 (simplify (list '(%gamma_incomplete) a z1))
1415 (simplify (list '(%gamma) a))))
1417 (give-up)))))
1419 ((zerop1 z1)
1420 (let ((sgn ($sign ($realpart a))))
1421 (cond
1422 ((member sgn '($pos $pz))
1423 (sub
1424 (simplify (list '(%gamma) a))
1425 (simplify (list '(%gamma_incomplete) a z2))))
1427 (give-up)))))
1429 ((zerop1 (sub z1 z2)) 0)
1431 ((eq z2 '$inf) (simplify (list '(%gamma_incomplete) a z1)))
1432 ((eq z1 '$inf) (mul -1 (simplify (list '(%gamma_incomplete) a z2))))
1434 ;; Check for numerical evaluation in Float or Bigfloat precision
1435 ;; Use the numerical routines of the Incomplete Gamma function
1437 ((float-numerical-eval-p a z1 z2)
1438 (complexify
1439 (- (gamma-incomplete ($float a) ($float z1))
1440 (gamma-incomplete ($float a) ($float z2)))))
1442 ((complex-float-numerical-eval-p a z1 z2)
1443 (let ((ca (complex ($float ($realpart a)) ($float ($imagpart a))))
1444 (cz1 (complex ($float ($realpart z1)) ($float ($imagpart z1))))
1445 (cz2 (complex ($float ($realpart z2)) ($float ($imagpart z2)))))
1446 (complexify (- (gamma-incomplete ca cz1) (gamma-incomplete ca cz2)))))
1448 ((bigfloat-numerical-eval-p a z1 z2)
1449 (sub (bfloat-gamma-incomplete ($bfloat a) ($bfloat z1))
1450 (bfloat-gamma-incomplete ($bfloat a) ($bfloat z2))))
1452 ((complex-bigfloat-numerical-eval-p a z1 z2)
1453 (let ((ca (add ($bfloat ($realpart a))
1454 (mul '$%i ($bfloat ($imagpart a)))))
1455 (cz1 (add ($bfloat ($realpart z1))
1456 (mul '$%i ($bfloat ($imagpart z1)))))
1457 (cz2 (add ($bfloat ($realpart z2))
1458 (mul '$%i ($bfloat ($imagpart z2))))))
1459 (sub (complex-bfloat-gamma-incomplete ca cz1)
1460 (complex-bfloat-gamma-incomplete ca cz2))))
1462 ;; Check for transformations and argument simplification
1464 ((and $gamma_expand (mplusp a) (integerp (cadr a)))
1465 ;; Expand gamma_incomplete_generalized(a+n,z1,z2) with n an integer
1466 (let ((n (cadr a))
1467 (a (simplify (cons '(mplus) (cddr a)))))
1468 (cond
1469 ((> n 0)
1470 (mul
1471 (simplify (list '($pochhammer) a n))
1472 (add
1473 (simplify (list '(%gamma_incomplete_generalized) a z1 z2))
1474 (mul
1475 (power '$%e (mul -1 z1))
1476 (let ((index (gensumindex)))
1477 (simpsum1
1478 (div
1479 (power z1 (add a index -1))
1480 (simplify (list '($pochhammer) a index)))
1481 index 1 n)))
1482 (mul -1
1483 (power '$%e (mul -1 z2))
1484 (let ((index (gensumindex)))
1485 (simpsum1
1486 (div
1487 (power z2 (add a index -1))
1488 (simplify (list '($pochhammer) a index)))
1489 index 1 n))))))
1491 ((< n 0)
1492 (setq n (- n))
1493 (add
1494 (mul
1495 (div
1496 (power -1 n)
1497 ($factor (simplify (list '($pochhammer) (sub 1 a) n))))
1498 (simplify (list '(%gamma_incomplete_generalized) a z1 z2)))
1499 (mul -1
1500 (power '$%e (mul -1 z2))
1501 (let ((index (gensumindex)))
1502 (simpsum1
1503 (div
1504 (power z1 (add a index (- n) -1))
1505 (simplify (list '($pochhammer) (sub a n) index)))
1506 index 1 n)))
1507 (mul
1508 (power '$%e (mul -1 z2))
1509 (let ((index (gensumindex)))
1510 (simpsum1
1511 (div
1512 (power z2 (add a index (- n) -1))
1513 (simplify (list '($pochhammer) (sub a n) index)))
1514 index 1 n))))))))
1516 (t (give-up)))))
1518 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1520 ;;; Implementation of the Regularized Incomplete Gamma function
1522 ;;; A&S 6.5.1
1524 ;;; gamma_incomplete(a, z)
1525 ;;; gamma_incomplete_regularized(a, z) = ----------------------
1526 ;;; gamma(a)
1527 ;;;
1528 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1530 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1532 ;;; Generalized Incomplete Gamma function has not mirror symmetry for z1 or z2
1533 ;;; on the negative real axis.
1534 ;;; We support a conjugate-function which test this case.
1536 (defprop %gamma_incomplete_regularized
1537 conjugate-gamma-incomplete-regularized conjugate-function)
1539 (defun conjugate-gamma-incomplete-regularized (args)
1540 (let ((a (first args)) (z (second args)))
1541 (cond ((off-negative-real-axisp z)
1542 ;; z definitely not on the negative real axis. Mirror symmetry.
1543 (simplify
1544 (list
1545 '(%gamma_incomplete_regularized)
1546 (simplify (list '($conjugate) a))
1547 (simplify (list '($conjugate) z)))))
1549 ;; On the negative real axis or no information. Unsimplified.
1550 (list
1551 '($conjugate simp)
1552 (simplify (list '(%gamma_incomplete_regularized) a z)))))))
1554 ;;; Regularized Incomplete Gamma distributes over bags
1556 (defprop %gamma_incomplete_regularized (mlist $matrix mequal) distribute_over)
1558 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1560 ;;; Differentiation of Regularized Incomplete Gamma function
1562 (defprop %gamma_incomplete_regularized
1563 ((a z)
1564 ;; The derivative wrt a in terms of hypergeometric_regularized 2F2 function
1565 ;; and the Regularized Generalized Incomplete Gamma function
1566 ;; (functions.wolfram.com)
1567 ((mplus)
1568 ((mtimes)
1569 ((%gamma) a)
1570 ((mexpt) z a)
1571 (($hypergeometric_regularized)
1572 ((mlist) a a)
1573 ((mlist) ((mplus) 1 a) ((mplus) 1 a))
1574 ((mtimes) -1 z)))
1575 ((mtimes)
1576 ((%gamma_incomplete_generalized_regularized) a z 0)
1577 ((mplus)
1578 ((%log) z)
1579 ((mtimes) -1 ((mqapply) (($psi array) 0) a)))))
1580 ;; The derivative wrt z
1581 ((mtimes)
1583 ((mexpt) $%e ((mtimes) -1 z))
1584 ((mexpt) z ((mplus) -1 a))
1585 ((mexpt) ((%gamma) a) -1)))
1586 grad)
1588 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1590 ;;; Regularized Incomplete Gamma function is a simplifying function
1592 (def-simplifier gamma_incomplete_regularized (a z)
1593 (let (($simpsum t)
1594 (ratorder 0))
1596 (cond
1598 ;; Check for specific values
1600 ((zerop1 z)
1601 (let ((sgn ($sign ($realpart a))))
1602 (cond ((member sgn '($neg $zero))
1603 (simp-domain-error
1604 (intl:gettext
1605 "gamma_incomplete_regularized: gamma_incomplete_regularized(~:M,~:M) is undefined.")
1606 a z))
1607 ((member sgn '($pos $pz)) 1)
1608 (t (give-up)))))
1610 ((zerop1 a) 0)
1611 ((eq z '$inf) 0)
1613 ;; Check for numerical evaluation in Float or Bigfloat precision
1615 ((float-numerical-eval-p a z)
1616 (complexify
1617 ;; gamma_incomplete returns a regularized result
1618 (gamma-incomplete ($float a) ($float z) t)))
1620 ((complex-float-numerical-eval-p a z)
1621 (let ((ca (complex ($float ($realpart a)) ($float ($imagpart a))))
1622 (cz (complex ($float ($realpart z)) ($float ($imagpart z)))))
1623 ;; gamma_incomplete returns a regularized result
1624 (complexify (gamma-incomplete ca cz t))))
1626 ((bigfloat-numerical-eval-p a z)
1627 (div (bfloat-gamma-incomplete ($bfloat a) ($bfloat z))
1628 (simplify (list '(%gamma) ($bfloat a)))))
1630 ((complex-bigfloat-numerical-eval-p a z)
1631 (let ((ca (add ($bfloat ($realpart a))
1632 (mul '$%i ($bfloat ($imagpart a)))))
1633 (cz (add ($bfloat ($realpart z))
1634 (mul '$%i ($bfloat ($imagpart z))))))
1635 ($rectform
1636 (div
1637 (complex-bfloat-gamma-incomplete ca cz)
1638 (simplify (list '(%gamma) ca))))))
1640 ;; Check for transformations and argument simplification
1642 ((and $gamma_expand (integerp a))
1643 ;; An integer. Expand the expression.
1644 (let ((sgn ($sign a)))
1645 (cond
1646 ((member sgn '($pos $pz))
1647 (mul
1648 (power '$%e (mul -1 z))
1649 (let ((index (gensumindex)))
1650 (simpsum1
1651 (div
1652 (power z index)
1653 (let (($gamma_expand nil))
1654 (simplify (list '(%gamma) (add index 1)))))
1655 index 0 (sub a 1)))))
1656 ((member sgn '($neg $nz)) 0)
1657 (t (give-up)))))
1659 ((and $gamma_expand (setq ratorder (max-numeric-ratio-p a 2)))
1660 ;; We have a half integral order and $gamma_expand is not NIL.
1661 ;; We expand in a series with the Erfc function
1662 (setq ratorder (- ratorder (/ 1 2)))
1663 (when *debug-gamma*
1664 (format t "~&SIMP-GAMMA-INCOMPLETE-REGULARIZED in RATORDER~%")
1665 (format t "~& : a = ~A~%" a)
1666 (format t "~& : ratorder = ~A~%" ratorder))
1667 (cond
1668 ((equal ratorder 0)
1669 (simplify (list '(%erfc) (power z '((rat simp) 1 2)))))
1671 ((> ratorder 0)
1672 (add
1673 (simplify (list '(%erfc) (power z '((rat simp) 1 2))))
1674 (mul
1675 (power -1 (sub ratorder 1))
1676 (power '$%e (mul -1 z))
1677 (power z '((rat simp) 1 2))
1678 (div 1 (simplify (list '(%gamma) a)))
1679 (let ((index (gensumindex)))
1680 (simpsum1
1681 (mul
1682 (power (mul -1 z) index)
1683 (simplify (list '($pochhammer)
1684 (sub '((rat simp) 1 2) ratorder)
1685 (sub ratorder (add index 1)))))
1686 index 0 (sub ratorder 1))))))
1688 ((< ratorder 0)
1689 (setq ratorder (- ratorder))
1690 (add
1691 (simplify (list '(%erfc) (power z '((rat simp) 1 2))))
1692 (mul -1
1693 (power '$%e (mul -1 z))
1694 (power z (sub '((rat simp) 1 2) ratorder))
1695 (inv (simplify (list '(%gamma) (sub '((rat simp) 1 2) ratorder))))
1696 (let ((index (gensumindex)))
1697 (simpsum1
1698 (div
1699 (power z index)
1700 (simplify (list '($pochhammer)
1701 (sub '((rat simp) 1 2) ratorder)
1702 (add index 1))))
1703 index 0 (sub ratorder 1))))))))
1705 ((and $gamma_expand (mplusp a) (integerp (cadr a)))
1706 (when *debug-gamma*
1707 (format t "~&SIMP-GAMMA-INCOMPLETE-REGULARIZED in COND (mplusp)~%"))
1708 (let ((n (cadr a))
1709 (a (simplify (cons '(mplus) (cddr a)))))
1710 (cond
1711 ((> n 0)
1712 (add
1713 (simplify (list '(%gamma_incomplete_regularized) a z))
1714 ;; We factor the second summand.
1715 ;; Some factors vanish and the result is more readable.
1716 ($factor
1717 (mul
1718 (power '$%e (mul -1 z))
1719 (power z (add a -1))
1720 (div 1 (simplify (list '(%gamma) a)))
1721 (let ((index (gensumindex)))
1722 (simpsum1
1723 (div
1724 (power z index)
1725 (simplify (list '($pochhammer) a index)))
1726 index 1 n))))))
1727 ((< n 0)
1728 (setq n (- n))
1729 (add
1730 (simplify (list '(%gamma_incomplete_regularized) a z))
1731 ;; We factor the second summand.
1732 ($factor
1733 (mul -1
1734 (power '$%e (mul -1 z))
1735 (power z (sub a (add n 1)))
1736 (div 1 (simplify (list '(%gamma) (add a (- n)))))
1737 (let ((index (gensumindex)))
1738 (simpsum1
1739 (div
1740 (power z index)
1741 (simplify (list '($pochhammer) (add a (- n)) index)))
1742 index 1 n)))))))))
1743 ((and $gamma_expand (consp a) (eq 'rat (caar a))
1744 (integerp (second a))
1745 (integerp (third a)))
1746 ;; gamma_incomplete_regularized of numeric rational order.
1747 ;; Expand it out so that the resulting order is between 0 and
1748 ;; 1. Use gamma_incomplete_regularized(a+n,z) to do the dirty
1749 ;; work.
1750 (multiple-value-bind (n order)
1751 (floor (/ (second a) (third a)))
1752 ;; a = n + order where 0 <= order < 1.
1753 (let ((rat-order (rat (numerator order) (denominator order))))
1754 (cond
1755 ((zerop n)
1756 ;; Nothing to do if the order is already between 0 and 1
1757 (give-up))
1759 ;; Use gamma_incomplete_regularized(a+n,z) above. and
1760 ;; then substitute a=order. This works for n positive or
1761 ;; negative.
1762 (let* ((ord (gensym))
1763 (g (simplify (list '(%gamma_incomplete_regularized) (add ord n) z))))
1764 ($substitute rat-order ord g)))))))
1766 (t (give-up)))))
1768 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1770 ;;; Implementation of the Logarithm of the Gamma function
1772 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1774 (defmfun $log_gamma (z)
1775 (simplify (list '(%log_gamma) z)))
1777 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1779 (defprop $log_gamma %log_gamma alias)
1780 (defprop $log_gamma %log_gamma verb)
1782 (defprop %log_gamma $log_gamma reversealias)
1783 (defprop %log_gamma $log_gamma noun)
1785 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1787 (defprop %log_gamma simp-log-gamma operators)
1789 ;;; Logarithm of the Gamma function distributes over bags
1791 (defprop %log_gamma (mlist $matrix mequal) distribute_over)
1793 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1795 (defprop %log_gamma
1796 ((z)
1797 ((mqapply) (($psi array) 0) z))
1798 grad)
1800 ;; integrate(log_gamma(x),x) = psi[-2](x)
1801 (defun log-gamma-integral (x)
1802 (take '(mqapply) (take '($psi) -2) x))
1804 (putprop '%log_gamma (list (list 'x) 'log-gamma-integral) 'integral)
1806 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1808 (defun simp-log-gamma (expr z simpflag)
1809 (oneargcheck expr)
1810 (setq z (simpcheck (cadr expr) simpflag))
1811 (cond
1813 ;; Check for specific values
1815 ((and (mnump z)
1816 (or (zerop1 z)
1817 (and (eq ($sign z) '$neg)
1818 (zerop1 (sub z ($truncate z))))))
1819 ;; We have zero, a negative integer or a float or bigfloat representation.
1820 (simp-domain-error
1821 (intl:gettext "log_gamma: log_gamma(~:M) is undefined.") z))
1823 ((eq z '$inf) '$inf)
1825 ;; Check for numerical evaluation
1827 ((float-numerical-eval-p z)
1828 (complexify (log-gamma-lanczos (complex ($float z) 0))))
1830 ((complex-float-numerical-eval-p z)
1831 (complexify
1832 (log-gamma-lanczos
1833 (complex ($float ($realpart z)) ($float ($imagpart z))))))
1835 ((bigfloat-numerical-eval-p z)
1836 (bfloat-log-gamma ($bfloat z)))
1838 ((complex-bigfloat-numerical-eval-p z)
1839 (complex-bfloat-log-gamma
1840 (add ($bfloat ($realpart z)) (mul '$%i ($bfloat ($imagpart z))))))
1842 ;; Transform to Logarithm of Factorial for integer values
1843 ;; At this point the integer value is positive and not zero.
1845 ((integerp z)
1846 (simplify (list '(%log) (simplify (list '(mfactorial) (- z 1))))))
1848 (t (eqtest (list '(%log_gamma) z) expr))))
1850 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1851 ;;; The functions log-gamma-lanczos, bfloat-log-gamma and
1852 ;;; complex-bfloat-log-gamma are modified versions of the related functions
1853 ;;; gamma-lanczos, bffac and cbffac. The functions return the Logarithm of
1854 ;;; the Gamma function. If we have to calculate the quotient of Gamma functions,
1855 ;;; e. g. for the Beta function, it is much more appropriate to use the
1856 ;;; logarithmic versions to avoid overflow.
1858 ;;; Be careful log(gamma(z)) is only for realpart(z) positive equal to
1859 ;;; log_gamma(z). For a negative realpart(z) log_gamma differ by multiple of
1860 ;;; %pi from log(gamma(z)). But we always have exp(log_gamma(z))= gamma(z).
1861 ;;; The terms to get the transformation for log_gamma(-z) are taken from
1862 ;;; functions.wolfram.com.
1863 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1865 (defun log-gamma-lanczos (z)
1866 (declare (type (complex flonum) z)
1867 (optimize (safety 3)))
1868 (let ((c (make-array 15 :element-type 'flonum
1869 :initial-contents
1870 '(0.99999999999999709182
1871 57.156235665862923517
1872 -59.597960355475491248
1873 14.136097974741747174
1874 -0.49191381609762019978
1875 .33994649984811888699e-4
1876 .46523628927048575665e-4
1877 -.98374475304879564677e-4
1878 .15808870322491248884e-3
1879 -.21026444172410488319e-3
1880 .21743961811521264320e-3
1881 -.16431810653676389022e-3
1882 .84418223983852743293e-4
1883 -.26190838401581408670e-4
1884 .36899182659531622704e-5))))
1885 (declare (type (simple-array flonum (15)) c))
1886 (if (minusp (realpart z))
1887 (let ((z (- z)))
1891 (- (float pi))
1892 (complex 0 1)
1893 (abs (floor (realpart z)))
1894 (- 1 (abs (signum (imagpart z)))))
1895 (log (float pi))
1896 (- (log (- z)))
1897 (- (log (sin (* (float pi) (- z (floor (realpart z)))))))
1899 (float pi)
1900 (complex 0 1)
1901 (floor (realpart z))
1902 (signum (imagpart z))))
1903 (log-gamma-lanczos z)))
1904 (let* ((z (- z 1))
1905 (zh (+ z 1/2))
1906 (zgh (+ zh 607/128))
1907 (lnzp (* (/ zh 2) (log zgh)))
1908 (ss
1909 (do ((sum 0.0)
1910 (pp (1- (length c)) (1- pp)))
1911 ((< pp 1)
1912 sum)
1913 (incf sum (/ (aref c pp) (+ z pp))))))
1914 (+ (log (sqrt (float (* 2 pi))))
1915 (log (+ ss (aref c 0)))
1916 (+ (- zgh) (* 2 lnzp)))))))
1918 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1920 (defun bfloat-log-gamma (z)
1921 (let (($ratprint nil)
1922 (bigfloat%pi ($bfloat '$%pi)))
1923 (cond
1924 ((eq ($sign z) '$neg)
1925 (let ((z (mul -1 z)))
1926 (sub
1927 (add
1928 (mul -1 bigfloat%pi '$%i
1929 (simplify (list '(mabs) (simplify (list '($floor) ($realpart z)))))
1930 (sub 1
1931 (simplify
1932 (list '(mabs) (simplify (list '(%signum) ($imagpart z)))))))
1933 (simplify (list '(%log) bigfloat%pi))
1934 (mul -1 (simplify (list '(%log) (mul -1 z))))
1935 (mul -1
1936 (simplify (list '(%log)
1937 (simplify (list '(%sin)
1938 (mul
1939 bigfloat%pi
1940 (sub z (simplify (list '($floor) ($realpart z))))))))))
1941 (mul
1942 bigfloat%pi '$%i
1943 (simplify (list '($floor) ($realpart z)))
1944 (simplify (list '(%signum) ($imagpart z)))))
1945 (bfloat-log-gamma z))))
1947 (let* ((k (* 2 (+ 1 ($entier (* 0.41 $fpprec)))))
1948 (m ($bfloat bigfloatone))
1949 (z+k (add z k -1))
1950 (y (power z+k 2))
1951 (x ($bfloat bigfloatzero))
1952 (ii))
1953 (dotimes (i (/ k 2))
1954 (setq ii (* 2 (+ i 1)))
1955 (setq m (mul m (add z ii -2) (add z ii -1)))
1956 (setq x (div
1957 (add x
1958 (div ($bern (+ k (- ii) 2))
1959 (* (+ k (- ii) 1) (+ k (- ii) 2))))
1960 y)))
1961 (add
1962 (div (simplify (list '(%log) (mul 2 bigfloat%pi z+k))) 2)
1963 (mul z+k (add (simplify (list '(%log) z+k)) x -1))
1964 (mul -1 (simplify (list '(%log) m)))))))))
1966 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1968 (defun complex-bfloat-log-gamma (z)
1969 (let (($ratprint nil)
1970 (bigfloat%pi ($bfloat '$%pi)))
1971 (cond
1972 ((eq ($sign ($realpart z)) '$neg)
1973 (let ((z (mul -1 z)))
1974 (sub
1975 (add
1976 (mul -1 bigfloat%pi '$%i
1977 (simplify (list '(mabs) (simplify (list '($floor) ($realpart z)))))
1978 (sub 1
1979 (simplify
1980 (list '(mabs) (simplify (list '(%signum) ($imagpart z)))))))
1981 (simplify (list '(%log) bigfloat%pi))
1982 (mul -1 (simplify (list '(%log) (mul -1 z))))
1983 (mul -1
1984 (simplify (list '(%log)
1985 (simplify (list '(%sin)
1986 (mul
1987 bigfloat%pi
1988 (sub z (simplify (list '($floor) ($realpart z))))))))))
1989 (mul
1990 bigfloat%pi '$%i
1991 (simplify (list '($floor) ($realpart z)))
1992 (simplify (list '(%signum) ($imagpart z)))))
1993 (complex-bfloat-log-gamma z))))
1995 (let* ((k (* 2 (+ 1 ($entier (* 0.41 $fpprec)))))
1996 (m ($bfloat bigfloatone))
1997 (z+k (add z k -1))
1998 (y ($rectform (power z+k 2)))
1999 (x ($bfloat bigfloatzero))
2000 (ii))
2001 (dotimes (i (/ k 2))
2002 (setq ii (* 2 (+ i 1)))
2003 (setq m ($rectform (mul m (add z ii -2) (add z ii -1))))
2004 (setq x ($rectform
2005 (div
2006 (add x
2007 (div ($bern (+ k (- ii) 2))
2008 (* (+ k (- ii) 1) (+ k (- ii) 2))))
2009 y))))
2010 ($rectform
2011 (add
2012 (div (simplify (list '(%log) (mul 2 bigfloat%pi z+k))) 2)
2013 (mul z+k (add (simplify (list '(%log) z+k)) x -1))
2014 (mul -1 (simplify (list '(%log) m))))))))))
2016 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2018 ;;; Implementation of the Error function Erf(z)
2020 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2022 ;; TODO: This is called by simp-expintegral-e in expintegral.lisp.
2023 ;; Can't remove this until that is fixed.
2024 (defmfun $erf (z)
2025 (simplify (list '(%erf) z)))
2027 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2029 ;;; erf has mirror symmetry
2031 (defprop %erf t commutes-with-conjugate)
2033 ;;; erf is an odd function
2035 (defprop %erf odd-function-reflect reflection-rule)
2037 ;;; erf distributes over bags
2039 (defprop %erf (mlist $matrix mequal) distribute_over)
2041 ;;; Derivative of the Error function erf
2043 (defprop %erf
2044 ((z)
2045 ((mtimes) 2
2046 ((mexpt) $%pi ((rat) -1 2))
2047 ((mexpt) $%e ((mtimes) -1 ((mexpt) z 2)))))
2048 grad)
2050 ;;; Integral of the Error function erf
2052 (defprop %erf
2053 ((z)
2054 ((mplus)
2055 ((mtimes)
2056 ((mexpt) $%pi ((rat) -1 2))
2057 ((mexpt) $%e ((mtimes) -1 ((mexpt) z 2))))
2058 ((mtimes) z ((%erf) z))))
2059 integral)
2061 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2063 (defun erf-hypergeometric (z)
2064 ; See A&S 7.1.21 or http://functions.wolfram.com/06.25.26.0001.01
2065 (mul 2
2067 (power '$%pi '((rat simp) -1 2))
2068 (list '(%hypergeometric simp)
2069 (list '(mlist simp) '((rat simp) 1 2))
2070 (list '(mlist simp) '((rat simp) 3 2))
2071 (mul -1 (power z 2)))))
2073 ;;; erf is a simplifying function
2075 (def-simplifier erf (z)
2076 (cond
2078 ;; Check for specific values
2080 ((zerop1 z) z)
2081 ((eq z '$inf) 1)
2082 ((eq z '$minf) -1)
2084 ;; Check for numerical evaluation
2086 ((float-numerical-eval-p z)
2087 (bigfloat::bf-erf ($float z)))
2088 ((complex-float-numerical-eval-p z)
2089 (complexify
2090 (bigfloat::bf-erf (complex ($float ($realpart z)) ($float ($imagpart z))))))
2091 ((bigfloat-numerical-eval-p z)
2092 (to (bigfloat::bf-erf (bigfloat:to ($bfloat z)))))
2093 ((complex-bigfloat-numerical-eval-p z)
2094 (to (bigfloat::bf-erf
2095 (bigfloat:to (add ($bfloat ($realpart z)) (mul '$%i ($bfloat ($imagpart z))))))))
2097 ;; Argument simplification
2099 ((taylorize (mop form) (second form)))
2100 ((and $erf_%iargs
2101 (not $erf_representation)
2102 (multiplep z '$%i))
2103 (mul '$%i (simplify (list '(%erfi) (coeff z '$%i 1)))))
2104 ((apply-reflection-simp (mop form) z $trigsign))
2106 ;; Representation through more general functions
2108 ($hypergeometric_representation
2109 (erf-hypergeometric z))
2111 ;; Transformation to Erfc or Erfi
2113 ((and $erf_representation
2114 (not (eq $erf_representation '$erf)))
2115 (case $erf_representation
2116 (%erfc
2117 (sub 1 (take '(%erfc) z)))
2118 (%erfi
2119 (mul -1 '$%i (take '(%erfi) (mul '$%i z))))
2121 (give-up))))
2124 (give-up))))
2126 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2128 (defun erf (z)
2129 ;; We use the slatec routine for float values.
2130 (slatec:derf (float z)))
2132 ;; Compute erf(z) using the relationship
2134 ;; erf(z) = sqrt(z^2)/z*(1 - gamma_incomplete(1/2,z^2)/sqrt(%pi))
2136 ;; When z is real sqrt(z^2)/z is signum(z). For complex z,
2137 ;; sqrt(z^2)/z = 1 if -%pi/2 < arg(z) <= %pi/2 and -1 otherwise.
2139 ;; This relationship has serious round-off issues when z is small
2140 ;; because gamma_incomplete(1/2,z^2)/sqrt(%pi) is near 1.
2142 ;; complex-erf is for (lisp) complex numbers; bfloat-erf is for real
2143 ;; bfloats, and complex-bfloat-erf is for complex bfloats. Care is
2144 ;; taken to return real results for real arguments and imaginary
2145 ;; results for imaginary arguments
2146 (defun complex-erf (z)
2147 (let ((result
2149 (if (or (< (realpart z) 0.0) (and (= (realpart z) 0.0) (< (imagpart z) 0.0)))
2152 (- 1.0
2153 (* (/ (sqrt (float pi))) (gamma-incomplete 0.5 (* z z)))))))
2154 (cond
2155 ((= (imagpart z) 0.0)
2156 ;; Pure real argument, the result is real
2157 (complex (realpart result) 0.0))
2158 ((= (realpart z) 0.0)
2159 ;; Pure imaginary argument, the result is pure imaginary
2160 (complex 0.0 (imagpart result)))
2162 result))))
2164 (defun bfloat-erf (z)
2165 ;; Warning! This has round-off problems when abs(z) is very small.
2166 (let ((1//2 ($bfloat '((rat simp) 1 2))))
2167 ;; The argument is real, the result is real too
2168 ($realpart
2169 (mul
2170 (simplify (list '(%signum) z))
2171 (sub 1
2172 (mul
2173 (div 1 (power ($bfloat '$%pi) 1//2))
2174 (bfloat-gamma-incomplete 1//2 ($bfloat (power z 2)))))))))
2176 (defun complex-bfloat-erf (z)
2177 ;; Warning! This has round-off problems when abs(z) is very small.
2178 (let* (($ratprint nil)
2179 (1//2 ($bfloat '((rat simp) 1 2)))
2180 (result
2181 (cmul
2182 (cdiv (cpower (cpower z 2) 1//2) z)
2183 (sub 1
2184 (cmul
2185 (div 1 (power ($bfloat '$%pi) 1//2))
2186 (complex-bfloat-gamma-incomplete
2187 1//2
2188 ($bfloat (cpower z 2))))))))
2189 (cond
2190 ((zerop1 ($imagpart z))
2191 ;; Pure real argument, the result is real
2192 ($realpart result))
2193 ((zerop1 ($realpart z))
2194 ;; Pure imaginary argument, the result is pure imaginary
2195 (mul '$%i ($imagpart result)))
2197 ;; A general complex result
2198 result))))
2200 (in-package :bigfloat)
2202 ;; Erf(z) for all z. Z must be a CL real or complex number or a
2203 ;; BIGFLOAT or COMPLEX-BIGFLOAT object. The result will be of the
2204 ;; same type as Z.
2205 (defun bf-erf (z)
2206 (cond ((typep z 'cl:real)
2207 ;; Use Slatec derf, which should be faster than the series.
2208 (maxima::erf z))
2209 ((<= (abs z) 0.476936)
2210 ;; Use the series A&S 7.1.5 for small x:
2212 ;; erf(z) = 2*z/sqrt(%pi) * sum((-1)^n*z^(2*n)/n!/(2*n+1), n, 0, inf)
2214 ;; The threshold is approximately erf(x) = 0.5. (Doesn't
2215 ;; have to be super accurate.) This gives max accuracy when
2216 ;; using the identity erf(x) = 1 - erfc(x).
2217 (let ((z^2 (* z z)))
2218 (/ (* 2 z (sum-power-series z^2
2219 #'(lambda (k)
2220 (let ((2k (+ k k)))
2221 (- (/ (- 2k 1)
2223 (+ 2k 1)))))))
2224 (sqrt (%pi z)))))
2226 ;; The general case.
2227 (etypecase z
2228 (cl:real (maxima::erf z))
2229 (cl:complex (maxima::complex-erf z))
2230 (bigfloat
2231 (bigfloat (maxima::$bfloat (maxima::$expand (maxima::bfloat-erf (maxima::to z))))))
2232 (complex-bigfloat
2233 (bigfloat (maxima::$bfloat (maxima::$expand (maxima::complex-bfloat-erf (maxima::to z))))))))))
2235 (defun bf-erfc (z)
2236 ;; Compute erfc(z) via 1 - erf(z) is not very accurate if erf(z) is
2237 ;; near 1. Wolfram says
2239 ;; erfc(z) = 1 - sqrt(z^2)/z * (1 - 1/sqrt(pi)*gamma_incomplete_tail(1/2, z^2))
2241 ;; For real(z) > 0, sqrt(z^2)/z is 1 so
2243 ;; erfc(z) = 1 - (1 - 1/sqrt(pi)*gamma_incomplete_tail(1/2,z^2))
2244 ;; = 1/sqrt(pi)*gamma_incomplete_tail(1/2,z^2)
2246 ;; For real(z) < 0, sqrt(z^2)/z is -1 so
2248 ;; erfc(z) = 1 + (1 - 1/sqrt(pi)*gamma_incomplete_tail(1/2,z^2))
2249 ;; = 2 - 1/sqrt(pi)*gamma_incomplete(1/2,z^2)
2250 (flet ((gamma-inc (z)
2251 (etypecase z
2252 (cl:number
2253 (maxima::gamma-incomplete 0.5 z))
2254 (bigfloat
2255 (bigfloat:to (maxima::$bfloat
2256 (maxima::bfloat-gamma-incomplete (maxima::$bfloat maxima::1//2)
2257 (maxima::to z)))))
2258 (complex-bigfloat
2259 (bigfloat:to (maxima::$bfloat
2260 (maxima::complex-bfloat-gamma-incomplete (maxima::$bfloat maxima::1//2)
2261 (maxima::to z))))))))
2262 (if (>= (realpart z) 0)
2263 (/ (gamma-inc (* z z))
2264 (sqrt (%pi z)))
2265 (- 2
2266 (/ (gamma-inc (* z z))
2267 (sqrt (%pi z)))))))
2269 (in-package :maxima)
2271 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2273 ;;; Implementation of the Generalized Error function Erf(z1,z2)
2275 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2277 ;;; Generalized Erf has mirror symmetry
2279 (defprop %erf_generalized t commutes-with-conjugate)
2281 ;;; Generalized Erf distributes over bags
2283 (defprop %erf_generalized (mlist $matrix mequal) distribute_over)
2285 ;;; Generalized Erf is antisymmetric Erf(z1,z2) = - Erf(z2,z1)
2287 (eval-when
2288 (:load-toplevel :execute)
2289 (let (($context '$global) (context '$global))
2290 (meval '(($declare) %erf_generalized $antisymmetric))
2291 ;; Let's remove built-in symbols from list for user-defined properties.
2292 (setq $props (remove '%erf_generalized $props))))
2294 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2296 (defprop %erf_generalized
2297 ((z1 z2)
2298 ;; derivative wrt z1
2299 ((mtimes) -2
2300 ((mexpt) $%pi ((rat) -1 2))
2301 ((mexpt) $%e ((mtimes) -1 ((mexpt) z1 2))))
2302 ;; derviative wrt z2
2303 ((mtimes) 2
2304 ((mexpt) $%pi ((rat) -1 2))
2305 ((mexpt) $%e ((mtimes) -1 ((mexpt) z2 2)))))
2306 grad)
2308 ;;; ----------------------------------------------------------------------------
2310 (defprop %erf_generalized simplim%erf_generalized simplim%function)
2312 (defun simplim%erf_generalized (expr var val)
2313 ;; Look for the limit of the arguments.
2314 (let ((z1 (limit (cadr expr) var val 'think))
2315 (z2 (limit (caddr expr) var val 'think)))
2316 (cond
2317 ;; Handle infinities at this place.
2318 ((or (eq z2 '$inf)
2319 (alike1 z2 '((mtimes) -1 $minf)))
2320 (sub 1 (take '(%erf) z1)))
2321 ((or (eq z2 '$minf)
2322 (alike1 z2 '((mtimes) -1 $inf)))
2323 (sub (mul -1 (take '(%erf) z1)) 1))
2324 ((or (eq z1 '$inf)
2325 (alike1 z1 '((mtimes) -1 $minf)))
2326 (sub (take '(%erf) z2) 1))
2327 ((or (eq z1 '$minf)
2328 (alike1 z1 '((mtimes) -1 $inf)))
2329 (add (take '(%erf) z2) 1))
2331 ;; All other cases are handled by the simplifier of the function.
2332 (simplify (list '(%erf_generalized) z1 z2))))))
2334 ;;; ----------------------------------------------------------------------------
2336 (def-simplifier erf_generalized (z1 z2)
2337 (cond
2339 ;; Check for specific values
2341 ((and (zerop1 z1) (zerop1 z2)) 0)
2342 ((zerop1 z1) (take '(%erf) z2))
2343 ((zerop1 z2) (mul -1 (take '(%erf) z1)))
2344 ((or (eq z2 '$inf)
2345 (alike1 z2 '((mtimes) -1 $minf)))
2346 (sub 1 (take '(%erf) z1)))
2347 ((or (eq z2 '$minf)
2348 (alike1 z2 '((mtimes) -1 $inf)))
2349 (sub (mul -1 (take '(%erf) z1)) 1))
2350 ((or (eq z1 '$inf)
2351 (alike1 z1 '((mtimes) -1 $minf)))
2352 (sub (take '(%erf) z2) 1))
2353 ((or (eq z1 '$minf)
2354 (alike1 z1 '((mtimes) -1 $inf)))
2355 (add (take '(%erf) z2) 1))
2357 ;; Check for numerical evaluation. Use erf(z1,z2) = erf(z2)-erf(z1)
2359 ((float-numerical-eval-p z1 z2)
2360 (- (bigfloat::bf-erf ($float z2))
2361 (bigfloat::bf-erf ($float z1))))
2362 ((complex-float-numerical-eval-p z1 z2)
2363 (complexify
2365 (bigfloat::bf-erf
2366 (complex ($float ($realpart z2)) ($float ($imagpart z2))))
2367 (bigfloat::bf-erf
2368 (complex ($float ($realpart z1)) ($float ($imagpart z1)))))))
2369 ((bigfloat-numerical-eval-p z1 z2)
2370 (to (bigfloat:-
2371 (bigfloat::bf-erf (bigfloat:to ($bfloat z2)))
2372 (bigfloat::bf-erf (bigfloat:to ($bfloat z1))))))
2373 ((complex-bigfloat-numerical-eval-p z1 z2)
2374 (to (bigfloat:-
2375 (bigfloat::bf-erf
2376 (bigfloat:to (add ($bfloat ($realpart z2)) (mul '$%i ($bfloat ($imagpart z2))))))
2377 (bigfloat::bf-erf
2378 (bigfloat:to (add ($bfloat ($realpart z1)) (mul '$%i ($bfloat ($imagpart z1)))))))))
2380 ;; Argument simplification
2382 ((and $trigsign (great (mul -1 z1) z1) (great (mul -1 z2) z2))
2383 (mul -1 (simplify (list '(%erf_generalized) (mul -1 z1) (mul -1 z2)))))
2385 ;; Representation through more general functions
2387 ($hypergeometric_representation
2388 (sub (erf-hypergeometric z2) (erf-hypergeometric z1)))
2390 ;; Transformation to Erf
2392 ($erf_representation
2393 (sub (simplify (list '(%erf) z2)) (simplify (list '(%erf) z1))))
2396 (give-up))))
2398 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2400 ;;; Implementation of the Complementary Error function Erfc(z)
2402 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2404 ;; TODO: called by simp-expintegral-e in expintegral.lisp. Need to
2405 ;; keep this around until that is fixed.
2406 (defmfun $erfc (z)
2407 (simplify (list '(%erfc) z)))
2409 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2411 (defprop %erfc t commutes-with-conjugate)
2413 ;;; Complementary Error function distributes over bags
2415 (defprop %erfc (mlist $matrix mequal) distribute_over)
2417 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2419 (defprop %erfc
2420 ((z)
2421 ((mtimes) -2
2422 ((mexpt) $%pi ((rat) -1 2))
2423 ((mexpt) $%e ((mtimes) -1 ((mexpt) z 2)))))
2424 grad)
2426 ;;; Integral of the Error function erfc
2428 (defprop %erfc
2429 ((z)
2430 ((mplus)
2431 ((mtimes) -1
2432 ((mexpt) $%pi ((rat) -1 2))
2433 ((mexpt) $%e ((mtimes) -1 ((mexpt) z 2))))
2434 ((mtimes) z ((%erfc) z))))
2435 integral)
2437 ;;; ----------------------------------------------------------------------------
2439 (defprop %erfc simplim%erfc simplim%function)
2441 (defun simplim%erfc (expr var val)
2442 ;; Look for the limit of the arguments.
2443 (let ((z (limit (cadr expr) var val 'think)))
2444 (cond
2445 ;; Handle infinities at this place.
2446 ((eq z '$inf) 0)
2447 ((eq z '$minf) 2)
2448 ((eq z '$infinity) ;; parallel to code in simplim%erf-%tanh
2449 (destructuring-let (((rpart . ipart) (trisplit (cadr expr)))
2450 (ans ()) (rlim ()))
2451 (setq rlim (limit rpart var val 'think))
2452 (setq ans
2453 (limit (m* rpart (m^t ipart -1)) var val 'think))
2454 (setq ans ($asksign (m+ `((mabs) ,ans) -1)))
2455 (cond ((or (eq ans '$pos) (eq ans '$zero))
2456 (cond ((eq rlim '$inf) 0)
2457 ((eq rlim '$minf) 2)
2458 (t '$und)))
2459 (t '$und))))
2460 ((eq z '$ind) '$ind)
2462 ;; All other cases are handled by the simplifier of the function.
2463 (simplify (list '(%erfc) z))))))
2465 ;;; ----------------------------------------------------------------------------
2467 (def-simplifier erfc (z)
2468 (cond
2470 ;; Check for specific values
2472 ((zerop1 z) 1)
2473 ((eq z '$inf) 0)
2474 ((eq z '$minf) 2)
2476 ;; Check for numerical evaluation.
2478 ((numerical-eval-p z)
2479 (to (bigfloat::bf-erfc (bigfloat:to z))))
2481 ;; Argument simplification
2483 ((taylorize (mop form) (second form)))
2484 ((and $trigsign (great (mul -1 z) z))
2485 (sub 2 (simplify (list '(%erfc) (mul -1 z)))))
2487 ;; Representation through more general functions
2489 ($hypergeometric_representation
2490 (sub 1 (erf-hypergeometric z)))
2492 ;; Transformation to Erf or Erfi
2494 ((and $erf_representation
2495 (not (eq $erf_representation '$erfc)))
2496 (case $erf_representation
2497 (%erf
2498 (sub 1 (take '(%erf) z)))
2499 (%erfi
2500 (add 1 (mul '$%i (take '(%erfi) (mul '$%i z)))))
2502 (give-up))))
2505 (give-up))))
2507 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2509 ;;; Implementation of the Imaginary Error function Erfi(z)
2511 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2513 ;; TODO: Called by integrate-exp-special in sin.lisp. That needs to
2514 ;; be fixed before this can be removed.
2515 (defmfun $erfi (z)
2516 (simplify (list '(%erfi) z)))
2518 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2520 ;;; erfi has mirror symmetry
2522 (defprop %erfi t commutes-with-conjugate)
2524 ;;; erfi is an odd function
2526 (defprop %erfi odd-function-reflect reflection-rule)
2528 ;;; erfi distributes over bags
2530 (defprop %erfi (mlist $matrix mequal) distribute_over)
2532 ;;; Derivative of the Error function erfi
2534 (defprop %erfi
2535 ((z)
2536 ((mtimes) 2
2537 ((mexpt) $%pi ((rat) -1 2))
2538 ((mexpt) $%e ((mexpt) z 2))))
2539 grad)
2541 ;;; Integral of the Error function erfi
2543 (defprop %erfi
2544 ((z)
2545 ((mplus)
2546 ((mtimes) -1
2547 ((mexpt) $%pi ((rat) -1 2))
2548 ((mexpt) $%e ((mexpt) z 2)))
2549 ((mtimes) z ((%erfi) z))))
2550 integral)
2552 ;;; ----------------------------------------------------------------------------
2554 (defprop %erfi simplim%erfi simplim%function)
2556 (defun simplim%erfi (expr var val)
2557 ;; Look for the limit of the arguments.
2558 (let ((z (limit (cadr expr) var val 'think)))
2559 (cond
2560 ;; Handle infinities at this place.
2561 ((eq z '$inf) '$inf)
2562 ((eq z '$minf) '$minf)
2564 ;; All other cases are handled by the simplifier of the function.
2565 (simplify (list '(%erfi) z))))))
2567 ;;; ----------------------------------------------------------------------------
2569 (in-package :bigfloat)
2570 (defun bf-erfi (z)
2571 (flet ((erfi (z)
2572 ;; Use the relationship erfi(z) = -%i*erf(%i*z)
2573 (let* ((iz (complex (- (imagpart z)) (realpart z))) ; %i*z
2574 (result (bf-erf iz)))
2575 (complex (imagpart result) (- (realpart result))))))
2576 ;; Take care to return real results when the argument is real.
2577 (if (realp z)
2578 (if (minusp z)
2579 (- (bf-erfi (- z)))
2580 (realpart (erfi z)))
2581 (erfi z))))
2583 (in-package :maxima)
2585 ;;; erfi is an simplifying function
2587 (def-simplifier erfi (z)
2588 (cond
2590 ;; Check for specific values
2592 ((zerop1 z) z)
2593 ((eq z '$inf) '$inf)
2594 ((eq z '$minf) '$minf)
2596 ;; Check for numerical evaluation. Use erfi(z) = -%i*erf(%i*z).
2598 ((numerical-eval-p z)
2599 (to (bigfloat::bf-erfi (bigfloat:to z))))
2601 ;; Argument simplification
2603 ((taylorize (mop form) (second form)))
2604 ((and $erf_%iargs
2605 (multiplep z '$%i))
2606 (mul '$%i (simplify (list '(%erf) (coeff z '$%i 1)))))
2607 ((apply-reflection-simp (mop form) z $trigsign))
2609 ;; Representation through more general functions
2611 ($hypergeometric_representation
2612 (mul -1 '$%i (erf-hypergeometric (mul '$%i z))))
2614 ;; Transformation to Erf or Erfc
2616 ((and $erf_representation
2617 (not (eq $erf_representation '$erfi)))
2618 (case $erf_representation
2619 (%erf
2620 (mul -1 '$%i (take '(%erf) (mul '$%i z))))
2621 (%erfc
2622 (sub (mul '$%i (take '(%erfc) (mul '$%i z))) '$%i))
2624 (give-up))))
2627 (give-up))))
2629 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2631 ;;; The implementation of the Inverse Error function
2633 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2635 ;;; The Inverse Error function distributes over bags
2637 (defprop %inverse_erf (mlist $matrix mequal) distribute_over)
2639 ;;; inverse_erf is the inverse of the erf function
2641 (defprop %inverse_erf %erf $inverse)
2642 (defprop %erf %inverse_erf $inverse)
2644 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2646 ;;; Differentiation of the Inverse Error function
2648 (defprop %inverse_erf
2649 ((z)
2650 ((mtimes)
2651 ((rat) 1 2)
2652 ((mexpt) $%pi ((rat) 1 2))
2653 ((mexpt) $%e ((mexpt) ((%inverse_erf) z) 2))))
2654 grad)
2656 ;;; Integral of the Inverse Error function
2658 (defprop %inverse_erf
2659 ((z)
2660 ((mtimes) -1
2661 ((mexpt) $%pi ((rat) -1 2))
2662 ((mexpt) $%e ((mtimes) -1 ((mexpt) ((%inverse_erf) z) 2)))))
2663 integral)
2665 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2667 ;;; We support a simplim%function. The function is looked up in simplimit and
2668 ;;; handles specific values of the function.
2670 (defprop %inverse_erf simplim%inverse_erf simplim%function)
2672 (defun simplim%inverse_erf (expr var val)
2673 ;; Look for the limit of the argument.
2674 (let ((z (limit (cadr expr) var val 'think)))
2675 (cond
2676 ;; Handle an argument 1 at this place
2677 ((onep1 z) '$inf)
2678 ((onep1 (mul -1 z)) '$minf)
2680 ;; All other cases are handled by the simplifier of the function.
2681 (simplify (list '(%inverse_erf) z))))))
2683 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2685 ;;; The Inverse Error function is a simplifying function
2687 (def-simplifier inverse_erf (z)
2688 (cond
2689 ((or (onep1 z)
2690 (onep1 (mul -1 z)))
2691 (simp-domain-error
2692 (intl:gettext "inverse_erf: inverse_erf(~:M) is undefined.") z))
2693 ((zerop1 z) z)
2694 ((numerical-eval-p z)
2695 (to (bigfloat::bf-inverse-erf (bigfloat:to z))))
2696 ((taylorize (mop form) (cadr form)))
2698 (give-up))))
2700 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2702 ;;; The implementation of the Inverse Complementary Error function
2704 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2706 ;;; Inverse Complementary Error function distributes over bags
2708 (defprop %inverse_erfc (mlist $matrix mequal) distribute_over)
2710 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2712 ;;; inverse_erfc is the inverse of the erfc function
2714 (defprop %inverse_erfc %erfc $inverse)
2715 (defprop %erfc %inverse_erfc $inverse)
2718 ;;; Differentiation of the Inverse Complementary Error function
2720 (defprop %inverse_erfc
2721 ((z)
2722 ((mtimes)
2723 ((rat) -1 2)
2724 ((mexpt) $%pi ((rat) 1 2))
2725 ((mexpt) $%e ((mexpt) ((%inverse_erfc) z) 2))))
2726 grad)
2728 ;;; Integral of the Inverse Complementary Error function
2730 (defprop %inverse_erfc
2731 ((z)
2732 ((mtimes)
2733 ((mexpt) $%pi ((rat) -1 2))
2734 ((mexpt) $%e
2735 ((mtimes) -1 ((mexpt) ((%inverse_erfc) z) 2)))))
2736 integral)
2738 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2740 ;;; We support a simplim%function. The function is looked up in simplimit and
2741 ;;; handles specific values of the function.
2743 (defprop %inverse_erfc simplim%inverse_erfc simplim%function)
2745 (defun simplim%inverse_erfc (expr var val)
2746 ;; Look for the limit of the argument.
2747 (let ((z (limit (cadr expr) var val 'think)))
2748 (cond
2749 ;; Handle an argument 0 at this place
2750 ((or (zerop1 z)
2751 (eq z '$zeroa)
2752 (eq z '$zerob))
2753 '$inf)
2754 ((zerop1 (add z -2)) '$minf)
2756 ;; All other cases are handled by the simplifier of the function.
2757 (simplify (list '(%inverse_erfc) z))))))
2759 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2761 ;;; Inverse Complementary Error function is a simplifying function
2762 (def-simplifier inverse_erfc (z)
2763 (cond
2764 ((or (zerop1 z)
2765 (zerop1 (add z -2)))
2766 (simp-domain-error
2767 (intl:gettext "inverse_erfc: inverse_erfc(~:M) is undefined.") z))
2768 ((onep1 z) 0)
2769 ((numerical-eval-p z)
2770 (to (bigfloat::bf-inverse-erfc (bigfloat:to z))))
2771 ((taylorize (mop form) (cadr form)))
2773 (give-up))))
2775 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2777 ;;; Implementation of the Newton algorithm to calculate numerical values
2778 ;;; of the Inverse Error functions in float or bigfloat precision.
2779 ;;; The algorithm is a modified version of the routine in newton1.mac.
2781 (defvar *debug-newton* nil)
2782 (defvar *newton-maxcount* 1000)
2783 (defvar *newton-epsilon-factor* 50)
2784 (defvar *newton-epsilon-factor-float* 10)
2786 (defun float-newton (expr var x0 eps)
2787 (do ((s (sdiff expr var))
2788 (xn x0)
2789 (sn)
2790 (count 0 (+ count 1)))
2791 ((> count *newton-maxcount*)
2792 (merror
2793 (intl:gettext "float-newton: Newton does not converge for ~:M") expr))
2794 (setq sn ($float (maxima-substitute xn var expr)))
2795 (when (< (abs sn) eps) (return xn))
2796 (when *debug-newton* (format t "~&xn = ~A~%" xn))
2797 (setq xn ($float (sub xn (div sn (maxima-substitute xn var s)))))))
2799 (defun bfloat-newton (expr var x0 eps)
2800 (do ((s (sdiff expr var))
2801 (xn x0)
2802 (sn)
2803 (count 0 (+ count 1)))
2804 ((> count *newton-maxcount*)
2805 (merror
2806 (intl:gettext "bfloat-newton: Newton does not converge for ~:M") expr))
2807 (setq sn ($bfloat (maxima-substitute xn var expr)))
2808 (when (eq ($sign (sub (simplify (list '(mabs) sn)) eps)) '$neg)
2809 (return xn))
2810 (when *debug-newton* (format t "~&xn = ~A~%" xn))
2811 (setq xn ($bfloat (sub xn (div sn (maxima-substitute xn var s)))))))
2814 (in-package :bigfloat)
2816 ;; Compute inverse_erf(z) for z a real or complex number, including
2817 ;; bigfloat objects. The value is computing using a Newton iteration
2818 ;; to solve erf(x) = z.
2819 (defun bf-inverse-erf (z)
2820 (cond ((zerop z)
2822 ((= (abs z) 1)
2823 (maxima::merror
2824 (intl:gettext "bf-inverse-erf: inverse_erf(~M) is undefined")
2826 ((minusp (realpart z))
2827 ;; inverse_erf is odd because erf is.
2828 (- (bf-inverse-erf (- z))))
2830 (labels
2831 ((approx (z)
2832 ;; Find an approximate solution for x = inverse_erf(z).
2833 (let ((result
2834 (cond ((<= (abs z) 1)
2835 ;; For small z, inverse_erf(z) = z*sqrt(%pi)/2
2836 ;; + O(z^3). Thus, x = z*sqrt(%pi)/2 is our
2837 ;; initial starting point.
2838 (* z (sqrt (%pi z)) 1/2))
2840 ;; For |z| > 1 and realpart(z) >= 0, we have
2841 ;; the asymptotic series z = erf(x) = 1 -
2842 ;; exp(-x^2)/x/sqrt(%pi).
2844 ;; Then
2845 ;; x = sqrt(-log(x*sqrt(%pi)*(1-z))
2847 ;; We can use this as a fixed-point iteration
2848 ;; to find x, and we can start the iteration at
2849 ;; x = 1. Just do one more iteration. I (RLT)
2850 ;; think that's close enough to get the Newton
2851 ;; algorithm to converge.
2852 (let* ((sp (sqrt (%pi z)))
2853 (a (sqrt (- (log (* sp (- 1 z)))))))
2854 (setf a (sqrt (- (log (* a sp (- 1 z))))))
2855 (setf a (sqrt (- (log (* a sp (- 1 z)))))))))))
2856 (when maxima::*debug-newton*
2857 (format t "approx = ~S~%" result))
2858 result)))
2859 (let ((two/sqrt-pi (/ 2 (sqrt (%pi z))))
2860 (eps
2861 ;; Try to pick a reasonable epsilon value for the
2862 ;; Newton iteration.
2863 (cond ((<= (abs z) 1)
2864 (typecase z
2865 (cl:real (* maxima::*newton-epsilon-factor-float*
2866 maxima::flonum-epsilon))
2867 (t (* maxima::*newton-epsilon-factor* (epsilon z)))))
2869 (* maxima::*newton-epsilon-factor* (epsilon z))))))
2870 (when maxima::*debug-newton*
2871 (format t "eps = ~S~%" eps))
2872 (flet ((diff (x)
2873 ;; Derivative of erf(x)
2874 (* two/sqrt-pi (exp (- (* x x))))))
2875 (bf-newton #'bf-erf
2876 #'diff
2878 (approx z)
2879 eps)))))))
2881 (defun bf-inverse-erfc (z)
2882 (cond ((zerop z)
2883 (maxima::merror
2884 (intl:gettext "bf-inverse-erfc: inverse_erfc(~M) is undefined")
2886 ((= z 1)
2887 (float 0 z))
2889 (flet
2890 ((approx (z)
2891 ;; Find an approximate solution for x =
2892 ;; inverse_erfc(z). We have inverse_erfc(z) =
2893 ;; inverse_erf(1-z), so that's a good starting point.
2894 ;; We don't need full precision, so a float value is
2895 ;; good enough. But if 1-z is 1, inverse_erf is
2896 ;; undefined, so we need to do something else.
2897 (let ((result
2898 (let ((1-z (float (- 1 z) 0.0)))
2899 (cond ((= 1 1-z)
2900 (if (minusp (realpart z))
2901 (bf-inverse-erf (+ 1 (* 5 maxima::flonum-epsilon)))
2902 (bf-inverse-erf (- 1 (* 5 maxima::flonum-epsilon)))))
2904 (bf-inverse-erf 1-z))))))
2905 (when maxima::*debug-newton*
2906 (format t "approx = ~S~%" result))
2907 result)))
2908 (let ((-two/sqrt-pi (/ -2 (sqrt (%pi z))))
2909 (eps (* maxima::*newton-epsilon-factor* (epsilon z))))
2910 (when maxima::*debug-newton*
2911 (format t "eps = ~S~%" eps))
2912 (flet ((diff (x)
2913 ;; Derivative of erfc(x)
2914 (* -two/sqrt-pi (exp (- (* x x))))))
2915 (bf-newton #'bf-erfc
2916 #'diff
2918 (approx z)
2919 eps)))))))
2921 ;; Newton iteration for solving f(x) = z, given f and the derivative
2922 ;; of f.
2923 (defun bf-newton (f df z start eps)
2924 (do ((x start)
2925 (delta (/ (- (funcall f start) z)
2926 (funcall df start))
2927 (/ (- (funcall f x) z)
2928 (funcall df x)))
2929 (count 0 (1+ count)))
2930 ((or (< (abs delta) (* (abs x) eps))
2931 (> count maxima::*newton-maxcount*))
2932 (if (> count maxima::*newton-maxcount*)
2933 (maxima::merror
2934 (intl:gettext "bf-newton: failed to converge after ~M iterations: delta = ~S, x = ~S eps = ~S")
2935 count delta x eps)
2937 (when maxima::*debug-newton*
2938 (format t "x = ~S, abs(delta) = ~S relerr = ~S~%"
2939 x (abs delta) (/ (abs delta) (abs x))))
2940 (setf x (- x delta))))
2942 (in-package :maxima)
2944 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2946 ;;; Implementation of the Fresnel Integral S(z)
2948 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2950 ;;; fresnel_s distributes over bags
2952 (defprop %fresnel_s (mlist $matrix mequal) distribute_over)
2954 ;;; fresnel_s has mirror symmetry
2956 (defprop %fresnel_s t commutes-with-conjugate)
2958 ;;; fresnel_s is an odd function
2960 ;;; Maxima has two mechanism to define a reflection property
2961 ;;; 1. Declare the feature oddfun or evenfun
2962 ;;; 2. Put a reflection rule on the property list
2964 ;;; The second way is used for the trig functions. We use it here too.
2966 ;;; This would be the first way to give the property of an odd function.
2967 ;(eval-when
2968 ; #+gcl (load eval)
2969 ; #-gcl (:load-toplevel :execute)
2970 ; (let (($context '$global) (context '$global))
2971 ; (meval '(($declare) %fresnel_s $oddfun))
2972 ; ;; Let's remove built-in symbols from list for user-defined properties.
2973 ; (setq $props (remove '%fresnel_s $props))))
2975 (defprop %fresnel_s odd-function-reflect reflection-rule)
2977 ;;; Differentiation of the Fresnel Integral S
2979 (defprop %fresnel_s
2980 ((z)
2981 ((%sin) ((mtimes) ((rat) 1 2) $%pi ((mexpt) z 2))))
2982 grad)
2984 ;;; Integration of the Fresnel Integral S
2986 (defprop %fresnel_s
2987 ((z)
2988 ((mplus)
2989 ((mtimes) z ((%fresnel_s) z))
2990 ((mtimes)
2991 ((mexpt) $%pi -1)
2992 ((%cos) ((mtimes) ((rat) 1 2) $%pi ((mexpt) z 2))))))
2993 integral)
2995 ;;; Limits of the Fresnel Integral S
2997 (defprop %fresnel_s simplim%fresnel_s simplim%function)
2998 (defun simplim%fresnel_s (exp var val)
2999 (let ((arg (limit (cadr exp) var val 'think)))
3000 (cond ((eq arg '$inf)
3001 '((rat simp) 1 2))
3002 ((eq arg '$minf)
3003 '((rat simp) -1 2))
3005 `((%fresnel_s) ,arg)))))
3007 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3009 (defvar *fresnel-maxit* 1000)
3010 (defvar *fresnel-eps* (* 2 flonum-epsilon))
3011 (defvar *fresnel-min* 1e-32)
3013 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3014 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3016 (in-package :bigfloat)
3018 (defun bf-fresnel (z)
3019 (let* ((eps (epsilon z))
3020 (maxit maxima::*fresnel-maxit*)
3021 (xmin 1.5)
3022 (bf-pi (%pi z))
3023 ;; For very small x, we have
3024 ;; fresnel_s(x) = %pi/6*z^3
3025 ;; fresnel_c(x) = x
3026 (s (* (/ bf-pi 6) z z z))
3027 (c z))
3028 (when (> (abs z) eps)
3029 (cond
3030 ((< (abs z) xmin)
3031 (when maxima::*debug-gamma*
3032 (format t "~& in FRESNEL series expansion.~%"))
3033 (let ((sums 0) (sumc z))
3034 (do ((sum 0)
3035 (sign 1)
3036 (fact (* (/ bf-pi 2) (* z z)))
3037 (term z)
3038 (odd t (not odd))
3039 (test 0)
3040 (n 3 (+ n 2))
3041 (k 1 (+ k 1)))
3042 ((> k maxit)
3043 (maxima::merror (intl:gettext "fresnel: series expansion failed for (COMPLEX-BFLOAT-FRESNEL ~:M).") z))
3044 (setq term (* term (/ fact k)))
3045 (setq sum (+ sum (/ (* sign term) n)))
3046 (setq test (* (abs sum) eps))
3047 (if odd
3048 (progn
3049 (setq sign (- sign))
3050 (setq sums sum)
3051 (setq sum sumc))
3052 (progn
3053 (setq sumc sum)
3054 (setq sum sums)))
3055 (if (< (abs term) test)
3056 (return)))
3057 (setq s sums)
3058 (setq c sumc)))
3060 (let* ((sqrt-pi (sqrt bf-pi))
3061 (z+ (* (complex 1/2 1/2)
3062 (* sqrt-pi
3063 z)))
3064 (z- (* (complex 1/2 -1/2)
3065 (* sqrt-pi
3066 z)))
3067 (erf+ (bf-erf z+))
3068 (erf- (bf-erf z-)))
3069 (setq s (* (complex 1/4 1/4)
3070 (+ erf+ (* (complex 0 -1) erf-))))
3071 (setq c (* (complex 1/4 -1/4)
3072 (+ erf+ (* (complex 0 1) erf-))))))))
3073 (values s c)))
3075 (defun bf-fresnel-s (z)
3076 (if (and (complexp z) (zerop (realpart z)))
3077 ;; A pure imaginary argument. Use fresnel_s(%i*x)=-%i*fresnel_s(x).
3078 (complex 0
3079 (- (bf-fresnel-s (imagpart z))))
3080 (let ((fs (bf-fresnel z)))
3081 (if (realp z)
3082 (realpart fs)
3083 fs))))
3085 (defun bf-fresnel-c (z)
3086 (if (and (complexp z) (zerop (realpart z)))
3087 ;; A pure imaginary argument. Use fresnel_c(%i*x)=%i*fresnel_c(x).
3088 (complex 0
3089 (bf-fresnel-c (imagpart z)))
3090 (let ((fc (nth-value 1 (bf-fresnel z))))
3091 (if (realp z)
3092 (realpart fc)
3093 fc))))
3095 (in-package :maxima)
3097 ;;; fresnel_s is a simplifying function
3098 (def-simplifier fresnel_s (z)
3099 (cond
3101 ;; Check for specific values
3103 ((zerop1 z) z)
3104 ((eq z '$inf) '((rat simp) 1 2))
3105 ((eq z '$minf) '((rat simp) -1 2))
3107 ;; Check for numerical evaluation
3108 ((numerical-eval-p z)
3109 (to (bigfloat::bf-fresnel-s (bigfloat::to z))))
3111 ;; Check for argument simplification
3113 ((taylorize (mop form) (second form)))
3114 ((and $%iargs (multiplep z '$%i))
3115 (mul -1 '$%i (simplify (list '(%fresnel_s) (coeff z '$%i 1)))))
3116 ((apply-reflection-simp (mop form) z $trigsign))
3118 ;; Representation through equivalent functions
3120 ($erf_representation
3121 (mul
3122 (div (add 1 '$%i) 4)
3123 (add
3124 (simplify
3125 (list
3126 '(%erf)
3127 (mul (div (add 1 '$%i) 2) (power '$%pi '((rat simp) 1 2)) z)))
3128 (mul -1 '$%i
3129 (simplify
3130 (list
3131 '(%erf)
3132 (mul (div (sub 1 '$%i) 2)
3133 (power '$%pi '((rat simp) 1 2)) z)))))))
3135 ($hypergeometric_representation
3136 (mul (div (mul '$%pi (power z 3)) 6)
3137 (take '($hypergeometric)
3138 (list '(mlist) (div 3 4))
3139 (list '(mlist) (div 3 2) (div 7 4))
3140 (mul -1 (div (mul (power '$%pi 2) (power z 4)) 16)))))
3143 (give-up))))
3144 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3146 ;;; Implementation of the Fresnel Integral C(z)
3148 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3150 ;;; fresnel_c distributes over bags
3152 (defprop %fresnel_c (mlist $matrix mequal) distribute_over)
3154 ;;; fresnel_c has mirror symmetry
3156 (defprop %fresnel_c t commutes-with-conjugate)
3158 ;;; fresnel_c is an odd function
3159 ;;; Maxima has two mechanism to define a reflection property
3160 ;;; 1. Declare the feature oddfun or evenfun
3161 ;;; 2. Put a reflection rule on the property list
3163 ;;; The second way is used for the trig functions. We use it here too.
3165 (defprop %fresnel_c odd-function-reflect reflection-rule)
3167 ;;; Differentiation of the Fresnel Integral C
3169 (defprop %fresnel_c
3170 ((z)
3171 ((%cos) ((mtimes) ((rat) 1 2) $%pi ((mexpt) z 2))))
3172 grad)
3174 ;;; Integration of the Fresnel Integral C
3176 (defprop %fresnel_c
3177 ((z)
3178 ((mplus)
3179 ((mtimes) z ((%fresnel_c) z))
3180 ((mtimes) -1
3181 ((mexpt) $%pi -1)
3182 ((%sin) ((mtimes) ((rat) 1 2) $%pi ((mexpt) z 2))))))
3183 integral)
3185 ;;; Limits of the Fresnel Integral C
3187 (defprop %fresnel_c simplim%fresnel_c simplim%function)
3188 (defun simplim%fresnel_c (exp var val)
3189 (let ((arg (limit (cadr exp) var val 'think)))
3190 (cond ((eq arg '$inf)
3191 '((rat simp) 1 2))
3192 ((eq arg '$minf)
3193 '((rat simp) -1 2))
3195 `((%fresnel_c) ,arg)))))
3197 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3199 ;;; fresnel_c is a simplifying function
3200 (def-simplifier fresnel_c (z)
3201 (cond
3203 ;; Check for specific values
3205 ((zerop1 z) z)
3206 ((eq z '$inf) '((rat simp) 1 2))
3207 ((eq z '$minf) '((rat simp) -1 2))
3209 ;; Check for numerical evaluation
3210 ((numerical-eval-p z)
3211 (to (bigfloat::bf-fresnel-c (bigfloat::to z))))
3214 ;; Check for argument simplification
3216 ((taylorize (mop form) (second form)))
3217 ((and $%iargs (multiplep z '$%i))
3218 (mul '$%i (simplify (list '(%fresnel_c) (coeff z '$%i 1)))))
3219 ((apply-reflection-simp (mop form) z $trigsign))
3221 ;; Representation through equivalent functions
3223 ($erf_representation
3224 (mul
3225 (div (sub 1 '$%i) 4)
3226 (add
3227 (simplify
3228 (list
3229 '(%erf)
3230 (mul (div (add 1 '$%i) 2) (power '$%pi '((rat simp) 1 2)) z)))
3231 (mul '$%i
3232 (simplify
3233 (list
3234 '(%erf)
3235 (mul (div (sub 1 '$%i) 2)
3236 (power '$%pi '((rat simp) 1 2)) z)))))))
3238 ($hypergeometric_representation
3239 (mul z
3240 (take '($hypergeometric)
3241 (list '(mlist) (div 1 4))
3242 (list '(mlist) (div 1 2) (div 5 4))
3243 (mul -1 (div (mul (power '$%pi 2) (power z 4)) 16)))))
3246 (give-up))))
3247 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3249 ;;; Implementation of the Beta function
3251 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3253 ;;; The code for the implementation of the beta function is in the files
3254 ;;; csimp2.lisp, simp.lisp and mactex.lisp.
3255 ;;; At this place we only implement the operator property SYMMETRIC.
3257 ;;; Beta is symmetric beta(a,b) = beta(b,a)
3259 (eval-when
3260 (:load-toplevel :execute)
3261 (let (($context '$global) (context '$global))
3262 (meval '(($declare) $beta $symmetric))
3263 ;; Let's remove built-in symbols from list for user-defined properties.
3264 (setq $props (remove '$beta $props))))
3266 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3268 ;;; Implementation of the Incomplete Beta function
3270 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3272 (defvar *beta-incomplete-maxit* 10000)
3273 (defvar *beta-incomplete-eps* 1.0e-15)
3275 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3277 ;;; beta_incomplete distributes over bags
3279 (defprop %beta_incomplete (mlist $matrix mequal) distribute_over)
3281 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3283 (defprop %beta_incomplete
3284 ((a b z)
3285 ;; Derivative wrt a
3286 ((mplus)
3287 ((mtimes) ((%beta_incomplete) a b z) ((%log) z))
3288 ((mtimes) -1
3289 ((mexpt) ((%gamma) a) 2)
3290 (($hypergeometric_regularized)
3291 ((mlist) a a ((mplus) 1 ((mtimes) -1 b)))
3292 ((mlist) ((mplus) 1 a) ((mplus) 1 a))
3294 ((mexpt) z a)))
3295 ;; Derivative wrt b
3296 ((mplus)
3297 ((mtimes)
3298 (($beta) a b)
3299 ((mplus)
3300 ((mqapply) (($psi array) 0) b)
3301 ((mtimes) -1 ((mqapply) (($psi array) 0) ((mplus) a b)))))
3302 ((mtimes) -1
3303 ((%beta_incomplete) b a ((mplus) 1 ((mtimes) -1 z)))
3304 ((%log) ((mplus) 1 ((mtimes) -1 z))))
3305 ((mtimes)
3306 ((mexpt) ((%gamma) b) 2)
3307 (($hypergeometric_regularized)
3308 ((mlist) b b ((mplus) 1 ((mtimes) -1 a)))
3309 ((mlist) ((mplus) 1 b) ((mplus) 1 b))
3310 ((mplus) 1 ((mtimes) -1 z)))
3311 ((mexpt) ((mplus) 1 ((mtimes) -1 z)) b)))
3312 ;; The derivative wrt z
3313 ((mtimes)
3314 ((mexpt) ((mplus) 1 ((mtimes) -1 z)) ((mplus) -1 b))
3315 ((mexpt) z ((mplus) -1 a))))
3316 grad)
3318 ;;; Integral of the Incomplete Beta function
3320 (defprop %beta_incomplete
3321 ((a b z)
3322 nil
3324 ((mplus)
3325 ((mtimes) -1 ((%beta_incomplete) ((mplus) 1 a) b z))
3326 ((mtimes) ((%beta_incomplete) a b z) z)))
3327 integral)
3329 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3331 (def-simplifier beta_incomplete (a b z)
3332 (let (($simpsum t))
3333 (when *debug-gamma*
3334 (format t "~&SIMP-BETA-INCOMPLETE:~%")
3335 (format t "~& : a = ~A~%" a)
3336 (format t "~& : b = ~A~%" b)
3337 (format t "~& : z = ~A~%" z))
3338 (cond
3340 ;; Check for specific values
3342 ((zerop1 z)
3343 (let ((sgn ($sign ($realpart a))))
3344 (cond ((member sgn '($neg $zero))
3345 (simp-domain-error
3346 (intl:gettext
3347 "beta_incomplete: beta_incomplete(~:M,~:M,~:M) is undefined.")
3348 a b z))
3349 ((member sgn '($pos $pz))
3352 (give-up)))))
3354 ((and (onep1 z) (eq ($sign ($realpart b)) '$pos))
3355 ;; z=1 and realpart(b)>0. Simplify to a Beta function.
3356 ;; If we have to evaluate numerically preserve the type.
3357 (cond
3358 ((complex-float-numerical-eval-p a b z)
3359 (simplify (list '($beta) ($float a) ($float b))))
3360 ((complex-bigfloat-numerical-eval-p a b z)
3361 (simplify (list '($beta) ($bfloat a) ($bfloat b))))
3363 (simplify (list '($beta) a b)))))
3365 ((or (zerop1 a)
3366 (and (integer-representation-p a)
3367 (eq ($sign a) '$neg)
3368 (or (and (mnump b)
3369 (not (integer-representation-p b)))
3370 (eq ($sign (add a b)) '$pos))))
3371 ;; The argument a is zero or a is negative and the argument b is
3372 ;; not in a valid range. Beta incomplete is undefined.
3373 ;; It would be more correct to return Complex infinity.
3374 (simp-domain-error
3375 (intl:gettext
3376 "beta_incomplete: beta_incomplete(~:M,~:M,~:M) is undefined.") a b z))
3378 ;; Check for numerical evaluation in Float or Bigfloat precision
3380 ((complex-float-numerical-eval-p a b z)
3381 (cond
3382 ((not (and (integer-representation-p a) (< a 0.0)))
3383 (let ((*beta-incomplete-eps* (bigfloat:epsilon ($float 1.0))))
3384 (beta-incomplete ($float a) ($float b) ($float z))))
3386 ;; Negative integer a and b is in a valid range. Expand.
3387 ($rectform
3388 (beta-incomplete-expand-negative-integer
3389 (truncate a) ($float b) ($float z))))))
3391 ((complex-bigfloat-numerical-eval-p a b z)
3392 (cond
3393 ((not (and (integer-representation-p a) (eq ($sign a) '$neg)))
3394 (let ((*beta-incomplete-eps*
3395 (bigfloat:epsilon (bigfloat:bigfloat 1.0))))
3396 (beta-incomplete ($bfloat a) ($bfloat b) ($bfloat z))))
3398 ;; Negative integer a and b is in a valid range. Expand.
3399 ($rectform
3400 (beta-incomplete-expand-negative-integer
3401 ($truncate a) ($bfloat b) ($bfloat z))))))
3403 ;; Argument simplifications and transformations
3405 ((and (integerp b)
3406 (plusp b)
3407 (or (not (integerp a))
3408 (plusp a)))
3409 ;; Expand for b a positive integer and a not a negative integer.
3410 (mul
3411 (simplify (list '($beta) a b))
3412 (power z a)
3413 (let ((index (gensumindex)))
3414 (simpsum1
3415 (div
3416 (mul
3417 (simplify (list '($pochhammer) a index))
3418 (power (sub 1 z) index))
3419 (simplify (list '(mfactorial) index)))
3420 index 0 (sub b 1)))))
3422 ((and (integerp a) (plusp a))
3423 ;; Expand for a a positive integer.
3424 (mul
3425 (simplify (list '($beta) a b))
3426 (sub 1
3427 (mul
3428 (power (sub 1 z) b)
3429 (let ((index (gensumindex)))
3430 (simpsum1
3431 (div
3432 (mul
3433 (simplify (list '($pochhammer) b index))
3434 (power z index))
3435 (simplify (list '(mfactorial) index)))
3436 index 0 (sub a 1)))))))
3438 ((and (integerp a) (minusp a) (integerp b) (plusp b) (<= b (- a)))
3439 ;; Expand for a a negative integer and b an integer with b <= -a.
3440 (mul
3441 (power z a)
3442 (let ((index (gensumindex)))
3443 (simpsum1
3444 (div
3445 (mul (simplify (list '($pochhammer) (sub 1 b) index))
3446 (power z index))
3447 (mul (add index a)
3448 (simplify (list '(mfactorial) index))))
3449 index 0 (sub b 1)))))
3451 ((and $beta_expand (mplusp a) (integerp (cadr a)) (plusp (cadr a)))
3452 (let ((n (cadr a))
3453 (a (simplify (cons '(mplus) (cddr a)))))
3454 (sub
3455 (mul
3456 (div
3457 (simplify (list '($pochhammer) a n))
3458 (simplify (list '($pochhammer) (add a b) n)))
3459 (ftake '%beta_incomplete a b z))
3460 (mul
3461 (power (add a b n -1) -1)
3462 (let ((index (gensumindex)))
3463 (simpsum1
3464 (mul
3465 (div
3466 (simplify (list '($pochhammer)
3467 (add 1 (mul -1 a) (mul -1 n))
3468 index))
3469 (simplify (list '($pochhammer)
3470 (add 2 (mul -1 a) (mul -1 b) (mul -1 n))
3471 index)))
3472 (mul (power (sub 1 z) b)
3473 (power z (add a n (mul -1 index) -1))))
3474 index 0 (sub n 1)))))))
3476 ((and $beta_expand (mplusp a) (integerp (cadr a)) (minusp (cadr a)))
3477 (let ((n (- (cadr a)))
3478 (a (simplify (cons '(mplus) (cddr a)))))
3479 (sub
3480 (mul
3481 (div
3482 (simplify (list '($pochhammer) (add 1 (mul -1 a) (mul -1 b)) n))
3483 (simplify (list '($pochhammer) (sub 1 a) n)))
3484 (ftake '%beta_incomplete a b z))
3485 (mul
3486 (div
3487 (simplify
3488 (list '($pochhammer) (add 2 (mul -1 a) (mul -1 b)) (sub n 1)))
3489 (simplify (list '($pochhammer) (sub 1 a) n)))
3490 (let ((index (gensumindex)))
3491 (simpsum1
3492 (mul
3493 (div
3494 (simplify (list '($pochhammer) (sub 1 a) index))
3495 (simplify (list '($pochhammer)
3496 (add 2 (mul -1 a) (mul -1 b))
3497 index)))
3498 (mul (power (sub 1 z) b)
3499 (power z (add a (mul -1 index) -1))))
3500 index 0 (sub n 1)))))))
3503 (give-up)))))
3505 (defun beta-incomplete-expand-negative-integer (a b z)
3506 (mul
3507 (power z a)
3508 (let ((index (gensumindex)) ($simpsum t))
3509 (simpsum1
3510 (div
3511 (mul (simplify (list '($pochhammer) (sub 1 b) index))
3512 (power z index))
3513 (mul (add index a) (simplify (list '(mfactorial) index))))
3514 index 0 (sub b 1)))))
3516 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3518 (defun beta-incomplete (a b z)
3519 (cond
3520 ((eq ($sign (sub (mul ($realpart z) ($realpart (add a b 2)))
3521 ($realpart (add a 1))))
3522 '$pos)
3523 ($rectform
3524 (sub
3525 (simplify (list '($beta) a b))
3526 (to (numeric-beta-incomplete b a (sub 1.0 z))))))
3528 (to (numeric-beta-incomplete a b z)))))
3530 (defun numeric-beta-incomplete (a b z)
3531 (when *debug-gamma*
3532 (format t "~&NUMERIC-BETA-INCOMPLETE enters continued fractions~%"))
3533 (let ((a (bigfloat:to a))
3534 (b (bigfloat:to b))
3535 (z (bigfloat:to z)))
3536 (do* ((beta-maxit *beta-incomplete-maxit*)
3537 (beta-eps *beta-incomplete-eps*)
3538 (beta-min (bigfloat:* beta-eps beta-eps))
3539 (ab (bigfloat:+ a b))
3540 (ap (bigfloat:+ a 1.0))
3541 (am (bigfloat:- a 1.0))
3542 (c 1.0)
3543 (d (bigfloat:- 1.0 (bigfloat:/ (bigfloat:* z ab) ap)))
3544 (d (if (bigfloat:< (bigfloat:abs d) beta-min) beta-min d))
3545 (d (bigfloat:/ 1.0 d))
3546 (h d)
3547 (aa 0.0)
3548 (de 0.0)
3549 (m2 0)
3550 (m 1 (+ m 1)))
3551 ((> m beta-maxit)
3552 (merror (intl:gettext "beta_incomplete: continued fractions failed for beta_incomplete(~:M, ~:M, ~:M).") a b z))
3553 (setq m2 (+ m m))
3554 (setq aa (bigfloat:/ (bigfloat:* m z (bigfloat:- b m))
3555 (bigfloat:* (bigfloat:+ am m2)
3556 (bigfloat:+ a m2))))
3557 (setq d (bigfloat:+ 1.0 (bigfloat:* aa d)))
3558 (when (bigfloat:< (bigfloat:abs d) beta-min) (setq d beta-min))
3559 (setq c (bigfloat:+ 1.0 (bigfloat:/ aa c)))
3560 (when (bigfloat:< (bigfloat:abs c) beta-min) (setq c beta-min))
3561 (setq d (bigfloat:/ 1.0 d))
3562 (setq h (bigfloat:* h d c))
3563 (setq aa (bigfloat:/ (bigfloat:* -1
3564 (bigfloat:+ a m)
3565 (bigfloat:+ ab m) z)
3566 (bigfloat:* (bigfloat:+ a m2)
3567 (bigfloat:+ ap m2))))
3568 (setq d (bigfloat:+ 1.0 (bigfloat:* aa d)))
3569 (when (bigfloat:< (bigfloat:abs d) beta-min) (setq d beta-min))
3570 (setq c (bigfloat:+ 1.0 (bigfloat:/ aa c)))
3571 (when (bigfloat:< (bigfloat:abs c) beta-min) (setq c beta-min))
3572 (setq d (bigfloat:/ 1.0 d))
3573 (setq de (bigfloat:* d c))
3574 (setq h (bigfloat:* h de))
3575 (when (bigfloat:< (bigfloat:abs (bigfloat:- de 1.0)) beta-eps)
3576 (when *debug-gamma*
3577 (format t "~&Continued fractions finished.~%")
3578 (format t "~& z = ~A~%" z)
3579 (format t "~& a = ~A~%" a)
3580 (format t "~& b = ~A~%" b)
3581 (format t "~& h = ~A~%" h))
3582 (return
3583 (let ((result
3584 (bigfloat:/
3585 (bigfloat:* h
3586 (bigfloat:expt z a)
3587 (bigfloat:expt (bigfloat:- 1.0 z) b)) a)))
3588 (when *debug-gamma*
3589 (format t "~& result = ~A~%" result))
3590 result))))))
3592 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3594 ;;; Implementation of the Generalized Incomplete Beta function
3596 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3598 ;;; beta_incomplete_generalized distributes over bags
3600 (defprop %beta_incomplete_generalized (mlist $matrix mequal) distribute_over)
3602 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3604 ;;; Generalized Incomplete Gamma function has not mirror symmetry for z1 or z2
3605 ;;; but not on the negative real axis and for z1 or z2 real and > 1.
3606 ;;; We support a conjugate-function which test these cases.
3608 (defprop %beta_incomplete_generalized
3609 conjugate-beta-incomplete-generalized conjugate-function)
3611 (defun conjugate-beta-incomplete-generalized (args)
3612 (let ((a (first args))
3613 (b (second args))
3614 (z1 (third args))
3615 (z2 (fourth args)))
3616 (cond ((and (off-negative-real-axisp z1)
3617 (not (and (zerop1 ($imagpart z1))
3618 (eq ($sign (sub ($realpart z1) 1)) '$pos)))
3619 (off-negative-real-axisp z2)
3620 (not (and (zerop1 ($imagpart z2))
3621 (eq ($sign (sub ($realpart z2) 1)) '$pos))))
3622 (simplify
3623 (list
3624 '(%beta_incomplete_generalized)
3625 (simplify (list '($conjugate) a))
3626 (simplify (list '($conjugate) b))
3627 (simplify (list '($conjugate) z1))
3628 (simplify (list '($conjugate) z2)))))
3630 (list
3631 '($conjugate simp)
3632 (simplify (list '(%beta_incomplete_generalized)
3633 a b z1 z2)))))))
3635 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3637 (defprop %beta_incomplete_generalized
3638 ((a b z1 z2)
3639 ;; Derivative wrt a
3640 ((mplus)
3641 ((mtimes) -1
3642 ((%beta_incomplete) a b z1)
3643 ((%log) z1))
3644 ((mtimes)
3645 ((mexpt) ((%gamma) a) 2)
3646 ((mplus)
3647 ((mtimes)
3648 (($hypergeometric_regularized)
3649 ((mlist) a a ((mplus) 1 ((mtimes) -1 b)))
3650 ((mlist) ((mplus) 1 a) ((mplus) 1 a))
3652 ((mexpt) z1 a))
3653 ((mtimes) -1
3654 (($hypergeometric_regularized)
3655 ((mlist) a a ((mplus) 1 ((mtimes) -1 b)))
3656 ((mlist) ((mplus) 1 a) ((mplus) 1 a))
3658 ((mexpt) z2 a))))
3659 ((mtimes) ((%beta_incomplete) a b z2) ((%log) z2)))
3660 ;; Derivative wrt b
3661 ((mplus)
3662 ((mtimes)
3663 ((%beta_incomplete) b a ((mplus) 1 ((mtimes) -1 z1)))
3664 ((%log) ((mplus) 1 ((mtimes) -1 z1))))
3665 ((mtimes) -1
3666 ((%beta_incomplete) b a ((mplus) 1 ((mtimes) -1 z2)))
3667 ((%log) ((mplus) 1 ((mtimes) -1 z2))))
3668 ((mtimes) -1
3669 ((mexpt) ((%gamma) b) 2)
3670 ((mplus)
3671 ((mtimes)
3672 (($hypergeometric_regularized)
3673 ((mlist) b b ((mplus) 1 ((mtimes) -1 a)))
3674 ((mlist) ((mplus) 1 b) ((mplus) 1 b))
3675 ((mplus) 1 ((mtimes) -1 z1)))
3676 ((mexpt) ((mplus) 1 ((mtimes) -1 z1)) b))
3677 ((mtimes) -1
3678 (($hypergeometric_regularized)
3679 ((mlist) b b ((mplus) 1 ((mtimes) -1 a)))
3680 ((mlist) ((mplus) 1 b) ((mplus) 1 b))
3681 ((mplus) 1 ((mtimes) -1 z2)))
3682 ((mexpt) ((mplus) 1 ((mtimes) -1 z2)) b)))))
3683 ;; The derivative wrt z1
3684 ((mtimes) -1
3685 ((mexpt)
3686 ((mplus) 1 ((mtimes) -1 z1))
3687 ((mplus) -1 b))
3688 ((mexpt) z1 ((mplus) -1 a)))
3689 ;; The derivative wrt z2
3690 ((mtimes)
3691 ((mexpt)
3692 ((mplus) 1 ((mtimes) -1 z2))
3693 ((mplus) -1 b))
3694 ((mexpt) z2 ((mplus) -1 a))))
3695 grad)
3697 ;;; Integral of the Incomplete Beta function
3699 (defprop %beta_incomplete_generalized
3700 ((a b z1 z2)
3701 nil
3703 ;; Integral wrt z1
3704 ((mplus)
3705 ((%beta_incomplete) ((mplus) 1 a) b z1)
3706 ((mtimes) ((%beta_incomplete_generalized) a b z1 z2) z1))
3707 ;; Integral wrt z2
3708 ((mplus)
3709 ((mtimes) -1
3710 ((%beta_incomplete) ((mplus) 1 a) b z2))
3711 ((mtimes) ((%beta_incomplete_generalized) a b z1 z2) z2)))
3712 integral)
3714 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3716 (def-simplifier beta_incomplete_generalized (a b z1 z2)
3717 (let (($simpsum t))
3718 (cond
3720 ;; Check for specific values
3722 ((zerop1 z2)
3723 (let ((sgn ($sign ($realpart a))))
3724 (cond ((eq sgn '$neg)
3725 (simp-domain-error
3726 (intl:gettext
3727 "beta_incomplete_generalized: beta_incomplete_generalized(~:M,~:M,~:M,~:M) is undefined.")
3728 a b z1 z2))
3729 ((member sgn '($pos $pz))
3730 (mul -1 (ftake '%beta_incomplete a b z1)))
3732 (give-up)))))
3734 ((zerop1 z1)
3735 (let ((sgn ($sign ($realpart a))))
3736 (cond ((eq sgn '$neg)
3737 (simp-domain-error
3738 (intl:gettext
3739 "beta_incomplete_generalized: beta_incomplete_generalized(~:M,~:M,~:M,~:M) is undefined.")
3740 a b z1 z2))
3741 ((member sgn '($pos $pz))
3742 (mul -1 (ftake '%beta_incomplete a b z2)))
3744 (give-up)))))
3746 ((and (onep1 z2) (or (not (mnump a)) (not (mnump b)) (not (mnump z1))))
3747 (let ((sgn ($sign ($realpart b))))
3748 (cond ((member sgn '($pos $pz))
3749 (sub (simplify (list '($beta) a b))
3750 (ftake '%beta_incomplete a b z1)))
3752 (give-up)))))
3754 ((and (onep1 z1) (or (not (mnump a)) (not (mnump b)) (not (mnump z2))))
3755 (let ((sgn ($sign ($realpart b))))
3756 (cond ((member sgn '($pos $pz))
3757 (sub (ftake '%beta_incomplete a b z2)
3758 (simplify (list '($beta) a b))))
3760 (give-up)))))
3762 ;; Check for numerical evaluation in Float or Bigfloat precision
3764 ((complex-float-numerical-eval-p a b z1 z2)
3765 (let ((*beta-incomplete-eps* (bigfloat:epsilon ($float 1.0))))
3766 (sub (beta-incomplete ($float a) ($float b) ($float z2))
3767 (beta-incomplete ($float a) ($float b) ($float z1)))))
3769 ((complex-bigfloat-numerical-eval-p a b z1 z2)
3770 (let ((*beta-incomplete-eps*
3771 (bigfloat:epsilon (bigfloat:bigfloat 1.0))))
3772 (sub (beta-incomplete ($bfloat a) ($bfloat b) ($bfloat z2))
3773 (beta-incomplete ($bfloat a) ($bfloat b) ($bfloat z1)))))
3775 ;; Check for argument simplifications and transformations
3777 ((and (integerp a) (plusp a))
3778 (mul
3779 (simplify (list '($beta) a b))
3780 (sub
3781 (mul
3782 (power (sub 1 z1) b)
3783 (let ((index (gensumindex)))
3784 (simpsum1
3785 (div
3786 (mul
3787 (simplify (list '($pochhammer) b index))
3788 (power z1 index))
3789 (simplify (list '(mfactorial) index)))
3790 index 0 (sub a 1))))
3791 (mul
3792 (power (sub 1 z2) b)
3793 (let ((index (gensumindex)))
3794 (simpsum1
3795 (div
3796 (mul
3797 (simplify (list '($pochhammer) b index))
3798 (power z2 index))
3799 (simplify (list '(mfactorial) index)))
3800 index 0 (sub a 1)))))))
3802 ((and (integerp b) (plusp b))
3803 (mul
3804 (simplify (list '($beta) a b))
3805 (sub
3806 (mul
3807 (power z2 a)
3808 (let ((index (gensumindex)))
3809 (simpsum1
3810 (div
3811 (mul
3812 (simplify (list '($pochhammer) a index))
3813 (power (sub 1 z2) index))
3814 (simplify (list '(mfactorial) index)))
3815 index 0 (sub b 1))))
3816 (mul
3817 (power z1 a)
3818 (let ((index (gensumindex)))
3819 (simpsum1
3820 (div
3821 (mul
3822 (simplify (list '($pochhammer) a index))
3823 (power (sub 1 z1) index))
3824 (simplify (list '(mfactorial) index)))
3825 index 0 (sub b 1)))))))
3827 ((and $beta_expand (mplusp a) (integerp (cadr a)) (plusp (cadr a)))
3828 (let ((n (cadr a))
3829 (a (simplify (cons '(mplus) (cddr a)))))
3830 (add
3831 (mul
3832 (div
3833 (simplify (list '($pochhammer) a n))
3834 (simplify (list '($pochhammer) (add a b) n)))
3835 (take '(%beta_incomplete_generalized) a b z1 z2))
3836 (mul
3837 (power (add a b n -1) -1)
3838 (let ((index (gensumindex)))
3839 (simpsum1
3840 (mul
3841 (div
3842 (simplify (list '($pochhammer)
3843 (add 1 (mul -1 a) (mul -1 n))
3844 index))
3845 (simplify (list '($pochhammer)
3846 (add 2 (mul -1 a) (mul -1 b) (mul -1 n))
3847 index)))
3848 (sub
3849 (mul (power (sub 1 z1) b)
3850 (power z1 (add a n (mul -1 index) -1)))
3851 (mul (power (sub 1 z2) b)
3852 (power z2 (add a n (mul -1 index) -1)))))
3853 index 0 (sub n 1)))))))
3855 ((and $beta_expand (mplusp a) (integerp (cadr a)) (minusp (cadr a)))
3856 (let ((n (- (cadr a)))
3857 (a (simplify (cons '(mplus) (cddr a)))))
3858 (sub
3859 (mul
3860 (div
3861 (simplify (list '($pochhammer) (add 1 (mul -1 a) (mul -1 b)) n))
3862 (simplify (list '($pochhammer) (sub 1 a) n)))
3863 (take '(%beta_incomplete_generalized) a b z1 z2))
3864 (mul
3865 (div
3866 (simplify
3867 (list '($pochhammer) (add 2 (mul -1 a) (mul -1 b)) (sub n 1)))
3868 (simplify (list '($pochhammer) (sub 1 a) n)))
3869 (let ((index (gensumindex)))
3870 (simpsum1
3871 (mul
3872 (div
3873 (simplify (list '($pochhammer) (sub 1 a) index))
3874 (simplify (list '($pochhammer)
3875 (add 2 (mul -1 a) (mul -1 b))
3876 index)))
3877 (sub
3878 (mul (power (sub 1 z2) b)
3879 (power z2 (add a (mul -1 index) -1)))
3880 (mul (power (sub 1 z1) b)
3881 (power z1 (add a (mul -1 index) -1)))))
3882 index 0 (sub n 1)))))))
3885 (give-up)))))
3887 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3889 ;;; Implementation of the Regularized Incomplete Beta function
3891 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3893 ;;; beta_incomplete_regularized distributes over bags
3895 (defprop %beta_incomplete_regularized (mlist $matrix mequal) distribute_over)
3897 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3899 (defprop %beta_incomplete_regularized
3900 ((a b z)
3901 ;; Derivative wrt a
3902 ((mplus)
3903 ((mtimes) -1
3904 ((%gamma) a)
3905 (($hypergeometric_regularized)
3906 ((mlist) a a ((mplus) 1 ((mtimes) -1 b)))
3907 ((mlist) ((mplus) 1 a) ((mplus) 1 a)) z)
3908 ((mexpt) ((%gamma) b) -1)
3909 ((%gamma) ((mplus) a b))
3910 ((mexpt) z a))
3911 ((mtimes)
3912 ((%beta_incomplete_regularized) a b z)
3913 ((mplus)
3914 ((mtimes) -1 ((mqapply) (($psi array) 0) a))
3915 ((mqapply) (($psi array) 0) ((mplus) a b))
3916 ((%log) z))))
3917 ;; Derivative wrt b
3918 ((mplus)
3919 ((mtimes)
3920 ((%beta_incomplete_regularized) b a ((mplus) 1 ((mtimes) -1 z)))
3921 ((mplus)
3922 ((mqapply) (($psi array) 0) b)
3923 ((mtimes) -1 ((mqapply) (($psi array) 0) ((mplus) a b)))
3924 ((mtimes) -1 ((%log) ((mplus) 1 ((mtimes) -1 z))))))
3925 ((mtimes)
3926 ((mexpt) ((%gamma) a) -1)
3927 ((%gamma) b)
3928 ((%gamma) ((mplus) a b))
3929 (($hypergeometric_regularized)
3930 ((mlist) b b ((mplus) 1 ((mtimes) -1 a)))
3931 ((mlist) ((mplus) 1 b) ((mplus) 1 b))
3932 ((mplus) 1 ((mtimes) -1 z)))
3933 ((mexpt) ((mplus) 1 ((mtimes) -1 z)) b)))
3934 ;; The derivative wrt z
3935 ((mtimes)
3936 ((mexpt) (($beta) a b) -1)
3937 ((mexpt) ((mplus) 1 ((mtimes) -1 z)) ((mplus) -1 b))
3938 ((mexpt) z ((mplus) -1 a))))
3939 grad)
3941 ;;; Integral of the Generalized Incomplete Beta function
3943 (defprop %beta_incomplete_regularized
3944 ((a b z)
3945 nil
3947 ;; Integral wrt z
3948 ((mplus)
3949 ((mtimes) -1 a
3950 ((%beta_incomplete_regularized) ((mplus) 1 a) b z)
3951 ((mexpt) ((mplus) a b) -1))
3952 ((mtimes) ((%beta_incomplete_regularized) a b z) z)))
3953 integral)
3955 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3957 (def-simplifier beta_incomplete_regularized (a b z)
3958 (let (($simpsum t))
3959 (cond
3961 ;; Check for specific values
3963 ((zerop1 z)
3964 (let ((sgn ($sign ($realpart a))))
3965 (cond ((eq sgn '$neg)
3966 (simp-domain-error
3967 (intl:gettext
3968 "beta_incomplete_regularized: beta_incomplete_regularized(~:M,~:M,~:M) is undefined.")
3969 a b z))
3970 ((member sgn '($pos $pz))
3973 (give-up)))))
3975 ((and (onep1 z)
3976 (or (not (mnump a))
3977 (not (mnump b))
3978 (not (mnump z))))
3979 (let ((sgn ($sign ($realpart b))))
3980 (cond ((member sgn '($pos $pz))
3983 (give-up)))))
3985 ((and (integer-representation-p b)
3986 (if ($bfloatp b) (minusp (cadr b)) (minusp b)) )
3987 ;; Problem: for b a negative integer the Regularized Incomplete
3988 ;; Beta function is defined to be zero. BUT: When we calculate
3989 ;; e.g. beta_incomplete(1.0,-2.0,1/2)/beta(1.0,-2.0) we get the
3990 ;; result -3.0, because beta_incomplete and beta are defined for
3991 ;; for this case. How do we get a consistent behaviour?
3994 ((and (integer-representation-p a)
3995 (if ($bfloatp a) (minusp (cadr a)) (minusp a)) )
3996 (cond
3997 ;; TODO: The following line does not work for bigfloats.
3998 ((and (integer-representation-p b) (<= b (- a)))
3999 ;; Does $beta_incomplete or simpbeta underflow in this case?
4000 (div (ftake '%beta_incomplete a b z)
4001 (simplify (list '($beta) a b))))
4003 1)))
4005 ;; Check for numerical evaluation in Float or Bigfloat precision
4007 ((complex-float-numerical-eval-p a b z)
4008 (let ((*beta-incomplete-eps* (bigfloat:epsilon ($float 1.0)))
4009 beta ibeta )
4010 (setq a ($float a) b ($float b))
4011 (if (or (< ($abs (setq beta (simplify (list '($beta) a b)))) 1e-307)
4012 (< ($abs (setq ibeta (beta-incomplete a b ($float z)))) 1e-307) )
4013 ;; In case of underflow (see bug #2999) or precision loss use bigfloats
4014 ;; and emporarily give some extra precision but avoid fpprec dependency.
4015 ;; Is this workaround correct for complex values?
4016 (let ((fpprec 70))
4017 ($float (take '(%beta_incomplete_regularized) ($bfloat a) ($bfloat b) z)) )
4018 ($rectform (div ibeta beta)) )))
4020 ((complex-bigfloat-numerical-eval-p a b z)
4021 (let ((*beta-incomplete-eps*
4022 (bigfloat:epsilon (bigfloat:bigfloat 1.0))))
4023 (setq a ($bfloat a) b ($bfloat b))
4024 ($rectform
4025 (div (beta-incomplete a b ($bfloat z))
4026 (simplify (list '($beta) a b))))))
4028 ;; Check for argument simplifications and transformations
4030 ((and (integerp b) (plusp b))
4031 (div (ftake '%beta_incomplete a b z)
4032 (simplify (list '($beta) a b))))
4034 ((and (integerp a) (plusp a))
4035 (div (ftake '%beta_incomplete a b z)
4036 (simplify (list '($beta) a b))))
4038 ((and $beta_expand (mplusp a) (integerp (cadr a)) (plusp (cadr a)))
4039 (let ((n (cadr a))
4040 (a (simplify (cons '(mplus) (cddr a)))))
4041 (sub
4042 (take '(%beta_incomplete_regularized) a b z)
4043 (mul
4044 (power (add a b n -1) -1)
4045 (power (simplify (list '($beta) (add a n) b)) -1)
4046 (let ((index (gensumindex)))
4047 (simpsum1
4048 (mul
4049 (div
4050 (simplify (list '($pochhammer)
4051 (add 1 (mul -1 a) (mul -1 n))
4052 index))
4053 (simplify (list '($pochhammer)
4054 (add 2 (mul -1 a) (mul -1 b) (mul -1 n))
4055 index)))
4056 (power (sub 1 z) b)
4057 (power z (add a n (mul -1 index) -1)))
4058 index 0 (sub n 1)))))))
4060 ((and $beta_expand (mplusp a) (integerp (cadr a)) (minusp (cadr a)))
4061 (let ((n (- (cadr a)))
4062 (a (simplify (cons '(mplus) (cddr a)))))
4063 (sub
4064 (take '(%beta_incomplete_regularized) a b z)
4065 (mul
4066 (power (add a b -1) -1)
4067 (power (simplify (list '($beta) a b)) -1)
4068 (let ((index (gensumindex)))
4069 (simpsum1
4070 (mul
4071 (div
4072 (simplify (list '($pochhammer) (sub 1 a) index))
4073 (simplify (list '($pochhammer)
4074 (add 2 (mul -1 a) (mul -1 b))
4075 index)))
4076 (power (sub 1 z) b)
4077 (power z (add a (mul -1 index) -1)))
4078 index 0 (sub n 1)))))))
4081 (give-up)))))
4083 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;