Merge branch (bug #4008)
[maxima.git] / src / airy.lisp
blobd7a46c11bff6354184d175cc48c355001ba00b3d
1 ;;; Airy functions Ai(z) and Bi(z) - A&S 10.4
2 ;;;
3 ;;; airy_ai(z) - Airy function Ai(z)
4 ;;; airy_dai(z) - Derivative of Airy function Ai(z)
5 ;;; airy_bi(z) - Airy function Bi(z)
6 ;;; airy_dbi(z) - Derivative of Airy function Bi(z)
8 ;;;; Copyright (C) 2005 David Billinghurst
10 ;;;; airy.lisp is free software; you can redistribute it
11 ;;;; and/or modify it under the terms of the GNU General Public
12 ;;;; License as published by the Free Software Foundation; either
13 ;;;; version 2, or (at your option) any later version.
15 ;;;; airy.lisp is distributed in the hope that it will be
16 ;;;; useful, but WITHOUT ANY WARRANTY; without even the implied
17 ;;;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 ;;;; See the GNU General Public License for more details.
20 ;;;; You should have received a copy of the GNU General Public License
21 ;;;; along with command-line.lisp; see the file COPYING. If not,
22 ;;;; write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 (in-package :maxima)
26 (declaim (special *flonum-op*))
28 ;; Airy Ai function
29 (defmfun $airy_ai (z)
30 "Airy function Ai(z)"
31 (simplify (list '(%airy_ai) (resimplify z))))
33 (defprop %airy_ai simplim%airy_ai simplim%function)
34 (defprop %airy_ai ((z) ((%airy_dai) z)) grad)
36 ;; airy_ai distributes over lists, matrices, and equations
37 (defprop %airy_ai (mlist $matrix mequal) distribute_over)
39 ;; airy_ai has mirror symmetry
40 (defprop %airy_ai t commutes-with-conjugate)
42 ;; Integral of Ai(z)
43 ;; http://functions.wolfram.com/03.05.21.0002.01
44 ;; (z/(3^(2/3)*gamma(2/3)))*hypergeometric([1/3],[2/3,4/3],z^3/9)
45 ;; - (3^(1/6)/(4*%pi))*z^2*gamma(2/3)*hypergeometric([2/3],[4/3,5/3],z^3/9);
46 (defprop %airy_ai
47 ((z)
48 ((mplus)
49 ((mtimes)
50 ((mexpt) 3 ((rat) -2 3))
51 ((mexpt) ((%gamma) ((rat) 2 3)) -1)
52 (($hypergeometric)
53 ((mlist) ((rat) 1 3))
54 ((mlist) ((rat) 2 3) ((rat) 4 3))
55 ((mtimes) ((rat) 1 9) ((mexpt) z 3)))
57 ((mtimes)
58 ((rat) -1 4) ((mexpt) 3 ((rat) 1 6)) ((mexpt) $%pi -1) ((%gamma) ((rat) 2 3))
59 (($hypergeometric)
60 ((mlist) ((rat) 2 3))
61 ((mlist) ((rat) 4 3) ((rat) 5 3))
62 ((mtimes) ((rat) 1 9) ((mexpt) z 3)))
63 ((mexpt) z 2))))
64 integral)
66 (defun airy-ai (z)
67 (cond ((floatp z) (airy-ai-real z))
68 ((complexp z) (airy-ai-complex z))))
70 (setf (gethash '%airy_ai *flonum-op*) #'airy-ai)
72 (defun simplim%airy_ai (expr var val)
73 ; Look for the limit of the argument
74 (let ((z (limit (cadr expr) var val 'think)))
75 (cond ((or (eq z '$inf) ; A&S 10.4.59
76 (eq z '$minf)) ; A&S 10.4.60
79 ; Handle other cases with the function simplifier
80 (simplify (list '(%airy_ai) z))))))
82 (defun airy-ai-hypergeometric (z)
83 "Returns the hypergeometric representation of Airy Ai"
84 ;; See http://functions.wolfram.com/03.05.26.0001.01 and
85 ;; https://fungrim.org/entry/01bbb6/:
87 ;; Ai(z) = Ai(0)*hypergeometric([],[2/3],z^3/9)
88 ;; + z*Ai'(0)*hypergeometric([],[4/3],z^3/9)
89 (add (mul (ftake '%airy_ai 0)
90 (ftake '$hypergeometric
91 (list '(mlist))
92 (list '(mlist) (div 2 3))
93 (div (power z 3)
94 9)))
95 (mul z
96 (ftake '%airy_dai 0)
97 (ftake '$hypergeometric
98 (list '(mlist))
99 (list '(mlist) (div 4 3))
100 (div (power z 3)
101 9)))))
103 (def-simplifier airy_ai (z)
104 (cond ((equal z 0) ; A&S 10.4.4: Ai(0) = 3^(-2/3)/gamma(2/3)
105 (div (power 3 (div -2 3))
106 (take '(%gamma) (div 2 3))))
107 ((flonum-eval (mop form) z))
108 ((or (bigfloat-numerical-eval-p z)
109 (complex-bigfloat-numerical-eval-p z))
110 ($rectform
111 ($bfloat (airy-ai-hypergeometric z))))
112 ($hypergeometric_representation
113 (airy-ai-hypergeometric z))
114 (t (give-up))))
117 ;; Derivative dAi/dz of Airy function Ai(z)
118 (defmfun $airy_dai (z)
119 "Derivative dAi/dz of Airy function Ai(z)"
120 (simplify (list '(%airy_dai) (resimplify z))))
122 (defprop %airy_dai simplim%airy_dai simplim%function)
123 (defprop %airy_dai ((z) ((mtimes) z ((%airy_ai) z))) grad)
124 (defprop %airy_dai ((z) ((%airy_ai) z)) integral)
126 ;; airy_dai distributes over lists, matrices, and equations
127 (defprop %airy_dai (mlist $matrix mequal) distribute_over)
129 ;; airy_dai has mirror symmetry
130 (defprop %airy_dai t commutes-with-conjugate)
132 (defun airy-dai (z)
133 (cond ((floatp z) (airy-dai-real z))
134 ((complexp z) (airy-dai-complex z))))
136 (setf (gethash '%airy_dai *flonum-op*) #'airy-dai)
138 (defun simplim%airy_dai (expr var val)
139 ; Look for the limit of the argument
140 (let ((z (limit (cadr expr) var val 'think)))
141 (cond ((eq z '$inf) ; A&S 10.4.61
143 ((eq z '$minf) ; A&S 10.4.62
144 '$und)
146 ; Handle other cases with the function simplifier
147 (simplify (list '(%airy_dai) z))))))
149 (defun airy-dai-hypergeometric (z)
150 "Returns the hypergeometric representation of Ai'(x), the derivative
151 of the Airy function Ai(x)"
152 ;; See http://functions.wolfram.com/03.07.26.0001.01 and
153 ;; https://fungrim.org/entry/20e530/.
156 ;; Ai'(z) = Ai'(0)*hypergeometric([],[1/3],z^3/9)
157 ;; + z^2/2*Ai(0)*hypergeometric([],[5/3],z^3/9)
158 (add (mul (ftake '%airy_dai 0)
159 (ftake '$hypergeometric
160 (list '(mlist))
161 (list '(mlist) (div 1 3))
162 (div (power z 3)
163 9)))
164 (mul z z 1//2
165 (ftake '%airy_ai 0)
166 (ftake '$hypergeometric
167 (list '(mlist))
168 (list '(mlist) (div 5 3))
169 (div (power z 3)
170 9)))))
172 (def-simplifier airy_dai (z)
173 (cond ((equal z 0) ; A&S 10.4.5: Ai'(0) = -3^(-1/3)/gamma(1/3)
174 (div -1
175 (mul (power 3 (div 1 3))
176 (take '(%gamma) (div 1 3)))))
177 ((flonum-eval (mop form) z))
178 ((or (bigfloat-numerical-eval-p z)
179 (complex-bigfloat-numerical-eval-p z))
180 ($rectform
181 ($bfloat (airy-dai-hypergeometric z))))
182 ($hypergeometric_representation
183 (airy-dai-hypergeometric z))
184 (t (give-up))))
186 ;; Airy Bi function
187 (defmfun $airy_bi (z)
188 "Airy function Bi(z)"
189 (simplify (list '(%airy_bi) (resimplify z))))
191 (defprop %airy_bi simplim%airy_bi simplim%function)
192 (defprop %airy_bi ((z) ((%airy_dbi) z)) grad)
194 ;; airy_bi distributes over lists, matrices, and equations
195 (defprop %airy_bi (mlist $matrix mequal) distribute_over)
197 ;; airy_bi has mirror symmetry
198 (defprop %airy_bi t commutes-with-conjugate)
200 ;; Integral of Bi(z)
201 ;; http://functions.wolfram.com/03.06.21.0002.01
202 ;; (z/(3^(1/6)*gamma(2/3)))*hypergeometric([1/3],[2/3,4/3],z^3/9)
203 ;; + (3^(2/3)/(4*%pi))*z^2*gamma(2/3)*hypergeometric([2/3],[4/3,5/3],z^3/9);
204 (defprop %airy_bi
205 ((z)
206 ((mplus)
207 ((mtimes)
208 ((mexpt) 3 ((rat) -1 6))
209 ((mexpt) ((%gamma) ((rat) 2 3)) -1)
210 (($hypergeometric)
211 ((mlist) ((rat) 1 3))
212 ((mlist) ((rat) 2 3) ((rat) 4 3))
213 ((mtimes) ((rat) 1 9) ((mexpt) z 3)))
215 ((mtimes)
216 ((rat) 1 4) ((mexpt) 3 ((rat) 2 3)) ((mexpt) $%pi -1) ((%gamma) ((rat) 2 3))
217 (($hypergeometric)
218 ((mlist) ((rat) 2 3))
219 ((mlist) ((rat) 4 3) ((rat) 5 3))
220 ((mtimes) ((rat) 1 9) ((mexpt) z 3)))
221 ((mexpt) z 2))))
222 integral)
224 (defun airy-bi (z)
225 (cond ((floatp z) (airy-bi-real z))
226 ((complexp z) (airy-bi-complex z))))
228 (setf (gethash '%airy_bi *flonum-op*) #'airy-bi)
230 (defun simplim%airy_bi (expr var val)
231 ; Look for the limit of the argument
232 (let ((z (limit (cadr expr) var val 'think)))
233 (cond ((eq z '$inf) ; A&S 10.4.63
234 '$inf)
235 ((eq z '$minf) ; A&S 10.4.64
238 ; Handle other cases with the function simplifier
239 (simplify (list '(%airy_bi) z))))))
241 (defun airy-bi-hypergeometric (z)
242 "Returns the hypergeometric representation of Airy Bi"
243 ;; See http://functions.wolfram.com/03.06.26.0001.01 and https://fungrim.org/entry/bd319e/
245 ;; Bi(z) = Bi(0)*hypergeometric([],[2/3],z^3/9)
246 ;; + z*Bi'(0)*hypergeometric([],[4/2],z^3/9)
247 (add (mul (ftake '%airy_bi 0)
248 (ftake '$hypergeometric
249 (list '(mlist))
250 (list '(mlist) (div 2 3))
251 (div (power z 3)
252 9)))
253 (mul z
254 (ftake '%airy_dbi 0)
255 (ftake '$hypergeometric
256 (list '(mlist))
257 (list '(mlist) (div 4 3))
258 (div (power z 3)
259 9)))))
261 (def-simplifier airy_bi (z)
262 (cond ((equal z 0) ; A&S 10.4.4: Bi(0) = sqrt(3) 3^(-2/3)/gamma(2/3)
263 (div (mul (power 3 1//2)
264 (power 3 (div -2 3)))
265 (take '(%gamma) (div 2 3))))
266 ((flonum-eval (mop form) z))
267 ((or (bigfloat-numerical-eval-p z)
268 (complex-bigfloat-numerical-eval-p z))
269 ($rectform
270 ($bfloat (airy-bi-hypergeometric z))))
271 ($hypergeometric_representation
272 (airy-bi-hypergeometric z))
273 (t (give-up))))
276 ;; Derivative dBi/dz of Airy function Bi(z)
277 (defmfun $airy_dbi (z)
278 "Derivative dBi/dz of Airy function Bi(z)"
279 (simplify (list '(%airy_dbi) (resimplify z))))
281 (defprop %airy_dbi simplim%airy_dbi simplim%function)
282 (defprop %airy_dbi ((z) ((mtimes) z ((%airy_bi) z))) grad)
283 (defprop %airy_dbi ((z) ((%airy_bi) z)) integral)
285 ;; airy_dbi distributes over lists, matrices, and equations
286 (defprop %airy_dbi (mlist $matrix mequal) distribute_over)
288 ;; airy_dbi has mirror symmetry
289 (defprop %airy_dbi t commutes-with-conjugate)
291 (defun airy-dbi (z)
292 (cond ((floatp z) (airy-dbi-real z))
293 ((complexp z) (airy-dbi-complex z))))
295 (setf (gethash '%airy_dbi *flonum-op*) #'airy-dbi)
297 (defun simplim%airy_dbi (expr var val)
298 ; Look for the limit of the argument
299 (let ((z (limit (cadr expr) var val 'think)))
300 (cond ((eq z '$inf) ; A&S 10.4.66
301 '$inf)
302 ((eq z '$minf) ; A&S 10.4.67
303 '$und)
305 ; Handle other cases with the function simplifier
306 (simplify (list '(%airy_dbi) z))))))
308 (defun airy-dbi-hypergeometric (z)
309 "Returns the hypergeometric representation of Bi'(z), the derivative
310 of Airy Bi"
311 ;; See http://functions.wolfram.com/03.08.26.0001.01 and
312 ;; https://fungrim.org/entry/4d65e5/.
314 ;; Bi'(z) = Bi'(0)*hypergeometric([],[1/3],z^3/9)
315 ;; + z^2/2*Bi(0)*hypergeometric([],[5/3],z^3/9)
316 (add (mul (ftake '%airy_dbi 0)
317 (ftake '$hypergeometric
318 (list '(mlist))
319 (list '(mlist) (div 1 3))
320 (div (power z 3)
321 9)))
322 (mul z z 1//2
323 (ftake '%airy_bi 0)
324 (ftake '$hypergeometric
325 (list '(mlist))
326 (list '(mlist) (div 5 3))
327 (div (power z 3)
328 9)))))
330 (def-simplifier airy_dbi (z)
331 (cond ((equal z 0) ; A&S 10.4.5: Bi'(0) = sqrt(3) 3^(-1/3)/gamma(1/3)
332 (div (mul (power 3 1//2)
333 (power 3 (div -1 3)))
334 (take '(%gamma) (div 1 3))))
335 ((flonum-eval (mop form) z))
336 ((or (bigfloat-numerical-eval-p z)
337 (complex-bigfloat-numerical-eval-p z))
338 ($rectform
339 ($bfloat (airy-dbi-hypergeometric z))))
340 ($hypergeometric_representation
341 (airy-dbi-hypergeometric z))
342 (t (give-up))))
344 ;; Numerical routines using slatec functions
346 (defun airy-ai-real (z)
347 " Airy function Ai(z) for real z"
348 (declare (type flonum z))
349 ;; slatec:dai issues underflow warning for z > zmax. See dai.{f,lisp}
350 ;; This value is correct for IEEE double precision
351 (let ((zmax 92.5747007268))
352 (declare (type flonum zmax))
353 (if (< z zmax) (slatec:dai z) 0.0)))
355 (defun airy-ai-complex (z)
356 "Airy function Ai(z) for complex z"
357 (declare (type (complex flonum) z))
358 (multiple-value-bind (var-0 var-1 var-2 var-3 air aii nz ierr)
359 (slatec:zairy (realpart z) (imagpart z) 0 1 0.0 0.0 0 0)
360 (declare (type flonum air aii)
361 (type f2cl-lib:integer4 nz ierr)
362 (ignore var-0 var-1 var-2 var-3))
363 ;; Check nz and ierr for errors
364 (if (and (= nz 0) (= ierr 0)) (complex air aii) nil)))
366 (defun airy-dai-real (z)
367 "Derivative dAi/dz of Airy function Ai(z) for real z"
368 (declare (type flonum z))
369 (let ((rz (sqrt (abs z)))
370 (c (* 2/3 (expt (abs z) 3/2))))
371 (declare (type flonum rz c))
372 (multiple-value-bind (var-0 var-1 var-2 ai dai)
373 (slatec:djairy z rz c 0.0 0.0)
374 (declare (ignore var-0 var-1 var-2 ai))
375 dai)))
377 (defun airy-dai-complex (z)
378 "Derivative dAi/dz of Airy function Ai(z) for complex z"
379 (declare (type (complex flonum) z))
380 (multiple-value-bind (var-0 var-1 var-2 var-3 air aii nz ierr)
381 (slatec:zairy (realpart z) (imagpart z) 1 1 0.0 0.0 0 0)
382 (declare (type flonum air aii)
383 (type f2cl-lib:integer4 nz ierr)
384 (ignore var-0 var-1 var-2 var-3))
385 ;; Check nz and ierr for errors
386 (if (and (= nz 0) (= ierr 0)) (complex air aii) nil)))
388 (defun airy-bi-real (z)
389 "Airy function Bi(z) for real z"
390 (declare (type flonum z))
391 ;; slatec:dbi issues overflows for z > zmax. See dbi.{f,lisp}
392 ;; This value is correct for IEEE double precision
393 (let ((zmax 104.2179765192136))
394 (declare (type flonum zmax))
395 (if (< z zmax) (slatec:dbi z) nil)))
397 (defun airy-bi-complex (z)
398 "Airy function Bi(z) for complex z"
399 (declare (type (complex flonum) z))
400 (multiple-value-bind (var-0 var-1 var-2 var-3 bir bii ierr)
401 (slatec:zbiry (realpart z) (imagpart z) 0 1 0.0 0.0 0)
402 (declare (type flonum bir bii)
403 (type f2cl-lib:integer4 ierr)
404 (ignore var-0 var-1 var-2 var-3))
405 ;; Check ierr for errors
406 (if (= ierr 0) (complex bir bii) nil)))
408 (defun airy-dbi-real (z)
409 "Derivative dBi/dz of Airy function Bi(z) for real z"
410 (declare (type flonum z))
411 ;; Overflows for z > zmax.
412 ;; This value is correct for IEEE double precision
413 (let ((zmax 104.1525))
414 (declare (type flonum zmax))
415 (if (< z zmax)
416 (let ((rz (sqrt (abs z)))
417 (c (* 2/3 (expt (abs z) 3/2))))
418 (declare (type flonum rz c))
419 (multiple-value-bind (var-0 var-1 var-2 bi dbi)
420 (slatec:dyairy z rz c 0.0 0.0)
421 (declare (type flonum bi dbi)
422 (ignore var-0 var-1 var-2 bi))
423 dbi))
424 ;; Will overflow. Return unevaluated.
425 nil)))
427 (defun airy-dbi-complex (z)
428 "Derivative dBi/dz of Airy function Bi(z) for complex z"
429 (declare (type (complex flonum) z))
430 (multiple-value-bind (var-0 var-1 var-2 var-3 bir bii ierr)
431 (slatec:zbiry (realpart z) (imagpart z) 1 1 0.0 0.0 0)
432 (declare (type flonum bir bii)
433 (type f2cl-lib:integer4 ierr)
434 (ignore var-0 var-1 var-2 var-3))
435 ;; Check ierr for errors
436 (if (= ierr 0) (complex bir bii) nil)))