;;; -*- Mode: Lisp; Package: Maxima; Syntax: Common-Lisp; Base: 10 -*- ;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; The data in this file contains enhancments. ;;;;; ;;; ;;;;; ;;; Copyright (c) 1984,1987 by William Schelter,University of Texas ;;;;; ;;; All rights reserved ;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (in-package :maxima) ;; When non-NIL, the Bessel functions of half-integral order are ;; expanded in terms of elementary functions. (defmvar $besselexpand nil) (defvar $iarray) (defvar $yarray) (defvar $besselarray) ;; ;; Bessel function of the first kind of order 0. ;; ;; One definition is ;; ;; INF ;; ==== k 2 k ;; \ (- 1) z ;; > ----------- ;; / 2 k 2 ;; ==== 2 k! ;; k = 0 ;; ;; We only support computing this for real z. ;; ;; rtoy: Commenting these out for now because they're deprecated and ;; we have equivalent functionality in bessel_j and bessel_i. ;; ;; XXX Remove these someday. #|| (defun j[0]-bessel (x) (slatec:dbesj0 (float x 1d0))) (defun $j0 ($x) "J[0](x). This is deprecated. Use bessel_j(0,x)" (cond ((numberp $x) (j[0]-bessel (float $x))) (t (list '(%bessel_j simp) 0 $x)))) ;; Bessel function of the first kind of order 1. ;; ;; One definition is ;; ;; INF ;; ==== - 2 k - 1 k 2 k + 1 ;; \ 2 (- 1) z ;; > -------------------------- ;; / k! (k + 1)! ;; ==== ;; k = 0 (defun j[1]-bessel (x) (slatec:dbesj1 (float x 1d0))) (defun $j1 ($x) "J[1](x). This is deprecated. Use bessel_j(1,x)" (cond ((numberp $x) (j[1]-bessel (float $x))) (t (list '(%bessel_j simp) 1 $x)))) ;; Bessel function of the first kind of order n ;; ;; The order n must be a non-negative real. (defun $jn ($x $n) "J[n](x). This is deprecated. Use bessel_j(n,x)" (cond ((and (numberp $x) (numberp $n) (>= $n 0)) (multiple-value-bind (n alpha) (floor (float $n)) (let ((jvals (make-array (1+ n) :element-type 'double-float))) (slatec:dbesj (float $x) alpha (1+ n) jvals 0) (narray $jarray n) (fillarray (symbol-value '$jarray) jvals) (aref jvals n)))) (t (list '(%bessel_j simp) $n $x)))) ;; Modified Bessel function of the first kind of order 0. This is ;; related to J[0] via ;; ;; I[0](z) = J[0](z*exp(%pi*%i/2)) ;; ;; and ;; ;; INF ;; ==== 2 k ;; \ z ;; > ---------------- ;; / 2 k 2 ;; ==== 2 k! ;; k = 0 (defun i[0]-bessel (x) (slatec:dbesi0 (float x 1d0))) (defun $i0 ($x) "I[0](x). This is deprecated. Use bessel_i(0,x)" (cond ((numberp $x) (i[0]-bessel (float $x))) (t (list '(%bessel_i simp) 0 $x)))) ;; Modified Bessel function of the first kind of order 1. This is ;; related to J[1] via ;; ;; I[1](z) = exp(-%pi*%I/2)*J[0](z*exp(%pi*%i/2)) ;; ;; and ;; ;; INF ;; ==== 2 k ;; \ z ;; > ---------------- ;; / 2 k ;; ==== 2 k! (k + 1)! ;; k = 0 (defun i[1]-bessel (x) (slatec:dbesi1 (float x 1d0))) (defun $i1 ($x) "I[1](x). This is deprecated. Use bessel_i(1,x)" (cond ((numberp $x) (i[1]-bessel (float $x))) (t (list '(%bessel_i simp) 1 $x)))) ;; Modified Bessel function of the first kind of order n, where n is a ;; non-negative real. ||# (defun bessel-i (order arg) (cond ((zerop (imagpart arg)) ;; We have numeric args and the first arg is purely ;; real. Call the real-valued Bessel function. We call i0 ;; and i1 instead of jn, if possible. (let ((arg (realpart arg))) (cond ((zerop order) (slatec:dbesi0 (float arg))) ((= order 1) (slatec:dbesi1 (float arg))) (t (multiple-value-bind (n alpha) (floor (float order)) (setf $besselarray (make-array (1+ n) :element-type 'double-float)) (slatec:dbesi (float (realpart arg)) alpha 1 (1+ n) $besselarray 0) (aref $besselarray n)))))) (t ;; The arg is complex. Use the complex-valued Bessel ;; function (multiple-value-bind (n alpha) (floor (float order)) (let ((cyr (make-array (1+ n) :element-type 'double-float)) (cyi (make-array (1+ n) :element-type 'double-float))) (multiple-value-bind (v-zr v-zi v-fnu v-kode v-n v-cyr v-cyi v-nz v-ierr) (slatec::zbesi (float (realpart arg)) (float (imagpart arg)) alpha 1 (1+ n) cyr cyi 0 0) (declare (ignore v-zr v-zi v-fnu v-kode v-n v-cyr v-cyi v-nz)) ;; We should check for errors here based on the ;; value of v-ierr. (when (plusp v-ierr) (format t "zbesi ierr = ~A~%" v-ierr)) (setf $besselarray (make-array (+ n 2))) (dotimes (k (1+ n) (aref $besselarray n)) (setf (aref $besselarray k) (simplify (list '(mplus) (simplify (list '(mtimes) '$%i (aref cyi k))) (aref cyr k))))))))))) (defun bessel-k (order arg) (cond ((zerop (imagpart arg)) ;; We have numeric args and the first arg is purely ;; real. Call the real-valued Bessel function. We call i0 ;; and i1 instead of jn, if possible. (let ((arg (realpart arg))) (cond ((zerop order) (slatec:dbesk0 (float arg))) ((= order 1) (slatec:dbesk1 (float arg))) (t ;; From A&S 9.6.6, K(-v,z) = K(v,z), so take the ;; absolute value of the order. (multiple-value-bind (n alpha) (floor (abs (float order))) (setf $besselarray (make-array (1+ n) :element-type 'double-float)) (slatec:dbesk (float (realpart arg)) alpha 1 (1+ n) $besselarray 0) (aref $besselarray n)))))) (t ;; The first arg is complex. Use the complex-valued Bessel ;; function (multiple-value-bind (n alpha) (floor (float order)) (let ((cyr (make-array (1+ n) :element-type 'double-float)) (cyi (make-array (1+ n) :element-type 'double-float))) (multiple-value-bind (v-zr v-zi v-fnu v-kode v-n v-cyr v-cyi v-nz v-ierr) (slatec::zbesk (float (realpart arg)) (float (imagpart arg)) alpha 1 (1+ n) cyr cyi 0 0) (declare (ignore v-zr v-zi v-fnu v-kode v-n v-cyr v-cyi v-nz)) ;; We should check for errors here based on the ;; value of v-ierr. (when (plusp v-ierr) (format t "zbesk ierr = ~A~%" v-ierr)) (setf $besselarray (make-array (+ n 2))) (dotimes (k (1+ n) (aref $besselarray n)) (setf (aref $besselarray k) (simplify (list '(mplus) (simplify (list '(mtimes) '$%i (aref cyi k))) (aref cyr k))))))))))) ;; I think g0(x) = exp(-x)*I[0](x), g1(x) = exp(-x)*I[1](x), and ;; gn(x,n) = exp(-x)*I[n](x), based on some simple numerical ;; evaluations. (defun $scaled_bessel_i0 ($x) (cond ((mnump $x) ;; XXX Should we return noun forms if $x is rational? (slatec:dbsi0e ($float $x))) (t (mul (power '$%e (neg (simplifya `((mabs) ,$x) nil))) `((%bessel_i) 0 ,$x))))) (defun $scaled_bessel_i1 ($x) (cond ((mnump $x) ;; XXX Should we return noun forms if $x is rational? (slatec:dbsi1e ($float $x))) (t (mul (power '$%e (neg (simplifya `((mabs) ,$x) nil))) `((%bessel_i) 1 ,$x))))) (defun $scaled_bessel_i ($n $x) (cond ((and (mnump $x) (mnump $n)) ;; XXX Should we return noun forms if $n and $x are rational? (multiple-value-bind (n alpha) (floor ($float $n)) (setf $iarray (make-array (1+ n) :element-type 'double-float)) (slatec:dbesi ($float $x) alpha 2 (1+ n) $iarray 0) (aref $iarray n))) (t (mul (power '$%e (neg (simplifya `((mabs) ,$x) nil))) ($bessel_i $n $x))))) ;; Numerically compute H1(v, z). ;; ;; A&S 9.1.3 says H1(v,z) = J(v,z) + i * Y(v,z) ;; (defun hankel-1 (v z) (let ((v (float v)) (z (coerce z '(complex double-float)))) (cond ((minusp v) ;; A&S 9.1.6: ;; ;; H1(-v,z) = exp(v*pi*i)*H1(v,z) ;; ;; or ;; ;; H1(v,z) = exp(-v*pi*i)*H1(-v,z) (* (cis (* pi (- v))) (hankel-1 (- v) z))) (t (multiple-value-bind (n fnu) (floor v) (let ((zr (realpart z)) (zi (imagpart z)) (cyr (make-array (1+ n) :element-type 'double-float)) (cyi (make-array (1+ n) :element-type 'double-float))) (multiple-value-bind (dzr dzi df dk dm dn dcyr dcyi nz ierr) (slatec::zbesh zr zi fnu 1 1 (1+ n) cyr cyi 0 0) (declare (ignore dzr dzi df dk dm dn dcyr dcyi nz ierr)) (complex (aref cyr n) (aref cyi n))))))))) ;; Numerically compute H2(v, z). ;; ;; A&S 9.1.4 says H2(v,z) = J(v,z) - i * Y(v,z) ;; (defun hankel-2 (v z) (let ((v (float v)) (z (coerce z '(complex double-float)))) (cond ((minusp v) ;; A&S 9.1.6: ;; ;; H2(-v,z) = exp(-v*pi*i)*H1(v,z) ;; ;; or ;; ;; H2(v,z) = exp(v*pi*i)*H1(-v,z) (* (cis (* pi v)) (hankel-2 (- v) z))) (t (multiple-value-bind (n fnu) (floor v) (let ((zr (realpart z)) (zi (imagpart z)) (cyr (make-array (1+ n) :element-type 'double-float)) (cyi (make-array (1+ n) :element-type 'double-float))) (multiple-value-bind (dzr dzi df dk dm dn dcyr dcyi nz ierr) (slatec::zbesh zr zi fnu 1 2 (1+ n) cyr cyi 0 0) (declare (ignore dzr dzi df dk dm dn dcyr dcyi nz ierr)) (complex (aref cyr n) (aref cyi n))))))))) ;; Bessel function of the first kind for real or complex arg and real ;; non-negative order. (defun $bessel ($arg $order) "bessel(arg, order) = J[order](arg). This is deprecated. Use bessel_j(order,arg)" (cond ((not (bessel-numerical-eval-p $order $arg)) ;; Args aren't numeric. Return unevaluated. (list '(%bessel_j simp) $order $arg)) ((zerop ($imagpart $arg)) ;; We have numeric args and the first arg is purely ;; real. Call the real-valued Bessel function. (Should we ;; try calling j0 and j1 as appropriate instead of jn?) (cond ((= $order 0) (slatec:dbesj0 (float $arg))) ((= $order 1) (slatec:dbesj1 (float $arg))) ((minusp $order) ;; Bessel function of negative order. We use the ;; Hankel function to compute this, because A&S 9.1.3 ;; says H1(v,z) = J(v,z) + i * Y(v,z), and we know ;; J(v,z) is real. (realpart (hankel-1 $order $arg))) (t (multiple-value-bind (n alpha) (floor (float $order)) (let ((jvals (make-array (1+ n) :element-type 'double-float))) ;; Use analytic continuation formula A&S 9.1.35: ;; ;; %j[v](z*exp(m*%pi*%i)) = exp(m*%pi*%i*v)*%j[v](z) ;; ;; for an integer m. In particular, for m = 1: ;; ;; %j[v](-x) = exp(v*%pi*%i)*%j[v](x) (cond ((>= $arg 0) (slatec:dbesj (float $arg) alpha (1+ n) jvals 0) (setf $besselarray (make-array (1+ n) :initial-contents jvals))) (t (slatec:dbesj (- (float $arg)) alpha (1+ n) jvals 0) (setf $besselarray (make-array (1+ n))) (let ((s (cis (* $order pi)))) (dotimes (k (1+ n)) (let ((v (* s (aref jvals k)))) (setf (aref $besselarray k) (simplify `((mplus) ,(realpart v) ((mtimes) $%i ,(imagpart v)))))))))) (aref $besselarray n)))))) (t ;; The first arg is complex. Use the complex-valued Bessel ;; function. (cond ((mminusp $order) ;; Bessel function of negative order. We use the ;; Hankel function to compute this, because A&S 9.1.3 ;; says H1(v,z) = J(v,z) + i * Y(v,z), and H2(v,z) = ;; J(v,z) - i * Y(v,z). Thus, J(v,z) = (H1(v,z) + ;; H2(v,z))/2. Not the most efficient way, but ;; perhaps good enough for maxima. (let* ((arg (complex ($realpart $arg) ($imagpart $arg))) (result (* 0.5d0 (+ (hankel-1 $order arg) (hankel-2 $order arg))))) (complexify result))) (t (multiple-value-bind (n alpha) (floor (float $order)) (let ((cyr (make-array (1+ n) :element-type 'double-float)) (cyi (make-array (1+ n) :element-type 'double-float))) (multiple-value-bind (v-zr v-zi v-fnu v-kode v-n v-cyr v-cyi v-nz v-ierr) (slatec:zbesj (float ($realpart $arg)) (float ($imagpart $arg)) alpha 1 (1+ n) cyr cyi 0 0) (declare (ignore v-zr v-zi v-fnu v-kode v-n v-cyr v-cyi v-nz)) ;; Should check the return status in v-ierr of this ;; routine. (when (plusp v-ierr) (format t "zbesj ierr = ~A~%" v-ierr)) (setf $besselarray (make-array (+ n 2))) (dotimes (k (1+ n) (aref $besselarray n)) (setf (aref $besselarray k) (simplify (list '(mplus) (simplify (list '(mtimes) '$%i (aref cyi k))) (aref cyr k))))))))))))) (defmfun $bessel_j (v z) (simplify (list '(%bessel_j) (resimplify v) (resimplify z)))) ;; Bessel function of the second kind, Y[n](z), for real or complex z ;; and non-negative real n. (defun bessel-y (order arg) (cond ((zerop (imagpart arg)) ;; We have numeric args and the first arg is purely ;; real. Call the real-valued Bessel function. ;; ;; For negative values, use the analytic continuation formula ;; A&S 9.1.36: ;; ;; %y[v](z*exp(m*%pi*%i)) = exp(-v*m*%pi*%i) * %y[v](z) ;; + 2*%i*sin(m*v*%pi)*cot(v*%pi)*%j[v](z) ;; ;; In particular for m = 1: ;; ;; %y[v](-z) = exp(-v*%pi*%i) * %y[v](z) + 2*%i*cos(v*%pi)*%j[v](z) ;; (let ((arg (realpart arg))) (cond ((zerop order) (cond ((>= arg 0) (slatec:dbesy0 (float arg))) (t ;; For v = 0, this simplifies to ;; ;; %y[0](-z) = %y[0](z) + 2*%i*%j[0](z) (simplify `((mplus) ,(slatec:dbesy0 (float (- arg))) ((mtimes) $%i ,(* 2 (slatec:dbesj0 (float (- arg)))))))))) ((= order 1) (cond ((>= arg 0) (slatec:dbesy1 (float arg))) (t ;; For v = 1, this simplifies to ;; ;; %y[1](-z) = -%y[1](z) - 2*%i*%j[1](v) (simplify `((mplus) ,(slatec:dbesy1 (float (- arg))) ((mtimes) $%i ,(* -2 (slatec:dbesj1 (float (- arg)))))))))) (t (multiple-value-bind (n alpha) (floor (float order)) (let ((jvals (make-array (1+ n) :element-type 'double-float))) (cond ((>= arg 0) (slatec:dbesy (float (realpart arg)) alpha (1+ n) jvals) (setf $besselarray (make-array (1+ n) :initial-contents jvals)) (aref jvals n)) (t (let* ((s1 (cis (- (* order pi)))) (s2 (* #c(0 2) (cos (* order pi))))) (slatec:dbesy (- (float arg)) alpha (1+ n) jvals) (setf $yarray (make-array (1+ n))) (dotimes (k (1+ n)) (let ((v (+ (* s1 (aref jvals k)) (* s2 (aref $besselarray k))))) (setf (aref $yarray k) (simplify `((mplus) ,(realpart v) ((mtimes) $%i ,(imagpart v))))))) (aref $yarray n)))))))))) (t ;; The first arg is complex. Use the complex-valued Bessel ;; function (multiple-value-bind (n alpha) (floor (float order)) (let ((cyr (make-array (1+ n) :element-type 'double-float)) (cyi (make-array (1+ n) :element-type 'double-float)) (cwrkr (make-array (1+ n) :element-type 'double-float)) (cwrki (make-array (1+ n) :element-type 'double-float))) (multiple-value-bind (v-zr v-zi v-fnu v-kode v-n v-cyr v-cyi v-nz v-cwrkr v-cwrki v-ierr) (slatec::zbesy (float (realpart arg)) (float (imagpart arg)) alpha 1 (1+ n) cyr cyi 0 cwrkr cwrki 0) (declare (ignore v-zr v-zi v-fnu v-kode v-n v-cyr v-cyi v-cwrkr v-cwrki v-nz)) ;; We should check for errors here based on the ;; value of v-ierr. (when (plusp v-ierr) (format t "zbesy ierr = ~A~%" v-ierr)) (setf $besselarray (make-array (+ n 2))) (dotimes (k (1+ n) (aref $besselarray n)) (setf (aref $besselarray k) (simplify (list '(mplus) (simplify (list '(mtimes) '$%i (aref cyi k))) (aref cyr k))))))))))) (defun z-function (x y) ((lambda (xs ys capn nu np1 h h2 lamb r1 r2 s s1 s2 t1 t2 c bool re im) (setq xs (cond ((> 0.0d0 x) -1.0d0) (t 1.0d0))) (setq ys (cond ((> 0.0d0 y) -1.0d0) (t 1.0d0))) (setq x (abs x) y (abs y)) (cond ((and (> 4.29d0 y) (> 5.33d0 x)) (setq s (* (1+ (* -0.23310023d0 y)) (sqrt (1+ (* -0.035198873d0 x x))))) (setq h (* 1.6d0 s) h2 (* 2.0d0 h) capn (+ 6 (floor (* 23.0d0 s)))) (setq nu (+ 9 (floor (* 21.0d0 s))))) (t (setq h 0.0d0) (setq capn 0) (setq nu 8))) (and (> h 0.0d0) (setq lamb (expt h2 capn))) (setq bool (or (zerop h) (zerop lamb))) (do ((n nu (1- n))) ((> 0 n)) (setq np1 (1+ n)) (setq t1 (+ h (* (float np1) r1) y)) (setq t2 (- x (* (float np1) r2))) (setq c (/ 0.5d0 (+ (* t1 t1) (* t2 t2)))) (setq r1 (* c t1) r2 (* c t2)) (cond ((and (> h 0.0) (not (< capn n))) (setq t1 (+ s1 lamb) s1 (- (* r1 t1) (* r2 s2))) (setq s2 (+ (* r1 s2) (* r2 t1)) lamb (/ lamb h2))))) (setq im (cond ((zerop y) (* 1.77245384d0 (exp (- (* x x))))) (t (* 2.0d0 (cond (bool r1) (t s1)))))) (setq re (* -2.0d0 (cond (bool r2) (t s2)))) (cond ((> ys 0.0d0) (setq re (* re xs))) (t (setq r1 (* 3.5449077 (exp (- (* y y) (* x x))))) (setq r2 (* 2.0 x y)) (setq re (* (- re (* r1 (sin r2))) xs)) (setq im (- (* r1 (cos r2)) im)))) (list '(mlist simp) re im)) (cond ((> 0.0d0 x) -1.0d0) (t 1.0d0)) (cond ((> 0.0d0 x) -1.0d0) (t 1.0d0)) 0 0 0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 nil 0.0d0 0.0d0)) (defun $nzeta ($z) (prog ($x $y $w) (cond ((and (numberp (setq $x ($realpart $z))) (numberp (setq $y ($imagpart $z)))) (setq $w (z-function (float $x) (float $y))) (return (simplify (list '(mplus) (simplify (list '(mtimes) (meval1 '$%i) (caddr $w))) (cadr $w))))) (t (return (list '($nzeta simp) $z)))))) (defun $nzetar ($z) (prog ($x $y $w) (cond ((and (numberp (setq $x ($realpart $z))) (numberp (setq $y ($imagpart $z)))) (setq $w (z-function (float $x) (float $y))) (return (cadr $w))) (t (return (list '($nzetar simp) $z)))))) (defun $nzetai ($z) (prog ($x $y $w) (cond ((and (numberp (setq $x ($realpart $z))) (numberp (setq $y ($imagpart $z)))) (setq $w (z-function (float $x) (float $y))) (return (caddr $w))) (t (return (list '($nzetai simp) $z)))))) (defmspec $gauss (form) (format t "NOTE: The gauss function is superseded by random_normal in the `distrib' package. Perhaps you meant to enter `~a'.~%" (print-invert-case (implode (mstring `(($random_normal) ,@ (cdr form)))))) '$done) ;; I think this is the function E1(x). At least some simple numerical ;; tests show that this expint matches the function de1 from SLATEC ;; Exponential integral E1(x). The Cauchy principal value is used for ;; negative x. (defun $expint (x) (cond ((numberp x) (values (slatec:de1 (float x)))) (t (list '($expint simp) x)))) ;; Define the Bessel funtion J[n](z) (defprop %bessel_j bessel-j-simp operators) ;; Derivatives of the Bessel function. (defprop %bessel_j ((n x) ;; Derivative wrt to order n. A&S 9.1.64. Do we really want to ;; do this? It's quite messy. ;; ;; J[n](x)*log(x/2) - (x/2)^n*sum((-1)^k*psi(n+k+1)/gamma(n+k+1)*(z^2/4)^k/k!,k,0,inf) ((mplus) ((mtimes simp) ((%bessel_j simp) n x) ((%log) ((mtimes) ((rat simp) 1 2) x))) ((mtimes simp) -1 ((mexpt simp) ((mtimes simp) x ((rat simp) 1 2)) n) ((%sum simp) ((mtimes simp) ((mexpt simp) -1 $%k) ((mexpt simp) ((mfactorial simp) $%k) -1) ((mqapply simp) (($psi simp array) 0) ((mplus simp) 1 $%k n)) ((mexpt simp) ((%gamma simp) ((mplus simp) 1 $%k n)) -1) ((mexpt simp) ((mtimes simp) x x ((rat simp) 1 4)) $%k)) $%k 0 $inf))) ;; Derivative wrt to arg x. A&S 9.1.27; changed from 9.1.30 so that taylor works on Bessel functions ((mtimes) ((mplus) ((%bessel_j) ((mplus) -1 n) x) ((mtimes) -1 ((%bessel_j) ((mplus) 1 n) x))) ((rat) 1 2))) ;; ((mplus) ;; ((%bessel_j) ((mplus) -1 n) x) ;; ((mtimes) -1 n ((%bessel_j) n x) ((mexpt) x -1)))) grad) ;; If E is a maxima ratio with a denominator of DEN, return the ratio ;; as a Lisp rational. Otherwise NIL. (defun max-numeric-ratio-p (e den) (if (and (listp e) (eq 'rat (caar e)) (= den (third e)) (integerp (second e))) (/ (second e) (third e)) nil)) ;; Compute the Bessel function of half-integral order. ;; ;; From A&S 10.1.1, we have ;; ;; J[n+1/2](z) = sqrt(2*z/pi)*j[n](z) ;; Y[n+1/2](z) = sqrt(2*z/pi)*y[n](z) ;; ;; where j[n](z) is the spherical bessel function of the first kind ;; and y[n](z) is the spherical bessel function of the second kind. ;; ;; A&S 10.1.8 and 10.1.9 give ;; ;; j[n](z) = 1/z*[P(n+1/2,z)*sin(z-n*pi/2) + Q(n+1/2)*cos(z-n*pi/2)] ;; ;; y[n](z) = (-1)^(n+1)*1/z*[P(n+1/2,z)*cos(z+n*pi/2) - Q(n+1/2)*sin(z+n*pi/2)] ;; ;; A&S 10.1.10 ;; ;; j[n](z) = f[n](z)*sin(z) + (-1)^n*f[-n-1](z)*cos(z) ;; ;; f[0](z) = 1/z, f[1](z) = 1/z^2 ;; ;; f[n-1](z) + f[n+1](z) = (2*n+1)/z*f[n](z) ;; (defun f-fun (n z) (cond ((= n 0) (div 1 z)) ((= n 1) (div 1 (mul z z))) ((= n -1) 0) ((>= n 2) ;; f[n+1](z) = (2*n+1)/z*f[n](z) - f[n-1](z) or ;; f[n](z) = (2*n-1)/z*f[n-1](z) - f[n-2](z) (sub (mul (div (+ n n -1) z) (f-fun (1- n) z)) (f-fun (- n 2) z))) (t ;; Negative n ;; ;; f[n-1](z) = (2*n+1)/z*f[n](z) - f[n+1](z) or ;; f[n](z) = (2*n+3)/z*f[n+1](z) - f[n+2](z) (sub (mul (div (+ n n 3) z) (f-fun (1+ n) z)) (f-fun (+ n 2) z))))) ;; Compute sqrt(2*z/%pi) (defun root-2z/pi (z) (let ((half (div 1 2))) (simplify (power (mul 2 z (inv '$%pi)) half)))) (defun bessel-j-half-order (order arg) "Compute J[n+1/2](z)" (let* ((n (floor order)) (sign (if (oddp n) -1 1)) (jn (sub (mul ($expand (f-fun n arg)) `((%sin) ,arg)) (mul sign ($expand (f-fun (- (- n) 1) arg)) `((%cos) ,arg))))) (mul (root-2z/pi arg) jn))) (defun bessel-y-half-order (order arg) "Compute Y[n+1/2](z)" ;; A&S 10.1.1: ;; Y[n+1/2](z) = sqrt(2*z/%pi)*y[n](z) ;; ;; A&S 10.1.15: ;; y[n](z) = (-1)^(n+1)*j[-n-1](z) ;; ;; So ;; Y[n+1/2](z) = sqrt(2*z/%pi)*(-1)^(n+1)*j[-n-1](z) ;; = (-1)^(n+1)*sqrt(2*z/%pi)*j[-n-1](z) ;; = (-1)^(n+1)*J[-n-1/2](z) (let* ((n (floor order)) (jn (bessel-j-half-order (- (- order) 1/2) arg))) (if (evenp n) (mul -1 jn) jn))) ;; See A&S 10.2.12 ;; ;; I[n+1/2](z) = sqrt(2*z/%pi)*[g[n](z)*sinh(z) + g[n-1](z)*cosh(z)] ;; ;; g[0](z) = 1/z, g[1](z) = -1/z^2 ;; ;; g[n-1](z) - g[n+1](z) = (2*n+1)/z*g[n](z) ;; ;; (defun g-fun (n z) (declare (type integer n)) (cond ((= n 0) (div 1 z)) ((= n 1) (div -1 (mul z z))) ((>= n 2) ;; g[n](z) = g[n-2](z) - (2*n-1)/z*g[n-1](z) (sub (g-fun (- n 2) z) (mul (div (+ n n -1) z) (g-fun (- n 1) z)))) (t ;; n is negative ;; ;; g[n](z) = (2*n+3)/z*g[n+1](z) + g[n+2](z) (add (mul (div (+ n n 3) z) (g-fun (+ n 1) z)) (g-fun (+ n 2) z))))) ;; See A&S 10.2.12 ;; ;; I[n+1/2](z) = sqrt(2*z/%pi)*[g[n](z)*sinh(z) + g[-n-1](z)*cosh(z)] (defun bessel-i-half-order (order arg) (let ((order (floor order))) (mul (root-2z/pi arg) (add (mul ($expand (g-fun order arg)) `((%sinh) ,arg)) (mul ($expand (g-fun (- (+ order 1)) arg)) `((%cosh) ,arg)))))) ;; See A&S 10.2.15 ;; ;; sqrt(%pi/2/z)*K[n+1/2](z) = (%pi/2/z)*exp(-z)*sum (n+1/2,k)/(2*z)^k ;; ;; or ;; n ;; K[n+1/2](z) = sqrt(%pi/(2*z)) sum (n+1/2,k)/(2*z)^k ;; k=0 ;; ;; where (A&S 10.1.9) ;; ;; (n+1/2,k) = (n+k)!/k!/(n-k)! ;; (defun k-fun (n z) (declare (type unsigned-byte n)) ;; Computes the sum above (let ((sum 1) (term 1)) (loop for k from 0 upto n do (setf term (mul term (div (div (* (- n k) (+ n k 1)) (+ k 1)) (mul 2 z)))) (setf sum (add sum term))) sum)) (defun bessel-k-half-order (order arg) (let ((order (truncate (abs order)))) (mul (mul `((mexpt) ,(div '$%pi (mul 2 arg)) ,(div 1 2)) `((mexpt) $%e ,(neg arg))) (k-fun (abs order) arg)))) (defun bessel-numerical-eval-p (order arg) ;; Return non-NIL if we should numerically evaluate a bessel ;; function. Basically, both args have to be numbers. If both args ;; are integers, we don't evaluate unless $numer is true. (or (and (numberp order) (complex-number-p arg) (or (floatp order) (floatp ($realpart arg)) (floatp ($imagpart arg)))) (and $numer (numberp order) (complex-number-p arg)))) (defun bessel-j-simp (exp ignored z) (declare (ignore ignored)) (twoargcheck exp) (let ((order (simpcheck (cadr exp) z)) (rat-order nil)) (let* ((arg (simpcheck (caddr exp) z))) (when (and (numberp arg) (zerop arg) (numberp order)) ;; J[v](0) = 1 if v = 0. Otherwise 0. (return-from bessel-j-simp (if (zerop order) 1 0))) (cond ((bessel-numerical-eval-p order arg) ;; We have numeric order and arg and $numer is true, or ;; we have either the order or arg being floating-point, ;; so let's evaluate it numerically. ($bessel arg order)) ((and (integerp order) (minusp order)) ;; Some special cases when the order is an integer ;; ;; A&S 9.1.5 ;; J[-n](x) = (-1)^n*J[n](x) (if (evenp order) (list '(%bessel_j simp) (- order) arg) `((mtimes simp) -1 ((%bessel_j simp) ,(- order) ,arg)))) ((and $besselexpand (setq rat-order (max-numeric-ratio-p order 2))) ;; When order is a fraction with a denominator of 2, we ;; can express the result in terms of elementary ;; functions. ;; (bessel-j-half-order rat-order arg) ) (t (eqtest (list '(%bessel_j) order arg) exp)))))) ;; Define the Bessel funtion Y[n](z) (defmfun $bessel_y (v z) (simplify (list '(%bessel_y) (resimplify v) (resimplify z)))) (defprop %bessel_y bessel-y-simp operators) (defprop %bessel_y ((n x) ;; A&S 9.1.65 ;; ;; cot(n*%pi)*[diff(bessel_j(n,x),n)-%pi*bessel_y(n,x)] ;; - csc(n*%pi)*diff(bessel_j(-n,x),n)-%pi*bessel_j(n,x) ((mplus simp) ((mtimes simp) $%pi ((%bessel_j simp) n x)) ((mtimes simp) -1 ((%csc simp) ((mtimes simp) $%pi n)) ((%derivative simp) ((%bessel_j simp) ((mtimes simp) -1 n) x) x 1)) ((mtimes simp) ((%cot simp) ((mtimes simp) $%pi n)) ((mplus simp) ((mtimes simp) -1 $%pi ((%bessel_y simp) n x)) ((%derivative simp) ((%bessel_j simp) n x) n 1)))) ;; Derivative wrt to arg x. A&S 9.1.27; changed from A&S 9.1.30 to be consistent with bessel_j. ((mtimes) ((mplus) ((%bessel_y)((mplus) -1 n) x) ((mtimes) -1 ((%bessel_y) ((mplus) 1 n) x))) ((rat) 1 2))) ;;((mplus) ;; ((%bessel_y) ((mplus) -1 n) x) ;; ((mtimes) -1 n ((%bessel_y) n x) ((mexpt) x -1)))) grad) (defun bessel-y-simp (exp ignored z) (declare (ignore ignored)) (twoargcheck exp) (let ((order (simpcheck (cadr exp) z)) (rat-order nil)) (let* ((arg (simpcheck (caddr exp) z))) (cond ((eql arg 0) (domain-error arg 'bessel_y)) ((and (>= (signum1 order) 0) (bessel-numerical-eval-p order arg)) ;; We have numeric order and arg and $numer is true, or ;; we have either the order or arg being floating-point, ;; so let's evaluate it numerically. (bessel-y (float order) (complex ($realpart arg) ($imagpart arg)))) ((and (integerp order) (minusp order)) ;; Special case when the order is an integer. ;; ;; A&S 9.1.5 ;; Y[-n](x) = (-1)^n*Y[n](x) (if (evenp order) (list '(%bessel_y) (- order) arg) `((mtimes simp) -1 ((%bessel_y simp) ,(- order) ,arg)))) ((and $besselexpand (setq rat-order (max-numeric-ratio-p order 2))) ;; When order is a fraction with a denominator of 2, we ;; can express the result in terms of elementary ;; functions. ;; ;; Y[1/2](z) = -J[1/2](z) is a function of sin. ;; Y[-1/2](z) = -J[-1/2](z) is a function of cos. (bessel-y-half-order rat-order arg)) (t (eqtest (list '(%bessel_y) order arg) exp)))))) ;; Define the Bessel funtion I[n](z) (defmfun $bessel_i (v z) (simplify (list '(%bessel_i) (resimplify v) (resimplify z)))) (defprop %bessel_i bessel-i-simp operators) (defprop %bessel_i ((n x) ;; A&S 9.6.42 ;; ;; I[n](x)*log(x/2) - (x/2)^n*sum(psi(n+k+1)/gamma(n+k+1)*(z^2/4)^k/k!,k,0,inf) ((mplus) ((mtimes simp) ((%bessel_i simp) n x) ((%log) ((mtimes) ((rat simp) 1 2) x))) ((mtimes simp) -1 ((mexpt simp) ((mtimes simp) x ((rat simp) 1 2)) n) ((%sum simp) ((mtimes simp) ((mexpt simp) ((mfactorial simp) $%k) -1) ((mqapply simp) (($psi simp array) 0) ((mplus simp) 1 $%k n)) ((mexpt simp) ((%gamma simp) ((mplus simp) 1 $%k n)) -1) ((mexpt simp) ((mtimes simp) x x ((rat simp) 1 4)) $%k)) $%k 0 $inf))) ((mplus) ((%bessel_i) ((mplus) -1 n) x) ((mtimes) -1 n ((%bessel_i) n x) ((mexpt) x -1)))) grad) (defun bessel-i-simp (exp ignored z) (declare (ignore ignored)) (twoargcheck exp) (let ((order (simpcheck (cadr exp) z)) (rat-order nil)) (let* ((arg (simpcheck (caddr exp) z))) (cond ((and (>= (signum1 order) 0) (bessel-numerical-eval-p order arg)) (bessel-i (float order) (complex ($realpart arg) ($imagpart arg)))) ((and (integerp order) (minusp order)) ;; Some special cases when the order is an integer ;; ;; A&S 9.6.6 ;; I[-n](x) = I[n](x) (list '(%bessel_i) (- order) arg)) ((and $besselexpand (setq rat-order (max-numeric-ratio-p order 2))) ;; When order is a fraction with a denominator of 2, we ;; can express the result in terms of elementary ;; functions. ;; ;; I[1/2](z) = sqrt(2/%pi/z)*sinh(z) ;; I[-1/2](z) = sqrt(2/%pi/z)*cosh(z) (bessel-i-half-order rat-order arg)) (t (eqtest (list '(%bessel_i) order arg) exp)))))) ;; Define the Bessel funtion K[n](z) (defmfun $bessel_k (v z) (simplify (list '(%bessel_k) (resimplify v) (resimplify z)))) (defprop %bessel_k bessel-k-simp operators) (defprop %bessel_k ((n x) ;; A&S 9.6.43 ;; ;; %pi/2*csc(n*%pi)*['diff(bessel_i(-n,x),n)-'diff(bessel_i(n,x),n)] ;; - %pi*cot(n*%pi)*bessel_k(n,x) ((mplus simp) ((mtimes simp) -1 $%pi ((%bessel_k simp) n x) ((%cot simp) ((mtimes simp) $%pi n))) ((mtimes simp) ((rat simp) 1 2) $%pi ((%csc simp) ((mtimes simp) $%pi n)) ((mplus simp) ((%derivative simp) ((%bessel_i simp) ((mtimes simp) -1 n) x) n 1) ((mtimes simp) -1 ((%derivative simp) ((%bessel_i simp) n x) n 1))))) ((mplus simp) ((mtimes) -1 ((%bessel_k) ((mplus) -1 n) x)) ((mtimes) -1 n ((mexpt) x -1) ((%bessel_k) n x)))) grad) (defun bessel-k-simp (exp ignored z) (declare (ignore ignored)) (let ((order (simpcheck (cadr exp) z)) (rat-order nil)) (let* ((arg (simpcheck (caddr exp) z))) (cond ((and (>= (signum1 order) 0) (bessel-numerical-eval-p order arg)) ;; A&S 9.6.6 ;; K[-v](x) = K[v](x) (bessel-k (abs (float order)) (complex ($realpart arg) ($imagpart arg)))) ((mminusp order) ;; A&S 9.6.6 ;; K[-v](x) = K[v](x) (resimplify (list '(%bessel_k) `((mtimes) -1 ,order) arg))) ((and $besselexpand (setq rat-order (max-numeric-ratio-p order 2))) ;; When order is a fraction with a denominator of 2, we ;; can express the result in terms of elementary ;; functions. ;; ;; K[1/2](z) = sqrt(2/%pi/z)*exp(-z) = K[1/2](z) (bessel-k-half-order rat-order arg)) (t (eqtest (list '(%bessel_k) order arg) exp))))))