1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;;; Double Factorial, Incomplete Gamma function, ...
5 ;;; This file will be extended with further functions related to the
6 ;;; Factorial and Gamma functions.
8 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
10 ;;; This file contains the following Maxima User functions:
12 ;;; double_factorial(z)
14 ;;; gamma_incomplete(a,z)
15 ;;; gamma_incomplete_generalized(a,z1,z2)
16 ;;; gamma_incomplete_regularized(a,z)
23 ;;; erf_generalized(z1,z2)
31 ;;; beta_incomplete(a,b,z)
32 ;;; beta_incomplete_generalized(a,b,z1,z2)
33 ;;; beta_incomplete_regularized(a,b,z)
35 ;;; Maxima User variable:
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
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
49 ;;; Maxima User variable (not definied in this file):
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
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.
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.
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
71 ;;; Copyright (C) 2008 Dieter Kaiser
72 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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
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
)
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"
116 (multiple-value-bind (bool rll ill
)
117 (complex-number-p ll
'float-or-rational-p
)
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!
128 ;;; Test for numerically evaluation in bigfloat precision
130 (defun bigfloat-numerical-eval-p (&rest args
)
133 (when (not (bigfloat-or-number-p ll
))
134 (return-from bigfloat-numerical-eval-p nil
))
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."
150 (multiple-value-bind (bool rll ill
)
151 (complex-number-p ll
'bigfloat-or-number-p
)
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
))
159 (when (or $numer flag
)
160 ;; Return the values in the same order as the args!
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)
175 (cond ((integerp x
) x
)
176 ((and (floatp x
) (= 0 (nth-value 1 (truncate x
))))
177 (nth-value 0 (truncate x
)))
179 (eq ($sign
(sub (setq val
($truncate x
)) x
)) '$zero
))
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)
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
218 ((%double_factorial
) z
)
223 ((mplus) 1 ((mtimes) ((rat) 1 2) z
)))
226 ((%log
) ((mtimes) 2 ((mexpt) $%pi -
1)))
227 ((%sin
) ((mtimes) $%pi z
))))))
230 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
232 ;;; Double factorial is a simplifying function
234 (def-simplifier double_factorial
(z)
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))
242 (zerop1 (sub (simplify (list '(%truncate
) (div z
2))) (div z
2))))
243 ;; Even negative integer or real representation. Not defined.
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
))
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.
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
268 ((and $factorial_expand
272 (n (simplify (cons '(mplus) (cddr z
)))))
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
282 ($factor
; we get more simple expression when factoring
285 (simplify (list '($pochhammer
) (add (div n
2) 1) k
))
286 (simplify (list '(%double_factorial
) n
)))))
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
)))
302 (/ (- 1 (cos (* pival z
))) 4))
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
))
318 (cdiv bigfloat2 pival
)
320 (simplify (list '(%cos
) (cmul pival z
)))) bigfloat4
))
322 (cpower bigfloat2
(cdiv z bigfloat2
))
323 (simplify (list '(%gamma
) (add bigfloat1
(cdiv z bigfloat2
))))))))
325 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
327 ;;; The implementation of the Incomplete Gamma function
334 ;;; gamma_incomplete(a, z) = I t %e dt
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.
367 (simplify (list '($conjugate
) a
))
368 (simplify (list '($conjugate
) z
)))))
370 ;; On the negative real axis or no information. Unsimplified.
373 (simplify (list '(%gamma_incomplete
) a z
)))))))
375 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
377 ;;; Derivative of the Incomplete Gamma function
379 (putprop '%gamma_incomplete
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.
388 ((mexpt) ((%gamma
) ,a
) 2)
390 (($hypergeometric_regularized
)
392 ((mlist) ((mplus) 1 ,a
) ((mplus) 1 ,a
))
395 ((%gamma_incomplete_generalized
) ,a
0 ,z
)
399 ((mqapply) (($psi array
) 0) ,a
))))
401 ;; No derivative. Maxima generates a noun form.
403 ;; The derivative wrt z
405 ((mexpt) $%e
((mtimes) -
1 z
))
406 ((mexpt) z
((mplus) -
1 a
))))
409 ;;; Integral of the Incomplete Gamma function
411 (defprop %gamma_incomplete
415 ((mtimes) -
1 ((%gamma_incomplete
) ((mplus) 1 a
) z
))
416 ((mtimes) ((%gamma_incomplete
) a z
) z
)))
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
)))
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.
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:
462 ;;; gamma_incomplete_lower(a, z) = I t %e dt
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
479 (def-simplifier gamma_incomplete_lower
(a z
)
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
))
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
502 (sub (simpgamma (list '(%gamma
) a
) 1 nil
)
503 (take '(%gamma_incomplete
) a z
)))
504 ((and $gamma_expand
(alike1 a
1//2))
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
516 (a (simplify (cons '(mplus) (cddr a
)))))
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)
526 (simplify (list '($pochhammer
) a n
))
527 (simplify (list '(%gamma_incomplete_lower
) a z
)))
530 (power '$%e
(mul -
1 z
))
531 (let ((gamma-a+n
(simpgamma (list '(%gamma
) (add a n
)) 1 nil
))
532 (index (gensumindex))
537 (simpgamma (list '(%gamma
) (add a index
1)) 1 nil
))
539 index
0 (add n -
1))))))
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)
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)
552 ;; g(a-n,z) = gamma(a-n)/gamma(a)*g(a,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))
562 (simplify (list '(%gamma_incomplete_lower
) a z
)))
565 (power '$%e
(mul -
1 z
))
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
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
))))
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
596 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
598 ;;; Incomplete Gamma function is a simplifying function
600 (def-simplifier gamma_incomplete
(a z
)
605 ;; Check for specific values
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
))
615 "gamma_incomplete: gamma_incomplete(~:M,~:M) is undefined.")
617 ((member sgn
'($pos $pz
)) ($gamma a
))
625 ;; Check for numerical evaluation in Float or Bigfloat precision
627 ((float-numerical-eval-p a z
)
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
635 (and (= 0 (- a
(truncate a
)))
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
)
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
)))))
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
)
667 (format t
"~&SIMP-GAMMA-INCOMPLETE in bigfloat-numerical-eval-p~%"))
668 (let ((a ($bfloat a
))
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
)
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
))))))
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
))))
694 (eq ($sign
($realpart ca
)) '$neg
))))
695 ;; Call bfloat-expintegral-e. See comment above.
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
)))
714 (simplify (list '(%expintegral_ei
) (mul -
1 z
))))
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
))
723 (simplify (list '(%gamma
) a
))
724 (power '$%e
(mul -
1 z
))
725 (let ((index (gensumindex)))
729 (let (($gamma_expand nil
))
730 ;; Simplify gamma, but do not expand to avoid division
732 (simplify (list '(%gamma
) (add index
1)))))
733 index
0 (sub a
1)))))
734 ((member sgn
'($neg $nz
))
738 (power -
1 (add (mul -
1 a
) -
1))
739 (simplify (list '(%gamma
) (add (mul -
1 a
) 1))))
741 (simplify (list '(%expintegral_ei
) (mul -
1 z
)))
745 (simplify (list '(%log
) (mul -
1 z
)))
746 (simplify (list '(%log
) (div -
1 z
)))))
747 (simplify (list '(%log
) z
))))
749 (power '$%e
(mul -
1 z
))
750 (let ((index (gensumindex)))
753 (power z
(add index a -
1))
754 (simplify (list '($pochhammer
) a index
)))
755 index
1 (mul -
1 a
))))))
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)))
765 (power '$%pi
'((rat simp
) 1 2))
766 (simplify (list '(%erfc
) (power z
'((rat simp
) 1 2))))))
770 (simplify (list '(%gamma
) a
))
771 (simplify (list '(%erfc
) (power z
'((rat simp
) 1 2)))))
773 (power -
1 (sub ratorder
1))
774 (power '$%e
(mul -
1 z
))
775 (power z
'((rat simp
) 1 2))
776 (let ((index (gensumindex)))
778 (mul -
1 ; we get more simple results
779 (simplify ; when multiplying with -1
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))))))
787 (setq ratorder
(- 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
)))
796 (power z
(sub '((rat simp
) 1 2) ratorder
))
797 (power '$%e
(mul -
1 z
))
798 (let ((index (gensumindex)))
805 (sub '((rat simp
) 1 2) ratorder
)
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
812 (a (simplify (cons '(mplus) (cddr a
)))))
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)
821 (simplify (list '($pochhammer
) a n
))
822 (simplify (list '(%gamma_incomplete
) a z
)))
824 (power '$%e
(mul -
1 z
))
826 (let ((gamma-a+n
(simpgamma (list '(%gamma
) (add a n
)) 1 nil
))
827 (index (gensumindex)))
831 (simpgamma (list '(%gamma
) (add a index
1)) 1 nil
))
833 index
0 (add n -
1))))))
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)
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)
860 ;; (-1)^n*gamma_incomplete(a,z)/pochhammer(1-a,n)
864 (simplify (list '(%gamma_incomplete
) a z
)))
865 (simplify (list '($pochhammer
) (sub 1 a
) n
)))
867 (power '$%e
(mul -
1 z
))
869 ;; sum(z^k/pochhammer(a-n,k+1),k,0,n-1)
870 (let ((index (gensumindex)))
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
))))
887 ;; Nothing to do if the order is already between 0 and 1
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
)))))))
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*
914 ;;; gamma(a,z) = exp(-x)*z^a * > ------------ * z^n
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):
931 ;;; gamma(a,z) = exp(-z) z^a *( -- --- --- --- --- ... )
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
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)
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))))
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.
982 (or (try-float-computation
984 ;; gammafloat is more accurate for real
987 (/ (* (expt x a
) (exp (- x
)))
990 (/ (* (expt x a
) (exp (- x
)))
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
)
1002 (or (try-float-computation
1004 (* (expt x a
) (exp (- x
)))))
1005 ;; Easy way failed, so use the log form.
1006 (exp (- (* a
(log 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),
1015 (- 1 (* result factor
)))
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
*))
1035 (format t
"~&GAMMA-INCOMPLETE with ~A and ~A~%" a x
))
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.
1064 (format t
"~&GAMMA-INCOMPLETE in continued fractions for lower integral~%"))
1065 (let ((a (bigfloat:to a
))
1067 (bigfloat::*debug-cf-eval
* *debug-gamma
*)
1068 (bigfloat::*max-cf-iterations
* *gamma-incomplete-maxit
*))
1069 (values (/ (bigfloat::lentz
#'(lambda (n)
1074 (- (* (+ a
(ash n -
1)) x
))))))
1077 ;; Expansion in continued fractions for gamma_incomplete.
1079 (format t
"~&GAMMA-INCOMPLETE in continued fractions~%"))
1081 (an (- a
1.0) (* i
(- a i
)))
1082 (b (+ 3.0 x
(- a
)) (+ b
2.0))
1084 (d (/ 1.0 (- b
2.0)))
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
))
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
1102 (format t
"~&GAMMA-INCOMPLETE in series~%"))
1105 (del (/ 1.0 a
) (* del
(/ x ap
)))
1106 (sum del
(+ sum del
)))
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
))
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
))
1131 (if (eq ($sign a
) '$pos
) a
0.0))))
1134 ((and (eq ($sign x
) '$pos
))
1135 ;; Expansion in continued fractions of the Incomplete Gamma function
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)))
1144 (merror (intl:gettext
"gamma_incomplete: continued fractions failed for gamma_incomplete(~:M, ~:M).") a x
))
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
)
1152 (setq c
(add b
(div an c
)))
1153 (when (eq ($sign
(sub (simplify (list '(mabs) c
)) gm-min
)) '$neg
)
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
))
1163 (power ($bfloat
'$%e
) (mul -
1 x
)))))))
1165 ;; Expand to multiply everything out.
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
1175 (power ($bfloat
'$%e
) (mul -
1 x
))
1176 (let ((a (bigfloat:to a
))
1177 (x (bigfloat:to x
)))
1184 (bigfloat:* (ash n -
1) x
)
1185 (bigfloat:-
(bigfloat:* (bigfloat:+ a
(ash n -
1))
1189 ;; Series expansion of the Incomplete Gamma function
1192 (del (div 1.0 a
) (mul del
(div x ap
)))
1193 (sum del
(add sum del
)))
1195 (merror (intl:gettext
"gamma_incomplete: series expansion failed for gamma_incomplete(~:M, ~:M).") a x
))
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
)))
1206 (when *debug-gamma
* (mformat t
"~&Series converged to ~M.~%" sum
))
1208 (sub (simplify (list '(%gamma
) 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
))
1222 (format t
"~&COMPLEX-BFLOAT-GAMMA-INCOMPLETE~%")
1223 (format t
" : a = ~A~%" a
)
1224 (format t
" : x = ~A~%" x
))
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
))
1232 (if (eq ($sign
($realpart a
)) '$pos
)
1237 ((not (and (eq ($sign
($realpart x
)) '$neg
)
1238 (eq ($sign
(sub (simplify (list '(mabs) ($imagpart x
)))
1239 (simplify (list '(mabs) ($realpart x
)))))
1241 ;; Expansion in continued fractions of the Incomplete Gamma function
1243 (format t
"~&in continued fractions:~%"))
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)))
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
)
1256 (setq c
(add b
(cdiv an c
)))
1257 (when (eq ($sign
(sub (simplify (list '(mabs) c
)) gm-min
)) '$neg
)
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)))
1266 ($bfloat
; force evaluation of expressions with sin or cos
1270 (cpower ($bfloat
'$%e
) ($bfloat
(mul -
1 x
))))))))))
1272 ;; Expand to multiply everything out.
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
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
)))
1291 (bigfloat:* (ash n -
1) x
)
1292 (bigfloat:-
(bigfloat:* (bigfloat:+ a
(ash n -
1))
1295 ;; Series expansion of the Incomplete Gamma function
1297 (format t
"~&GAMMA-INCOMPLETE in series:~%"))
1300 (del (cdiv 1.0 a
) (cmul del
(cdiv x ap
)))
1301 (sum del
(add sum del
)))
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
)))
1307 (when *debug-gamma
* (format t
"~&Series converged.~%"))
1309 ($bfloat
; force evaluation of expressions with sin or cos
1310 (sub (simplify (list '(%gamma
) a
))
1314 (cpower ($bfloat
'$%e
) (mul -
1 x
)))))))))))))
1316 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1318 ;;; Implementation of the Generalized Incomplete Gamma function
1323 ;;; gamma_incomplete_generalized(a, z1, z2) = I t %e dt
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.
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.
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
1366 ;; The derivative wrt a in terms of hypergeometric_regularized 2F2 function
1367 ;; and the Generalized Incomplete Gamma function (functions.wolfram.com)
1370 ((mexpt) ((%gamma
) a
) 2)
1372 (($hypergeometric_regularized
)
1374 ((mlist) ((mplus) 1 a
) ((mplus) 1 a
))
1377 ((mexpt) ((%gamma
) a
) 2)
1379 (($hypergeometric_regularized
)
1381 ((mlist) ((mplus) 1 a
) ((mplus) 1 a
))
1384 ((%gamma_incomplete_generalized
) a
0 z1
)
1387 ((%gamma_incomplete_generalized
) a
0 z2
)
1389 ;; The derivative wrt z1
1391 ((mexpt) $%e
((mtimes) -
1 z1
))
1392 ((mexpt) z1
((mplus) -
1 a
)))
1393 ;; The derivative wrt z2
1395 ((mexpt) $%e
((mtimes) -
1 z2
))
1396 ((mexpt) z2
((mplus) -
1 a
))))
1399 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1401 ;;; Generalized Incomplete Gamma function is a simplifying function
1403 (def-simplifier gamma_incomplete_generalized
(a z1 z2
)
1407 ;; Check for specific values
1410 (let ((sgn ($sign
($realpart a
))))
1412 ((member sgn
'($pos $pz
))
1414 (simplify (list '(%gamma_incomplete
) a z1
))
1415 (simplify (list '(%gamma
) a
))))
1420 (let ((sgn ($sign
($realpart a
))))
1422 ((member sgn
'($pos $pz
))
1424 (simplify (list '(%gamma
) a
))
1425 (simplify (list '(%gamma_incomplete
) a z2
))))
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
)
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
1467 (a (simplify (cons '(mplus) (cddr a
)))))
1471 (simplify (list '($pochhammer
) a n
))
1473 (simplify (list '(%gamma_incomplete_generalized
) a z1 z2
))
1475 (power '$%e
(mul -
1 z1
))
1476 (let ((index (gensumindex)))
1479 (power z1
(add a index -
1))
1480 (simplify (list '($pochhammer
) a index
)))
1483 (power '$%e
(mul -
1 z2
))
1484 (let ((index (gensumindex)))
1487 (power z2
(add a index -
1))
1488 (simplify (list '($pochhammer
) a index
)))
1497 ($factor
(simplify (list '($pochhammer
) (sub 1 a
) n
))))
1498 (simplify (list '(%gamma_incomplete_generalized
) a z1 z2
)))
1500 (power '$%e
(mul -
1 z2
))
1501 (let ((index (gensumindex)))
1504 (power z1
(add a index
(- n
) -
1))
1505 (simplify (list '($pochhammer
) (sub a n
) index
)))
1508 (power '$%e
(mul -
1 z2
))
1509 (let ((index (gensumindex)))
1512 (power z2
(add a index
(- n
) -
1))
1513 (simplify (list '($pochhammer
) (sub a n
) index
)))
1518 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1520 ;;; Implementation of the Regularized Incomplete Gamma function
1524 ;;; gamma_incomplete(a, z)
1525 ;;; gamma_incomplete_regularized(a, z) = ----------------------
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.
1545 '(%gamma_incomplete_regularized
)
1546 (simplify (list '($conjugate
) a
))
1547 (simplify (list '($conjugate
) z
)))))
1549 ;; On the negative real axis or no information. Unsimplified.
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
1564 ;; The derivative wrt a in terms of hypergeometric_regularized 2F2 function
1565 ;; and the Regularized Generalized Incomplete Gamma function
1566 ;; (functions.wolfram.com)
1571 (($hypergeometric_regularized
)
1573 ((mlist) ((mplus) 1 a
) ((mplus) 1 a
))
1576 ((%gamma_incomplete_generalized_regularized
) a z
0)
1579 ((mtimes) -
1 ((mqapply) (($psi array
) 0) a
)))))
1580 ;; The derivative wrt z
1583 ((mexpt) $%e
((mtimes) -
1 z
))
1584 ((mexpt) z
((mplus) -
1 a
))
1585 ((mexpt) ((%gamma
) a
) -
1)))
1588 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1590 ;;; Regularized Incomplete Gamma function is a simplifying function
1592 (def-simplifier gamma_incomplete_regularized
(a z
)
1598 ;; Check for specific values
1601 (let ((sgn ($sign
($realpart a
))))
1602 (cond ((member sgn
'($neg $zero
))
1605 "gamma_incomplete_regularized: gamma_incomplete_regularized(~:M,~:M) is undefined.")
1607 ((member sgn
'($pos $pz
)) 1)
1613 ;; Check for numerical evaluation in Float or Bigfloat precision
1615 ((float-numerical-eval-p a z
)
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
))))))
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
)))
1646 ((member sgn
'($pos $pz
))
1648 (power '$%e
(mul -
1 z
))
1649 (let ((index (gensumindex)))
1653 (let (($gamma_expand nil
))
1654 (simplify (list '(%gamma
) (add index
1)))))
1655 index
0 (sub a
1)))))
1656 ((member sgn
'($neg $nz
)) 0)
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)))
1664 (format t
"~&SIMP-GAMMA-INCOMPLETE-REGULARIZED in RATORDER~%")
1665 (format t
"~& : a = ~A~%" a
)
1666 (format t
"~& : ratorder = ~A~%" ratorder
))
1669 (simplify (list '(%erfc
) (power z
'((rat simp
) 1 2)))))
1673 (simplify (list '(%erfc
) (power z
'((rat simp
) 1 2))))
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)))
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))))))
1689 (setq ratorder
(- ratorder
))
1691 (simplify (list '(%erfc
) (power z
'((rat simp
) 1 2))))
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)))
1700 (simplify (list '($pochhammer
)
1701 (sub '((rat simp
) 1 2) ratorder
)
1703 index
0 (sub ratorder
1))))))))
1705 ((and $gamma_expand
(mplusp a
) (integerp (cadr a
)))
1707 (format t
"~&SIMP-GAMMA-INCOMPLETE-REGULARIZED in COND (mplusp)~%"))
1709 (a (simplify (cons '(mplus) (cddr a
)))))
1713 (simplify (list '(%gamma_incomplete_regularized
) a z
))
1714 ;; We factor the second summand.
1715 ;; Some factors vanish and the result is more readable.
1718 (power '$%e
(mul -
1 z
))
1719 (power z
(add a -
1))
1720 (div 1 (simplify (list '(%gamma
) a
)))
1721 (let ((index (gensumindex)))
1725 (simplify (list '($pochhammer
) a index
)))
1730 (simplify (list '(%gamma_incomplete_regularized
) a z
))
1731 ;; We factor the second summand.
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)))
1741 (simplify (list '($pochhammer
) (add a
(- n
)) index
)))
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
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
))))
1756 ;; Nothing to do if the order is already between 0 and 1
1759 ;; Use gamma_incomplete_regularized(a+n,z) above. and
1760 ;; then substitute a=order. This works for n positive or
1762 (let* ((ord (gensym))
1763 (g (simplify (list '(%gamma_incomplete_regularized
) (add ord n
) z
))))
1764 ($substitute rat-order ord g
)))))))
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1797 ((mqapply) (($psi array
) 0) z
))
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
)
1810 (setq z
(simpcheck (cadr expr
) simpflag
))
1813 ;; Check for specific values
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.
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
)
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.
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
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
))
1893 (abs (floor (realpart z
)))
1894 (- 1 (abs (signum (imagpart z
)))))
1897 (- (log (sin (* (float pi
) (- z
(floor (realpart z
)))))))
1901 (floor (realpart z
))
1902 (signum (imagpart z
))))
1903 (log-gamma-lanczos z
)))
1906 (zgh (+ zh
607/128))
1907 (lnzp (* (/ zh
2) (log zgh
)))
1910 (pp (1- (length c
)) (1- pp
)))
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
)))
1924 ((eq ($sign z
) '$neg
)
1925 (let ((z (mul -
1 z
)))
1928 (mul -
1 bigfloat%pi
'$%i
1929 (simplify (list '(mabs) (simplify (list '($floor
) ($realpart z
)))))
1932 (list '(mabs) (simplify (list '(%signum
) ($imagpart z
)))))))
1933 (simplify (list '(%log
) bigfloat%pi
))
1934 (mul -
1 (simplify (list '(%log
) (mul -
1 z
))))
1936 (simplify (list '(%log
)
1937 (simplify (list '(%sin
)
1940 (sub z
(simplify (list '($floor
) ($realpart z
))))))))))
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
))
1951 (x ($bfloat bigfloatzero
))
1953 (dotimes (i (/ k
2))
1954 (setq ii
(* 2 (+ i
1)))
1955 (setq m
(mul m
(add z ii -
2) (add z ii -
1)))
1958 (div ($bern
(+ k
(- ii
) 2))
1959 (* (+ k
(- ii
) 1) (+ k
(- ii
) 2))))
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
)))
1972 ((eq ($sign
($realpart z
)) '$neg
)
1973 (let ((z (mul -
1 z
)))
1976 (mul -
1 bigfloat%pi
'$%i
1977 (simplify (list '(mabs) (simplify (list '($floor
) ($realpart z
)))))
1980 (list '(mabs) (simplify (list '(%signum
) ($imagpart z
)))))))
1981 (simplify (list '(%log
) bigfloat%pi
))
1982 (mul -
1 (simplify (list '(%log
) (mul -
1 z
))))
1984 (simplify (list '(%log
)
1985 (simplify (list '(%sin
)
1988 (sub z
(simplify (list '($floor
) ($realpart z
))))))))))
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
))
1998 (y ($rectform
(power z
+k
2)))
1999 (x ($bfloat bigfloatzero
))
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))))
2007 (div ($bern
(+ k
(- ii
) 2))
2008 (* (+ k
(- ii
) 1) (+ k
(- ii
) 2))))
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.
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
2046 ((mexpt) $%pi
((rat) -
1 2))
2047 ((mexpt) $%e
((mtimes) -
1 ((mexpt) z
2)))))
2050 ;;; Integral of the Error function erf
2056 ((mexpt) $%pi
((rat) -
1 2))
2057 ((mexpt) $%e
((mtimes) -
1 ((mexpt) z
2))))
2058 ((mtimes) z
((%erf
) z
))))
2061 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2063 (defun erf-hypergeometric (z)
2064 ; See A&S 7.1.21 or http://functions.wolfram.com/06.25.26.0001.01
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)
2078 ;; Check for specific values
2084 ;; Check for numerical evaluation
2086 ((float-numerical-eval-p z
)
2087 (bigfloat::bf-erf
($float z
)))
2088 ((complex-float-numerical-eval-p z
)
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
)))
2101 (not $erf_representation
)
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
2117 (sub 1 (take '(%erfc
) z
)))
2119 (mul -
1 '$%i
(take '(%erfi
) (mul '$%i z
))))
2126 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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)
2149 (if (or (< (realpart z
) 0.0) (and (= (realpart z
) 0.0) (< (imagpart z
) 0.0)))
2153 (* (/ (sqrt (float pi
))) (gamma-incomplete 0.5 (* z z
)))))))
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
)))
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
2170 (simplify (list '(%signum
) z
))
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)))
2182 (cdiv (cpower (cpower z
2) 1//2) z
)
2185 (div 1 (power ($bfloat
'$%pi
) 1//2))
2186 (complex-bfloat-gamma-incomplete
2188 ($bfloat
(cpower z
2))))))))
2190 ((zerop1 ($imagpart z
))
2191 ;; Pure real argument, the result is real
2193 ((zerop1 ($realpart z
))
2194 ;; Pure imaginary argument, the result is pure imaginary
2195 (mul '$%i
($imagpart result
)))
2197 ;; A general complex 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
2206 (cond ((typep z
'cl
:real
)
2207 ;; Use Slatec derf, which should be faster than the series.
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
2226 ;; The general case.
2228 (cl:real
(maxima::erf z
))
2229 (cl:complex
(maxima::complex-erf z
))
2231 (bigfloat (maxima::$bfloat
(maxima::$expand
(maxima::bfloat-erf
(maxima::to z
))))))
2233 (bigfloat (maxima::$bfloat
(maxima::$expand
(maxima::complex-bfloat-erf
(maxima::to 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)
2253 (maxima::gamma-incomplete
0.5 z
))
2255 (bigfloat:to
(maxima::$bfloat
2256 (maxima::bfloat-gamma-incomplete
(maxima::$bfloat maxima
::1//2)
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
))
2266 (/ (gamma-inc (* z 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)
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
2298 ;; derivative wrt z1
2300 ((mexpt) $%pi
((rat) -
1 2))
2301 ((mexpt) $%e
((mtimes) -
1 ((mexpt) z1
2))))
2302 ;; derviative wrt z2
2304 ((mexpt) $%pi
((rat) -
1 2))
2305 ((mexpt) $%e
((mtimes) -
1 ((mexpt) z2
2)))))
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
)))
2317 ;; Handle infinities at this place.
2319 (alike1 z2
'((mtimes) -
1 $minf
)))
2320 (sub 1 (take '(%erf
) z1
)))
2322 (alike1 z2
'((mtimes) -
1 $inf
)))
2323 (sub (mul -
1 (take '(%erf
) z1
)) 1))
2325 (alike1 z1
'((mtimes) -
1 $minf
)))
2326 (sub (take '(%erf
) z2
) 1))
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
)
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
)))
2345 (alike1 z2
'((mtimes) -
1 $minf
)))
2346 (sub 1 (take '(%erf
) z1
)))
2348 (alike1 z2
'((mtimes) -
1 $inf
)))
2349 (sub (mul -
1 (take '(%erf
) z1
)) 1))
2351 (alike1 z1
'((mtimes) -
1 $minf
)))
2352 (sub (take '(%erf
) z2
) 1))
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
)
2366 (complex ($float
($realpart z2
)) ($float
($imagpart z2
))))
2368 (complex ($float
($realpart z1
)) ($float
($imagpart z1
)))))))
2369 ((bigfloat-numerical-eval-p z1 z2
)
2371 (bigfloat::bf-erf
(bigfloat:to
($bfloat z2
)))
2372 (bigfloat::bf-erf
(bigfloat:to
($bfloat z1
))))))
2373 ((complex-bigfloat-numerical-eval-p z1 z2
)
2376 (bigfloat:to
(add ($bfloat
($realpart z2
)) (mul '$%i
($bfloat
($imagpart z2
))))))
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
))))
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.
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2422 ((mexpt) $%pi
((rat) -
1 2))
2423 ((mexpt) $%e
((mtimes) -
1 ((mexpt) z
2)))))
2426 ;;; Integral of the Error function erfc
2432 ((mexpt) $%pi
((rat) -
1 2))
2433 ((mexpt) $%e
((mtimes) -
1 ((mexpt) z
2))))
2434 ((mtimes) z
((%erfc
) z
))))
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
)))
2445 ;; Handle infinities at this place.
2448 ((eq z
'$infinity
) ;; parallel to code in simplim%erf-%tanh
2449 (destructuring-let (((rpart . ipart
) (trisplit (cadr expr
)))
2451 (setq rlim
(limit rpart var val
'think
))
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)
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)
2470 ;; Check for specific values
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
2498 (sub 1 (take '(%erf
) z
)))
2500 (add 1 (mul '$%i
(take '(%erfi
) (mul '$%i z
)))))
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.
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
2537 ((mexpt) $%pi
((rat) -
1 2))
2538 ((mexpt) $%e
((mexpt) z
2))))
2541 ;;; Integral of the Error function erfi
2547 ((mexpt) $%pi
((rat) -
1 2))
2548 ((mexpt) $%e
((mexpt) z
2)))
2549 ((mtimes) z
((%erfi
) z
))))
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
)))
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
)
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.
2580 (realpart (erfi z
)))
2583 (in-package :maxima
)
2585 ;;; erfi is an simplifying function
2587 (def-simplifier erfi
(z)
2590 ;; Check for specific values
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
)))
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
2620 (mul -
1 '$%i
(take '(%erf
) (mul '$%i z
))))
2622 (sub (mul '$%i
(take '(%erfc
) (mul '$%i z
))) '$%i
))
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
2652 ((mexpt) $%pi
((rat) 1 2))
2653 ((mexpt) $%e
((mexpt) ((%inverse_erf
) z
) 2))))
2656 ;;; Integral of the Inverse Error function
2658 (defprop %inverse_erf
2661 ((mexpt) $%pi
((rat) -
1 2))
2662 ((mexpt) $%e
((mtimes) -
1 ((mexpt) ((%inverse_erf
) z
) 2)))))
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
)))
2676 ;; Handle an argument 1 at this place
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)
2692 (intl:gettext
"inverse_erf: inverse_erf(~:M) is undefined.") z
))
2694 ((numerical-eval-p z
)
2695 (to (bigfloat::bf-inverse-erf
(bigfloat:to z
))))
2696 ((taylorize (mop form
) (cadr form
)))
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
2724 ((mexpt) $%pi
((rat) 1 2))
2725 ((mexpt) $%e
((mexpt) ((%inverse_erfc
) z
) 2))))
2728 ;;; Integral of the Inverse Complementary Error function
2730 (defprop %inverse_erfc
2733 ((mexpt) $%pi
((rat) -
1 2))
2735 ((mtimes) -
1 ((mexpt) ((%inverse_erfc
) z
) 2)))))
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
)))
2749 ;; Handle an argument 0 at this place
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)
2765 (zerop1 (add z -
2)))
2767 (intl:gettext
"inverse_erfc: inverse_erfc(~:M) is undefined.") z
))
2769 ((numerical-eval-p z
)
2770 (to (bigfloat::bf-inverse-erfc
(bigfloat:to z
))))
2771 ((taylorize (mop form
) (cadr form
)))
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
))
2790 (count 0 (+ count
1)))
2791 ((> count
*newton-maxcount
*)
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
))
2803 (count 0 (+ count
1)))
2804 ((> count
*newton-maxcount
*)
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
)
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)
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
))))
2832 ;; Find an approximate solution for x = inverse_erf(z).
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).
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
))
2859 (let ((two/sqrt-pi
(/ 2 (sqrt (%pi z
))))
2861 ;; Try to pick a reasonable epsilon value for the
2862 ;; Newton iteration.
2863 (cond ((<= (abs z
) 1)
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
))
2873 ;; Derivative of erf(x)
2874 (* two
/sqrt-pi
(exp (- (* x x
))))))
2881 (defun bf-inverse-erfc (z)
2884 (intl:gettext
"bf-inverse-erfc: inverse_erfc(~M) is undefined")
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.
2898 (let ((1-z (float (- 1 z
) 0.0)))
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
))
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
))
2913 ;; Derivative of erfc(x)
2914 (* -two
/sqrt-pi
(exp (- (* x x
))))))
2915 (bf-newton #'bf-erfc
2921 ;; Newton iteration for solving f(x) = z, given f and the derivative
2923 (defun bf-newton (f df z start eps
)
2925 (delta (/ (- (funcall f start
) z
)
2927 (/ (- (funcall f x
) z
)
2929 (count 0 (1+ count
)))
2930 ((or (< (abs delta
) (* (abs x
) eps
))
2931 (> count maxima
::*newton-maxcount
*))
2932 (if (> count maxima
::*newton-maxcount
*)
2934 (intl:gettext
"bf-newton: failed to converge after ~M iterations: delta = ~S, x = ~S eps = ~S")
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.
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
2981 ((%sin
) ((mtimes) ((rat) 1 2) $%pi
((mexpt) z
2))))
2984 ;;; Integration of the Fresnel Integral S
2989 ((mtimes) z
((%fresnel_s
) z
))
2992 ((%cos
) ((mtimes) ((rat) 1 2) $%pi
((mexpt) z
2))))))
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
)
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
*)
3023 ;; For very small x, we have
3024 ;; fresnel_s(x) = %pi/6*z^3
3026 (s (* (/ bf-pi
6) z z z
))
3028 (when (> (abs z
) eps
)
3031 (when maxima
::*debug-gamma
*
3032 (format t
"~& in FRESNEL series expansion.~%"))
3033 (let ((sums 0) (sumc z
))
3036 (fact (* (/ bf-pi
2) (* z z
)))
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
))
3049 (setq sign
(- sign
))
3055 (if (< (abs term
) test
)
3060 (let* ((sqrt-pi (sqrt bf-pi
))
3061 (z+ (* (complex 1/2 1/2)
3064 (z- (* (complex 1/2 -
1/2)
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-
))))))))
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).
3079 (- (bf-fresnel-s (imagpart z
))))
3080 (let ((fs (bf-fresnel z
)))
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).
3089 (bf-fresnel-c (imagpart z
)))
3090 (let ((fc (nth-value 1 (bf-fresnel z
))))
3095 (in-package :maxima
)
3097 ;;; fresnel_s is a simplifying function
3098 (def-simplifier fresnel_s
(z)
3101 ;; Check for specific values
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
3122 (div (add 1 '$%i
) 4)
3127 (mul (div (add 1 '$%i
) 2) (power '$%pi
'((rat simp
) 1 2)) z
)))
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)))))
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
3171 ((%cos
) ((mtimes) ((rat) 1 2) $%pi
((mexpt) z
2))))
3174 ;;; Integration of the Fresnel Integral C
3179 ((mtimes) z
((%fresnel_c
) z
))
3182 ((%sin
) ((mtimes) ((rat) 1 2) $%pi
((mexpt) z
2))))))
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
)
3195 `((%fresnel_c
) ,arg
)))))
3197 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3199 ;;; fresnel_c is a simplifying function
3200 (def-simplifier fresnel_c
(z)
3203 ;; Check for specific values
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
3225 (div (sub 1 '$%i
) 4)
3230 (mul (div (add 1 '$%i
) 2) (power '$%pi
'((rat simp
) 1 2)) z
)))
3235 (mul (div (sub 1 '$%i
) 2)
3236 (power '$%pi
'((rat simp
) 1 2)) z
)))))))
3238 ($hypergeometric_representation
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)))))
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)
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
3287 ((mtimes) ((%beta_incomplete
) a b z
) ((%log
) z
))
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
))
3300 ((mqapply) (($psi array
) 0) b
)
3301 ((mtimes) -
1 ((mqapply) (($psi array
) 0) ((mplus) a b
)))))
3303 ((%beta_incomplete
) b a
((mplus) 1 ((mtimes) -
1 z
)))
3304 ((%log
) ((mplus) 1 ((mtimes) -
1 z
))))
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
3314 ((mexpt) ((mplus) 1 ((mtimes) -
1 z
)) ((mplus) -
1 b
))
3315 ((mexpt) z
((mplus) -
1 a
))))
3318 ;;; Integral of the Incomplete Beta function
3320 (defprop %beta_incomplete
3325 ((mtimes) -
1 ((%beta_incomplete
) ((mplus) 1 a
) b z
))
3326 ((mtimes) ((%beta_incomplete
) a b z
) z
)))
3329 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3331 (def-simplifier beta_incomplete
(a b z
)
3334 (format t
"~&SIMP-BETA-INCOMPLETE:~%")
3335 (format t
"~& : a = ~A~%" a
)
3336 (format t
"~& : b = ~A~%" b
)
3337 (format t
"~& : z = ~A~%" z
))
3340 ;; Check for specific values
3343 (let ((sgn ($sign
($realpart a
))))
3344 (cond ((member sgn
'($neg $zero
))
3347 "beta_incomplete: beta_incomplete(~:M,~:M,~:M) is undefined.")
3349 ((member sgn
'($pos $pz
))
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.
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
)))))
3366 (and (integer-representation-p a
)
3367 (eq ($sign a
) '$neg
)
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.
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
)
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.
3388 (beta-incomplete-expand-negative-integer
3389 (truncate a
) ($float b
) ($float z
))))))
3391 ((complex-bigfloat-numerical-eval-p a b z
)
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.
3400 (beta-incomplete-expand-negative-integer
3401 ($truncate a
) ($bfloat b
) ($bfloat z
))))))
3403 ;; Argument simplifications and transformations
3407 (or (not (integerp a
))
3409 ;; Expand for b a positive integer and a not a negative integer.
3411 (simplify (list '($beta
) a b
))
3413 (let ((index (gensumindex)))
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.
3425 (simplify (list '($beta
) a b
))
3429 (let ((index (gensumindex)))
3433 (simplify (list '($pochhammer
) b 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.
3442 (let ((index (gensumindex)))
3445 (mul (simplify (list '($pochhammer
) (sub 1 b
) index
))
3448 (simplify (list '(mfactorial) index
))))
3449 index
0 (sub b
1)))))
3451 ((and $beta_expand
(mplusp a
) (integerp (cadr a
)) (plusp (cadr a
)))
3453 (a (simplify (cons '(mplus) (cddr a
)))))
3457 (simplify (list '($pochhammer
) a n
))
3458 (simplify (list '($pochhammer
) (add a b
) n
)))
3459 (ftake '%beta_incomplete a b z
))
3461 (power (add a b n -
1) -
1)
3462 (let ((index (gensumindex)))
3466 (simplify (list '($pochhammer
)
3467 (add 1 (mul -
1 a
) (mul -
1 n
))
3469 (simplify (list '($pochhammer
)
3470 (add 2 (mul -
1 a
) (mul -
1 b
) (mul -
1 n
))
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
)))))
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
))
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)))
3494 (simplify (list '($pochhammer
) (sub 1 a
) index
))
3495 (simplify (list '($pochhammer
)
3496 (add 2 (mul -
1 a
) (mul -
1 b
))
3498 (mul (power (sub 1 z
) b
)
3499 (power z
(add a
(mul -
1 index
) -
1))))
3500 index
0 (sub n
1)))))))
3505 (defun beta-incomplete-expand-negative-integer (a b z
)
3508 (let ((index (gensumindex)) ($simpsum t
))
3511 (mul (simplify (list '($pochhammer
) (sub 1 b
) index
))
3513 (mul (add index a
) (simplify (list '(mfactorial) index
))))
3514 index
0 (sub b
1)))))
3516 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3518 (defun beta-incomplete (a b z
)
3520 ((eq ($sign
(sub (mul ($realpart z
) ($realpart
(add a b
2)))
3521 ($realpart
(add a
1))))
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
)
3532 (format t
"~&NUMERIC-BETA-INCOMPLETE enters continued fractions~%"))
3533 (let ((a (bigfloat:to a
))
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))
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
))
3552 (merror (intl:gettext
"beta_incomplete: continued fractions failed for beta_incomplete(~:M, ~:M, ~:M).") a b z
))
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
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
)
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
))
3587 (bigfloat:expt
(bigfloat:-
1.0 z
) b
)) a
)))
3589 (format t
"~& result = ~A~%" 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
))
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
))))
3624 '(%beta_incomplete_generalized
)
3625 (simplify (list '($conjugate
) a
))
3626 (simplify (list '($conjugate
) b
))
3627 (simplify (list '($conjugate
) z1
))
3628 (simplify (list '($conjugate
) z2
)))))
3632 (simplify (list '(%beta_incomplete_generalized
)
3635 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3637 (defprop %beta_incomplete_generalized
3642 ((%beta_incomplete
) a b z1
)
3645 ((mexpt) ((%gamma
) a
) 2)
3648 (($hypergeometric_regularized
)
3649 ((mlist) a a
((mplus) 1 ((mtimes) -
1 b
)))
3650 ((mlist) ((mplus) 1 a
) ((mplus) 1 a
))
3654 (($hypergeometric_regularized
)
3655 ((mlist) a a
((mplus) 1 ((mtimes) -
1 b
)))
3656 ((mlist) ((mplus) 1 a
) ((mplus) 1 a
))
3659 ((mtimes) ((%beta_incomplete
) a b z2
) ((%log
) z2
)))
3663 ((%beta_incomplete
) b a
((mplus) 1 ((mtimes) -
1 z1
)))
3664 ((%log
) ((mplus) 1 ((mtimes) -
1 z1
))))
3666 ((%beta_incomplete
) b a
((mplus) 1 ((mtimes) -
1 z2
)))
3667 ((%log
) ((mplus) 1 ((mtimes) -
1 z2
))))
3669 ((mexpt) ((%gamma
) b
) 2)
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
))
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
3686 ((mplus) 1 ((mtimes) -
1 z1
))
3688 ((mexpt) z1
((mplus) -
1 a
)))
3689 ;; The derivative wrt z2
3692 ((mplus) 1 ((mtimes) -
1 z2
))
3694 ((mexpt) z2
((mplus) -
1 a
))))
3697 ;;; Integral of the Incomplete Beta function
3699 (defprop %beta_incomplete_generalized
3705 ((%beta_incomplete
) ((mplus) 1 a
) b z1
)
3706 ((mtimes) ((%beta_incomplete_generalized
) a b z1 z2
) z1
))
3710 ((%beta_incomplete
) ((mplus) 1 a
) b z2
))
3711 ((mtimes) ((%beta_incomplete_generalized
) a b z1 z2
) z2
)))
3714 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3716 (def-simplifier beta_incomplete_generalized
(a b z1 z2
)
3720 ;; Check for specific values
3723 (let ((sgn ($sign
($realpart a
))))
3724 (cond ((eq sgn
'$neg
)
3727 "beta_incomplete_generalized: beta_incomplete_generalized(~:M,~:M,~:M,~:M) is undefined.")
3729 ((member sgn
'($pos $pz
))
3730 (mul -
1 (ftake '%beta_incomplete a b z1
)))
3735 (let ((sgn ($sign
($realpart a
))))
3736 (cond ((eq sgn
'$neg
)
3739 "beta_incomplete_generalized: beta_incomplete_generalized(~:M,~:M,~:M,~:M) is undefined.")
3741 ((member sgn
'($pos $pz
))
3742 (mul -
1 (ftake '%beta_incomplete a b z2
)))
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
)))
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
))))
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
))
3779 (simplify (list '($beta
) a b
))
3782 (power (sub 1 z1
) b
)
3783 (let ((index (gensumindex)))
3787 (simplify (list '($pochhammer
) b index
))
3789 (simplify (list '(mfactorial) index
)))
3790 index
0 (sub a
1))))
3792 (power (sub 1 z2
) b
)
3793 (let ((index (gensumindex)))
3797 (simplify (list '($pochhammer
) b index
))
3799 (simplify (list '(mfactorial) index
)))
3800 index
0 (sub a
1)))))))
3802 ((and (integerp b
) (plusp b
))
3804 (simplify (list '($beta
) a b
))
3808 (let ((index (gensumindex)))
3812 (simplify (list '($pochhammer
) a index
))
3813 (power (sub 1 z2
) index
))
3814 (simplify (list '(mfactorial) index
)))
3815 index
0 (sub b
1))))
3818 (let ((index (gensumindex)))
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
)))
3829 (a (simplify (cons '(mplus) (cddr a
)))))
3833 (simplify (list '($pochhammer
) a n
))
3834 (simplify (list '($pochhammer
) (add a b
) n
)))
3835 (take '(%beta_incomplete_generalized
) a b z1 z2
))
3837 (power (add a b n -
1) -
1)
3838 (let ((index (gensumindex)))
3842 (simplify (list '($pochhammer
)
3843 (add 1 (mul -
1 a
) (mul -
1 n
))
3845 (simplify (list '($pochhammer
)
3846 (add 2 (mul -
1 a
) (mul -
1 b
) (mul -
1 n
))
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
)))))
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
))
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)))
3873 (simplify (list '($pochhammer
) (sub 1 a
) index
))
3874 (simplify (list '($pochhammer
)
3875 (add 2 (mul -
1 a
) (mul -
1 b
))
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)))))))
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
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
))
3912 ((%beta_incomplete_regularized
) a b z
)
3914 ((mtimes) -
1 ((mqapply) (($psi array
) 0) a
))
3915 ((mqapply) (($psi array
) 0) ((mplus) a b
))
3920 ((%beta_incomplete_regularized
) b a
((mplus) 1 ((mtimes) -
1 z
)))
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
))))))
3926 ((mexpt) ((%gamma
) a
) -
1)
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
3936 ((mexpt) (($beta
) a b
) -
1)
3937 ((mexpt) ((mplus) 1 ((mtimes) -
1 z
)) ((mplus) -
1 b
))
3938 ((mexpt) z
((mplus) -
1 a
))))
3941 ;;; Integral of the Generalized Incomplete Beta function
3943 (defprop %beta_incomplete_regularized
3950 ((%beta_incomplete_regularized
) ((mplus) 1 a
) b z
)
3951 ((mexpt) ((mplus) a b
) -
1))
3952 ((mtimes) ((%beta_incomplete_regularized
) a b z
) z
)))
3955 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3957 (def-simplifier beta_incomplete_regularized
(a b z
)
3961 ;; Check for specific values
3964 (let ((sgn ($sign
($realpart a
))))
3965 (cond ((eq sgn
'$neg
)
3968 "beta_incomplete_regularized: beta_incomplete_regularized(~:M,~:M,~:M) is undefined.")
3970 ((member sgn
'($pos $pz
))
3979 (let ((sgn ($sign
($realpart b
))))
3980 (cond ((member sgn
'($pos $pz
))
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
)) )
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
))))
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)))
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?
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
))
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
)))
4040 (a (simplify (cons '(mplus) (cddr a
)))))
4042 (take '(%beta_incomplete_regularized
) a b z
)
4044 (power (add a b n -
1) -
1)
4045 (power (simplify (list '($beta
) (add a n
) b
)) -
1)
4046 (let ((index (gensumindex)))
4050 (simplify (list '($pochhammer
)
4051 (add 1 (mul -
1 a
) (mul -
1 n
))
4053 (simplify (list '($pochhammer
)
4054 (add 2 (mul -
1 a
) (mul -
1 b
) (mul -
1 n
))
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
)))))
4064 (take '(%beta_incomplete_regularized
) a b z
)
4066 (power (add a b -
1) -
1)
4067 (power (simplify (list '($beta
) a b
)) -
1)
4068 (let ((index (gensumindex)))
4072 (simplify (list '($pochhammer
) (sub 1 a
) index
))
4073 (simplify (list '($pochhammer
)
4074 (add 2 (mul -
1 a
) (mul -
1 b
))
4077 (power z
(add a
(mul -
1 index
) -
1)))
4078 index
0 (sub n
1)))))))
4083 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;