Adding src/tools (created during build) and gp_image_01.png (created by the share...
[maxima.git] / src / gamma.lisp
blob74a74db63882dfe25ed7fe8a7114856d6a591ad7
1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2 ;;;
3 ;;; Double Factorial, Incomplete Gamma function, ...
4 ;;;
5 ;;; This file will be extended with further functions related to the
6 ;;; Factorial and Gamma functions.
7 ;;;
8 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
9 ;;;
10 ;;; This file contains the following Maxima User functions:
11 ;;;
12 ;;; double_factorial(z)
13 ;;;
14 ;;; gamma_incomplete(a,z)
15 ;;; gamma_incomplete_generalized(a,z1,z2)
16 ;;; gamma_incomplete_regularized(a,z)
17 ;;;
18 ;;; log_gamma(z)
19 ;;;
20 ;;; erf(z)
21 ;;; erfc(z)
22 ;;; erfi(z)
23 ;;; erf_generalized(z1,z2)
24 ;;;
25 ;;; inverse_erf(z)
26 ;;; inverse_erfc(z)
27 ;;;
28 ;;; fresnel_s(z)
29 ;;; fresnel_c(z)
30 ;;;
31 ;;; beta_incomplete(a,b,z)
32 ;;; beta_incomplete_generalized(a,b,z1,z2)
33 ;;; beta_incomplete_regularized(a,b,z)
34 ;;;
35 ;;; Maxima User variable:
36 ;;;
37 ;;; $factorial_expand - Allows argument simplificaton for expressions like
38 ;;; double_factorial(n-1) and double_factorial(2*k+n)
39 ;;; $beta_expand - Switch on further expansions for the Beta functions
40 ;;;
41 ;;; $erf_representation - When T erfc, erfi, erf_generalized, fresnel_s
42 ;;; and fresnel_c are transformed to erf.
43 ;;; $erf_%iargs - Enable simplification of Erf and Erfi for
44 ;;; imaginary arguments
45 ;;; $hypergeometric_representation
46 ;;; - Enables transformation to a Hypergeometric
47 ;;; representation for fresnel_s and fresnel_c
48 ;;;
49 ;;; Maxima User variable (not definied in this file):
50 ;;;
51 ;;; $factlim - biggest integer for numerically evaluation
52 ;;; of the Double factorial
53 ;;; $gamma_expand - Expansion of the Gamma und Incomplete Gamma
54 ;;; function for some special cases
55 ;;;
56 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
57 ;;; This library is free software; you can redistribute it and/or modify it
58 ;;; under the terms of the GNU General Public License as published by the
59 ;;; Free Software Foundation; either version 2 of the License, or (at
60 ;;; your option) any later version.
61 ;;;
62 ;;; This library is distributed in the hope that it will be useful, but
63 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
64 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
65 ;;; Library General Public License for more details.
66 ;;;
67 ;;; You should have received a copy of the GNU General Public License along
68 ;;; with this library; if not, write to the Free Software
69 ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
70 ;;;
71 ;;; Copyright (C) 2008 Dieter Kaiser
72 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
74 (in-package :maxima)
76 (declare-top (special $factlim $gamma_expand))
78 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
80 (defmvar $factorial_expand nil)
81 (defmvar $beta_expand nil)
83 (defmvar $erf_representation nil
84 "When T erfc, erfi and erf_generalized are transformed to erf.")
86 (defmvar $erf_%iargs nil
87 "When T erf and erfi simplifies for an imaginary argument.")
89 (defmvar $hypergeometric_representation nil
90 "When T a transformation to a hypergeometric representation is done.")
92 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
93 ;;; The following functions test if numerical evaluation has to be done.
94 ;;; The functions should help to test for numerical evaluation more consitent
95 ;;; and without complicated conditional tests including more than one or two
96 ;;; arguments.
97 ;;;
98 ;;; The functions take a list of arguments. All arguments have to be a CL or
99 ;;; Maxima number. If all arguments are numbers we have two cases:
100 ;;; 1. $numer is T we return T. The function has to be evaluated numerically.
101 ;;; 2. One of the args is a float or a bigfloat. Evaluate numerically.
102 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
104 ;;; Test for numerically evaluation in float precision
106 (defun float-numerical-eval-p (&rest args)
107 (let ((flag nil))
108 (dolist (ll args)
109 (when (not (float-or-rational-p ll))
110 (return-from float-numerical-eval-p nil))
111 (when (floatp ll) (setq flag t)))
112 (if (or $numer flag) t nil)))
114 ;;; Test for numerically evaluation in complex float precision
116 (defun complex-float-numerical-eval-p (&rest args)
117 "Determine if ARGS consists of numerical values by determining if
118 the real and imaginary parts of each arg are nuemrical (but not
119 bigfloats). A non-NIL result is returned if at least one of args is
120 a floating-point value or if numer is true. If the result is
121 non-NIL, it is a list of the arguments reduced via rectform"
122 (let (flag values)
123 (dolist (ll args)
124 (destructuring-bind (rll . ill)
125 (trisplit ll)
126 (unless (and (float-or-rational-p rll)
127 (float-or-rational-p ill))
128 (return-from complex-float-numerical-eval-p nil))
129 ;; Always save the result from trisplit. But for backward
130 ;; compatibility, only set the flag if any item is a float.
131 (push (add rll (mul ill '$%i)) values)
132 (setf flag (or flag (or (floatp rll) (floatp ill))))))
133 (when (or $numer flag)
134 ;; Return the values in the same order as the args!
135 (nreverse values))))
137 ;;; Test for numerically evaluation in bigfloat precision
139 (defun bigfloat-numerical-eval-p (&rest args)
140 (let ((flag nil))
141 (dolist (ll args)
142 (when (not (bigfloat-or-number-p ll))
143 (return-from bigfloat-numerical-eval-p nil))
144 (when ($bfloatp ll)
145 (setq flag t)))
146 (if (or $numer flag) t nil)))
148 ;;; Test for numerically evaluation in complex bigfloat precision
150 (defun complex-bigfloat-numerical-eval-p (&rest args)
151 "Determine if ARGS consists of numerical values by determining if
152 the real and imaginary parts of each arg are nuemrical (including
153 bigfloats). A non-NIL result is returned if at least one of args is
154 a floating-point value or if numer is true. If the result is
155 non-NIL, it is a list of the arguments reduced via rectform."
157 (let (flag values)
158 (dolist (ll args)
159 (destructuring-bind (rll . ill)
160 (trisplit ll)
161 (unless (and (bigfloat-or-number-p rll)
162 (bigfloat-or-number-p ill))
163 (return-from complex-bigfloat-numerical-eval-p nil))
164 ;; Always save the result from trisplit. But for backward
165 ;; compatibility, only set the flag if any item is a bfloat.
166 (push (add rll (mul ill '$%i)) values)
167 (when (or ($bfloatp rll) ($bfloatp ill))
168 (setf flag t))))
169 (when (or $numer flag)
170 ;; Return the values in the same order as the args!
171 (nreverse values))))
173 ;;; Test for numerical evaluation in any precision, real or complex.
174 (defun numerical-eval-p (&rest args)
175 (or (apply 'float-numerical-eval-p args)
176 (apply 'complex-float-numerical-eval-p args)
177 (apply 'bigfloat-numerical-eval-p args)
178 (apply 'complex-bigfloat-numerical-eval-p args)))
180 ;;; Check for an integer or a float or bigfloat representation. When we
181 ;;; have a float or bigfloat representation return the integer value.
183 (defun integer-representation-p (x)
184 (let ((val nil))
185 (cond ((integerp x) x)
186 ((and (floatp x) (= 0 (nth-value 1 (truncate x))))
187 (nth-value 0 (truncate x)))
188 ((and ($bfloatp x)
189 (eq ($sign (sub (setq val ($truncate x)) x)) '$zero))
190 val)
191 (t nil))))
193 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
195 ;;; The changes to the parser to connect the operator !! to double_factorial(z)
197 ;(def-mheader |$!!| (%double_factorial))
199 ;(def-led (|$!!| 160.) (op left)
200 ; (list '$expr
201 ; (mheader '$!!)
202 ; (convert left '$expr)))
204 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
206 ;;; The implementation of the function Double factorial
208 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
210 (defmfun $double_factorial (z)
211 (simplify (list '(%double_factorial) z)))
213 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
215 ;;; Set properties to give full support to the parser and display
217 (defprop $double_factorial %double_factorial alias)
218 (defprop $double_factorial %double_factorial verb)
220 (defprop %double_factorial $double_factorial reversealias)
221 (defprop %double_factorial $double_factorial noun)
223 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
225 ;;; Double factorial is a simplifying function
227 (defprop %double_factorial simp-double-factorial operators)
229 ;;; Double factorial distributes over bags
231 (defprop %double_factorial (mlist $matrix mequal) distribute_over)
233 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
235 ;;; Double factorial has mirror symmetry
237 (defprop %double_factorial t commutes-with-conjugate)
239 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
241 ;;; Differentiation of Double factorial
243 (defprop %double_factorial
244 ((z)
245 ((mtimes)
246 ((rat) 1 2)
247 ((%double_factorial) z)
248 ((mplus)
249 ((%log) 2)
250 ((mqapply)
251 (($psi array) 0)
252 ((mplus) 1 ((mtimes) ((rat) 1 2) z)))
253 ((mtimes)
254 ((rat) 1 2) $%pi
255 ((%log) ((mtimes) 2 ((mexpt) $%pi -1)))
256 ((%sin) ((mtimes) $%pi z))))))
257 grad)
259 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
261 (defun simp-double-factorial (expr z simpflag)
262 (oneargcheck expr)
263 (setq z (simpcheck (cadr expr) simpflag))
264 (cond
265 ((and (fixnump z) (> z -1) (or (minusp $factlim) (< z $factlim)))
266 ;; Positive Integer less then $factlim or $factlim is -1. Call gfact.
267 (gfact z (floor (/ z 2)) 2))
269 ((and (mnump z)
270 (eq ($sign z) '$neg)
271 (zerop1 (sub (simplify (list '(%truncate) (div z 2))) (div z 2))))
272 ;; Even negative integer or real representation. Not defined.
273 (simp-domain-error
274 (intl:gettext
275 "double_factorial: double_factorial(~:M) is undefined.") z))
277 ((or (integerp z) ; at this point odd negative integer. Evaluate.
278 (complex-float-numerical-eval-p z))
279 (cond
280 ((and (integerp z) (= z -1)) 1) ; Special cases -1 and -3
281 ((and (integerp z) (= z -3)) -1)
283 ;; Odd negative integer, float or complex float.
284 (complexify
285 (double-factorial
286 (complex ($float ($realpart z)) ($float ($imagpart z))))))))
288 ((and (not (ratnump z))
289 (complex-bigfloat-numerical-eval-p z))
290 ;; bigfloat or complex bigfloat.
291 (bfloat-double-factorial
292 (add ($bfloat ($realpart z)) (mul '$%i ($bfloat ($imagpart z))))))
294 ;; double_factorial(inf) -> inf
295 ((eq z '$inf) '$inf)
297 ((and $factorial_expand
298 (mplusp z)
299 (integerp (cadr z)))
300 (let ((k (cadr z))
301 (n (simplify (cons '(mplus) (cddr z)))))
302 (cond
303 ((= k -1)
304 ;; Special case double_factorial(n-1)
305 ;; Not sure if this simplification is useful.
306 (div (simplify (list '(mfactorial) n))
307 (simplify (list '(%double_factorial) n))))
308 ((= k (* 2 (truncate (/ k 2))))
309 ;; Special case double_factorial(2*k+n), k integer
310 (setq k (/ k 2))
311 ($factor ; we get more simple expression when factoring
312 (mul
313 (power 2 k)
314 (simplify (list '($pochhammer) (add (div n 2) 1) k))
315 (simplify (list '(%double_factorial) n)))))
317 (eqtest (list '(%double_factorial) z) expr)))))
320 (eqtest (list '(%double_factorial) z) expr))))
322 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
323 ;;; Double factorial for a complex float argument. The result is a CL complex.
324 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
326 (defun double-factorial (z)
327 (let ((pival (float pi)))
329 (expt
330 (/ 2 pival)
331 (/ (- 1 (cos (* pival z))) 4))
332 (expt 2e0 (/ z 2))
333 (gamma-lanczos (+ 1 (/ z 2))))))
335 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
336 ;;; Double factorial for a bigfloat or complex bigfloat argument
337 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
339 (defun bfloat-double-factorial (z)
340 (let* ((pival ($bfloat '$%pi))
341 (bigfloat1 ($bfloat bigfloatone))
342 (bigfloat2 (add bigfloat1 bigfloat1))
343 (bigfloat4 (add bigfloat2 bigfloat2))
344 ($ratprint nil))
345 (cmul
346 (cpower
347 (cdiv bigfloat2 pival)
348 (cdiv (sub bigfloat1
349 (simplify (list '(%cos) (cmul pival z)))) bigfloat4))
350 (cmul
351 (cpower bigfloat2 (cdiv z bigfloat2))
352 (simplify (list '(%gamma) (add bigfloat1 (cdiv z bigfloat2))))))))
354 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
356 ;;; The implementation of the Incomplete Gamma function
358 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
360 (defvar *debug-gamma* nil)
362 (defmfun $gamma_incomplete (a z)
363 (simplify (list '(%gamma_incomplete) a z)))
365 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
367 ;;; Set properties to give full support to the parser and display
369 (defprop $gamma_incomplete %gamma_incomplete alias)
370 (defprop $gamma_incomplete %gamma_incomplete verb)
372 (defprop %gamma_incomplete $gamma_incomplete reversealias)
373 (defprop %gamma_incomplete $gamma_incomplete noun)
375 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
377 ;;; Incomplete Gamma function is a simplifying function
379 (defprop %gamma_incomplete simp-gamma-incomplete operators)
381 ;;; Incomplete Gamma distributes over bags
383 (defprop %gamma_incomplete (mlist $matrix mequal) distribute_over)
385 ;;; Incomplete Gamma function has not mirror symmetry for z on the negative
386 ;;; real axis. We support a conjugate-function which test this case.
388 (defprop %gamma_incomplete conjugate-gamma-incomplete conjugate-function)
390 (defun conjugate-gamma-incomplete (args)
391 (let ((a (first args)) (z (second args)))
392 (cond ((off-negative-real-axisp z)
393 ;; Definitely not on the negative real axis for z. Mirror symmetry.
394 (simplify
395 (list
396 '(%gamma_incomplete)
397 (simplify (list '($conjugate) a))
398 (simplify (list '($conjugate) z)))))
400 ;; On the negative real axis or no information. Unsimplified.
401 (list
402 '($conjugate simp)
403 (simplify (list '(%gamma_incomplete) a z)))))))
405 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
407 ;;; Derivative of the Incomplete Gamma function
409 (putprop '%gamma_incomplete
410 `((a z)
411 ,(lambda (a z)
412 (cond ((member ($sign a) '($pos $pz))
413 ;; The derivative wrt a in terms of hypergeometric_regularized 2F2
414 ;; function and the Generalized Incomplete Gamma function
415 ;; (functions.wolfram.com), only for a>0.
416 `((mplus)
417 ((mtimes)
418 ((mexpt) ((%gamma) ,a) 2)
419 ((mexpt) ,z ,a)
420 (($hypergeometric_regularized)
421 ((mlist) ,a ,a)
422 ((mlist) ((mplus) 1 ,a) ((mplus) 1 ,a))
423 ((mtimes) -1 ,z)))
424 ((mtimes) -1
425 ((%gamma_incomplete_generalized) ,a 0 ,z)
426 ((%log) ,z))
427 ((mtimes)
428 ((%gamma) ,a)
429 ((mqapply) (($psi array) 0) ,a))))
431 ;; No derivative. Maxima generates a noun form.
432 nil)))
433 ;; The derivative wrt z
434 ((mtimes) -1
435 ((mexpt) $%e ((mtimes) -1 z))
436 ((mexpt) z ((mplus) -1 a))))
437 'grad)
439 ;;; Integral of the Incomplete Gamma function
441 (defprop %gamma_incomplete
442 ((a z)
444 ((mplus)
445 ((mtimes) -1 ((%gamma_incomplete) ((mplus) 1 a) z))
446 ((mtimes) ((%gamma_incomplete) a z) z)))
447 integral)
449 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
451 ;;; We support a simplim%function. The function is looked up in simplimit and
452 ;;; handles specific values of the function.
454 (defprop %gamma_incomplete simplim%gamma_incomplete simplim%function)
456 (defun simplim%gamma_incomplete (expr var val)
457 ;; Look for the limit of the arguments.
458 (let ((a (limit (cadr expr) var val 'think))
459 (z (limit (caddr expr) var val 'think)))
460 (cond
462 ((eq z '$infinity) ;; http://dlmf.nist.gov/8.11#i
463 (cond ((and (zerop1 ($realpart (caddr expr)))
464 (eq ($csign (m+ -1 (cadr expr))) '$neg))
466 (t (throw 'limit t))))
468 ;; Handle an argument 0 at this place.
469 ((or (zerop1 z)
470 (eq z '$zeroa)
471 (eq z '$zerob))
472 (let ((sgn ($sign ($realpart a))))
473 (cond ((zerop1 a) '$inf)
474 ((member sgn '($neg $nz)) '$infinity)
475 ;; Call the simplifier of the function.
476 (t (simplify (list '(%gamma_incomplete) a z))))))
478 ;; All other cases are handled by the simplifier of the function.
479 (simplify (list '(%gamma_incomplete) a z))))))
481 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
483 (defmfun $gamma_incomplete_lower (a z)
484 (simplify (list '(%gamma_incomplete_lower) a z)))
486 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
488 (defprop $gamma_incomplete_lower %gamma_incomplete_lower alias)
489 (defprop $gamma_incomplete_lower %gamma_incomplete_lower verb)
491 (defprop %gamma_incomplete_lower $gamma_incomplete_lower reversealias)
492 (defprop %gamma_incomplete_lower $gamma_incomplete_lower noun)
494 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
496 (defprop %gamma_incomplete_lower simp-gamma-incomplete-lower operators)
498 ;;; distribute over bags (aggregates)
500 (defprop %gamma_incomplete_lower (mlist $matrix mequal) distribute_over)
502 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
504 ;; (defprop %gamma_incomplete_lower ??? grad) WHAT TO PUT HERE ??
506 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
508 (defun simp-gamma-incomplete-lower (expr ignored simpflag)
509 (declare (ignore ignored))
510 (twoargcheck expr)
511 (let ((a (simpcheck (cadr expr) simpflag))
512 (z (simpcheck (caddr expr) simpflag)))
513 (cond
514 ((or
515 (float-numerical-eval-p a z)
516 (complex-float-numerical-eval-p a z)
517 (bigfloat-numerical-eval-p a z)
518 (complex-bigfloat-numerical-eval-p a z))
519 (take '(%gamma_incomplete_generalized) a 0 z))
521 (gamma-incomplete-lower a z)))))
523 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
525 (defun simp-gamma-incomplete (expr ignored simpflag)
526 (declare (ignore ignored))
527 (twoargcheck expr)
528 (let ((a (simpcheck (cadr expr) simpflag))
529 (z (simpcheck (caddr expr) simpflag))
530 ($simpsum t)
531 (ratorder))
532 (cond
534 ;; Check for specific values
536 ((zerop1 z)
537 ;; gamma_incomplete(v,0) is gamma(v) only if the realpart(v) >
538 ;; 0. If realpart(v) <= 0, gamma_incomplete is undefined. For
539 ;; all other cases, return the noun form.
540 (let ((sgn ($sign ($realpart a))))
541 (cond ((member sgn '($neg $zero))
542 (simp-domain-error
543 (intl:gettext
544 "gamma_incomplete: gamma_incomplete(~:M,~:M) is undefined.")
545 a z))
546 ((member sgn '($pos $pz)) ($gamma a))
547 (t (eqtest (list '(%gamma_incomplete) a z) expr)))))
549 ((eq z '$inf) 0)
550 ((and (eq z '$minf)
551 (eql a 0))
552 '$infinity)
554 ;; Check for numerical evaluation in Float or Bigfloat precision
556 ((float-numerical-eval-p a z)
557 (when *debug-gamma*
558 (format t "~&SIMP-GAMMA-INCOMPLETE in float-numerical-eval-p~%"))
559 ;; a and z are Maxima numbers, at least one has a float value
560 (let ((a ($float a))
561 (z ($float z)))
562 (cond
563 ((or (= a 0.0)
564 (and (= 0 (- a (truncate a)))
565 (< a 0.0)))
566 ;; a is zero or a negative float representing an integer.
567 ;; For these cases the numerical routines of gamma-incomplete
568 ;; do not work. Call the numerical routine for the Exponential
569 ;; Integral E(n,z). The routine is called with a positive integer!.
570 (setq a (truncate a))
571 (complexify (* (expt z a) (expintegral-e (- 1 a) z))))
573 (complexify (gamma-incomplete a z))))))
575 ((complex-float-numerical-eval-p a z)
576 (when *debug-gamma*
577 (format t
578 "~&SIMP-GAMMA-INCOMPLETE in complex-float-numerical-eval-p~%"))
579 ;; a and z are Maxima numbers, at least one is a complex value and
580 ;; we have at least one float part
581 (let ((ca (complex ($float ($realpart a)) ($float ($imagpart a))))
582 (cz (complex ($float ($realpart z)) ($float ($imagpart z)))))
583 (cond
584 ((and (= (imagpart ca) 0.0)
585 (or (= (realpart ca) 0.0)
586 (and (= 0 (- (realpart ca) (truncate (realpart ca))))
587 (< (realpart ca) 0.0))))
588 ;; Call expintegral-e. See comment above.
589 (setq ca (truncate (realpart ca)))
590 (complexify (* (expt cz ca) (expintegral-e (- 1 ca) cz))))
592 (complexify (gamma-incomplete ca cz))))))
594 ((bigfloat-numerical-eval-p a z)
595 (when *debug-gamma*
596 (format t "~&SIMP-GAMMA-INCOMPLETE in bigfloat-numerical-eval-p~%"))
597 (let ((a ($bfloat a))
598 (z ($bfloat z)))
599 (cond
600 ((or (eq ($sign a) '$zero)
601 (and (eq ($sign (sub a ($truncate a))) '$zero)
602 (eq ($sign a) '$neg)))
603 ;; Call bfloat-expintegral-e. See comment above.
604 (setq a ($truncate a))
605 ($rectform (mul (power z a) (bfloat-expintegral-e (- 1 a) z))))
607 (bfloat-gamma-incomplete a z)))))
609 ((complex-bigfloat-numerical-eval-p a z)
610 (when *debug-gamma*
611 (format t
612 "~&SIMP-GAMMA-INCOMPLETE in complex-bigfloat-numerical-eval-p~%"))
613 (let ((ca (add ($bfloat ($realpart a))
614 (mul '$%i ($bfloat ($imagpart a)))))
615 (cz (add ($bfloat ($realpart z))
616 (mul '$%i ($bfloat ($imagpart z))))))
617 (cond
618 ((and (eq ($sign ($imagpart ca)) '$zero)
619 (or (eq ($sign ($realpart ca)) '$zero)
620 (and (eq ($sign (sub ($realpart ca)
621 ($truncate ($realpart ca))))
622 '$zero)
623 (eq ($sign ($realpart ca)) '$neg))))
624 ;; Call bfloat-expintegral-e. See comment above.
625 (when *debug-gamma*
626 (format t
627 "~& bigfloat-numerical-eval-p calls bfloat-expintegral-e~%"))
628 (setq a ($truncate ($realpart a)))
629 ($rectform (mul (power cz a)
630 (bfloat-expintegral-e (- 1 a) cz))))
632 (complex-bfloat-gamma-incomplete ca cz)))))
634 ;; Check for transformations and argument simplification
636 ((and $gamma_expand (integerp a))
637 ;; Integer or a symbol declared to be an integer. Expand in a series.
638 (let ((sgn ($sign a)))
639 (cond
640 ((eq sgn '$zero)
641 (add
642 (mul -1
643 (simplify (list '(%expintegral_ei) (mul -1 z))))
644 (mul
645 '((rat simp) 1 2)
646 (sub
647 (simplify (list '(%log) (mul -1 z)))
648 (simplify (list '(%log) (div -1 z)))))
649 (mul -1 (simplify (list '(%log) z)))))
650 ((member sgn '($pos $pz))
651 (mul
652 (simplify (list '(%gamma) a))
653 (power '$%e (mul -1 z))
654 (let ((index (gensumindex)))
655 (simpsum1
656 (div
657 (power z index)
658 (let (($gamma_expand nil))
659 ;; Simplify gamma, but do not expand to avoid division
660 ;; by zero.
661 (simplify (list '(%gamma) (add index 1)))))
662 index 0 (sub a 1)))))
663 ((member sgn '($neg $nz))
664 (sub
665 (mul
666 (div
667 (power -1 (add (mul -1 a) -1))
668 (simplify (list '(%gamma) (add (mul -1 a) 1))))
669 (add
670 (simplify (list '(%expintegral_ei) (mul -1 z)))
671 (mul
672 '((rat simp) -1 2)
673 (sub
674 (simplify (list '(%log) (mul -1 z)))
675 (simplify (list '(%log) (div -1 z)))))
676 (simplify (list '(%log) z))))
677 (mul
678 (power '$%e (mul -1 z))
679 (let ((index (gensumindex)))
680 (simpsum1
681 (div
682 (power z (add index a -1))
683 (simplify (list '($pochhammer) a index)))
684 index 1 (mul -1 a))))))
685 (t (eqtest (list '(%gamma_incomplete) a z) expr)))))
687 ((and $gamma_expand (setq ratorder (max-numeric-ratio-p a 2)))
688 ;; We have a half integral order and $gamma_expand is not NIL.
689 ;; We expand in a series with the Erfc function
690 (setq ratorder (- ratorder (/ 1 2)))
691 (cond
692 ((equal ratorder 0)
693 (mul
694 (power '$%pi '((rat simp) 1 2))
695 (simplify (list '(%erfc) (power z '((rat simp) 1 2))))))
696 ((> ratorder 0)
697 (sub
698 (mul
699 (simplify (list '(%gamma) a))
700 (simplify (list '(%erfc) (power z '((rat simp) 1 2)))))
701 (mul
702 (power -1 (sub ratorder 1))
703 (power '$%e (mul -1 z))
704 (power z '((rat simp) 1 2))
705 (let ((index (gensumindex)))
706 (simpsum1
707 (mul -1 ; we get more simple results
708 (simplify ; when multiplying with -1
709 (list
710 '($pochhammer)
711 (sub '((rat simp) 1 2) ratorder)
712 (sub ratorder (add index 1))))
713 (power (mul -1 z) index))
714 index 0 (sub ratorder 1))))))
715 ((< ratorder 0)
716 (setq ratorder (- ratorder))
717 (sub
718 (div
719 (mul
720 (power -1 ratorder)
721 (power '$%pi '((rat simp) 1 2))
722 (simplify (list '(%erfc) (power z '((rat simp) 1 2)))))
723 (simplify (list '($pochhammer) '((rat simp) 1 2) ratorder)))
724 (mul
725 (power z (sub '((rat simp) 1 2) ratorder))
726 (power '$%e (mul -1 z))
727 (let ((index (gensumindex)))
728 (simpsum1
729 (div
730 (power z index)
731 (simplify
732 (list
733 '($pochhammer)
734 (sub '((rat simp) 1 2) ratorder)
735 (add index 1))))
736 index 0 (sub ratorder 1))))))))
738 ((and $gamma_expand (mplusp a) (integerp (cadr a)))
739 (let ((n (cadr a))
740 (a (simplify (cons '(mplus) (cddr a)))))
741 (cond
742 ((> n 0)
743 (add
744 (mul
745 (simplify (list '($pochhammer) a n))
746 (simplify (list '(%gamma_incomplete) a z)))
747 (mul
748 (power '$%e (mul -1 z))
749 (power z (add a n -1))
750 (let ((index (gensumindex)))
751 (simpsum1
752 (mul
753 (simplify
754 (list
755 '($pochhammer) (add 1 (mul -1 a) (mul -1 n)) index))
756 (power (mul -1 z) (mul -1 index)))
757 index 0 (add n -1))))))
758 ((< n 0)
759 (setq n (- n))
760 (sub
761 (div
762 (mul
763 (power -1 n)
764 (simplify (list '(%gamma_incomplete) a z)))
765 (simplify (list '($pochhammer) (sub 1 a) n)))
766 (mul
767 (power '$%e (mul -1 z))
768 (power z (sub a n))
769 (let ((index (gensumindex)))
770 (simpsum1
771 (div
772 (power z index)
773 (simplify (list '($pochhammer) (sub a n) (add index 1))))
774 index 0 (sub n 1)))))))))
776 (t (eqtest (list '(%gamma_incomplete) a z) expr)))))
778 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
779 ;;; Numerical evaluation of the Incomplete Gamma function
781 ;;; gamma-incomplete (a,z) - real and complex double float
782 ;;; bfloat-gamma-incomplete (a z) - bigfloat
783 ;;; complex-bfloat-gamma-incomplete (a z) - complex bigfloat
785 ;;; Expansion in a power series for abs(x) < R, where R is
786 ;;; *gamma-radius* + real(a) if real(a) > 0 or *gamma-radius*
787 ;;; otherwise.
789 ;;; (A&S 6.5.29):
791 ;;; inf
792 ;;; ===
793 ;;; \ gamma(a)
794 ;;; gamma(a,z) = exp(-x)*z^a * > ------------ * z^n
795 ;;; / gamma(a+1+n)
796 ;;; ===
797 ;;; n=0
799 ;;; This expansion does not work for an integer a<=0, because the Gamma function
800 ;;; in the denominator is not defined for a=0 and negative integers. For this
801 ;;; case we use the Exponential Integral E for numerically evaluation. The
802 ;;; Incomplete Gamma function and the Exponential integral are connected by
804 ;;; gamma(a,z) = z^a * expintegral_e(1-a,z)
806 ;;; When the series is not used, two forms of the continued fraction
807 ;;; are used. When z is not near the negative real axis use the
808 ;;; continued fractions (A&S 6.5.31):
810 ;;; 1 1-a 1 2-a 2
811 ;;; gamma(a,z) = exp(-z) z^a *( -- --- --- --- --- ... )
812 ;;; z+ 1+ z+ 1+ z+
814 ;;; The accuracy is controlled by *gamma-incomplete-eps* for double float
815 ;;; precision. For bigfloat precision epsilon is 10^(-$fpprec). The expansions
816 ;;; in a power series or continued fractions stops if *gamma-incomplete-maxit*
817 ;;; is exceeded and an Maxima error is thrown.
819 ;;; The above fraction does not converge on the negative real axis and
820 ;;; converges very slowly near the axis. In this case, use the
821 ;;; relationship
823 ;;; gamma(a,z) = gamma(a) - gamma_lower(a,z)
825 ;;; The continued fraction for gamma_incomplete_lower(a,z) is
826 ;;; (http://functions.wolfram.com/06.06.10.0009.01):
828 ;;; gamma_lower(a,z) = exp(-z) * z^a / cf(a,z)
830 ;;; where
832 ;;; -a*z z (a+1)*z 2*z (a+2)*z
833 ;;; cf(a,z) = a + ---- ---- ------- ---- -------
834 ;;; a+1+ a+2- a+3+ a+4- a+5+
836 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
838 (defvar *gamma-incomplete-maxit* 10000)
839 (defvar *gamma-incomplete-eps* (* 2 flonum-epsilon))
840 (defvar *gamma-incomplete-min* 1.0e-32)
842 (defvar *gamma-radius* 1.0
843 "Controls the radius within a series expansion is done.")
844 (defvar *gamma-imag* 1.0)
846 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
848 ;;; The numerical evaluation for CL float or complex values a and x
849 ;;; When the flag regularized is T, the result is divided by gamma(a) and
850 ;;; Maxima returns the numercial result for gamma_incomplete_regularized
852 (defun gamma-incomplete (a x &optional (regularized nil))
853 (setq x (+ x (cond ((complexp x) #C(0.0 0.0)) ((realp x) 0.0))))
855 (let ((factor
856 ;; Compute the factor needed to scale the series or continued
857 ;; fraction. This is x^a*exp(-x) or x^a*exp(-x)/gamma(a)
858 ;; depending on whether we want a non-regularized or
859 ;; regularized form. We want to compute the factor carefully
860 ;; to avoid unnecessary overflow if possible.
861 (cond (regularized
862 (or (try-float-computation
863 #'(lambda ()
864 ;; gammafloat is more accurate for real
865 ;; values of a.
866 (cond ((complexp a)
867 (/ (* (expt x a) (exp (- x)))
868 (gamma-lanczos a)))
870 (/ (* (expt x a) (exp (- x)))
871 (gammafloat a))))))
872 ;; Easy way failed. Use logs to compute the
873 ;; result. This loses some precision, especially
874 ;; for large values of a and/or x because we use
875 ;; many bits to hold the exponent part.
876 (exp (- (* a (log x))
878 (log-gamma-lanczos (if (complexp a)
880 (complex a)))))))
882 (or (try-float-computation
883 #'(lambda ()
884 (* (expt x a) (exp (- x)))))
885 ;; Easy way failed, so use the log form.
886 (exp (- (* a (log x))
887 x)))))))
888 (multiple-value-bind (result lower-incomplete-tail-p)
889 (%gamma-incomplete a x)
890 (cond (lower-incomplete-tail-p
891 ;; %gamma-incomplete compute the lower incomplete gamma
892 ;; function, so we need to subtract that from gamma(a),
893 ;; more or less.
894 (cond (regularized
895 (- 1 (* result factor)))
896 ((complexp a)
897 (- (gamma-lanczos a) (* result factor)))
899 (- (gammafloat a) (* result factor)))))
901 ;; Continued fraction used. Just multiply by the factor
902 ;; to get the final result.
903 (* factor result))))))
905 ;; Compute the key part of the gamma incomplete function using either
906 ;; a series expression or a continued fraction expression. Two values
907 ;; are returned: the value itself and a boolean, indicating what the
908 ;; computed value is. If the boolean non-NIL, then the computed value
909 ;; is the lower incomplete gamma function.
910 (defun %gamma-incomplete (a x)
911 (let ((gm-maxit *gamma-incomplete-maxit*)
912 (gm-eps *gamma-incomplete-eps*)
913 (gm-min *gamma-incomplete-min*))
914 (when *debug-gamma*
915 (format t "~&GAMMA-INCOMPLETE with ~A and ~A~%" a x))
916 (cond
917 ;; The series expansion is done for x within a circle of radius
918 ;; R, where R = *gamma-radius*+(realpart(a)) for realpart(a) > 0
919 ;; and R = *gamma-radisu* for realpart(a) < 0. Otherwise a
920 ;; continued fraction is used.
921 ((and (> (abs x) (+ *gamma-radius*
922 (if (> (realpart a) 0.0) (realpart a) 0.0))))
923 (cond ((and (< (realpart x) 0)
924 (< (abs (imagpart x))
925 (* *gamma-imag* (abs (realpart x)))))
926 ;; For x near the negative real axis, use the
927 ;; relationship gamma_incomplete(a,z) = gamma(a) -
928 ;; gamma_incomplete_lower(a,z), where
929 ;; gamma_incomplete_lower(a,z) is the lower poart of the
930 ;; incomplete gamma function. We can evaluate that
931 ;; using a continued fraction from
932 ;; http://functions.wolfram.com/06.06.10.0009.01. (Note
933 ;; that the alternative fraction,
934 ;; http://functions.wolfram.com/06.06.10.0007.01,
935 ;; appears to be less accurate.)
937 ;; Also note that this appears to be valid for all
938 ;; values of x (real or complex), but we don't want to
939 ;; use this everywhere for gamma_incomplete. Consider
940 ;; what happens for large real x. gamma_incomplete(a,x)
941 ;; is small, but gamma_incomplete(a,x) = gamma(x) - cf
942 ;; will have large roundoff errors.
943 (when *debug-gamma*
944 (format t "~&GAMMA-INCOMPLETE in continued fractions for lower integral~%"))
945 (let ((a (bigfloat:to a))
946 (x (bigfloat:to x))
947 (bigfloat::*debug-cf-eval* *debug-gamma*)
948 (bigfloat::*max-cf-iterations* *gamma-incomplete-maxit*))
949 (values (/ (bigfloat::lentz #'(lambda (n)
950 (+ n a))
951 #'(lambda (n)
952 (if (evenp n)
953 (* (ash n -1) x)
954 (- (* (+ a (ash n -1)) x))))))
955 t)))
957 ;; Expansion in continued fractions for gamma_incomplete.
958 (when *debug-gamma*
959 (format t "~&GAMMA-INCOMPLETE in continued fractions~%"))
960 (do* ((i 1 (+ i 1))
961 (an (- a 1.0) (* i (- a i)))
962 (b (+ 3.0 x (- a)) (+ b 2.0))
963 (c (/ 1.0 gm-min))
964 (d (/ 1.0 (- b 2.0)))
965 (h d)
966 (del 0.0))
967 ((> i gm-maxit)
968 (merror (intl:gettext "gamma_incomplete: continued fractions failed for gamma_incomplete(~:M, ~:M).") a x))
969 (setq d (+ (* an d) b))
970 (when (< (abs d) gm-min) (setq d gm-min))
971 (setq c (+ b (/ an c)))
972 (when (< (abs c) gm-min) (setq c gm-min))
973 (setq d (/ 1.0 d))
974 (setq del (* d c))
975 (setq h (* h del))
976 (when (< (abs (- del 1.0)) gm-eps)
977 ;; Return nil to indicate we used the continued fraction.
978 (return (values h nil)))))))
980 ;; Expansion in a series
981 (when *debug-gamma*
982 (format t "~&GAMMA-INCOMPLETE in series~%"))
983 (do* ((i 1 (+ i 1))
984 (ap a (+ ap 1.0))
985 (del (/ 1.0 a) (* del (/ x ap)))
986 (sum del (+ sum del)))
987 ((> i gm-maxit)
988 (merror (intl:gettext "gamma_incomplete: series expansion failed for gamma_incomplete(~:M, ~:M).") a x))
989 (when (< (abs del) (* (abs sum) gm-eps))
990 (when *debug-gamma* (format t "~&Series converged.~%"))
991 ;; Return T to indicate we used the series and the series
992 ;; is for the integral from 0 to x, not x to inf.
993 (return (values sum t))))))))
995 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
997 ;;; This function is called for a and x real
999 (defun bfloat-gamma-incomplete (a x)
1000 (let* ((gm-maxit *gamma-incomplete-maxit*)
1001 (gm-eps (power ($bfloat 10.0) (- $fpprec)))
1002 (gm-min (mul gm-eps gm-eps))
1003 ($ratprint nil))
1004 (cond
1005 ;; The series expansion is done for x within a circle of radius
1006 ;; R, where R = *gamma-radius*+(realpart(a)) for realpart(a) > 0
1007 ;; and R = *gamma-radisu* for realpart(a) < 0. Otherwise a
1008 ;; continued fraction is used.
1009 ((eq ($sign (sub (simplify (list '(mabs) x))
1010 (add *gamma-radius*
1011 (if (eq ($sign a) '$pos) a 0.0))))
1012 '$pos)
1013 (cond
1014 ((and (eq ($sign x) '$pos))
1015 ;; Expansion in continued fractions of the Incomplete Gamma function
1016 (do* ((i 1 (+ i 1))
1017 (an (sub a 1.0) (mul i (sub a i)))
1018 (b (add 3.0 x (mul -1 a)) (add b 2.0))
1019 (c (div 1.0 gm-min))
1020 (d (div 1.0 (sub b 2.0)))
1021 (h d)
1022 (del 0.0))
1023 ((> i gm-maxit)
1024 (merror (intl:gettext "gamma_incomplete: continued fractions failed for gamma_incomplete(~:M, ~:M).") a x))
1025 (when *debug-gamma*
1026 (format t "~&in continued fractions:~%")
1027 (mformat t "~& : i = ~M~%" i)
1028 (mformat t "~& : h = ~M~%" h))
1029 (setq d (add (mul an d) b))
1030 (when (eq ($sign (sub (simplify (list '(mabs) d)) gm-min)) '$neg)
1031 (setq d gm-min))
1032 (setq c (add b (div an c)))
1033 (when (eq ($sign (sub (simplify (list '(mabs) c)) gm-min)) '$neg)
1034 (setq c gm-min))
1035 (setq d (div 1.0 d))
1036 (setq del (mul d c))
1037 (setq h (mul h del))
1038 (when (eq ($sign (sub (simplify (list '(mabs) (sub del 1.0))) gm-eps))
1039 '$neg)
1040 (return
1041 (mul h
1042 (power x a)
1043 (power ($bfloat '$%e) (mul -1 x)))))))
1045 ;; Expand to multiply everything out.
1046 ($expand
1047 ;; Expansion in continued fraction for the lower incomplete gamma.
1048 (sub (simplify (list '(%gamma) a))
1049 ;; NOTE: We want (power x a) instead of bigfloat:expt
1050 ;; because this preserves how maxima computes x^a when
1051 ;; x is negative and a is rational. For, example
1052 ;; (-8)^(1/2) is -2. bigfloat:expt returns the
1053 ;; principal value.
1054 (mul (power x a)
1055 (power ($bfloat '$%e) (mul -1 x))
1056 (let ((a (bigfloat:to a))
1057 (x (bigfloat:to x)))
1058 (to (bigfloat:/
1059 (bigfloat:lentz
1060 #'(lambda (n)
1061 (bigfloat:+ n a))
1062 #'(lambda (n)
1063 (if (evenp n)
1064 (bigfloat:* (ash n -1) x)
1065 (bigfloat:- (bigfloat:* (bigfloat:+ a (ash n -1))
1066 x))))))))))))))
1069 ;; Series expansion of the Incomplete Gamma function
1070 (do* ((i 1 (+ i 1))
1071 (ap a (add ap 1.0))
1072 (del (div 1.0 a) (mul del (div x ap)))
1073 (sum del (add sum del)))
1074 ((> i gm-maxit)
1075 (merror (intl:gettext "gamma_incomplete: series expansion failed for gamma_incomplete(~:M, ~:M).") a x))
1076 (when *debug-gamma*
1077 (format t "~&GAMMA-INCOMPLETE in series:~%")
1078 (mformat t "~& : i = ~M~%" i)
1079 (mformat t "~& : ap = ~M~%" ap)
1080 (mformat t "~& : x/ap = ~M~%" (div x ap))
1081 (mformat t "~& : del = ~M~%" del)
1082 (mformat t "~& : sum = ~M~%" sum))
1083 (when (eq ($sign (sub (simplify (list '(mabs) del))
1084 (mul (simplify (list '(mabs) sum)) gm-eps)))
1085 '$neg)
1086 (when *debug-gamma* (mformat t "~&Series converged to ~M.~%" sum))
1087 (return
1088 (sub (simplify (list '(%gamma) a))
1089 ($rectform
1090 (mul sum
1091 (power x a)
1092 (power ($bfloat '$%e) (mul -1 x))))))))))))
1094 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1096 (defun complex-bfloat-gamma-incomplete (a x)
1097 (let* ((gm-maxit *gamma-incomplete-maxit*)
1098 (gm-eps (power ($bfloat 10.0) (- $fpprec)))
1099 (gm-min (mul gm-eps gm-eps))
1100 ($ratprint nil))
1101 (when *debug-gamma*
1102 (format t "~&COMPLEX-BFLOAT-GAMMA-INCOMPLETE~%")
1103 (format t " : a = ~A~%" a)
1104 (format t " : x = ~A~%" x))
1105 (cond
1106 ;; The series expansion is done for x within a circle of radius
1107 ;; R, where R = *gamma-radius*+(realpart(a)) for realpart(a) > 0
1108 ;; and R = *gamma-radisu* for realpart(a) < 0. Otherwise a
1109 ;; continued fraction is used.
1110 ((and (eq ($sign (sub (simplify (list '(mabs) x))
1111 (add *gamma-radius*
1112 (if (eq ($sign ($realpart a)) '$pos)
1113 ($realpart a)
1114 0.0))))
1115 '$pos))
1116 (cond
1117 ((not (and (eq ($sign ($realpart x)) '$neg)
1118 (eq ($sign (sub (simplify (list '(mabs) ($imagpart x)))
1119 (simplify (list '(mabs) ($realpart x)))))
1120 '$neg)))
1121 ;; Expansion in continued fractions of the Incomplete Gamma function
1122 (when *debug-gamma*
1123 (format t "~&in continued fractions:~%"))
1124 (do* ((i 1 (+ i 1))
1125 (an (sub a 1.0) (mul i (sub a i)))
1126 (b (add 3.0 x (mul -1 a)) (add b 2.0))
1127 (c (cdiv 1.0 gm-min))
1128 (d (cdiv 1.0 (sub b 2.0)))
1129 (h d)
1130 (del 0.0))
1131 ((> i gm-maxit)
1132 (merror (intl:gettext "gamma_incomplete: continued fractions failed for gamma_incomplete(~:M, ~:M).") a x))
1133 (setq d (add (cmul an d) b))
1134 (when (eq ($sign (sub (simplify (list '(mabs) d)) gm-min)) '$neg)
1135 (setq d gm-min))
1136 (setq c (add b (cdiv an c)))
1137 (when (eq ($sign (sub (simplify (list '(mabs) c)) gm-min)) '$neg)
1138 (setq c gm-min))
1139 (setq d (cdiv 1.0 d))
1140 (setq del (cmul d c))
1141 (setq h (cmul h del))
1142 (when (eq ($sign (sub (simplify (list '(mabs) (sub del 1.0)))
1143 gm-eps))
1144 '$neg)
1145 (return
1146 ($bfloat ; force evaluation of expressions with sin or cos
1147 (cmul h
1148 (cmul
1149 (cpower x a)
1150 (cpower ($bfloat '$%e) ($bfloat (mul -1 x))))))))))
1152 ;; Expand to multiply everything out.
1153 ($expand
1154 ;; Expansion in continued fraction for the lower incomplete gamma.
1155 (sub ($rectform (simplify (list '(%gamma) a)))
1156 ;; NOTE: We want (power x a) instead of bigfloat:expt
1157 ;; because this preserves how maxima computes x^a when
1158 ;; x is negative and a is rational. For, example
1159 ;; (-8)^(1/2) is -2. bigfloat:expt returns the
1160 ;; principal value.
1161 (mul ($rectform (power x a))
1162 ($rectform (power ($bfloat '$%e) (mul -1 x)))
1163 (let ((a (bigfloat:to a))
1164 (x (bigfloat:to x)))
1165 (to (bigfloat:/
1166 (bigfloat:lentz
1167 #'(lambda (n)
1168 (bigfloat:+ n a))
1169 #'(lambda (n)
1170 (if (evenp n)
1171 (bigfloat:* (ash n -1) x)
1172 (bigfloat:- (bigfloat:* (bigfloat:+ a (ash n -1))
1173 x))))))))))))))
1175 ;; Series expansion of the Incomplete Gamma function
1176 (when *debug-gamma*
1177 (format t "~&GAMMA-INCOMPLETE in series:~%"))
1178 (do* ((i 1 (+ i 1))
1179 (ap a (add ap 1.0))
1180 (del (cdiv 1.0 a) (cmul del (cdiv x ap)))
1181 (sum del (add sum del)))
1182 ((> i gm-maxit)
1183 (merror (intl:gettext "gamma_incomplete: series expansion failed for gamma_incomplete(~:M, ~:M).") a x))
1184 (when (eq ($sign (sub (simplify (list '(mabs) del))
1185 (mul (simplify (list '(mabs) sum)) gm-eps)))
1186 '$neg)
1187 (when *debug-gamma* (format t "~&Series converged.~%"))
1188 (return
1189 ($bfloat ; force evaluation of expressions with sin or cos
1190 (sub (simplify (list '(%gamma) a))
1191 (cmul sum
1192 (cmul
1193 (cpower x a)
1194 (cpower ($bfloat '$%e) (mul -1 x)))))))))))))
1196 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1198 ;;; Implementation of the Generalized Incomplete Gamma function
1200 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1202 (defmfun $gamma_incomplete_generalized (a z1 z2)
1203 (simplify (list '(%gamma_incomplete_generalized) a z1 z2)))
1205 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1207 ;;; Set the properties alias, reversealias, noun and verb
1209 (defprop $gamma_incomplete_generalized %gamma_incomplete_generalized alias)
1210 (defprop $gamma_incomplete_generalized %gamma_incomplete_generalized verb)
1212 (defprop %gamma_incomplete_generalized
1213 $gamma_incomplete_generalized reversealias)
1214 (defprop %gamma_incomplete_generalized
1215 $gamma_incomplete_generalized noun)
1217 ;;; Generalized Incomplete Gamma function has not mirror symmetry for z1 or z2
1218 ;;; on the negative real axis.
1219 ;;; We support a conjugate-function which test this case.
1221 (defprop %gamma_incomplete_generalized
1222 conjugate-gamma-incomplete-generalized conjugate-function)
1224 (defun conjugate-gamma-incomplete-generalized (args)
1225 (let ((a (first args)) (z1 (second args)) (z2 (third args)))
1226 (cond ((and (off-negative-real-axisp z1) (off-negative-real-axisp z2))
1227 ;; z1 and z2 definitely not on the negative real axis.
1228 ;; Mirror symmetry.
1229 (simplify
1230 (list
1231 '(%gamma_incomplete_generalized)
1232 (simplify (list '($conjugate) a))
1233 (simplify (list '($conjugate) z1))
1234 (simplify (list '($conjugate) z2)))))
1236 ;; On the negative real axis or no information. Unsimplified.
1237 (list
1238 '($conjugate simp)
1239 (simplify (list '(%gamma_incomplete_generalized) a z1 z2)))))))
1241 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1243 ;;; Generalized Incomplete Gamma function is a simplifying function
1245 (defprop %gamma_incomplete_generalized
1246 simp-gamma-incomplete-generalized operators)
1248 ;;; Generalized Incomplete Gamma distributes over bags
1250 (defprop %gamma_incomplete_generalized (mlist $matrix mequal) distribute_over)
1252 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1254 ;;; Differentiation of Generalized Incomplete Gamma function
1256 (defprop %gamma_incomplete_generalized
1257 ((a z1 z2)
1258 ;; The derivative wrt a in terms of hypergeometric_regularized 2F2 function
1259 ;; and the Generalized Incomplete Gamma function (functions.wolfram.com)
1260 ((mplus)
1261 ((mtimes)
1262 ((mexpt) ((%gamma) a) 2)
1263 ((mexpt) z1 a)
1264 (($hypergeometric_regularized)
1265 ((mlist) a a)
1266 ((mlist) ((mplus) 1 a) ((mplus) 1 a))
1267 ((mtimes) -1 z1)))
1268 ((mtimes) -1
1269 ((mexpt) ((%gamma) a) 2)
1270 ((mexpt) z2 a)
1271 (($hypergeometric_regularized)
1272 ((mlist) a a)
1273 ((mlist) ((mplus) 1 a) ((mplus) 1 a))
1274 ((mtimes) -1 z2)))
1275 ((mtimes) -1
1276 ((%gamma_incomplete_generalized) a 0 z1)
1277 ((%log) z1))
1278 ((mtimes)
1279 ((%gamma_incomplete_generalized) a 0 z2)
1280 ((%log) z2)))
1281 ;; The derivative wrt z1
1282 ((mtimes) -1
1283 ((mexpt) $%e ((mtimes) -1 z1))
1284 ((mexpt) z1 ((mplus) -1 a)))
1285 ;; The derivative wrt z2
1286 ((mtimes)
1287 ((mexpt) $%e ((mtimes) -1 z2))
1288 ((mexpt) z2 ((mplus) -1 a))))
1289 grad)
1291 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1293 (defun simp-gamma-incomplete-generalized (expr ignored simpflag)
1294 (declare (ignore ignored))
1295 (arg-count-check 3 expr)
1296 (let ((a (simpcheck (cadr expr) simpflag))
1297 (z1 (simpcheck (caddr expr) simpflag))
1298 (z2 (simpcheck (cadddr expr) simpflag))
1299 ($simpsum t))
1301 (cond
1303 ;; Check for specific values
1305 ((zerop1 z2)
1306 (let ((sgn ($sign ($realpart a))))
1307 (cond
1308 ((member sgn '($pos $pz))
1309 (sub
1310 (simplify (list '(%gamma_incomplete) a z1))
1311 (simplify (list '(%gamma) a))))
1313 (eqtest (list '(%gamma_incomplete_generalized) a z1 z2) expr)))))
1315 ((zerop1 z1)
1316 (let ((sgn ($sign ($realpart a))))
1317 (cond
1318 ((member sgn '($pos $pz))
1319 (sub
1320 (simplify (list '(%gamma) a))
1321 (simplify (list '(%gamma_incomplete) a z2))))
1323 (eqtest (list '(%gamma_incomplete_generalized) a z1 z2) expr)))))
1325 ((zerop1 (sub z1 z2)) 0)
1327 ((eq z2 '$inf) (simplify (list '(%gamma_incomplete) a z1)))
1328 ((eq z1 '$inf) (mul -1 (simplify (list '(%gamma_incomplete) a z2))))
1330 ;; Check for numerical evaluation in Float or Bigfloat precision
1331 ;; Use the numerical routines of the Incomplete Gamma function
1333 ((float-numerical-eval-p a z1 z2)
1334 (complexify
1335 (- (gamma-incomplete ($float a) ($float z1))
1336 (gamma-incomplete ($float a) ($float z2)))))
1338 ((complex-float-numerical-eval-p a z1 z2)
1339 (let ((ca (complex ($float ($realpart a)) ($float ($imagpart a))))
1340 (cz1 (complex ($float ($realpart z1)) ($float ($imagpart z1))))
1341 (cz2 (complex ($float ($realpart z2)) ($float ($imagpart z2)))))
1342 (complexify (- (gamma-incomplete ca cz1) (gamma-incomplete ca cz2)))))
1344 ((bigfloat-numerical-eval-p a z1 z2)
1345 (sub (bfloat-gamma-incomplete ($bfloat a) ($bfloat z1))
1346 (bfloat-gamma-incomplete ($bfloat a) ($bfloat z2))))
1348 ((complex-bigfloat-numerical-eval-p a z1 z2)
1349 (let ((ca (add ($bfloat ($realpart a))
1350 (mul '$%i ($bfloat ($imagpart a)))))
1351 (cz1 (add ($bfloat ($realpart z1))
1352 (mul '$%i ($bfloat ($imagpart z1)))))
1353 (cz2 (add ($bfloat ($realpart z2))
1354 (mul '$%i ($bfloat ($imagpart z2))))))
1355 (sub (complex-bfloat-gamma-incomplete ca cz1)
1356 (complex-bfloat-gamma-incomplete ca cz2))))
1358 ;; Check for transformations and argument simplification
1360 ((and $gamma_expand (mplusp a) (integerp (cadr a)))
1361 ;; Expand gamma_incomplete_generalized(a+n,z1,z2) with n an integer
1362 (let ((n (cadr a))
1363 (a (simplify (cons '(mplus) (cddr a)))))
1364 (cond
1365 ((> n 0)
1366 (mul
1367 (simplify (list '($pochhammer) a n))
1368 (add
1369 (simplify (list '(%gamma_incomplete_generalized) a z1 z2))
1370 (mul
1371 (power '$%e (mul -1 z1))
1372 (let ((index (gensumindex)))
1373 (simpsum1
1374 (div
1375 (power z1 (add a index -1))
1376 (simplify (list '($pochhammer) a index)))
1377 index 1 n)))
1378 (mul -1
1379 (power '$%e (mul -1 z2))
1380 (let ((index (gensumindex)))
1381 (simpsum1
1382 (div
1383 (power z2 (add a index -1))
1384 (simplify (list '($pochhammer) a index)))
1385 index 1 n))))))
1387 ((< n 0)
1388 (setq n (- n))
1389 (add
1390 (mul
1391 (div
1392 (power -1 n)
1393 ($factor (simplify (list '($pochhammer) (sub 1 a) n))))
1394 (simplify (list '(%gamma_incomplete_generalized) a z1 z2)))
1395 (mul -1
1396 (power '$%e (mul -1 z2))
1397 (let ((index (gensumindex)))
1398 (simpsum1
1399 (div
1400 (power z1 (add a index (- n) -1))
1401 (simplify (list '($pochhammer) (sub a n) index)))
1402 index 1 n)))
1403 (mul
1404 (power '$%e (mul -1 z2))
1405 (let ((index (gensumindex)))
1406 (simpsum1
1407 (div
1408 (power z2 (add a index (- n) -1))
1409 (simplify (list '($pochhammer) (sub a n) index)))
1410 index 1 n))))))))
1412 (t (eqtest (list '(%gamma_incomplete_generalized) a z1 z2) expr)))))
1414 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1416 ;;; Implementation of the Regularized Incomplete Gamma function
1418 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1420 (defmfun $gamma_incomplete_regularized (a z)
1421 (simplify (list '(%gamma_incomplete_regularized) a z)))
1423 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1425 (defprop $gamma_incomplete_regularized %gamma_incomplete_regularized alias)
1426 (defprop $gamma_incomplete_regularized %gamma_incomplete_regularized verb)
1428 (defprop %gamma_incomplete_regularized
1429 $gamma_incomplete_regularized reversealias)
1430 (defprop %gamma_incomplete_regularized
1431 $gamma_incomplete_regularized noun)
1433 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1435 ;;; Generalized Incomplete Gamma function has not mirror symmetry for z1 or z2
1436 ;;; on the negative real axis.
1437 ;;; We support a conjugate-function which test this case.
1439 (defprop %gamma_incomplete_regularized
1440 conjugate-gamma-incomplete-regularized conjugate-function)
1442 (defun conjugate-gamma-incomplete-regularized (args)
1443 (let ((a (first args)) (z (second args)))
1444 (cond ((off-negative-real-axisp z)
1445 ;; z definitely not on the negative real axis. Mirror symmetry.
1446 (simplify
1447 (list
1448 '(%gamma_incomplete_regularized)
1449 (simplify (list '($conjugate) a))
1450 (simplify (list '($conjugate) z)))))
1452 ;; On the negative real axis or no information. Unsimplified.
1453 (list
1454 '($conjugate simp)
1455 (simplify (list '(%gamma_incomplete_regularized) a z)))))))
1457 ;;; Regularized Incomplete Gamma function is a simplifying function
1459 (defprop %gamma_incomplete_regularized
1460 simp-gamma-incomplete-regularized operators)
1462 ;;; Regularized Incomplete Gamma distributes over bags
1464 (defprop %gamma_incomplete_regularized (mlist $matrix mequal) distribute_over)
1466 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1468 ;;; Differentiation of Regularized Incomplete Gamma function
1470 (defprop %gamma_incomplete_regularized
1471 ((a z)
1472 ;; The derivative wrt a in terms of hypergeometric_regularized 2F2 function
1473 ;; and the Regularized Generalized Incomplete Gamma function
1474 ;; (functions.wolfram.com)
1475 ((mplus)
1476 ((mtimes)
1477 ((%gamma) a)
1478 ((mexpt) z a)
1479 (($hypergeometric_regularized)
1480 ((mlist) a a)
1481 ((mlist) ((mplus) 1 a) ((mplus) 1 a))
1482 ((mtimes) -1 z)))
1483 ((mtimes)
1484 ((%gamma_incomplete_generalized_regularized) a z 0)
1485 ((mplus)
1486 ((%log) z)
1487 ((mtimes) -1 ((mqapply) (($psi array) 0) a)))))
1488 ;; The derivative wrt z
1489 ((mtimes)
1491 ((mexpt) $%e ((mtimes) -1 z))
1492 ((mexpt) z ((mplus) -1 a))
1493 ((mexpt) ((%gamma) a) -1)))
1494 grad)
1496 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1498 (defun simp-gamma-incomplete-regularized (expr ignored simpflag)
1499 (declare (ignore ignored))
1500 (twoargcheck expr)
1501 (let ((a (simpcheck (cadr expr) simpflag))
1502 (z (simpcheck (caddr expr) simpflag))
1503 ($simpsum t)
1504 (ratorder 0))
1506 (cond
1508 ;; Check for specific values
1510 ((zerop1 z)
1511 (let ((sgn ($sign ($realpart a))))
1512 (cond ((member sgn '($neg $zero))
1513 (simp-domain-error
1514 (intl:gettext
1515 "gamma_incomplete_regularized: gamma_incomplete_regularized(~:M,~:M) is undefined.")
1516 a z))
1517 ((member sgn '($pos $pz)) 1)
1518 (t (eqtest (list '(%gamma_incomplete_regularized) a z) expr)))))
1520 ((zerop1 a) 0)
1521 ((eq z '$inf) 0)
1523 ;; Check for numerical evaluation in Float or Bigfloat precision
1525 ((float-numerical-eval-p a z)
1526 (complexify
1527 ;; gamma_incomplete returns a regularized result
1528 (gamma-incomplete ($float a) ($float z) t)))
1530 ((complex-float-numerical-eval-p a z)
1531 (let ((ca (complex ($float ($realpart a)) ($float ($imagpart a))))
1532 (cz (complex ($float ($realpart z)) ($float ($imagpart z)))))
1533 ;; gamma_incomplete returns a regularized result
1534 (complexify (gamma-incomplete ca cz t))))
1536 ((bigfloat-numerical-eval-p a z)
1537 (div (bfloat-gamma-incomplete ($bfloat a) ($bfloat z))
1538 (simplify (list '(%gamma) ($bfloat a)))))
1540 ((complex-bigfloat-numerical-eval-p a z)
1541 (let ((ca (add ($bfloat ($realpart a))
1542 (mul '$%i ($bfloat ($imagpart a)))))
1543 (cz (add ($bfloat ($realpart z))
1544 (mul '$%i ($bfloat ($imagpart z))))))
1545 ($rectform
1546 (div
1547 (complex-bfloat-gamma-incomplete ca cz)
1548 (simplify (list '(%gamma) ca))))))
1550 ;; Check for transformations and argument simplification
1552 ((and $gamma_expand (integerp a))
1553 ;; An integer. Expand the expression.
1554 (let ((sgn ($sign a)))
1555 (cond
1556 ((member sgn '($pos $pz))
1557 (mul
1558 (power '$%e (mul -1 z))
1559 (let ((index (gensumindex)))
1560 (simpsum1
1561 (div
1562 (power z index)
1563 (let (($gamma_expand nil))
1564 (simplify (list '(%gamma) (add index 1)))))
1565 index 0 (sub a 1)))))
1566 ((member sgn '($neg $nz)) 0)
1567 (t (eqtest (list '(%gamma_incomplete_regularized) a z) expr)))))
1569 ((and $gamma_expand (setq ratorder (max-numeric-ratio-p a 2)))
1570 ;; We have a half integral order and $gamma_expand is not NIL.
1571 ;; We expand in a series with the Erfc function
1572 (setq ratorder (- ratorder (/ 1 2)))
1573 (when *debug-gamma*
1574 (format t "~&SIMP-GAMMA-INCOMPLETE-REGULARIZED in RATORDER~%")
1575 (format t "~& : a = ~A~%" a)
1576 (format t "~& : ratorder = ~A~%" ratorder))
1577 (cond
1578 ((equal ratorder 0)
1579 (simplify (list '(%erfc) (power z '((rat simp) 1 2)))))
1581 ((> ratorder 0)
1582 (add
1583 (simplify (list '(%erfc) (power z '((rat simp) 1 2))))
1584 (mul
1585 (power -1 (sub ratorder 1))
1586 (power '$%e (mul -1 z))
1587 (power z '((rat simp) 1 2))
1588 (div 1 (simplify (list '(%gamma) a)))
1589 (let ((index (gensumindex)))
1590 (simpsum1
1591 (mul
1592 (power (mul -1 z) index)
1593 (simplify (list '($pochhammer)
1594 (sub '((rat simp) 1 2) ratorder)
1595 (sub ratorder (add index 1)))))
1596 index 0 (sub ratorder 1))))))
1598 ((< ratorder 0)
1599 (setq ratorder (- ratorder))
1600 (add
1601 (simplify (list '(%erfc) (power z '((rat simp) 1 2))))
1602 (mul -1
1603 (power '$%e (mul -1 z))
1604 (power z (sub '((rat simp) 1 2) ratorder))
1605 (inv (simplify (list '(%gamma) (sub '((rat simp) 1 2) ratorder))))
1606 (let ((index (gensumindex)))
1607 (simpsum1
1608 (div
1609 (power z index)
1610 (simplify (list '($pochhammer)
1611 (sub '((rat simp) 1 2) ratorder)
1612 (add index 1))))
1613 index 0 (sub ratorder 1))))))))
1615 ((and $gamma_expand (mplusp a) (integerp (cadr a)))
1616 (when *debug-gamma*
1617 (format t "~&SIMP-GAMMA-INCOMPLETE-REGULARIZED in COND (mplusp)~%"))
1618 (let ((n (cadr a))
1619 (a (simplify (cons '(mplus) (cddr a)))))
1620 (cond
1621 ((> n 0)
1622 (add
1623 (simplify (list '(%gamma_incomplete_regularized) a z))
1624 ;; We factor the second summand.
1625 ;; Some factors vanish and the result is more readable.
1626 ($factor
1627 (mul
1628 (power '$%e (mul -1 z))
1629 (power z (add a -1))
1630 (div 1 (simplify (list '(%gamma) a)))
1631 (let ((index (gensumindex)))
1632 (simpsum1
1633 (div
1634 (power z index)
1635 (simplify (list '($pochhammer) a index)))
1636 index 1 n))))))
1637 ((< n 0)
1638 (setq n (- n))
1639 (add
1640 (simplify (list '(%gamma_incomplete_regularized) a z))
1641 ;; We factor the second summand.
1642 ($factor
1643 (mul -1
1644 (power '$%e (mul -1 z))
1645 (power z (sub a (add n 1)))
1646 (div 1 (simplify (list '(%gamma) (add a (- n)))))
1647 (let ((index (gensumindex)))
1648 (simpsum1
1649 (div
1650 (power z index)
1651 (simplify (list '($pochhammer) (add a (- n)) index)))
1652 index 1 n)))))))))
1654 (t (eqtest (list '(%gamma_incomplete_regularized) a z) expr)))))
1656 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1658 ;;; Implementation of the Logarithm of the Gamma function
1660 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1662 (defmfun $log_gamma (z)
1663 (simplify (list '(%log_gamma) z)))
1665 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1667 (defprop $log_gamma %log_gamma alias)
1668 (defprop $log_gamma %log_gamma verb)
1670 (defprop %log_gamma $log_gamma reversealias)
1671 (defprop %log_gamma $log_gamma noun)
1673 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1675 (defprop %log_gamma simp-log-gamma operators)
1677 ;;; Logarithm of the Gamma function distributes over bags
1679 (defprop %log_gamma (mlist $matrix mequal) distribute_over)
1681 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1683 (defprop %log_gamma
1684 ((z)
1685 ((mqapply) (($psi array) 0) z))
1686 grad)
1688 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1690 (defun simp-log-gamma (expr z simpflag)
1691 (oneargcheck expr)
1692 (setq z (simpcheck (cadr expr) simpflag))
1693 (cond
1695 ;; Check for specific values
1697 ((and (mnump z)
1698 (or (zerop1 z)
1699 (and (eq ($sign z) '$neg)
1700 (zerop1 (sub z ($truncate z))))))
1701 ;; We have zero, a negative integer or a float or bigfloat representation.
1702 (simp-domain-error
1703 (intl:gettext "log_gamma: log_gamma(~:M) is undefined.") z))
1705 ((eq z '$inf) '$inf)
1707 ;; Check for numerical evaluation
1709 ((float-numerical-eval-p z)
1710 (complexify (log-gamma-lanczos (complex ($float z) 0))))
1712 ((complex-float-numerical-eval-p z)
1713 (complexify
1714 (log-gamma-lanczos
1715 (complex ($float ($realpart z)) ($float ($imagpart z))))))
1717 ((bigfloat-numerical-eval-p z)
1718 (bfloat-log-gamma ($bfloat z)))
1720 ((complex-bigfloat-numerical-eval-p z)
1721 (complex-bfloat-log-gamma
1722 (add ($bfloat ($realpart z)) (mul '$%i ($bfloat ($imagpart z))))))
1724 ;; Transform to Logarithm of Factorial for integer values
1725 ;; At this point the integer value is positive and not zero.
1727 ((integerp z)
1728 (simplify (list '(%log) (simplify (list '(mfactorial) (- z 1))))))
1730 (t (eqtest (list '(%log_gamma) z) expr))))
1732 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1733 ;;; The functions log-gamma-lanczos, bfloat-log-gamma and
1734 ;;; complex-bfloat-log-gamma are modified versions of the related functions
1735 ;;; gamma-lanczos, bffac and cbffac. The functions return the Logarithm of
1736 ;;; the Gamma function. If we have to calculate the quotient of Gamma functions,
1737 ;;; e. g. for the Beta function, it is much more appropriate to use the
1738 ;;; logarithmic versions to avoid overflow.
1740 ;;; Be careful log(gamma(z)) is only for realpart(z) positive equal to
1741 ;;; log_gamma(z). For a negative realpart(z) log_gamma differ by multiple of
1742 ;;; %pi from log(gamma(z)). But we always have exp(log_gamma(z))= gamma(z).
1743 ;;; The terms to get the transformation for log_gamma(-z) are taken from
1744 ;;; functions.wolfram.com.
1745 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1747 (defun log-gamma-lanczos (z)
1748 (declare (type (complex flonum) z)
1749 (optimize (safety 3)))
1750 (let ((c (make-array 15 :element-type 'flonum
1751 :initial-contents
1752 '(0.99999999999999709182
1753 57.156235665862923517
1754 -59.597960355475491248
1755 14.136097974741747174
1756 -0.49191381609762019978
1757 .33994649984811888699e-4
1758 .46523628927048575665e-4
1759 -.98374475304879564677e-4
1760 .15808870322491248884e-3
1761 -.21026444172410488319e-3
1762 .21743961811521264320e-3
1763 -.16431810653676389022e-3
1764 .84418223983852743293e-4
1765 -.26190838401581408670e-4
1766 .36899182659531622704e-5))))
1767 (declare (type (simple-array flonum (15)) c))
1768 (if (minusp (realpart z))
1769 (let ((z (- z)))
1773 (- (float pi))
1774 (complex 0 1)
1775 (abs (floor (realpart z)))
1776 (- 1 (abs (signum (imagpart z)))))
1777 (log (float pi))
1778 (- (log (- z)))
1779 (- (log (sin (* (float pi) (- z (floor (realpart z)))))))
1781 (float pi)
1782 (complex 0 1)
1783 (floor (realpart z))
1784 (signum (imagpart z))))
1785 (log-gamma-lanczos z)))
1786 (let* ((z (- z 1))
1787 (zh (+ z 1/2))
1788 (zgh (+ zh 607/128))
1789 (lnzp (* (/ zh 2) (log zgh)))
1790 (ss
1791 (do ((sum 0.0)
1792 (pp (1- (length c)) (1- pp)))
1793 ((< pp 1)
1794 sum)
1795 (incf sum (/ (aref c pp) (+ z pp))))))
1796 (+ (log (sqrt (float (* 2 pi))))
1797 (log (+ ss (aref c 0)))
1798 (+ (- zgh) (* 2 lnzp)))))))
1800 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1802 (defun bfloat-log-gamma (z)
1803 (let (($ratprint nil)
1804 (bigfloat%pi ($bfloat '$%pi)))
1805 (cond
1806 ((eq ($sign z) '$neg)
1807 (let ((z (mul -1 z)))
1808 (sub
1809 (add
1810 (mul -1 bigfloat%pi '$%i
1811 (simplify (list '(mabs) (simplify (list '($floor) ($realpart z)))))
1812 (sub 1
1813 (simplify
1814 (list '(mabs) (simplify (list '(%signum) ($imagpart z)))))))
1815 (simplify (list '(%log) bigfloat%pi))
1816 (mul -1 (simplify (list '(%log) (mul -1 z))))
1817 (mul -1
1818 (simplify (list '(%log)
1819 (simplify (list '(%sin)
1820 (mul
1821 bigfloat%pi
1822 (sub z (simplify (list '($floor) ($realpart z))))))))))
1823 (mul
1824 bigfloat%pi '$%i
1825 (simplify (list '($floor) ($realpart z)))
1826 (simplify (list '(%signum) ($imagpart z)))))
1827 (bfloat-log-gamma z))))
1829 (let* ((k (* 2 (+ 1 ($entier (* 0.41 $fpprec)))))
1830 (m ($bfloat bigfloatone))
1831 (z+k (add z k -1))
1832 (y (power z+k 2))
1833 (x ($bfloat bigfloatzero))
1834 (ii))
1835 (dotimes (i (/ k 2))
1836 (setq ii (* 2 (+ i 1)))
1837 (setq m (mul m (add z ii -2) (add z ii -1)))
1838 (setq x (div
1839 (add x
1840 (div ($bern (+ k (- ii) 2))
1841 (* (+ k (- ii) 1) (+ k (- ii) 2))))
1842 y)))
1843 (add
1844 (div (simplify (list '(%log) (mul 2 bigfloat%pi z+k))) 2)
1845 (mul z+k (add (simplify (list '(%log) z+k)) x -1))
1846 (mul -1 (simplify (list '(%log) m)))))))))
1848 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1850 (defun complex-bfloat-log-gamma (z)
1851 (let (($ratprint nil)
1852 (bigfloat%pi ($bfloat '$%pi)))
1853 (cond
1854 ((eq ($sign ($realpart z)) '$neg)
1855 (let ((z (mul -1 z)))
1856 (sub
1857 (add
1858 (mul -1 bigfloat%pi '$%i
1859 (simplify (list '(mabs) (simplify (list '($floor) ($realpart z)))))
1860 (sub 1
1861 (simplify
1862 (list '(mabs) (simplify (list '(%signum) ($imagpart z)))))))
1863 (simplify (list '(%log) bigfloat%pi))
1864 (mul -1 (simplify (list '(%log) (mul -1 z))))
1865 (mul -1
1866 (simplify (list '(%log)
1867 (simplify (list '(%sin)
1868 (mul
1869 bigfloat%pi
1870 (sub z (simplify (list '($floor) ($realpart z))))))))))
1871 (mul
1872 bigfloat%pi '$%i
1873 (simplify (list '($floor) ($realpart z)))
1874 (simplify (list '(%signum) ($imagpart z)))))
1875 (complex-bfloat-log-gamma z))))
1877 (let* ((k (* 2 (+ 1 ($entier (* 0.41 $fpprec)))))
1878 (m ($bfloat bigfloatone))
1879 (z+k (add z k -1))
1880 (y ($rectform (power z+k 2)))
1881 (x ($bfloat bigfloatzero))
1882 (ii))
1883 (dotimes (i (/ k 2))
1884 (setq ii (* 2 (+ i 1)))
1885 (setq m ($rectform (mul m (add z ii -2) (add z ii -1))))
1886 (setq x ($rectform
1887 (div
1888 (add x
1889 (div ($bern (+ k (- ii) 2))
1890 (* (+ k (- ii) 1) (+ k (- ii) 2))))
1891 y))))
1892 ($rectform
1893 (add
1894 (div (simplify (list '(%log) (mul 2 bigfloat%pi z+k))) 2)
1895 (mul z+k (add (simplify (list '(%log) z+k)) x -1))
1896 (mul -1 (simplify (list '(%log) m))))))))))
1898 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1900 ;;; Implementation of the Error function Erf(z)
1902 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1904 (defmfun $erf (z)
1905 (simplify (list '(%erf) z)))
1907 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1909 (defprop $erf %erf alias)
1910 (defprop $erf %erf verb)
1912 (defprop %erf $erf reversealias)
1913 (defprop %erf $erf noun)
1915 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1917 ;;; erf has mirror symmetry
1919 (defprop %erf t commutes-with-conjugate)
1921 ;;; erf is an odd function
1923 (defprop %erf odd-function-reflect reflection-rule)
1925 ;;; erf is a simplifying function
1927 (defprop %erf simp-erf operators)
1929 ;;; erf distributes over bags
1931 (defprop %erf (mlist $matrix mequal) distribute_over)
1933 ;;; Derivative of the Error function erf
1935 (defprop %erf
1936 ((z)
1937 ((mtimes) 2
1938 ((mexpt) $%pi ((rat) -1 2))
1939 ((mexpt) $%e ((mtimes) -1 ((mexpt) z 2)))))
1940 grad)
1942 ;;; Integral of the Error function erf
1944 (defprop %erf
1945 ((z)
1946 ((mplus)
1947 ((mtimes)
1948 ((mexpt) $%pi ((rat) -1 2))
1949 ((mexpt) $%e ((mtimes) -1 ((mexpt) z 2))))
1950 ((mtimes) z ((%erf) z))))
1951 integral)
1953 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1955 (defun simp-erf (expr z simpflag)
1956 (oneargcheck expr)
1957 (setq z (simpcheck (cadr expr) simpflag))
1958 (cond
1960 ;; Check for specific values
1962 ((zerop1 z) z)
1963 ((eq z '$inf) 1)
1964 ((eq z '$minf) -1)
1966 ;; Check for numerical evaluation
1968 ((float-numerical-eval-p z)
1969 (bigfloat::bf-erf ($float z)))
1970 ((complex-float-numerical-eval-p z)
1971 (complexify
1972 (bigfloat::bf-erf (complex ($float ($realpart z)) ($float ($imagpart z))))))
1973 ((bigfloat-numerical-eval-p z)
1974 (to (bigfloat::bf-erf (bigfloat:to ($bfloat z)))))
1975 ((complex-bigfloat-numerical-eval-p z)
1976 (to (bigfloat::bf-erf
1977 (bigfloat:to (add ($bfloat ($realpart z)) (mul '$%i ($bfloat ($imagpart z))))))))
1979 ;; Argument simplification
1981 ((taylorize (mop expr) (second expr)))
1982 ((and $erf_%iargs
1983 (not $erf_representation)
1984 (multiplep z '$%i))
1985 (mul '$%i (simplify (list '(%erfi) (coeff z '$%i 1)))))
1986 ((apply-reflection-simp (mop expr) z $trigsign))
1988 ;; Representation through equivalent functions
1990 ($hypergeometric_representation
1991 (mul 2 z
1992 (power '$%pi '((rat simp) 1 2))
1993 (list '(%hypergeometric simp)
1994 (list '(mlist simp) '((rat simp) 1 2))
1995 (list '(mlist simp) '((rat simp) 3 2))
1996 (mul -1 (power z 2)))))
1998 ;; Transformation to Erfc or Erfi
2000 ((and $erf_representation
2001 (not (eq $erf_representation '$erf)))
2002 (case $erf_representation
2003 (%erfc
2004 (sub 1 (take '(%erfc) z)))
2005 (%erfi
2006 (mul -1 '$%i (take '(%erfi) (mul '$%i z))))
2008 (eqtest (list '(%erf) z) expr))))
2011 (eqtest (list '(%erf) z) expr))))
2013 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2015 (defun erf (z)
2016 ;; We use the slatec routine for float values.
2017 (slatec:derf (float z)))
2019 ;; Compute erf(z) using the relationship
2021 ;; erf(z) = sqrt(z^2)/z*(1 - gamma_incomplete(1/2,z^2)/sqrt(%pi))
2023 ;; When z is real sqrt(z^2)/z is signum(z). For complex z,
2024 ;; sqrt(z^2)/z = 1 if -%pi/2 < arg(z) <= %pi/2 and -1 otherwise.
2026 ;; This relationship has serious round-off issues when z is small
2027 ;; because gamma_incomplete(1/2,z^2)/sqrt(%pi) is near 1.
2029 ;; complex-erf is for (lisp) complex numbers; bfloat-erf is for real
2030 ;; bfloats, and complex-bfloat-erf is for complex bfloats. Care is
2031 ;; taken to return real results for real arguments and imaginary
2032 ;; results for imaginary arguments
2033 (defun complex-erf (z)
2034 (let ((result
2036 (if (or (< (realpart z) 0.0) (and (= (realpart z) 0.0) (< (imagpart z) 0.0)))
2039 (- 1.0
2040 (* (/ (sqrt (float pi))) (gamma-incomplete 0.5 (* z z)))))))
2041 (cond
2042 ((= (imagpart z) 0.0)
2043 ;; Pure real argument, the result is real
2044 (complex (realpart result) 0.0))
2045 ((= (realpart z) 0.0)
2046 ;; Pure imaginary argument, the result is pure imaginary
2047 (complex 0.0 (imagpart result)))
2049 result))))
2051 (defun bfloat-erf (z)
2052 ;; Warning! This has round-off problems when abs(z) is very small.
2053 (let ((1//2 ($bfloat '((rat simp) 1 2))))
2054 ;; The argument is real, the result is real too
2055 ($realpart
2056 (mul
2057 (simplify (list '(%signum) z))
2058 (sub 1
2059 (mul
2060 (div 1 (power ($bfloat '$%pi) 1//2))
2061 (bfloat-gamma-incomplete 1//2 ($bfloat (power z 2)))))))))
2063 (defun complex-bfloat-erf (z)
2064 ;; Warning! This has round-off problems when abs(z) is very small.
2065 (let* (($ratprint nil)
2066 (1//2 ($bfloat '((rat simp) 1 2)))
2067 (result
2068 (cmul
2069 (cdiv (cpower (cpower z 2) 1//2) z)
2070 (sub 1
2071 (cmul
2072 (div 1 (power ($bfloat '$%pi) 1//2))
2073 (complex-bfloat-gamma-incomplete
2074 1//2
2075 ($bfloat (cpower z 2))))))))
2076 (cond
2077 ((zerop1 ($imagpart z))
2078 ;; Pure real argument, the result is real
2079 ($realpart result))
2080 ((zerop1 ($realpart z))
2081 ;; Pure imaginary argument, the result is pure imaginary
2082 (mul '$%i ($imagpart result)))
2084 ;; A general complex result
2085 result))))
2087 (in-package :bigfloat)
2089 ;; Erf(z) for all z. Z must be a CL real or complex number or a
2090 ;; BIGFLOAT or COMPLEX-BIGFLOAT object. The result will be of the
2091 ;; same type as Z.
2092 (defun bf-erf (z)
2093 (cond ((typep z 'cl:real)
2094 ;; Use Slatec derf, which should be faster than the series.
2095 (maxima::erf z))
2096 ((<= (abs z) 0.476936)
2097 ;; Use the series A&S 7.1.5 for small x:
2099 ;; erf(z) = 2*z/sqrt(%pi) * sum((-1)^n*z^(2*n)/n!/(2*n+1), n, 0, inf)
2101 ;; The threshold is approximately erf(x) = 0.5. (Doesn't
2102 ;; have to be super accurate.) This gives max accuracy when
2103 ;; using the identity erf(x) = 1 - erfc(x).
2104 (let ((z^2 (* z z)))
2105 (/ (* 2 z (sum-power-series z^2
2106 #'(lambda (k)
2107 (let ((2k (+ k k)))
2108 (- (/ (- 2k 1)
2110 (+ 2k 1)))))))
2111 (sqrt (%pi z)))))
2113 ;; The general case.
2114 (etypecase z
2115 (cl:real (maxima::erf z))
2116 (cl:complex (maxima::complex-erf z))
2117 (bigfloat
2118 (bigfloat (maxima::$bfloat (maxima::$expand (maxima::bfloat-erf (maxima::to z))))))
2119 (complex-bigfloat
2120 (bigfloat (maxima::$bfloat (maxima::$expand (maxima::complex-bfloat-erf (maxima::to z))))))))))
2122 (defun bf-erfc (z)
2123 ;; Compute erfc(z) via 1 - erf(z) is not very accurate if erf(z) is
2124 ;; near 1. Wolfram says
2126 ;; erfc(z) = 1 - sqrt(z^2)/z * (1 - 1/sqrt(pi)*gamma_incomplete_tail(1/2, z^2))
2128 ;; For real(z) > 0, sqrt(z^2)/z is 1 so
2130 ;; erfc(z) = 1 - (1 - 1/sqrt(pi)*gamma_incomplete_tail(1/2,z^2))
2131 ;; = 1/sqrt(pi)*gamma_incomplete_tail(1/2,z^2)
2133 ;; For real(z) < 0, sqrt(z^2)/z is -1 so
2135 ;; erfc(z) = 1 + (1 - 1/sqrt(pi)*gamma_incomplete_tail(1/2,z^2))
2136 ;; = 2 - 1/sqrt(pi)*gamma_incomplete(1/2,z^2)
2137 (flet ((gamma-inc (z)
2138 (etypecase z
2139 (cl:number
2140 (maxima::gamma-incomplete 0.5 z))
2141 (bigfloat
2142 (bigfloat:to (maxima::$bfloat
2143 (maxima::bfloat-gamma-incomplete (maxima::$bfloat maxima::1//2)
2144 (maxima::to z)))))
2145 (complex-bigfloat
2146 (bigfloat:to (maxima::$bfloat
2147 (maxima::complex-bfloat-gamma-incomplete (maxima::$bfloat maxima::1//2)
2148 (maxima::to z))))))))
2149 (if (>= (realpart z) 0)
2150 (/ (gamma-inc (* z z))
2151 (sqrt (%pi z)))
2152 (- 2
2153 (/ (gamma-inc (* z z))
2154 (sqrt (%pi z)))))))
2156 (in-package :maxima)
2158 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2160 ;;; Implementation of the Generalized Error function Erf(z1,z2)
2162 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2164 (defmfun $erf_generalized (z1 z2)
2165 (simplify (list '(%erf_generalized) z1 z2)))
2167 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2169 (defprop $erf_generalized %erf_generalized alias)
2170 (defprop $erf_generalized %erf_generalized verb)
2172 (defprop %erf_generalized $erf_generalized reversealias)
2173 (defprop %erf_generalized $erf_generalized noun)
2175 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2177 ;;; Generalized Erf has mirror symmetry
2179 (defprop %erf_generalized t commutes-with-conjugate)
2181 ;;; Generalized Erf distributes over bags
2183 (defprop %erf_generalized (mlist $matrix mequal) distribute_over)
2185 ;;; Generalized Erf is antisymmetric Erf(z1,z2) = - Erf(z2,z1)
2187 (eval-when
2188 #+gcl (load eval)
2189 #-gcl (:load-toplevel :execute)
2190 (let (($context '$global) (context '$global))
2191 (meval '(($declare) %erf_generalized $antisymmetric))
2192 ;; Let's remove built-in symbols from list for user-defined properties.
2193 (setq $props (remove '%erf_generalized $props))))
2195 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2197 (defprop %erf_generalized simp-erf-generalized operators)
2199 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2201 (defprop %erf_generalized
2202 ((z1 z2)
2203 ;; derivative wrt z1
2204 ((mtimes) -2
2205 ((mexpt) $%pi ((rat) -1 2))
2206 ((mexpt) $%e ((mtimes) -1 ((mexpt) z1 2))))
2207 ;; derviative wrt z2
2208 ((mtimes) 2
2209 ((mexpt) $%pi ((rat) -1 2))
2210 ((mexpt) $%e ((mtimes) -1 ((mexpt) z2 2)))))
2211 grad)
2213 ;;; ----------------------------------------------------------------------------
2215 (defprop %erf_generalized simplim%erf_generalized simplim%function)
2217 (defun simplim%erf_generalized (expr var val)
2218 ;; Look for the limit of the arguments.
2219 (let ((z1 (limit (cadr expr) var val 'think))
2220 (z2 (limit (caddr expr) var val 'think)))
2221 (cond
2222 ;; Handle infinities at this place.
2223 ((or (eq z2 '$inf)
2224 (alike1 z2 '((mtimes) -1 $minf)))
2225 (sub 1 (take '(%erf) z1)))
2226 ((or (eq z2 '$minf)
2227 (alike1 z2 '((mtimes) -1 $inf)))
2228 (sub (mul -1 (take '(%erf) z1)) 1))
2229 ((or (eq z1 '$inf)
2230 (alike1 z1 '((mtimes) -1 $minf)))
2231 (sub (take '(%erf) z2) 1))
2232 ((or (eq z1 '$minf)
2233 (alike1 z1 '((mtimes) -1 $inf)))
2234 (add (take '(%erf) z2) 1))
2236 ;; All other cases are handled by the simplifier of the function.
2237 (simplify (list '(%erf_generalized) z1 z2))))))
2239 ;;; ----------------------------------------------------------------------------
2241 (defun simp-erf-generalized (expr ignored simpflag)
2242 (declare (ignore ignored))
2243 (twoargcheck expr)
2244 (let ((z1 (simpcheck (cadr expr) simpflag))
2245 (z2 (simpcheck (caddr expr) simpflag)))
2246 (cond
2248 ;; Check for specific values
2250 ((and (zerop1 z1) (zerop1 z2)) 0)
2251 ((zerop1 z1) (take '(%erf) z2))
2252 ((zerop1 z2) (mul -1 (take '(%erf) z1)))
2253 ((or (eq z2 '$inf)
2254 (alike1 z2 '((mtimes) -1 $minf)))
2255 (sub 1 (take '(%erf) z1)))
2256 ((or (eq z2 '$minf)
2257 (alike1 z2 '((mtimes) -1 $inf)))
2258 (sub (mul -1 (take '(%erf) z1)) 1))
2259 ((or (eq z1 '$inf)
2260 (alike1 z1 '((mtimes) -1 $minf)))
2261 (sub (take '(%erf) z2) 1))
2262 ((or (eq z1 '$minf)
2263 (alike1 z1 '((mtimes) -1 $inf)))
2264 (add (take '(%erf) z2) 1))
2266 ;; Check for numerical evaluation. Use erf(z1,z2) = erf(z2)-erf(z1)
2268 ((float-numerical-eval-p z1 z2)
2269 (- (bigfloat::bf-erf ($float z2))
2270 (bigfloat::bf-erf ($float z1))))
2271 ((complex-float-numerical-eval-p z1 z2)
2272 (complexify
2274 (bigfloat::bf-erf
2275 (complex ($float ($realpart z2)) ($float ($imagpart z2))))
2276 (bigfloat::bf-erf
2277 (complex ($float ($realpart z1)) ($float ($imagpart z1)))))))
2278 ((bigfloat-numerical-eval-p z1 z2)
2279 (to (bigfloat:-
2280 (bigfloat::bf-erf (bigfloat:to ($bfloat z2)))
2281 (bigfloat::bf-erf (bigfloat:to ($bfloat z1))))))
2282 ((complex-bigfloat-numerical-eval-p z1 z2)
2283 (to (bigfloat:-
2284 (bigfloat::bf-erf
2285 (bigfloat:to (add ($bfloat ($realpart z2)) (mul '$%i ($bfloat ($imagpart z2))))))
2286 (bigfloat::bf-erf
2287 (bigfloat:to (add ($bfloat ($realpart z1)) (mul '$%i ($bfloat ($imagpart z1)))))))))
2289 ;; Argument simplification
2291 ((and $trigsign (great (mul -1 z1) z1) (great (mul -1 z2) z2))
2292 (mul -1 (simplify (list '(%erf_generalized) (mul -1 z1) (mul -1 z2)))))
2294 ;; Transformation to Erf
2296 ($erf_representation
2297 (sub (simplify (list '(%erf) z2)) (simplify (list '(%erf) z1))))
2300 (eqtest (list '(%erf_generalized) z1 z2) expr)))))
2302 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2304 ;;; Implementation of the Complementary Error function Erfc(z)
2306 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2308 (defmfun $erfc (z)
2309 (simplify (list '(%erfc) z)))
2311 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2313 (defprop $erfc %erfc alias)
2314 (defprop $erfc %erfc verb)
2316 (defprop %erfc $erfc reversealias)
2317 (defprop %erfc $erfc noun)
2319 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2321 (defprop %erfc t commutes-with-conjugate)
2323 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2325 (defprop %erfc simp-erfc operators)
2327 ;;; Complementary Error function distributes over bags
2329 (defprop %erfc (mlist $matrix mequal) distribute_over)
2331 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2333 (defprop %erfc
2334 ((z)
2335 ((mtimes) -2
2336 ((mexpt) $%pi ((rat) -1 2))
2337 ((mexpt) $%e ((mtimes) -1 ((mexpt) z 2)))))
2338 grad)
2340 ;;; Integral of the Error function erfc
2342 (defprop %erfc
2343 ((z)
2344 ((mplus)
2345 ((mtimes) -1
2346 ((mexpt) $%pi ((rat) -1 2))
2347 ((mexpt) $%e ((mtimes) -1 ((mexpt) z 2))))
2348 ((mtimes) z ((%erfc) z))))
2349 integral)
2351 ;;; ----------------------------------------------------------------------------
2353 (defprop %erfc simplim%erfc simplim%function)
2355 (defun simplim%erfc (expr var val)
2356 ;; Look for the limit of the arguments.
2357 (let ((z (limit (cadr expr) var val 'think)))
2358 (cond
2359 ;; Handle infinities at this place.
2360 ((eq z '$inf) 0)
2361 ((eq z '$minf) 2)
2362 ((eq z '$infinity) ;; parallel to code in simplim%erf-%tanh
2363 (destructuring-let (((rpart . ipart) (trisplit (cadr expr)))
2364 (ans ()) (rlim ()))
2365 (setq rlim (limit rpart var val 'think))
2366 (setq ans
2367 (limit (m* rpart (m^t ipart -1)) var val 'think))
2368 (setq ans ($asksign (m+ `((mabs) ,ans) -1)))
2369 (cond ((or (eq ans '$pos) (eq ans '$zero))
2370 (cond ((eq rlim '$inf) 0)
2371 ((eq rlim '$minf) 2)
2372 (t '$und)))
2373 (t '$und))))
2374 ((eq z '$ind) '$ind)
2376 ;; All other cases are handled by the simplifier of the function.
2377 (simplify (list '(%erfc) z))))))
2379 ;;; ----------------------------------------------------------------------------
2381 (defun simp-erfc (expr z simpflag)
2382 (oneargcheck expr)
2383 (setq z (simpcheck (cadr expr) simpflag))
2384 (cond
2386 ;; Check for specific values
2388 ((zerop1 z) 1)
2389 ((eq z '$inf) 0)
2390 ((eq z '$minf) 2)
2392 ;; Check for numerical evaluation.
2394 ((numerical-eval-p z)
2395 (to (bigfloat::bf-erfc (bigfloat:to z))))
2397 ;; Argument simplification
2399 ((taylorize (mop expr) (second expr)))
2400 ((and $trigsign (great (mul -1 z) z))
2401 (sub 2 (simplify (list '(%erfc) (mul -1 z)))))
2403 ;; Representation through equivalent functions
2405 ($hypergeometric_representation
2406 (sub 1
2407 (mul 2 z
2408 (power '$%pi '((rat simp) 1 2))
2409 (list '(%hypergeometric simp)
2410 (list '(mlist simp) '((rat simp) 1 2))
2411 (list '(mlist simp) '((rat simp) 3 2))
2412 (mul -1 (power z 2))))))
2414 ;; Transformation to Erf or Erfi
2416 ((and $erf_representation
2417 (not (eq $erf_representation '$erfc)))
2418 (case $erf_representation
2419 (%erf
2420 (sub 1 (take '(%erf) z)))
2421 (%erfi
2422 (add 1 (mul '$%i (take '(%erfi) (mul '$%i z)))))
2424 (eqtest (list '(%erfc) z) expr))))
2427 (eqtest (list '(%erfc) z) expr))))
2429 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2431 ;;; Implementation of the Imaginary Error function Erfi(z)
2433 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2435 (defmfun $erfi (z)
2436 (simplify (list '(%erfi) z)))
2438 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2440 (defprop $erfi %erfi alias)
2441 (defprop $erfi %erfi verb)
2443 (defprop %erfi $erfi reversealias)
2444 (defprop %erfi $erfi noun)
2446 ;;; erfi has mirror symmetry
2448 (defprop %erfi t commutes-with-conjugate)
2450 ;;; erfi is an odd function
2452 (defprop %erfi odd-function-reflect reflection-rule)
2454 ;;; erfi is an simplifying function
2456 (defprop %erfi simp-erfi operators)
2458 ;;; erfi distributes over bags
2460 (defprop %erfi (mlist $matrix mequal) distribute_over)
2462 ;;; Derivative of the Error function erfi
2464 (defprop %erfi
2465 ((z)
2466 ((mtimes) 2
2467 ((mexpt) $%pi ((rat) -1 2))
2468 ((mexpt) $%e ((mexpt) z 2))))
2469 grad)
2471 ;;; Integral of the Error function erfi
2473 (defprop %erfi
2474 ((z)
2475 ((mplus)
2476 ((mtimes) -1
2477 ((mexpt) $%pi ((rat) -1 2))
2478 ((mexpt) $%e ((mexpt) z 2)))
2479 ((mtimes) z ((%erfi) z))))
2480 integral)
2482 ;;; ----------------------------------------------------------------------------
2484 (defprop %erfi simplim%erfi simplim%function)
2486 (defun simplim%erfi (expr var val)
2487 ;; Look for the limit of the arguments.
2488 (let ((z (limit (cadr expr) var val 'think)))
2489 (cond
2490 ;; Handle infinities at this place.
2491 ((eq z '$inf) '$inf)
2492 ((eq z '$minf) '$minf)
2494 ;; All other cases are handled by the simplifier of the function.
2495 (simplify (list '(%erfi) z))))))
2497 ;;; ----------------------------------------------------------------------------
2499 (in-package :bigfloat)
2500 (defun bf-erfi (z)
2501 (flet ((erfi (z)
2502 ;; Use the relationship erfi(z) = -%i*erf(%i*z)
2503 (let* ((iz (complex (- (imagpart z)) (realpart z))) ; %i*z
2504 (result (bf-erf iz)))
2505 (complex (imagpart result) (- (realpart result))))))
2506 ;; Take care to return real results when the argument is real.
2507 (if (realp z)
2508 (if (minusp z)
2509 (- (bf-erfi (- z)))
2510 (realpart (erfi z)))
2511 (erfi z))))
2513 (in-package :maxima)
2515 (defun simp-erfi (expr z simpflag)
2516 (oneargcheck expr)
2517 (setq z (simpcheck (cadr expr) simpflag))
2518 (cond
2520 ;; Check for specific values
2522 ((zerop1 z) z)
2523 ((eq z '$inf) '$inf)
2524 ((eq z '$minf) '$minf)
2526 ;; Check for numerical evaluation. Use erfi(z) = -%i*erf(%i*z).
2528 ((numerical-eval-p z)
2529 (to (bigfloat::bf-erfi (bigfloat:to z))))
2531 ;; Argument simplification
2533 ((taylorize (mop expr) (second expr)))
2534 ((and $erf_%iargs
2535 (multiplep z '$%i))
2536 (mul '$%i (simplify (list '(%erf) (coeff z '$%i 1)))))
2537 ((apply-reflection-simp (mop expr) z $trigsign))
2539 ;; Representation through equivalent functions
2541 ($hypergeometric_representation
2542 (mul 2 z
2543 (power '$%pi '((rat simp) 1 2))
2544 (list '(%hypergeometric simp)
2545 (list '(mlist simp) '((rat simp) 1 2))
2546 (list '(mlist simp) '((rat simp) 3 2))
2547 (power z 2))))
2549 ;; Transformation to Erf or Erfc
2551 ((and $erf_representation
2552 (not (eq $erf_representation '$erfi)))
2553 (case $erf_representation
2554 (%erf
2555 (mul -1 '$%i (take '(%erf) (mul '$%i z))))
2556 (%erfc
2557 (sub (mul '$%i (take '(%erfc) (mul '$%i z))) '$%i))
2559 (eqtest (list '(%erfi) z) expr))))
2562 (eqtest (list '(%erfi) z) expr))))
2564 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2566 ;;; The implementation of the Inverse Error function
2568 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2570 (defmfun $inverse_erf (z)
2571 (simplify (list '(%inverse_erf) z)))
2573 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2575 ;;; Set properties to give full support to the parser and display
2577 (defprop $inverse_erf %inverse_erf alias)
2578 (defprop $inverse_erf %inverse_erf verb)
2580 (defprop %inverse_erf $inverse_erf reversealias)
2581 (defprop %inverse_erf $inverse_erf noun)
2583 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2585 ;;; The Inverse Error function is a simplifying function
2587 (defprop %inverse_erf simp-inverse-erf operators)
2589 ;;; The Inverse Error function distributes over bags
2591 (defprop %inverse_erf (mlist $matrix mequal) distribute_over)
2593 ;;; inverse_erf is the inverse of the erf function
2595 (defprop %inverse_erf %erf $inverse)
2596 (defprop %erf %inverse_erf $inverse)
2598 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2600 ;;; Differentiation of the Inverse Error function
2602 (defprop %inverse_erf
2603 ((z)
2604 ((mtimes)
2605 ((rat) 1 2)
2606 ((mexpt) $%pi ((rat) 1 2))
2607 ((mexpt) $%e ((mexpt) ((%inverse_erf) z) 2))))
2608 grad)
2610 ;;; Integral of the Inverse Error function
2612 (defprop %inverse_erf
2613 ((z)
2614 ((mtimes) -1
2615 ((mexpt) $%pi ((rat) -1 2))
2616 ((mexpt) $%e ((mtimes) -1 ((mexpt) ((%inverse_erf) z) 2)))))
2617 integral)
2619 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2621 ;;; We support a simplim%function. The function is looked up in simplimit and
2622 ;;; handles specific values of the function.
2624 (defprop %inverse_erf simplim%inverse_erf simplim%function)
2626 (defun simplim%inverse_erf (expr var val)
2627 ;; Look for the limit of the argument.
2628 (let ((z (limit (cadr expr) var val 'think)))
2629 (cond
2630 ;; Handle an argument 1 at this place
2631 ((onep1 z) '$inf)
2632 ((onep1 (mul -1 z)) '$minf)
2634 ;; All other cases are handled by the simplifier of the function.
2635 (simplify (list '(%inverse_erf) z))))))
2637 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2639 (defun simp-inverse-erf (expr z simpflag)
2640 (oneargcheck expr)
2641 (setq z (simpcheck (cadr expr) simpflag))
2642 (cond
2643 ((or (onep1 z)
2644 (onep1 (mul -1 z)))
2645 (simp-domain-error
2646 (intl:gettext "inverse_erf: inverse_erf(~:M) is undefined.") z))
2647 ((zerop1 z) z)
2648 ((numerical-eval-p z)
2649 (to (bigfloat::bf-inverse-erf (bigfloat:to z))))
2650 ((taylorize (mop expr) (cadr expr)))
2652 (eqtest (list '(%inverse_erf) z) expr))))
2654 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2656 ;;; The implementation of the Inverse Complementary Error function
2658 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2660 (defmfun $inverse_erfc (z)
2661 (simplify (list '(%inverse_erfc) z)))
2663 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2665 ;;; Set properties to give full support to the parser and display
2667 (defprop $inverse_erfc %inverse_erfc alias)
2668 (defprop $inverse_erfc %inverse_erfc verb)
2670 (defprop %inverse_erfc $inverse_erfc reversealias)
2671 (defprop %inverse_erfc $inverse_erfc noun)
2673 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2675 ;;; Inverse Complementary Error function is a simplifying function
2677 (defprop %inverse_erfc simp-inverse-erfc operators)
2679 ;;; Inverse Complementary Error function distributes over bags
2681 (defprop %inverse_erfc (mlist $matrix mequal) distribute_over)
2683 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2685 ;;; inverse_erfc is the inverse of the erfc function
2687 (defprop %inverse_erfc %erfc $inverse)
2688 (defprop %erfc %inverse_erfc $inverse)
2691 ;;; Differentiation of the Inverse Complementary Error function
2693 (defprop %inverse_erfc
2694 ((z)
2695 ((mtimes)
2696 ((rat) -1 2)
2697 ((mexpt) $%pi ((rat) 1 2))
2698 ((mexpt) $%e ((mexpt) ((%inverse_erfc) z) 2))))
2699 grad)
2701 ;;; Integral of the Inverse Complementary Error function
2703 (defprop %inverse_erfc
2704 ((z)
2705 ((mtimes)
2706 ((mexpt) $%pi ((rat) -1 2))
2707 ((mexpt) $%e
2708 ((mtimes) -1 ((mexpt) ((%inverse_erfc) z) 2)))))
2709 integral)
2711 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2713 ;;; We support a simplim%function. The function is looked up in simplimit and
2714 ;;; handles specific values of the function.
2716 (defprop %inverse_erfc simplim%inverse_erfc simplim%function)
2718 (defun simplim%inverse_erfc (expr var val)
2719 ;; Look for the limit of the argument.
2720 (let ((z (limit (cadr expr) var val 'think)))
2721 (cond
2722 ;; Handle an argument 0 at this place
2723 ((or (zerop1 z)
2724 (eq z '$zeroa)
2725 (eq z '$zerob))
2726 '$inf)
2727 ((zerop1 (add z -2)) '$minf)
2729 ;; All other cases are handled by the simplifier of the function.
2730 (simplify (list '(%inverse_erfc) z))))))
2732 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2734 (defun simp-inverse-erfc (expr z simpflag)
2735 (oneargcheck expr)
2736 (setq z (simpcheck (cadr expr) simpflag))
2737 (cond
2738 ((or (zerop1 z)
2739 (zerop1 (add z -2)))
2740 (simp-domain-error
2741 (intl:gettext "inverse_erfc: inverse_erfc(~:M) is undefined.") z))
2742 ((onep1 z) 0)
2743 ((numerical-eval-p z)
2744 (to (bigfloat::bf-inverse-erfc (bigfloat:to z))))
2745 ((taylorize (mop expr) (cadr expr)))
2747 (eqtest (list '(%inverse_erfc) z) expr))))
2749 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2751 ;;; Implementation of the Newton algorithm to calculate numerical values
2752 ;;; of the Inverse Error functions in float or bigfloat precision.
2753 ;;; The algorithm is a modified version of the routine in newton1.mac.
2755 (defvar *debug-newton* nil)
2756 (defvar *newton-maxcount* 1000)
2757 (defvar *newton-epsilon-factor* 50)
2758 (defvar *newton-epsilon-factor-float* 10)
2760 (defun float-newton (expr var x0 eps)
2761 (do ((s (sdiff expr var))
2762 (xn x0)
2763 (sn)
2764 (count 0 (+ count 1)))
2765 ((> count *newton-maxcount*)
2766 (merror
2767 (intl:gettext "float-newton: Newton does not converge for ~:M") expr))
2768 (setq sn ($float (maxima-substitute xn var expr)))
2769 (when (< (abs sn) eps) (return xn))
2770 (when *debug-newton* (format t "~&xn = ~A~%" xn))
2771 (setq xn ($float (sub xn (div sn (maxima-substitute xn var s)))))))
2773 (defun bfloat-newton (expr var x0 eps)
2774 (do ((s (sdiff expr var))
2775 (xn x0)
2776 (sn)
2777 (count 0 (+ count 1)))
2778 ((> count *newton-maxcount*)
2779 (merror
2780 (intl:gettext "bfloat-newton: Newton does not converge for ~:M") expr))
2781 (setq sn ($bfloat (maxima-substitute xn var expr)))
2782 (when (eq ($sign (sub (simplify (list '(mabs) sn)) eps)) '$neg)
2783 (return xn))
2784 (when *debug-newton* (format t "~&xn = ~A~%" xn))
2785 (setq xn ($bfloat (sub xn (div sn (maxima-substitute xn var s)))))))
2788 (in-package :bigfloat)
2790 ;; Compute inverse_erf(z) for z a real or complex number, including
2791 ;; bigfloat objects. The value is computing using a Newton iteration
2792 ;; to solve erf(x) = z.
2793 (defun bf-inverse-erf (z)
2794 (cond ((zerop z)
2796 ((= (abs z) 1)
2797 (maxima::merror
2798 (intl:gettext "bf-inverse-erf: inverse_erf(~M) is undefined")
2800 ((minusp (realpart z))
2801 ;; inverse_erf is odd because erf is.
2802 (- (bf-inverse-erf (- z))))
2804 (labels
2805 ((approx (z)
2806 ;; Find an approximate solution for x = inverse_erf(z).
2807 (let ((result
2808 (cond ((<= (abs z) 1)
2809 ;; For small z, inverse_erf(z) = z*sqrt(%pi)/2
2810 ;; + O(z^3). Thus, x = z*sqrt(%pi)/2 is our
2811 ;; initial starting point.
2812 (* z (sqrt (%pi z)) 1/2))
2814 ;; For |z| > 1 and realpart(z) >= 0, we have
2815 ;; the asymptotic series z = erf(x) = 1 -
2816 ;; exp(-x^2)/x/sqrt(%pi).
2818 ;; Then
2819 ;; x = sqrt(-log(x*sqrt(%pi)*(1-z))
2821 ;; We can use this as a fixed-point iteration
2822 ;; to find x, and we can start the iteration at
2823 ;; x = 1. Just do one more iteration. I (RLT)
2824 ;; think that's close enough to get the Newton
2825 ;; algorithm to converge.
2826 (let* ((sp (sqrt (%pi z)))
2827 (a (sqrt (- (log (* sp (- 1 z)))))))
2828 (setf a (sqrt (- (log (* a sp (- 1 z))))))
2829 (setf a (sqrt (- (log (* a sp (- 1 z)))))))))))
2830 (when maxima::*debug-newton*
2831 (format t "approx = ~S~%" result))
2832 result)))
2833 (let ((two/sqrt-pi (/ 2 (sqrt (%pi z))))
2834 (eps
2835 ;; Try to pick a reasonable epsilon value for the
2836 ;; Newton iteration.
2837 (cond ((<= (abs z) 1)
2838 (typecase z
2839 (cl:real (* maxima::*newton-epsilon-factor-float*
2840 maxima::flonum-epsilon))
2841 (t (* maxima::*newton-epsilon-factor* (epsilon z)))))
2843 (* maxima::*newton-epsilon-factor* (epsilon z))))))
2844 (when maxima::*debug-newton*
2845 (format t "eps = ~S~%" eps))
2846 (flet ((diff (x)
2847 ;; Derivative of erf(x)
2848 (* two/sqrt-pi (exp (- (* x x))))))
2849 (bf-newton #'bf-erf
2850 #'diff
2852 (approx z)
2853 eps)))))))
2855 (defun bf-inverse-erfc (z)
2856 (cond ((zerop z)
2857 (maxima::merror
2858 (intl:gettext "bf-inverse-erfc: inverse_erfc(~M) is undefined")
2860 ((= z 1)
2861 (float 0 z))
2863 (flet
2864 ((approx (z)
2865 ;; Find an approximate solution for x =
2866 ;; inverse_erfc(z). We have inverse_erfc(z) =
2867 ;; inverse_erf(1-z), so that's a good starting point.
2868 ;; We don't need full precision, so a float value is
2869 ;; good enough. But if 1-z is 1, inverse_erf is
2870 ;; undefined, so we need to do something else.
2871 (let ((result
2872 (let ((1-z (float (- 1 z) 0.0)))
2873 (cond ((= 1 1-z)
2874 (if (minusp (realpart z))
2875 (bf-inverse-erf (+ 1 (* 5 maxima::flonum-epsilon)))
2876 (bf-inverse-erf (- 1 (* 5 maxima::flonum-epsilon)))))
2878 (bf-inverse-erf 1-z))))))
2879 (when maxima::*debug-newton*
2880 (format t "approx = ~S~%" result))
2881 result)))
2882 (let ((-two/sqrt-pi (/ -2 (sqrt (%pi z))))
2883 (eps (* maxima::*newton-epsilon-factor* (epsilon z))))
2884 (when maxima::*debug-newton*
2885 (format t "eps = ~S~%" eps))
2886 (flet ((diff (x)
2887 ;; Derivative of erfc(x)
2888 (* -two/sqrt-pi (exp (- (* x x))))))
2889 (bf-newton #'bf-erfc
2890 #'diff
2892 (approx z)
2893 eps)))))))
2895 ;; Newton iteration for solving f(x) = z, given f and the derivative
2896 ;; of f.
2897 (defun bf-newton (f df z start eps)
2898 (do ((x start)
2899 (delta (/ (- (funcall f start) z)
2900 (funcall df start))
2901 (/ (- (funcall f x) z)
2902 (funcall df x)))
2903 (count 0 (1+ count)))
2904 ((or (< (abs delta) (* (abs x) eps))
2905 (> count maxima::*newton-maxcount*))
2906 (if (> count maxima::*newton-maxcount*)
2907 (maxima::merror
2908 (intl:gettext "bf-newton: failed to converge after ~M iterations: delta = ~S, x = ~S eps = ~S")
2909 count delta x eps)
2911 (when maxima::*debug-newton*
2912 (format t "x = ~S, abs(delta) = ~S relerr = ~S~%"
2913 x (abs delta) (/ (abs delta) (abs x))))
2914 (setf x (- x delta))))
2916 (in-package :maxima)
2918 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2920 ;;; Implementation of the Fresnel Integral S(z)
2922 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2924 (defmfun $fresnel_s (z)
2925 (simplify (list '(%fresnel_s) z)))
2927 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2929 ;;; Set properties to give full support to the parser and display
2931 (defprop $fresnel_s %fresnel_s alias)
2932 (defprop $fresnel_s %fresnel_s verb)
2934 (defprop %fresnel_s $fresnel_s reversealias)
2935 (defprop %fresnel_s $fresnel_s noun)
2937 ;;; fresnel_s is a simplifying function
2939 (defprop %fresnel_s simp-fresnel-s operators)
2941 ;;; fresnel_s distributes over bags
2943 (defprop %fresnel_s (mlist $matrix mequal) distribute_over)
2945 ;;; fresnel_s has mirror symmetry
2947 (defprop %fresnel_s t commutes-with-conjugate)
2949 ;;; fresnel_s is an odd function
2951 ;;; Maxima has two mechanism to define a reflection property
2952 ;;; 1. Declare the feature oddfun or evenfun
2953 ;;; 2. Put a reflection rule on the property list
2955 ;;; The second way is used for the trig functions. We use it here too.
2957 ;;; This would be the first way to give the property of an odd function.
2958 ;(eval-when
2959 ; #+gcl (load eval)
2960 ; #-gcl (:load-toplevel :execute)
2961 ; (let (($context '$global) (context '$global))
2962 ; (meval '(($declare) %fresnel_s $oddfun))
2963 ; ;; Let's remove built-in symbols from list for user-defined properties.
2964 ; (setq $props (remove '%fresnel_s $props))))
2966 (defprop %fresnel_s odd-function-reflect reflection-rule)
2968 ;;; Differentiation of the Fresnel Integral S
2970 (defprop %fresnel_s
2971 ((z)
2972 ((%sin) ((mtimes) ((rat) 1 2) $%pi ((mexpt) z 2))))
2973 grad)
2975 ;;; Integration of the Fresnel Integral S
2977 (defprop %fresnel_s
2978 ((z)
2979 ((mplus)
2980 ((mtimes) z ((%fresnel_s) z))
2981 ((mtimes)
2982 ((mexpt) $%pi -1)
2983 ((%cos) ((mtimes) ((rat) 1 2) $%pi ((mexpt) z 2))))))
2984 integral)
2986 ;;; Limits of the Fresnel Integral S
2988 (defprop %fresnel_s simplim%fresnel_s simplim%function)
2989 (defun simplim%fresnel_s (exp var val)
2990 (let ((arg (limit (cadr exp) var val 'think)))
2991 (cond ((eq arg '$inf)
2992 '((rat simp) 1 2))
2993 ((eq arg '$minf)
2994 '((rat simp) -1 2))
2996 `((%fresnel_s) ,arg)))))
2998 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3000 (defvar *fresnel-maxit* 1000)
3001 (defvar *fresnel-eps* (* 2 flonum-epsilon))
3002 (defvar *fresnel-min* 1e-32)
3004 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3005 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3007 (in-package :bigfloat)
3009 (defun bf-fresnel (z)
3010 (let* ((eps (epsilon z))
3011 (maxit maxima::*fresnel-maxit*)
3012 (xmin 1.5)
3013 (bf-pi (%pi z))
3014 ;; For very small x, we have
3015 ;; fresnel_s(x) = %pi/6*z^3
3016 ;; fresnel_c(x) = x
3017 (s (* (/ bf-pi 6) z z z))
3018 (c z))
3019 (when (> (abs z) eps)
3020 (cond
3021 ((< (abs z) xmin)
3022 (when maxima::*debug-gamma*
3023 (format t "~& in FRESNEL series expansion.~%"))
3024 (let ((sums 0) (sumc z))
3025 (do ((sum 0)
3026 (sign 1)
3027 (fact (* (/ bf-pi 2) (* z z)))
3028 (term z)
3029 (odd t (not odd))
3030 (test 0)
3031 (n 3 (+ n 2))
3032 (k 1 (+ k 1)))
3033 ((> k maxit)
3034 (maxima::merror (intl:gettext "fresnel: series expansion failed for (COMPLEX-BFLOAT-FRESNEL ~:M).") z))
3035 (setq term (* term (/ fact k)))
3036 (setq sum (+ sum (/ (* sign term) n)))
3037 (setq test (* (abs sum) eps))
3038 (if odd
3039 (progn
3040 (setq sign (- sign))
3041 (setq sums sum)
3042 (setq sum sumc))
3043 (progn
3044 (setq sumc sum)
3045 (setq sum sums)))
3046 (if (< (abs term) test)
3047 (return)))
3048 (setq s sums)
3049 (setq c sumc)))
3051 (let* ((sqrt-pi (sqrt bf-pi))
3052 (z+ (* (complex 1/2 1/2)
3053 (* sqrt-pi
3054 z)))
3055 (z- (* (complex 1/2 -1/2)
3056 (* sqrt-pi
3057 z)))
3058 (erf+ (bf-erf z+))
3059 (erf- (bf-erf z-)))
3060 (setq s (* (complex 1/4 1/4)
3061 (+ erf+ (* (complex 0 -1) erf-))))
3062 (setq c (* (complex 1/4 -1/4)
3063 (+ erf+ (* (complex 0 1) erf-))))))))
3064 (values s c)))
3066 (defun bf-fresnel-s (z)
3067 (if (and (complexp z) (zerop (realpart z)))
3068 ;; A pure imaginary argument. Use fresnel_s(%i*x)=-%i*fresnel_s(x).
3069 (complex 0
3070 (- (bf-fresnel-s (imagpart z))))
3071 (let ((fs (bf-fresnel z)))
3072 (if (realp z)
3073 (realpart fs)
3074 fs))))
3076 (defun bf-fresnel-c (z)
3077 (if (and (complexp z) (zerop (realpart z)))
3078 ;; A pure imaginary argument. Use fresnel_c(%i*x)=%i*fresnel_c(x).
3079 (complex 0
3080 (bf-fresnel-c (imagpart z)))
3081 (let ((fc (nth-value 1 (bf-fresnel z))))
3082 (if (realp z)
3083 (realpart fc)
3084 fc))))
3086 (in-package :maxima)
3087 (defun simp-fresnel-s (expr z simpflag)
3088 (oneargcheck expr)
3089 (setq z (simpcheck (cadr expr) simpflag))
3090 (cond
3092 ;; Check for specific values
3094 ((zerop1 z) z)
3095 ((eq z '$inf) '((rat simp) 1 2))
3096 ((eq z '$minf) '((rat simp) -1 2))
3098 ;; Check for numerical evaluation
3099 ((numerical-eval-p z)
3100 (to (bigfloat::bf-fresnel-s (bigfloat::to z))))
3102 ;; Check for argument simplification
3104 ((taylorize (mop expr) (second expr)))
3105 ((and $%iargs (multiplep z '$%i))
3106 (mul -1 '$%i (simplify (list '(%fresnel_s) (coeff z '$%i 1)))))
3107 ((apply-reflection-simp (mop expr) z $trigsign))
3109 ;; Representation through equivalent functions
3111 ($erf_representation
3112 (mul
3113 (div (add 1 '$%i) 4)
3114 (add
3115 (simplify
3116 (list
3117 '(%erf)
3118 (mul (div (add 1 '$%i) 2) (power '$%pi '((rat simp) 1 2)) z)))
3119 (mul -1 '$%i
3120 (simplify
3121 (list
3122 '(%erf)
3123 (mul (div (sub 1 '$%i) 2)
3124 (power '$%pi '((rat simp) 1 2)) z)))))))
3126 ($hypergeometric_representation
3127 (mul (div (mul '$%pi (power z 3)) 6)
3128 (take '($hypergeometric)
3129 (list '(mlist) (div 3 4))
3130 (list '(mlist) (div 3 2) (div 7 4))
3131 (mul -1 (div (mul (power '$%pi 2) (power z 4)) 16)))))
3134 (eqtest (list '(%fresnel_s) z) expr))))
3136 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3138 ;;; Implementation of the Fresnel Integral C(z)
3140 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3142 (defmfun $fresnel_c (z)
3143 (simplify (list '(%fresnel_c) z)))
3145 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3147 ;;; Set properties to give full support to the parser and display
3149 (defprop $fresnel_c %fresnel_c alias)
3150 (defprop $fresnel_c %fresnel_c verb)
3152 (defprop %fresnel_c $fresnel_c reversealias)
3153 (defprop %fresnel_c $fresnel_c noun)
3155 ;;; fresnel_c is a simplifying function
3157 (defprop %fresnel_c simp-fresnel-c operators)
3159 ;;; fresnel_c distributes over bags
3161 (defprop %fresnel_c (mlist $matrix mequal) distribute_over)
3163 ;;; fresnel_c has mirror symmetry
3165 (defprop %fresnel_c t commutes-with-conjugate)
3167 ;;; fresnel_c is an odd function
3168 ;;; Maxima has two mechanism to define a reflection property
3169 ;;; 1. Declare the feature oddfun or evenfun
3170 ;;; 2. Put a reflection rule on the property list
3172 ;;; The second way is used for the trig functions. We use it here too.
3174 (defprop %fresnel_c odd-function-reflect reflection-rule)
3176 ;;; Differentiation of the Fresnel Integral C
3178 (defprop %fresnel_c
3179 ((z)
3180 ((%cos) ((mtimes) ((rat) 1 2) $%pi ((mexpt) z 2))))
3181 grad)
3183 ;;; Integration of the Fresnel Integral C
3185 (defprop %fresnel_c
3186 ((z)
3187 ((mplus)
3188 ((mtimes) z ((%fresnel_c) z))
3189 ((mtimes) -1
3190 ((mexpt) $%pi -1)
3191 ((%sin) ((mtimes) ((rat) 1 2) $%pi ((mexpt) z 2))))))
3192 integral)
3194 ;;; Limits of the Fresnel Integral C
3196 (defprop %fresnel_c simplim%fresnel_c simplim%function)
3197 (defun simplim%fresnel_c (exp var val)
3198 (let ((arg (limit (cadr exp) var val 'think)))
3199 (cond ((eq arg '$inf)
3200 '((rat simp) 1 2))
3201 ((eq arg '$minf)
3202 '((rat simp) -1 2))
3204 `((%fresnel_c) ,arg)))))
3206 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3208 (defun simp-fresnel-c (expr z simpflag)
3209 (oneargcheck expr)
3210 (setq z (simpcheck (cadr expr) simpflag))
3211 (cond
3213 ;; Check for specific values
3215 ((zerop1 z) z)
3216 ((eq z '$inf) '((rat simp) 1 2))
3217 ((eq z '$minf) '((rat simp) -1 2))
3219 ;; Check for numerical evaluation
3220 ((numerical-eval-p z)
3221 (to (bigfloat::bf-fresnel-c (bigfloat::to z))))
3224 ;; Check for argument simplification
3226 ((taylorize (mop expr) (second expr)))
3227 ((and $%iargs (multiplep z '$%i))
3228 (mul '$%i (simplify (list '(%fresnel_c) (coeff z '$%i 1)))))
3229 ((apply-reflection-simp (mop expr) z $trigsign))
3231 ;; Representation through equivalent functions
3233 ($erf_representation
3234 (mul
3235 (div (sub 1 '$%i) 4)
3236 (add
3237 (simplify
3238 (list
3239 '(%erf)
3240 (mul (div (add 1 '$%i) 2) (power '$%pi '((rat simp) 1 2)) z)))
3241 (mul '$%i
3242 (simplify
3243 (list
3244 '(%erf)
3245 (mul (div (sub 1 '$%i) 2)
3246 (power '$%pi '((rat simp) 1 2)) z)))))))
3248 ($hypergeometric_representation
3249 (mul z
3250 (take '($hypergeometric)
3251 (list '(mlist) (div 1 4))
3252 (list '(mlist) (div 1 2) (div 5 4))
3253 (mul -1 (div (mul (power '$%pi 2) (power z 4)) 16)))))
3256 (eqtest (list '(%fresnel_c) z) expr))))
3259 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3261 ;;; Implementation of the Beta function
3263 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3265 ;;; The code for the implementation of the beta function is in the files
3266 ;;; csimp2.lisp, simp.lisp and mactex.lisp.
3267 ;;; At this place we only implement the operator property SYMMETRIC.
3269 ;;; Beta is symmetric beta(a,b) = beta(b,a)
3271 (eval-when
3272 #+gcl (load eval)
3273 #-gcl (:load-toplevel :execute)
3274 (let (($context '$global) (context '$global))
3275 (meval '(($declare) $beta $symmetric))
3276 ;; Let's remove built-in symbols from list for user-defined properties.
3277 (setq $props (remove '$beta $props))))
3279 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3281 ;;; Implementation of the Incomplete Beta function
3283 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3285 (defvar *beta-incomplete-maxit* 10000)
3286 (defvar *beta-incomplete-eps* 1.0e-15)
3288 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3290 (defmfun $beta_incomplete (a b z)
3291 (simplify (list '(%beta_incomplete) a b z)))
3293 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3295 (defprop $beta_incomplete %beta_incomplete alias)
3296 (defprop $beta_incomplete %beta_incomplete verb)
3298 (defprop %beta_incomplete $beta_incomplete reversealias)
3299 (defprop %beta_incomplete $beta_incomplete noun)
3301 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3303 (defprop %beta_incomplete simp-beta-incomplete operators)
3305 ;;; beta_incomplete distributes over bags
3307 (defprop %beta_incomplete (mlist $matrix mequal) distribute_over)
3309 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3311 (defprop %beta_incomplete
3312 ((a b z)
3313 ;; Derivative wrt a
3314 ((mplus)
3315 ((%beta_incomplete) a b z)
3316 ((mtimes) -1
3317 ((mexpt) ((%gamma) a) 2)
3318 (($hypergeometric_regularized)
3319 ((mlist) a a ((mplus) 1 ((mtimes) -1 b)))
3320 ((mlist) ((mplus) 1 a) ((mplus) 1 a))
3322 ((mexpt) z a)))
3323 ;; Derivative wrt b
3324 ((mplus)
3325 ((mtimes)
3326 (($beta) a b)
3327 ((mplus)
3328 ((mqapply)
3329 (($psi array 0) b)
3330 ((mtimes) -1 ((mqapply) (($psi array) 0) ((mplus) a b)))))
3331 ((mtimes) -1
3332 ((%beta_incomplete) b a ((mplus) 1 ((mtimes) -1 z)))
3333 ((%log) ((mplus) 1 ((mtimes) -1 z))))
3334 ((mtimes)
3335 ((mexpt) ((%gamma) b) 2)
3336 (($hypergeometric_regularized)
3337 ((mlist) b b ((mplus) 1 ((mtimes) -1 a)))
3338 ((mlist) ((mplus) 1 b) ((mplus) 1 b))
3339 ((mplus) 1 ((mtimes) -1 z)))
3340 ((mexpt) ((mplus) 1 ((mtimes) -1 z)) b))))
3341 ;; The derivative wrt z
3342 ((mtimes)
3343 ((mexpt) ((mplus) 1 ((mtimes) -1 z)) ((mplus) -1 b))
3344 ((mexpt) z ((mplus) -1 a))))
3345 grad)
3347 ;;; Integral of the Incomplete Beta function
3349 (defprop %beta_incomplete
3350 ((a b z)
3351 nil
3353 ((mplus)
3354 ((mtimes) -1 ((%beta_incomplete) ((mplus) 1 a) b z))
3355 ((mtimes) ((%beta_incomplete) a b z) z)))
3356 integral)
3358 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3360 (defun simp-beta-incomplete (expr ignored simpflag)
3361 (declare (ignore ignored))
3362 (arg-count-check 3 expr)
3363 (let ((a (simpcheck (cadr expr) simpflag))
3364 (b (simpcheck (caddr expr) simpflag))
3365 (z (simpcheck (cadddr expr) simpflag))
3366 ($simpsum t))
3367 (when *debug-gamma*
3368 (format t "~&SIMP-BETA-INCOMPLETE:~%")
3369 (format t "~& : a = ~A~%" a)
3370 (format t "~& : b = ~A~%" b)
3371 (format t "~& : z = ~A~%" z))
3372 (cond
3374 ;; Check for specific values
3376 ((zerop1 z)
3377 (let ((sgn ($sign ($realpart a))))
3378 (cond ((member sgn '($neg $zero))
3379 (simp-domain-error
3380 (intl:gettext
3381 "beta_incomplete: beta_incomplete(~:M,~:M,~:M) is undefined.")
3382 a b z))
3383 ((member sgn '($pos $pz))
3386 (eqtest (list '(%beta_incomplete) a b z) expr)))))
3388 ((and (onep1 z) (eq ($sign ($realpart b)) '$pos))
3389 ;; z=1 and realpart(b)>0. Simplify to a Beta function.
3390 ;; If we have to evaluate numerically preserve the type.
3391 (cond
3392 ((complex-float-numerical-eval-p a b z)
3393 (simplify (list '($beta) ($float a) ($float b))))
3394 ((complex-bigfloat-numerical-eval-p a b z)
3395 (simplify (list '($beta) ($bfloat a) ($bfloat b))))
3397 (simplify (list '($beta) a b)))))
3399 ((or (zerop1 a)
3400 (and (integer-representation-p a)
3401 (eq ($sign a) '$neg)
3402 (or (and (mnump b)
3403 (not (integer-representation-p b)))
3404 (eq ($sign (add a b)) '$pos))))
3405 ;; The argument a is zero or a is negative and the argument b is
3406 ;; not in a valid range. Beta incomplete is undefined.
3407 ;; It would be more correct to return Complex infinity.
3408 (simp-domain-error
3409 (intl:gettext
3410 "beta_incomplete: beta_incomplete(~:M,~:M,~:M) is undefined.") a b z))
3412 ;; Check for numerical evaluation in Float or Bigfloat precision
3414 ((complex-float-numerical-eval-p a b z)
3415 (cond
3416 ((not (and (integer-representation-p a) (< a 0.0)))
3417 (let ((*beta-incomplete-eps* (bigfloat:epsilon ($float 1.0))))
3418 (beta-incomplete ($float a) ($float b) ($float z))))
3420 ;; Negative integer a and b is in a valid range. Expand.
3421 ($rectform
3422 (beta-incomplete-expand-negative-integer
3423 (truncate a) ($float b) ($float z))))))
3425 ((complex-bigfloat-numerical-eval-p a b z)
3426 (cond
3427 ((not (and (integer-representation-p a) (eq ($sign a) '$neg)))
3428 (let ((*beta-incomplete-eps*
3429 (bigfloat:epsilon (bigfloat:bigfloat 1.0))))
3430 (beta-incomplete ($bfloat a) ($bfloat b) ($bfloat z))))
3432 ;; Negative integer a and b is in a valid range. Expand.
3433 ($rectform
3434 (beta-incomplete-expand-negative-integer
3435 ($truncate a) ($bfloat b) ($bfloat z))))))
3437 ;; Argument simplifications and transformations
3439 ((and (integerp b)
3440 (plusp b)
3441 (or (not (integerp a))
3442 (plusp a)))
3443 ;; Expand for b a positive integer and a not a negative integer.
3444 (mul
3445 (simplify (list '($beta) a b))
3446 (power z a)
3447 (let ((index (gensumindex)))
3448 (simpsum1
3449 (div
3450 (mul
3451 (simplify (list '($pochhammer) a index))
3452 (power (sub 1 z) index))
3453 (simplify (list '(mfactorial) index)))
3454 index 0 (sub b 1)))))
3456 ((and (integerp a) (plusp a))
3457 ;; Expand for a a positive integer.
3458 (mul
3459 (simplify (list '($beta) a b))
3460 (sub 1
3461 (mul
3462 (power (sub 1 z) b)
3463 (let ((index (gensumindex)))
3464 (simpsum1
3465 (div
3466 (mul
3467 (simplify (list '($pochhammer) b index))
3468 (power z index))
3469 (simplify (list '(mfactorial) index)))
3470 index 0 (sub a 1)))))))
3472 ((and (integerp a) (minusp a) (integerp b) (plusp b) (<= b (- a)))
3473 ;; Expand for a a negative integer and b an integer with b <= -a.
3474 (mul
3475 (power z a)
3476 (let ((index (gensumindex)))
3477 (simpsum1
3478 (div
3479 (mul (simplify (list '($pochhammer) (sub 1 b) index))
3480 (power z index))
3481 (mul (add index a)
3482 (simplify (list '(mfactorial) index))))
3483 index 0 (sub b 1)))))
3485 ((and $beta_expand (mplusp a) (integerp (cadr a)) (plusp (cadr a)))
3486 (let ((n (cadr a))
3487 (a (simplify (cons '(mplus) (cddr a)))))
3488 (sub
3489 (mul
3490 (div
3491 (simplify (list '($pochhammer) a n))
3492 (simplify (list '($pochhammer) (add a b) n)))
3493 ($beta_incomplete a b z))
3494 (mul
3495 (power (add a b n -1) -1)
3496 (let ((index (gensumindex)))
3497 (simpsum1
3498 (mul
3499 (div
3500 (simplify (list '($pochhammer)
3501 (add 1 (mul -1 a) (mul -1 n))
3502 index))
3503 (simplify (list '($pochhammer)
3504 (add 2 (mul -1 a) (mul -1 b) (mul -1 n))
3505 index)))
3506 (mul (power (sub 1 z) b)
3507 (power z (add a n (mul -1 index) -1))))
3508 index 0 (sub n 1)))))))
3510 ((and $beta_expand (mplusp a) (integerp (cadr a)) (minusp (cadr a)))
3511 (let ((n (- (cadr a)))
3512 (a (simplify (cons '(mplus) (cddr a)))))
3513 (sub
3514 (mul
3515 (div
3516 (simplify (list '($pochhammer) (add 1 (mul -1 a) (mul -1 b)) n))
3517 (simplify (list '($pochhammer) (sub 1 a) n)))
3518 ($beta_incomplete a b z))
3519 (mul
3520 (div
3521 (simplify
3522 (list '($pochhammer) (add 2 (mul -1 a) (mul -1 b)) (sub n 1)))
3523 (simplify (list '($pochhammer) (sub 1 a) n)))
3524 (let ((index (gensumindex)))
3525 (simpsum1
3526 (mul
3527 (div
3528 (simplify (list '($pochhammer) (sub 1 a) index))
3529 (simplify (list '($pochhammer)
3530 (add 2 (mul -1 a) (mul -1 b))
3531 index)))
3532 (mul (power (sub 1 z) b)
3533 (power z (add a (mul -1 index) -1))))
3534 index 0 (sub n 1)))))))
3537 (eqtest (list '(%beta_incomplete) a b z) expr)))))
3539 (defun beta-incomplete-expand-negative-integer (a b z)
3540 (mul
3541 (power z a)
3542 (let ((index (gensumindex)) ($simpsum t))
3543 (simpsum1
3544 (div
3545 (mul (simplify (list '($pochhammer) (sub 1 b) index))
3546 (power z index))
3547 (mul (add index a) (simplify (list '(mfactorial) index))))
3548 index 0 (sub b 1)))))
3550 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3552 (defun beta-incomplete (a b z)
3553 (cond
3554 ((eq ($sign (sub (mul ($realpart z) ($realpart (add a b 2)))
3555 ($realpart (add a 1))))
3556 '$pos)
3557 ($rectform
3558 (sub
3559 (simplify (list '($beta) a b))
3560 (to (numeric-beta-incomplete b a (sub 1.0 z))))))
3562 (to (numeric-beta-incomplete a b z)))))
3564 (defun numeric-beta-incomplete (a b z)
3565 (when *debug-gamma*
3566 (format t "~&NUMERIC-BETA-INCOMPLETE enters continued fractions~%"))
3567 (let ((a (bigfloat:to a))
3568 (b (bigfloat:to b))
3569 (z (bigfloat:to z)))
3570 (do* ((beta-maxit *beta-incomplete-maxit*)
3571 (beta-eps *beta-incomplete-eps*)
3572 (beta-min (bigfloat:* beta-eps beta-eps))
3573 (ab (bigfloat:+ a b))
3574 (ap (bigfloat:+ a 1.0))
3575 (am (bigfloat:- a 1.0))
3576 (c 1.0)
3577 (d (bigfloat:- 1.0 (bigfloat:/ (bigfloat:* z ab) ap)))
3578 (d (if (bigfloat:< (bigfloat:abs d) beta-min) beta-min d))
3579 (d (bigfloat:/ 1.0 d))
3580 (h d)
3581 (aa 0.0)
3582 (de 0.0)
3583 (m2 0)
3584 (m 1 (+ m 1)))
3585 ((> m beta-maxit)
3586 (merror (intl:gettext "beta_incomplete: continued fractions failed for beta_incomplete(~:M, ~:M, ~:M).") a b z))
3587 (setq m2 (+ m m))
3588 (setq aa (bigfloat:/ (bigfloat:* m z (bigfloat:- b m))
3589 (bigfloat:* (bigfloat:+ am m2)
3590 (bigfloat:+ a m2))))
3591 (setq d (bigfloat:+ 1.0 (bigfloat:* aa d)))
3592 (when (bigfloat:< (bigfloat:abs d) beta-min) (setq d beta-min))
3593 (setq c (bigfloat:+ 1.0 (bigfloat:/ aa c)))
3594 (when (bigfloat:< (bigfloat:abs c) beta-min) (setq c beta-min))
3595 (setq d (bigfloat:/ 1.0 d))
3596 (setq h (bigfloat:* h d c))
3597 (setq aa (bigfloat:/ (bigfloat:* -1
3598 (bigfloat:+ a m)
3599 (bigfloat:+ ab m) z)
3600 (bigfloat:* (bigfloat:+ a m2)
3601 (bigfloat:+ ap m2))))
3602 (setq d (bigfloat:+ 1.0 (bigfloat:* aa d)))
3603 (when (bigfloat:< (bigfloat:abs d) beta-min) (setq d beta-min))
3604 (setq c (bigfloat:+ 1.0 (bigfloat:/ aa c)))
3605 (when (bigfloat:< (bigfloat:abs c) beta-min) (setq c beta-min))
3606 (setq d (bigfloat:/ 1.0 d))
3607 (setq de (bigfloat:* d c))
3608 (setq h (bigfloat:* h de))
3609 (when (bigfloat:< (bigfloat:abs (bigfloat:- de 1.0)) beta-eps)
3610 (when *debug-gamma*
3611 (format t "~&Continued fractions finished.~%")
3612 (format t "~& z = ~A~%" z)
3613 (format t "~& a = ~A~%" a)
3614 (format t "~& b = ~A~%" b)
3615 (format t "~& h = ~A~%" h))
3616 (return
3617 (let ((result
3618 (bigfloat:/
3619 (bigfloat:* h
3620 (bigfloat:expt z a)
3621 (bigfloat:expt (bigfloat:- 1.0 z) b)) a)))
3622 (when *debug-gamma*
3623 (format t "~& result = ~A~%" result))
3624 result))))))
3626 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3628 ;;; Implementation of the Generalized Incomplete Beta function
3630 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3632 (defmfun $beta_incomplete_generalized (a b z1 z2)
3633 (simplify (list '(%beta_incomplete_generalized) a b z1 z2)))
3635 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3637 (defprop $beta_incomplete_generalized %beta_incomplete_generalized alias)
3638 (defprop $beta_incomplete_generalized %beta_incomplete_generalized verb)
3640 (defprop %beta_incomplete_generalized $beta_incomplete_generalized reversealias)
3641 (defprop %beta_incomplete_generalized $beta_incomplete_generalized noun)
3643 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3645 (defprop %beta_incomplete_generalized
3646 simp-beta-incomplete-generalized operators)
3648 ;;; beta_incomplete_generalized distributes over bags
3650 (defprop %beta_incomplete_generalized (mlist $matrix mequal) distribute_over)
3652 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3654 ;;; Generalized Incomplete Gamma function has not mirror symmetry for z1 or z2
3655 ;;; but not on the negative real axis and for z1 or z2 real and > 1.
3656 ;;; We support a conjugate-function which test these cases.
3658 (defprop %beta_incomplete_generalized
3659 conjugate-beta-incomplete-generalized conjugate-function)
3661 (defun conjugate-beta-incomplete-generalized (args)
3662 (let ((a (first args))
3663 (b (second args))
3664 (z1 (third args))
3665 (z2 (fourth args)))
3666 (cond ((and (off-negative-real-axisp z1)
3667 (not (and (zerop1 ($imagpart z1))
3668 (eq ($sign (sub ($realpart z1) 1)) '$pos)))
3669 (off-negative-real-axisp z2)
3670 (not (and (zerop1 ($imagpart z2))
3671 (eq ($sign (sub ($realpart z2) 1)) '$pos))))
3672 (simplify
3673 (list
3674 '(%beta_incomplete_generalized)
3675 (simplify (list '($conjugate) a))
3676 (simplify (list '($conjugate) b))
3677 (simplify (list '($conjugate) z1))
3678 (simplify (list '($conjugate) z2)))))
3680 (list
3681 '($conjugate simp)
3682 (simplify (list '(%beta_incomplete_generalized)
3683 a b z1 z2)))))))
3685 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3687 (defprop %beta_incomplete_generalized
3688 ((a b z1 z2)
3689 ;; Derivative wrt a
3690 ((mplus)
3691 ((mtimes) -1
3692 ((%beta_incomplete) a b z1)
3693 ((%log) z1))
3694 ((mtimes)
3695 ((mexpt) ((%gamma) a) 2)
3696 ((mplus)
3697 ((mtimes)
3698 (($hypergeometric_regularized)
3699 ((mlist) a a ((mplus) 1 ((mtimes) -1 b)))
3700 ((mlist) ((mplus) 1 a) ((mplus) 1 a))
3702 ((mexpt) z1 1))
3703 ((mtimes) -1
3704 (($hypergeometric_regularized)
3705 ((mlist) a a ((mplus) 1 ((mtimes) -1 b)))
3706 ((mlist) ((mplus) 1 a) ((mplus) 1 a))
3708 ((mexpt) z2 a))))
3709 ((mtimes) ((%beta_incomplete) a b z2) ((%log) z2)))
3710 ;; Derivative wrt b
3711 ((mplus)
3712 ((mtimes)
3713 ((%beta_incomplete) b a ((mplus) 1 ((mtimes) -1 z1)))
3714 ((%log) ((mplus) 1 ((mtimes) -1 z1))))
3715 ((mtimes) -1
3716 ((%beta_incomplete) b a ((mplus) 1 ((mtimes) -1 z2)))
3717 ((%log) ((mplus) 1 ((mtimes) -1 z2))))
3718 ((mtimes) -1
3719 ((mexpt) ((%gamma) b) 2)
3720 ((mplus)
3721 ((mtimes)
3722 (($hypergeometric_regularized)
3723 ((mlist) b b ((mplus) 1 ((mtimes) -1 a)))
3724 ((mlist) ((mplus) 1 b) ((mplus) 1 b))
3725 ((mplus) 1 ((mtimes) -1 z1)))
3726 ((mexpt) ((mplus) 1 ((mtimes) -1 z1)) b))
3727 ((mtimes) -1
3728 (($hypergeometric_regularized)
3729 ((mlist) b b ((mplus) 1 ((mtimes) -1 a)))
3730 ((mlist) ((mplus) 1 b) ((mplus) 1 b))
3731 ((mplus) 1 ((mtimes) -1 z2)))
3732 ((mexpt) ((mplus) 1 ((mtimes) -1 z2)) b)))))
3733 ;; The derivative wrt z1
3734 ((mtimes) -1
3735 ((mexpt)
3736 ((mplus) 1 ((mtimes) -1 z1))
3737 ((mplus) -1 b))
3738 ((mexpt) z1 ((mplus) -1 a)))
3739 ;; The derivative wrt z2
3740 ((mtimes)
3741 ((mexpt)
3742 ((mplus) 1 ((mtimes) -1 z2))
3743 ((mplus) -1 b))
3744 ((mexpt) z2 ((mplus) -1 a))))
3745 grad)
3747 ;;; Integral of the Incomplete Beta function
3749 (defprop %beta_incomplete_generalized
3750 ((a b z1 z2)
3751 nil
3753 ;; Integral wrt z1
3754 ((mplus)
3755 ((%beta_incomplete) ((mplus) 1 a) b z1)
3756 ((mtimes) ((%beta_incomplete_generalized) a b z1 z2) z1))
3757 ;; Integral wrt z2
3758 ((mplus)
3759 ((mtimes) -1
3760 ((%beta_incomplete) ((mplus) 1 a) b z2))
3761 ((mtimes) ((%beta_incomplete_generalized) a b z1 z2) z2)))
3762 integral)
3764 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3766 (defun simp-beta-incomplete-generalized (expr ignored simpflag)
3767 (declare (ignore ignored))
3768 (arg-count-check 4 expr)
3769 (let ((a (simpcheck (second expr) simpflag))
3770 (b (simpcheck (third expr) simpflag))
3771 (z1 (simpcheck (fourth expr) simpflag))
3772 (z2 (simpcheck (fifth expr) simpflag))
3773 ($simpsum t))
3774 (cond
3776 ;; Check for specific values
3778 ((zerop1 z2)
3779 (let ((sgn ($sign ($realpart a))))
3780 (cond ((eq sgn '$neg)
3781 (simp-domain-error
3782 (intl:gettext
3783 "beta_incomplete_generalized: beta_incomplete_generalized(~:M,~:M,~:M,~:M) is undefined.")
3784 a b z1 z2))
3785 ((member sgn '($pos $pz))
3786 (mul -1 ($beta_incomplete a b z1)))
3788 (eqtest
3789 (list '(%beta_incomplete_generalized) a b z1 z2) expr)))))
3791 ((zerop1 z1)
3792 (let ((sgn ($sign ($realpart a))))
3793 (cond ((eq sgn '$neg)
3794 (simp-domain-error
3795 (intl:gettext
3796 "beta_incomplete_generalized: beta_incomplete_generalized(~:M,~:M,~:M,~:M) is undefined.")
3797 a b z1 z2))
3798 ((member sgn '($pos $pz))
3799 (mul -1 ($beta_incomplete a b z2)))
3801 (eqtest
3802 (list '(%beta_incomplete_generalized) a b z1 z2) expr)))))
3804 ((and (onep1 z2) (or (not (mnump a)) (not (mnump b)) (not (mnump z1))))
3805 (let ((sgn ($sign ($realpart b))))
3806 (cond ((member sgn '($pos $pz))
3807 (sub (simplify (list '($beta) a b))
3808 ($beta_incomplete a b z1)))
3810 (eqtest
3811 (list '(%beta_incomplete_generalized) a b z1 z2) expr)))))
3813 ((and (onep1 z1) (or (not (mnump a)) (not (mnump b)) (not (mnump z2))))
3814 (let ((sgn ($sign ($realpart b))))
3815 (cond ((member sgn '($pos $pz))
3816 (sub ($beta_incomplete a b z2)
3817 (simplify (list '($beta) a b))))
3819 (eqtest
3820 (list '(%beta_incomplete_generalized) a b z1 z2) expr)))))
3822 ;; Check for numerical evaluation in Float or Bigfloat precision
3824 ((complex-float-numerical-eval-p a b z1 z2)
3825 (let ((*beta-incomplete-eps* (bigfloat:epsilon ($float 1.0))))
3826 (sub (beta-incomplete ($float a) ($float b) ($float z2))
3827 (beta-incomplete ($float a) ($float b) ($float z1)))))
3829 ((complex-bigfloat-numerical-eval-p a b z1 z2)
3830 (let ((*beta-incomplete-eps*
3831 (bigfloat:epsilon (bigfloat:bigfloat 1.0))))
3832 (sub (beta-incomplete ($bfloat a) ($bfloat b) ($bfloat z2))
3833 (beta-incomplete ($bfloat a) ($bfloat b) ($bfloat z1)))))
3835 ;; Check for argument simplifications and transformations
3837 ((and (integerp a) (plusp a))
3838 (mul
3839 (simplify (list '($beta) a b))
3840 (sub
3841 (mul
3842 (power (sub 1 z1) b)
3843 (let ((index (gensumindex)))
3844 (simpsum1
3845 (div
3846 (mul
3847 (simplify (list '($pochhammer) b index))
3848 (power z1 index))
3849 (simplify (list '(mfactorial) index)))
3850 index 0 (sub a 1))))
3851 (mul
3852 (power (sub 1 z2) b)
3853 (let ((index (gensumindex)))
3854 (simpsum1
3855 (div
3856 (mul
3857 (simplify (list '($pochhammer) b index))
3858 (power z2 index))
3859 (simplify (list '(mfactorial) index)))
3860 index 0 (sub a 1)))))))
3862 ((and (integerp b) (plusp b))
3863 (mul
3864 (simplify (list '($beta) a b))
3865 (sub
3866 (mul
3867 (power z2 a)
3868 (let ((index (gensumindex)))
3869 (simpsum1
3870 (div
3871 (mul
3872 (simplify (list '($pochhammer) a index))
3873 (power (sub 1 z2) index))
3874 (simplify (list '(mfactorial) index)))
3875 index 0 (sub b 1))))
3876 (mul
3877 (power z1 a)
3878 (let ((index (gensumindex)))
3879 (simpsum1
3880 (div
3881 (mul
3882 (simplify (list '($pochhammer) a index))
3883 (power (sub 1 z1) index))
3884 (simplify (list '(mfactorial) index)))
3885 index 0 (sub b 1)))))))
3887 ((and $beta_expand (mplusp a) (integerp (cadr a)) (plusp (cadr a)))
3888 (let ((n (cadr a))
3889 (a (simplify (cons '(mplus) (cddr a)))))
3890 (add
3891 (mul
3892 (div
3893 (simplify (list '($pochhammer) a n))
3894 (simplify (list '($pochhammer) (add a b) n)))
3895 ($beta_incomplete_generalized a b z1 z2))
3896 (mul
3897 (power (add a b n -1) -1)
3898 (let ((index (gensumindex)))
3899 (simpsum1
3900 (mul
3901 (div
3902 (simplify (list '($pochhammer)
3903 (add 1 (mul -1 a) (mul -1 n))
3904 index))
3905 (simplify (list '($pochhammer)
3906 (add 2 (mul -1 a) (mul -1 b) (mul -1 n))
3907 index)))
3908 (sub
3909 (mul (power (sub 1 z1) b)
3910 (power z1 (add a n (mul -1 index) -1)))
3911 (mul (power (sub 1 z2) b)
3912 (power z2 (add a n (mul -1 index) -1)))))
3913 index 0 (sub n 1)))))))
3915 ((and $beta_expand (mplusp a) (integerp (cadr a)) (minusp (cadr a)))
3916 (let ((n (- (cadr a)))
3917 (a (simplify (cons '(mplus) (cddr a)))))
3918 (sub
3919 (mul
3920 (div
3921 (simplify (list '($pochhammer) (add 1 (mul -1 a) (mul -1 b)) n))
3922 (simplify (list '($pochhammer) (sub 1 a) n)))
3923 ($beta_incomplete_generalized a b z1 z2))
3924 (mul
3925 (div
3926 (simplify
3927 (list '($pochhammer) (add 2 (mul -1 a) (mul -1 b)) (sub n 1)))
3928 (simplify (list '($pochhammer) (sub 1 a) n)))
3929 (let ((index (gensumindex)))
3930 (simpsum1
3931 (mul
3932 (div
3933 (simplify (list '($pochhammer) (sub 1 a) index))
3934 (simplify (list '($pochhammer)
3935 (add 2 (mul -1 a) (mul -1 b))
3936 index)))
3937 (sub
3938 (mul (power (sub 1 z2) b)
3939 (power z2 (add a (mul -1 index) -1)))
3940 (mul (power (sub 1 z1) b)
3941 (power z1 (add a (mul -1 index) -1)))))
3942 index 0 (sub n 1)))))))
3945 (eqtest (list '(%beta_incomplete_generalized) a b z1 z2) expr)))))
3947 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3949 ;;; Implementation of the Regularized Incomplete Beta function
3951 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3953 (defmfun $beta_incomplete_regularized (a b z)
3954 (simplify (list '(%beta_incomplete_regularized) a b z)))
3956 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3958 (defprop $beta_incomplete_regularized %beta_incomplete_regularized alias)
3959 (defprop $beta_incomplete_regularized %beta_incomplete_regularized verb)
3961 (defprop %beta_incomplete_regularized $beta_incomplete_regularized reversealias)
3962 (defprop %beta_incomplete_regularized $beta_incomplete_regularized noun)
3964 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3966 (defprop %beta_incomplete_regularized
3967 simp-beta-incomplete-regularized operators)
3969 ;;; beta_incomplete_regularized distributes over bags
3971 (defprop %beta_incomplete_regularized (mlist $matrix mequal) distribute_over)
3973 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3975 (defprop %beta_incomplete_regularized
3976 ((a b z)
3977 ;; Derivative wrt a
3978 ((mplus)
3979 ((mtimes) -1
3980 ((%gamma) a)
3981 (($hypergeometric_regularized)
3982 ((mlist) a a ((mplus) 1 ((mtimes) -1 b)))
3983 ((mlist) ((mplus) 1 a) ((mplus) 2 a)) z)
3984 ((mexpt) ((%gamma) b) -1)
3985 ((%gamma) ((mplus) a b))
3986 ((mexpt) z a))
3987 ((mtimes)
3988 ((%beta_incomplete_regularized) a b z)
3989 ((mplus)
3990 ((mtimes) -1 ((mqapply) (($psi array) 0) a))
3991 ((mqapply) (($psi array) 0) ((mplus) a b))
3992 ((%log) z))))
3993 ;; Derivative wrt b
3994 ((mplus)
3995 ((mtimes)
3996 ((%beta_incomplete_regularized) b a ((mplus) 1 ((mtimes) -1 z)))
3997 ((mplus)
3998 ((mqapply) (($psi array) 0) b)
3999 ((mtimes) -1 ((mqapply) (($psi array) 0) ((mplus) a b)))
4000 ((mtimes) -1 ((%log) ((mplus) 1 ((mtimes) -1 z))))))
4001 ((mtimes)
4002 ((mexpt) ((%gamma) a) -1)
4003 ((%gamma) b)
4004 ((%gamma) ((mplus) a b))
4005 (($hypergeometric_regularized)
4006 ((mlist) b b ((mplus) 1 ((mtimes) -1 a)))
4007 ((mlist) ((mplus) 1 b) ((mplus) 1 b))
4008 ((mplus) 1 ((mtimes) -1 z)))
4009 ((mexpt) ((mplus) 1 ((mtimes) -1 z)) b)))
4010 ;; The derivative wrt z
4011 ((mtimes)
4012 ((mexpt) (($beta) a b) -1)
4013 ((mexpt) ((mplus) 1 ((mtimes) -1 z)) ((mplus) -1 b))
4014 ((mexpt) z ((mplus) -1 a))))
4015 grad)
4017 ;;; Integral of the Generalized Incomplete Beta function
4019 (defprop %beta_incomplete_regularized
4020 ((a b z)
4021 nil
4023 ;; Integral wrt z
4024 ((mplus)
4025 ((mtimes) -1 a
4026 ((%beta_incomplete_regularized) ((mplus) 1 a) b z)
4027 ((mexpt) ((mplus) a b) -1))
4028 ((mtimes) ((%beta_incomplete_regularized) a b z) z)))
4029 integral)
4031 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4033 (defun simp-beta-incomplete-regularized (expr ignored simpflag)
4034 (declare (ignore ignored))
4035 (arg-count-check 3 expr)
4036 (let ((a (simpcheck (second expr) simpflag))
4037 (b (simpcheck (third expr) simpflag))
4038 (z (simpcheck (fourth expr) simpflag))
4039 ($simpsum t))
4040 (cond
4042 ;; Check for specific values
4044 ((zerop1 z)
4045 (let ((sgn ($sign ($realpart a))))
4046 (cond ((eq sgn '$neg)
4047 (simp-domain-error
4048 (intl:gettext
4049 "beta_incomplete_regularized: beta_incomplete_regularized(~:M,~:M,~:M) is undefined.")
4050 a b z))
4051 ((member sgn '($pos $pz))
4054 (eqtest
4055 (list '(%beta_incomplete_regularized) a b z) expr)))))
4057 ((and (onep1 z)
4058 (or (not (mnump a))
4059 (not (mnump b))
4060 (not (mnump z))))
4061 (let ((sgn ($sign ($realpart b))))
4062 (cond ((member sgn '($pos $pz))
4065 (eqtest
4066 (list '(%beta_incomplete_regularized) a b z) expr)))))
4068 ((and (integer-representation-p b)
4069 (if ($bfloatp b) (minusp (cadr b)) (minusp b)) )
4070 ;; Problem: for b a negative integer the Regularized Incomplete
4071 ;; Beta function is defined to be zero. BUT: When we calculate
4072 ;; e.g. beta_incomplete(1.0,-2.0,1/2)/beta(1.0,-2.0) we get the
4073 ;; result -3.0, because beta_incomplete and beta are defined for
4074 ;; for this case. How do we get a consistent behaviour?
4077 ((and (integer-representation-p a)
4078 (if ($bfloatp a) (minusp (cadr a)) (minusp a)) )
4079 (cond
4080 ;; TODO: The following line does not work for bigfloats.
4081 ((and (integer-representation-p b) (<= b (- a)))
4082 ;; Does $beta_incomplete or simpbeta underflow in this case?
4083 (div ($beta_incomplete a b z)
4084 (simplify (list '($beta) a b))))
4086 1)))
4088 ;; Check for numerical evaluation in Float or Bigfloat precision
4090 ((complex-float-numerical-eval-p a b z)
4091 (let ((*beta-incomplete-eps* (bigfloat:epsilon ($float 1.0)))
4092 beta ibeta )
4093 (setq a ($float a) b ($float b))
4094 (if (or (< ($abs (setq beta (simplify (list '($beta) a b)))) 1e-307)
4095 (< ($abs (setq ibeta (beta-incomplete a b ($float z)))) 1e-307) )
4096 ;; In case of underflow (see bug #2999) or precision loss use bigfloats
4097 ;; and emporarily give some extra precision but avoid fpprec dependency.
4098 ;; Is this workaround correct for complex values?
4099 (let ((fpprec 70))
4100 ($float ($beta_incomplete_regularized ($bfloat a) ($bfloat b) z)) )
4101 ($rectform (div ibeta beta)) )))
4103 ((complex-bigfloat-numerical-eval-p a b z)
4104 (let ((*beta-incomplete-eps*
4105 (bigfloat:epsilon (bigfloat:bigfloat 1.0))))
4106 (setq a ($bfloat a) b ($bfloat b))
4107 ($rectform
4108 (div (beta-incomplete a b ($bfloat z))
4109 (simplify (list '($beta) a b))))))
4111 ;; Check for argument simplifications and transformations
4113 ((and (integerp b) (plusp b))
4114 (div ($beta_incomplete a b z)
4115 (simplify (list '($beta) a b))))
4117 ((and (integerp a) (plusp a))
4118 (div ($beta_incomplete a b z)
4119 (simplify (list '($beta) a b))))
4121 ((and $beta_expand (mplusp a) (integerp (cadr a)) (plusp (cadr a)))
4122 (let ((n (cadr a))
4123 (a (simplify (cons '(mplus) (cddr a)))))
4124 (sub
4125 ($beta_incomplete_regularized a b z)
4126 (mul
4127 (power (add a b n -1) -1)
4128 (power (simplify (list '($beta) (add a n) b)) -1)
4129 (let ((index (gensumindex)))
4130 (simpsum1
4131 (mul
4132 (div
4133 (simplify (list '($pochhammer)
4134 (add 1 (mul -1 a) (mul -1 n))
4135 index))
4136 (simplify (list '($pochhammer)
4137 (add 2 (mul -1 a) (mul -1 b) (mul -1 n))
4138 index)))
4139 (power (sub 1 z) b)
4140 (power z (add a n (mul -1 index) -1)))
4141 index 0 (sub n 1)))))))
4143 ((and $beta_expand (mplusp a) (integerp (cadr a)) (minusp (cadr a)))
4144 (let ((n (- (cadr a)))
4145 (a (simplify (cons '(mplus) (cddr a)))))
4146 (sub
4147 ($beta_incomplete_regularized a b z)
4148 (mul
4149 (power (add a b -1) -1)
4150 (power (simplify (list '($beta) a b)) -1)
4151 (let ((index (gensumindex)))
4152 (simpsum1
4153 (mul
4154 (div
4155 (simplify (list '($pochhammer) (sub 1 a) index))
4156 (simplify (list '($pochhammer)
4157 (add 2 (mul -1 a) (mul -1 b))
4158 index)))
4159 (power (sub 1 z) b)
4160 (power z (add a (mul -1 index) -1)))
4161 index 0 (sub n 1)))))))
4164 (eqtest (list '(%beta_incomplete_regularized) a b z) expr)))))
4166 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;