Replace some constant expressions with functions
[maxima.git] / src / airy.lisp
blob33e5bcac7a6a4a5aa545cc5431644d4897dec4c6
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 ;; Airy Ai function
27 (defmfun $airy_ai (z)
28 "Airy function Ai(z)"
29 (simplify (list '(%airy_ai) (resimplify z))))
31 (defprop %airy_ai simplim%airy_ai simplim%function)
32 (defprop %airy_ai ((z) ((%airy_dai) z)) grad)
34 ;; airy_ai distributes over lists, matrices, and equations
35 (defprop %airy_ai (mlist $matrix mequal) distribute_over)
37 ;; airy_ai has mirror symmetry
38 (defprop %airy_ai t commutes-with-conjugate)
40 ;; Integral of Ai(z)
41 ;; http://functions.wolfram.com/03.05.21.0002.01
42 ;; (z/(3^(2/3)*gamma(2/3)))*hypergeometric([1/3],[2/3,4/3],z^3/9)
43 ;; - (3^(1/6)/(4*%pi))*z^2*gamma(2/3)*hypergeometric([2/3],[4/3,5/3],z^3/9);
44 (defprop %airy_ai
45 ((z)
46 ((mplus)
47 ((mtimes)
48 ((mexpt) 3 ((rat) -2 3))
49 ((mexpt) ((%gamma) ((rat) 2 3)) -1)
50 ((%hypergeometric)
51 ((mlist) ((rat) 1 3))
52 ((mlist) ((rat) 2 3) ((rat) 4 3))
53 ((mtimes) ((rat) 1 9) ((mexpt) z 3)))
55 ((mtimes)
56 ((rat) -1 4) ((mexpt) 3 ((rat) 1 6)) ((mexpt) $%pi -1) ((%gamma) ((rat) 2 3))
57 ((%hypergeometric)
58 ((mlist) ((rat) 2 3))
59 ((mlist) ((rat) 4 3) ((rat) 5 3))
60 ((mtimes) ((rat) 1 9) ((mexpt) z 3)))
61 ((mexpt) z 2))))
62 integral)
64 (defun airy-ai (z)
65 (cond ((floatp z) (airy-ai-real z))
66 ((complexp z) (airy-ai-complex z))))
68 (setf (gethash '%airy_ai *flonum-op*) 'airy-ai)
70 (defun simplim%airy_ai (expr var val)
71 ; Look for the limit of the argument
72 (let ((z (limit (cadr expr) var val 'think)))
73 (cond ((or (eq z '$inf) ; A&S 10.4.59
74 (eq z '$minf)) ; A&S 10.4.60
77 ; Handle other cases with the function simplifier
78 (simplify (list '(%airy_ai) z))))))
80 (defun airy-ai-hypergeometric (z)
81 "Returns the hypergeometric representation of Airy Ai"
82 ;; See http://functions.wolfram.com/03.05.26.0001.01 and
83 ;; https://fungrim.org/entry/01bbb6/:
85 ;; Ai(z) = Ai(0)*hypergeometric([],[2/3],z^3/9)
86 ;; + z*Ai'(0)*hypergeometric([],[4/3],z^3/9)
87 (add (mul (ftake '%airy_ai 0)
88 (ftake '%hypergeometric
89 (list '(mlist))
90 (list '(mlist) (div 2 3))
91 (div (power z 3)
92 9)))
93 (mul z
94 (ftake '%airy_dai 0)
95 (ftake '%hypergeometric
96 (list '(mlist))
97 (list '(mlist) (div 4 3))
98 (div (power z 3)
99 9)))))
101 (def-simplifier airy_ai (z)
102 (cond ((equal z 0) ; A&S 10.4.4: Ai(0) = 3^(-2/3)/gamma(2/3)
103 (div (power 3 (div -2 3))
104 (take '(%gamma) (div 2 3))))
105 ((flonum-eval (mop form) z))
106 ((or (bigfloat-numerical-eval-p z)
107 (complex-bigfloat-numerical-eval-p z))
108 ($rectform
109 ($bfloat (airy-ai-hypergeometric z))))
110 ($hypergeometric_representation
111 (airy-ai-hypergeometric z))
112 (t (give-up))))
115 ;; Derivative dAi/dz of Airy function Ai(z)
116 (defmfun $airy_dai (z)
117 "Derivative dAi/dz of Airy function Ai(z)"
118 (simplify (list '(%airy_dai) (resimplify z))))
120 (defprop %airy_dai simplim%airy_dai simplim%function)
121 (defprop %airy_dai ((z) ((mtimes) z ((%airy_ai) z))) grad)
122 (defprop %airy_dai ((z) ((%airy_ai) z)) integral)
124 ;; airy_dai distributes over lists, matrices, and equations
125 (defprop %airy_dai (mlist $matrix mequal) distribute_over)
127 ;; airy_dai has mirror symmetry
128 (defprop %airy_dai t commutes-with-conjugate)
130 (defun airy-dai (z)
131 (cond ((floatp z) (airy-dai-real z))
132 ((complexp z) (airy-dai-complex z))))
134 (setf (gethash '%airy_dai *flonum-op*) 'airy-dai)
136 (defun simplim%airy_dai (expr var val)
137 ; Look for the limit of the argument
138 (let ((z (limit (cadr expr) var val 'think)))
139 (cond ((eq z '$inf) ; A&S 10.4.61
141 ((eq z '$minf) ; A&S 10.4.62
142 '$und)
144 ; Handle other cases with the function simplifier
145 (simplify (list '(%airy_dai) z))))))
147 (defun airy-dai-hypergeometric (z)
148 "Returns the hypergeometric representation of Ai'(x), the derivative
149 of the Airy function Ai(x)"
150 ;; See http://functions.wolfram.com/03.07.26.0001.01 and
151 ;; https://fungrim.org/entry/20e530/.
154 ;; Ai'(z) = Ai'(0)*hypergeometric([],[1/3],z^3/9)
155 ;; + z^2/2*Ai(0)*hypergeometric([],[5/3],z^3/9)
156 (add (mul (ftake '%airy_dai 0)
157 (ftake '%hypergeometric
158 (list '(mlist))
159 (list '(mlist) (div 1 3))
160 (div (power z 3)
161 9)))
162 (mul z z 1//2
163 (ftake '%airy_ai 0)
164 (ftake '%hypergeometric
165 (list '(mlist))
166 (list '(mlist) (div 5 3))
167 (div (power z 3)
168 9)))))
170 (def-simplifier airy_dai (z)
171 (cond ((equal z 0) ; A&S 10.4.5: Ai'(0) = -3^(-1/3)/gamma(1/3)
172 (div -1
173 (mul (power 3 (div 1 3))
174 (take '(%gamma) (div 1 3)))))
175 ((flonum-eval (mop form) z))
176 ((or (bigfloat-numerical-eval-p z)
177 (complex-bigfloat-numerical-eval-p z))
178 ($rectform
179 ($bfloat (airy-dai-hypergeometric z))))
180 ($hypergeometric_representation
181 (airy-dai-hypergeometric z))
182 (t (give-up))))
184 ;; Airy Bi function
185 (defmfun $airy_bi (z)
186 "Airy function Bi(z)"
187 (simplify (list '(%airy_bi) (resimplify z))))
189 (defprop %airy_bi simplim%airy_bi simplim%function)
190 (defprop %airy_bi ((z) ((%airy_dbi) z)) grad)
192 ;; airy_bi distributes over lists, matrices, and equations
193 (defprop %airy_bi (mlist $matrix mequal) distribute_over)
195 ;; airy_bi has mirror symmetry
196 (defprop %airy_bi t commutes-with-conjugate)
198 ;; Integral of Bi(z)
199 ;; http://functions.wolfram.com/03.06.21.0002.01
200 ;; (z/(3^(1/6)*gamma(2/3)))*hypergeometric([1/3],[2/3,4/3],z^3/9)
201 ;; + (3^(2/3)/(4*%pi))*z^2*gamma(2/3)*hypergeometric([2/3],[4/3,5/3],z^3/9);
202 (defprop %airy_bi
203 ((z)
204 ((mplus)
205 ((mtimes)
206 ((mexpt) 3 ((rat) -1 6))
207 ((mexpt) ((%gamma) ((rat) 2 3)) -1)
208 ((%hypergeometric)
209 ((mlist) ((rat) 1 3))
210 ((mlist) ((rat) 2 3) ((rat) 4 3))
211 ((mtimes) ((rat) 1 9) ((mexpt) z 3)))
213 ((mtimes)
214 ((rat) 1 4) ((mexpt) 3 ((rat) 2 3)) ((mexpt) $%pi -1) ((%gamma) ((rat) 2 3))
215 ((%hypergeometric)
216 ((mlist) ((rat) 2 3))
217 ((mlist) ((rat) 4 3) ((rat) 5 3))
218 ((mtimes) ((rat) 1 9) ((mexpt) z 3)))
219 ((mexpt) z 2))))
220 integral)
222 (defun airy-bi (z)
223 (cond ((floatp z) (airy-bi-real z))
224 ((complexp z) (airy-bi-complex z))))
226 (setf (gethash '%airy_bi *flonum-op*) 'airy-bi)
228 (defun simplim%airy_bi (expr var val)
229 ; Look for the limit of the argument
230 (let ((z (limit (cadr expr) var val 'think)))
231 (cond ((eq z '$inf) ; A&S 10.4.63
232 '$inf)
233 ((eq z '$minf) ; A&S 10.4.64
236 ; Handle other cases with the function simplifier
237 (simplify (list '(%airy_bi) z))))))
239 (defun airy-bi-hypergeometric (z)
240 "Returns the hypergeometric representation of Airy Bi"
241 ;; See http://functions.wolfram.com/03.06.26.0001.01 and https://fungrim.org/entry/bd319e/
243 ;; Bi(z) = Bi(0)*hypergeometric([],[2/3],z^3/9)
244 ;; + z*Bi'(0)*hypergeometric([],[4/2],z^3/9)
245 (add (mul (ftake '%airy_bi 0)
246 (ftake '%hypergeometric
247 (list '(mlist))
248 (list '(mlist) (div 2 3))
249 (div (power z 3)
250 9)))
251 (mul z
252 (ftake '%airy_dbi 0)
253 (ftake '%hypergeometric
254 (list '(mlist))
255 (list '(mlist) (div 4 3))
256 (div (power z 3)
257 9)))))
259 (def-simplifier airy_bi (z)
260 (cond ((equal z 0) ; A&S 10.4.4: Bi(0) = sqrt(3) 3^(-2/3)/gamma(2/3)
261 (div (mul (power 3 1//2)
262 (power 3 (div -2 3)))
263 (take '(%gamma) (div 2 3))))
264 ((flonum-eval (mop form) z))
265 ((or (bigfloat-numerical-eval-p z)
266 (complex-bigfloat-numerical-eval-p z))
267 ($rectform
268 ($bfloat (airy-bi-hypergeometric z))))
269 ($hypergeometric_representation
270 (airy-bi-hypergeometric z))
271 (t (give-up))))
274 ;; Derivative dBi/dz of Airy function Bi(z)
275 (defmfun $airy_dbi (z)
276 "Derivative dBi/dz of Airy function Bi(z)"
277 (simplify (list '(%airy_dbi) (resimplify z))))
279 (defprop %airy_dbi simplim%airy_dbi simplim%function)
280 (defprop %airy_dbi ((z) ((mtimes) z ((%airy_bi) z))) grad)
281 (defprop %airy_dbi ((z) ((%airy_bi) z)) integral)
283 ;; airy_dbi distributes over lists, matrices, and equations
284 (defprop %airy_dbi (mlist $matrix mequal) distribute_over)
286 ;; airy_dbi has mirror symmetry
287 (defprop %airy_dbi t commutes-with-conjugate)
289 (defun airy-dbi (z)
290 (cond ((floatp z) (airy-dbi-real z))
291 ((complexp z) (airy-dbi-complex z))))
293 (setf (gethash '%airy_dbi *flonum-op*) 'airy-dbi)
295 (defun simplim%airy_dbi (expr var val)
296 ; Look for the limit of the argument
297 (let ((z (limit (cadr expr) var val 'think)))
298 (cond ((eq z '$inf) ; A&S 10.4.66
299 '$inf)
300 ((eq z '$minf) ; A&S 10.4.67
301 '$und)
303 ; Handle other cases with the function simplifier
304 (simplify (list '(%airy_dbi) z))))))
306 (defun airy-dbi-hypergeometric (z)
307 "Returns the hypergeometric representation of Bi'(z), the derivative
308 of Airy Bi"
309 ;; See http://functions.wolfram.com/03.08.26.0001.01 and
310 ;; https://fungrim.org/entry/4d65e5/.
312 ;; Bi'(z) = Bi'(0)*hypergeometric([],[1/3],z^3/9)
313 ;; + z^2/2*Bi(0)*hypergeometric([],[5/3],z^3/9)
314 (add (mul (ftake '%airy_dbi 0)
315 (ftake '%hypergeometric
316 (list '(mlist))
317 (list '(mlist) (div 1 3))
318 (div (power z 3)
319 9)))
320 (mul z z 1//2
321 (ftake '%airy_bi 0)
322 (ftake '%hypergeometric
323 (list '(mlist))
324 (list '(mlist) (div 5 3))
325 (div (power z 3)
326 9)))))
328 (def-simplifier airy_dbi (z)
329 (cond ((equal z 0) ; A&S 10.4.5: Bi'(0) = sqrt(3) 3^(-1/3)/gamma(1/3)
330 (div (mul (power 3 1//2)
331 (power 3 (div -1 3)))
332 (take '(%gamma) (div 1 3))))
333 ((flonum-eval (mop form) z))
334 ((or (bigfloat-numerical-eval-p z)
335 (complex-bigfloat-numerical-eval-p z))
336 ($rectform
337 ($bfloat (airy-dbi-hypergeometric z))))
338 ($hypergeometric_representation
339 (airy-dbi-hypergeometric z))
340 (t (give-up))))
342 ;; Numerical routines using slatec functions
344 (defun airy-ai-real (z)
345 " Airy function Ai(z) for real z"
346 (declare (type flonum z))
347 ;; slatec:dai issues underflow warning for z > zmax. See dai.{f,lisp}
348 ;; This value is correct for IEEE double precision
349 (let ((zmax 92.5747007268))
350 (declare (type flonum zmax))
351 (if (< z zmax) (slatec:dai z) 0.0)))
353 (defun airy-ai-complex (z)
354 "Airy function Ai(z) for complex z"
355 (declare (type (complex flonum) z))
356 (multiple-value-bind (var-0 var-1 var-2 var-3 air aii nz ierr)
357 (slatec:zairy (realpart z) (imagpart z) 0 1 0.0 0.0 0 0)
358 (declare (type flonum air aii)
359 (type f2cl-lib:integer4 nz ierr)
360 (ignore var-0 var-1 var-2 var-3))
361 ;; Check nz and ierr for errors
362 (if (and (= nz 0) (= ierr 0)) (complex air aii) nil)))
364 (defun airy-dai-real (z)
365 "Derivative dAi/dz of Airy function Ai(z) for real z"
366 (declare (type flonum z))
367 (let ((rz (sqrt (abs z)))
368 (c (* 2/3 (expt (abs z) 3/2))))
369 (declare (type flonum rz c))
370 (multiple-value-bind (var-0 var-1 var-2 ai dai)
371 (slatec:djairy z rz c 0.0 0.0)
372 (declare (ignore var-0 var-1 var-2 ai))
373 dai)))
375 (defun airy-dai-complex (z)
376 "Derivative dAi/dz of Airy function Ai(z) for complex z"
377 (declare (type (complex flonum) z))
378 (multiple-value-bind (var-0 var-1 var-2 var-3 air aii nz ierr)
379 (slatec:zairy (realpart z) (imagpart z) 1 1 0.0 0.0 0 0)
380 (declare (type flonum air aii)
381 (type f2cl-lib:integer4 nz ierr)
382 (ignore var-0 var-1 var-2 var-3))
383 ;; Check nz and ierr for errors
384 (if (and (= nz 0) (= ierr 0)) (complex air aii) nil)))
386 (defun airy-bi-real (z)
387 "Airy function Bi(z) for real z"
388 (declare (type flonum z))
389 ;; slatec:dbi issues overflows for z > zmax. See dbi.{f,lisp}
390 ;; This value is correct for IEEE double precision
391 (let ((zmax 104.2179765192136))
392 (declare (type flonum zmax))
393 (if (< z zmax) (slatec:dbi z) nil)))
395 (defun airy-bi-complex (z)
396 "Airy function Bi(z) for complex z"
397 (declare (type (complex flonum) z))
398 (multiple-value-bind (var-0 var-1 var-2 var-3 bir bii ierr)
399 (slatec:zbiry (realpart z) (imagpart z) 0 1 0.0 0.0 0)
400 (declare (type flonum bir bii)
401 (type f2cl-lib:integer4 ierr)
402 (ignore var-0 var-1 var-2 var-3))
403 ;; Check ierr for errors
404 (if (= ierr 0) (complex bir bii) nil)))
406 (defun airy-dbi-real (z)
407 "Derivative dBi/dz of Airy function Bi(z) for real z"
408 (declare (type flonum z))
409 ;; Overflows for z > zmax.
410 ;; This value is correct for IEEE double precision
411 (let ((zmax 104.1525))
412 (declare (type flonum zmax))
413 (if (< z zmax)
414 (let ((rz (sqrt (abs z)))
415 (c (* 2/3 (expt (abs z) 3/2))))
416 (declare (type flonum rz c))
417 (multiple-value-bind (var-0 var-1 var-2 bi dbi)
418 (slatec:dyairy z rz c 0.0 0.0)
419 (declare (type flonum bi dbi)
420 (ignore var-0 var-1 var-2 bi))
421 dbi))
422 ;; Will overflow. Return unevaluated.
423 nil)))
425 (defun airy-dbi-complex (z)
426 "Derivative dBi/dz of Airy function Bi(z) for complex z"
427 (declare (type (complex flonum) z))
428 (multiple-value-bind (var-0 var-1 var-2 var-3 bir bii ierr)
429 (slatec:zbiry (realpart z) (imagpart z) 1 1 0.0 0.0 0)
430 (declare (type flonum bir bii)
431 (type f2cl-lib:integer4 ierr)
432 (ignore var-0 var-1 var-2 var-3))
433 ;; Check ierr for errors
434 (if (= ierr 0) (complex bir bii) nil)))