| Title: | Computations and Approximations for Bessel Functions |
|---|---|
| Description: | Computations for Bessel function for complex, real and partly 'mpfr' (arbitrary precision) numbers; notably interfacing TOMS 644; approximations for large arguments, experiments, etc. |
| Authors: | Martin Maechler [aut, cre] (ORCID: <https://orcid.org/0000-0002-8685-9910>) |
| Maintainer: | Martin Maechler <[email protected]> |
| License: | GPL (>= 2) |
| Version: | 0.7-0 |
| Built: | 2026-05-08 04:56:33 UTC |
| Source: | https://github.com/r-forge/specfun |
Compute the Airy functions or or their first
derivatives,
and
.
The Airy functions are solutions of the differential equation
for , and are related to each other and to the
(modified) Bessel functions via (many identities, see
https://dlmf.nist.gov/9.6), e.g., if
,
and
AiryA(z, deriv = 0, expon.scaled = FALSE, verbose = 0) AiryB(z, deriv = 0, expon.scaled = FALSE, verbose = 0)AiryA(z, deriv = 0, expon.scaled = FALSE, verbose = 0) AiryB(z, deriv = 0, expon.scaled = FALSE, verbose = 0)
z |
complex or numeric vector. |
deriv |
order of derivative; must be 0 or 1. |
expon.scaled |
logical indicating if the result should be scaled by an exponential factor (typically to avoid under- or over-flow). |
verbose |
integer defaulting to 0, indicating the level of verbosity notably from C code. |
By default, when expon.scaled is false, AiryA()
computes the complex Airy function or its derivative
on deriv=0 or deriv=1
respectively.
When expon.scaled is true, it returns
or
,
effectively removing the exponential decay in
and
the exponential growth in
,
where , and
Arg(z).
While the Airy functions and are
analytic in the whole plane, the corresponding scaled
functions (for expon.scaled=TRUE) have a cut along the
negative real axis.
By default, when expon.scaled is false, AiryB()
computes the complex Airy function or its derivative
on deriv=0 or deriv=1
respectively.
When expon.scaled is true, it returns
or
,
to remove the exponential behavior in both the left and right half
planes where, as above,
.
a complex or numeric vector of the same length (and class) as z.
Donald E. Amos, Sandia National Laboratories, wrote the original fortran code. Martin Maechler did the R interface.
see BesselJ; notably for many results the
Digital Library of Mathematical Functions (DLMF), Chapter 9 Airy and Related Functions at https://dlmf.nist.gov/9.
BesselI etc; the Hankel functions Hankel.
The CRAN package Rmpfr has Ai(x) for
arbitrary precise "mpfr"-numbers x.
## The AiryA() := Ai() function ------------- curve(AiryA, -20, 100, n=1001) curve(AiryA, -1, 100, n=1011, log="y") -> Aix curve(AiryA(x, expon.scaled=TRUE), -1, 50, n=1001) ## Numerically "proving" the 1st identity above : z <- Aix$x; i <- z > 0; head(z <- z[i <- z > 0]) Aix <- Aix$y[i]; zeta <- 2/3*z*sqrt(z) stopifnot(all.equal(Aix, 1/pi * sqrt(z/3)* BesselK(zeta, nu = 1/3), tol = 4e-15)) # 64b Lnx: 7.9e-16; 32b Win: 1.8e-15 ## This gives many warnings (248 on nb-mm4, F24) about lost accuracy, but on Windows takes ~ 4 sec: curve(AiryA(x, expon.scaled=TRUE), 1, 10000, n=1001, log="xy") ## The AiryB() := Bi() function ------------- curve(AiryB, -20, 2, n=1001); abline(h=0,v=0, col="gray",lty=2) curve(AiryB, -1, 20, n=1001, log = "y") # exponential growth (x > 0) curve(AiryB(x,expon.scaled=TRUE), -1, 20, n=1001) curve(AiryB(x,expon.scaled=TRUE), 1, 10000, n=1001, log="x")## The AiryA() := Ai() function ------------- curve(AiryA, -20, 100, n=1001) curve(AiryA, -1, 100, n=1011, log="y") -> Aix curve(AiryA(x, expon.scaled=TRUE), -1, 50, n=1001) ## Numerically "proving" the 1st identity above : z <- Aix$x; i <- z > 0; head(z <- z[i <- z > 0]) Aix <- Aix$y[i]; zeta <- 2/3*z*sqrt(z) stopifnot(all.equal(Aix, 1/pi * sqrt(z/3)* BesselK(zeta, nu = 1/3), tol = 4e-15)) # 64b Lnx: 7.9e-16; 32b Win: 1.8e-15 ## This gives many warnings (248 on nb-mm4, F24) about lost accuracy, but on Windows takes ~ 4 sec: curve(AiryA(x, expon.scaled=TRUE), 1, 10000, n=1001, log="xy") ## The AiryB() := Bi() function ------------- curve(AiryB, -20, 2, n=1001); abline(h=0,v=0, col="gray",lty=2) curve(AiryB, -1, 20, n=1001, log = "y") # exponential growth (x > 0) curve(AiryB(x,expon.scaled=TRUE), -1, 20, n=1001) curve(AiryB(x,expon.scaled=TRUE), 1, 10000, n=1001, log="x")
Compute the Bessel functions I(), J(), K(), and Y(), of complex
arguments z and real nu,
BesselI(z, nu, expon.scaled = FALSE, nSeq = 1, verbose = 0) BesselJ(z, nu, expon.scaled = FALSE, nSeq = 1, verbose = 0) BesselK(z, nu, expon.scaled = FALSE, nSeq = 1, verbose = 0) BesselY(z, nu, expon.scaled = FALSE, nSeq = 1, verbose = 0)BesselI(z, nu, expon.scaled = FALSE, nSeq = 1, verbose = 0) BesselJ(z, nu, expon.scaled = FALSE, nSeq = 1, verbose = 0) BesselK(z, nu, expon.scaled = FALSE, nSeq = 1, verbose = 0) BesselY(z, nu, expon.scaled = FALSE, nSeq = 1, verbose = 0)
z |
complex or numeric vector. |
nu |
numeric (scalar). |
expon.scaled |
logical indicating if the result should be scaled by an exponential factor, typically to avoid under- or over-flow. See the ‘Details’ about the specific scaling. |
nSeq |
positive integer; if |
verbose |
integer defaulting to 0, indicating the level of verbosity notably from C code. |
The case nu < 0 is handled by using simple formula from
Abramowitz and Stegun, see details in besselI().
The scaling activated by expon.scaled = TRUE depends on the
function and the scaled versions are
BesselJ(z, nu, expo=TRUE)
BesselY(z, nu, expo=TRUE)
BesselI(z, nu, expo=TRUE)
BesselK(z, nu, expo=TRUE)
a complex or numeric vector (or matrix with nSeq
columns if nSeq > 1)
of the same length (or nrow when nSeq > 1) and
mode as z.
Donald E. Amos, Sandia National Laboratories, wrote the original
fortran code.
Martin Maechler did the translation to C, and partial cleanup
(replacing goto's), in addition to the R interface.
Abramowitz, M., and Stegun, I. A. (1964, etc). Handbook of mathematical functions (NBS AMS series 55, U.S. Dept. of Commerce), https://personal.math.ubc.ca/~cbm/aands/
Wikipedia (20nn). Bessel Function, https://en.wikipedia.org/wiki/Bessel_function
D. E. Amos (1986) Algorithm 644: A portable package for Bessel functions of a complex argument and nonnegative order; ACM Trans. Math. Software 12, 3, 265–273.
D. E. Amos (1983) Computation of Bessel Functions of Complex Argument; Sand83-0083.
D. E. Amos (1983) Computation of Bessel Functions of Complex Argument and Large Order; Sand83-0643.
D. E. Amos (1985) A subroutine package for Bessel functions of a complex argument and nonnegative order; Sand85-1018.
Olver, F.W.J. (1974). Asymptotics and Special Functions; Academic Press, N.Y., p.420
The base R functions besselI(), besselK(), etc.
The Hankel functions (of first and second kind),
and : Hankel.
The Airy functions and and their first
derivatives, Airy.
For large x and/or nu arguments, algorithm AS~644 is not
good enough, and the results may overflow to Inf or underflow
to zero, such that direct computation of and
are desirable. For this, we provide
besselI.nuAsym(), besselIasym() and
besselK.nuAsym(*, log= *), based on asymptotic expansions.
## For real small arguments, BesselI() gives the same as base::besselI() : set.seed(47); x <- sort(round(rlnorm(20), 2)) M <- cbind(x, b = besselI(x, 3), B = BesselI(x, 3)) stopifnot(all.equal(M[,"b"], M[,"B"], tol = 2e-15)) # ~4e-16 even M ## and this is true also for the 'exponentially scaled' version: Mx <- cbind(x, b = besselI(x, 3, expon.scaled=TRUE), B = BesselI(x, 3, expon.scaled=TRUE)) stopifnot(all.equal(Mx[,"b"], Mx[,"B"], tol = 2e-15)) # ~4e-16 even## For real small arguments, BesselI() gives the same as base::besselI() : set.seed(47); x <- sort(round(rlnorm(20), 2)) M <- cbind(x, b = besselI(x, 3), B = BesselI(x, 3)) stopifnot(all.equal(M[,"b"], M[,"B"], tol = 2e-15)) # ~4e-16 even M ## and this is true also for the 'exponentially scaled' version: Mx <- cbind(x, b = besselI(x, 3, expon.scaled=TRUE), B = BesselI(x, 3, expon.scaled=TRUE)) stopifnot(all.equal(Mx[,"b"], Mx[,"B"], tol = 2e-15)) # ~4e-16 even
Compute the Hankel functions and ,
also called ‘H-Bessel’ function (of the third kind),
of complex arguments. They are defined as
where and are the
Bessel functions of the first and second kind, see
BesselJ, etc.
BesselH(m, z, nu, expon.scaled = FALSE, nSeq = 1, verbose = 0)BesselH(m, z, nu, expon.scaled = FALSE, nSeq = 1, verbose = 0)
m |
integer, either 1 or 2, indicating the kind of Hankel function. |
z |
complex or numeric vector of values different from 0. |
nu |
numeric, must currently be non-negative. |
expon.scaled |
logical indicating if the result should be scaled by an exponential factor (typically to avoid under- or over-flow). |
nSeq |
positive integer; if |
verbose |
integer defaulting to 0, indicating the level of verbosity notably from C code. |
By default (when expon.scaled is false), the resulting sequence
(of length nSeq) is for ,
computed for .
If expon.scaled is true, the sequence is for
where
(and ), for .
a complex or numeric vector (or matrix if nSeq > 1)
of the same length and mode as z.
Donald E. Amos, Sandia National Laboratories, wrote the original fortran code. Martin Maechler did the R interface.
see BesselI.
BesselI etc; the Airy function Airy.
##------------------ H(1, *) ---------------- nus <- c(1,2,5,10) for(i in seq_along(nus)) curve(BesselH(1, x, nu=nus[i]), -10, 10, add= i > 1, col=i, n=1000) legend("topleft", paste("nu = ", format(nus)), col = seq_along(nus), lty=1) ## nu = 10 looks a bit "special" ... hmm... curve(BesselH(1, x, nu=10), -.3, .3, col=4, ylim = c(-10,10), n=1000) ##------------------ H(2, *) ---------------- for(i in seq_along(nus)) curve(BesselH(2, x, nu=nus[i]), -10, 10, add= i > 1, col=i, n=1000) legend("bottomright", paste("nu = ", format(nus)), col = seq_along(nus), lty=1) ## the same nu = 10 behavior ..##------------------ H(1, *) ---------------- nus <- c(1,2,5,10) for(i in seq_along(nus)) curve(BesselH(1, x, nu=nus[i]), -10, 10, add= i > 1, col=i, n=1000) legend("topleft", paste("nu = ", format(nus)), col = seq_along(nus), lty=1) ## nu = 10 looks a bit "special" ... hmm... curve(BesselH(1, x, nu=10), -.3, .3, col=4, ylim = c(-10,10), n=1000) ##------------------ H(2, *) ---------------- for(i in seq_along(nus)) curve(BesselH(2, x, nu=nus[i]), -10, 10, add= i > 1, col=i, n=1000) legend("bottomright", paste("nu = ", format(nus)), col = seq_along(nus), lty=1) ## the same nu = 10 behavior ..
Compute Bessel functions and
for large and possibly large
,
using asymptotic expansions in Debye polynomials.
besselI.nuAsym(x, nu, k.max, expon.scaled = FALSE, log = FALSE) besselK.nuAsym(x, nu, k.max, expon.scaled = FALSE, log = FALSE)besselI.nuAsym(x, nu, k.max, expon.scaled = FALSE, log = FALSE) besselK.nuAsym(x, nu, k.max, expon.scaled = FALSE, log = FALSE)
x |
numeric or |
nu |
numeric; The order (maybe fractional!) of the corresponding Bessel function. |
k.max |
integer number of terms in the expansion. Must be in
|
expon.scaled |
logical; if |
log |
logical; if TRUE, |
Abramowitz & Stegun , page 378, has formula 9.7.7 and 9.7.8 for the asymptotic
expansions of and , respectively,
also saying
When , these expansions
(of and
)
hold uniformly with respect to in the sector
,
where is an arbitrary positive number.
and for this reason, we require .
The Debye polynomials are defined in 9.3.9 and 9.3.10 (page 366).
a numeric vector of the same length as the long of x and
nu. (usual argument recycling is applied implicitly.)
Martin Maechler
Abramowitz, M., and Stegun, I. A. (1964, etc). Handbook of mathematical functions, pp. 366, 378.
From this package Bessel: BesselI(); further,
besselIasym() for the case when is large and
is small or moderate (using A.&S. (9.7.1) and (9.7.2)).
Further, from base: besselI, etc.
x <- c(1:10, 20, 50, 100, 100000) nu <- c(1, 10, 20, 50, 10^(2:10)) sapply(0:4, function(k.) sapply(nu, function(n.) besselI.nuAsym(x, nu=n., k.max = k., log = TRUE))) sapply(0:4, function(k.) sapply(nu, function(n.) besselK.nuAsym(x, nu=n., k.max = k., log = TRUE)))x <- c(1:10, 20, 50, 100, 100000) nu <- c(1, 10, 20, 50, 10^(2:10)) sapply(0:4, function(k.) sapply(nu, function(n.) besselI.nuAsym(x, nu=n., k.max = k., log = TRUE))) sapply(0:4, function(k.) sapply(nu, function(n.) besselK.nuAsym(x, nu=n., k.max = k., log = TRUE)))
Compute Bessel function
and
for large and small or moderate
, using the asymptotic expansions (9.7.1) and (9.7.2), p.377-8 of
Abramowitz & Stegun, for , even valid for
complex ,
where
and and .
Whereas besselIasym(x,a) computes a possibly exponentially scaled
and/or logged version of ,
besselI.ftrms returns the corresponding terms in the
series expansion of above.
besselIasym (x, nu, k.max = 10, expon.scaled = FALSE, log = FALSE) besselKasym (x, nu, k.max = 10, expon.scaled = FALSE, log = FALSE) besselI.ftrms(x, nu, K = 20)besselIasym (x, nu, k.max = 10, expon.scaled = FALSE, log = FALSE) besselKasym (x, nu, k.max = 10, expon.scaled = FALSE, log = FALSE) besselI.ftrms(x, nu, K = 20)
x |
numeric or complex (with real part) |
nu |
numeric; the order (maybe fractional!) of the corresponding Bessel function. |
k.max, K
|
integer number of terms in the expansion. |
expon.scaled |
logical; if |
log |
logical; if TRUE, |
Even though the reference (A. & S.) requires
for and
for ,
where Arg(z),
the zero-th order term seems correct also for negative (real) numbers.
a numeric (or complex) vector of the same length as x.
Martin Maechler
Abramowitz, M., and Stegun, I. A. (1964, etc). Handbook of mathematical functions (NBS AMS series 55, U.S. Dept. of Commerce).
From this package Bessel() BesselI(); further,
besselI.nuAsym() which is useful when is large
as well, using A.&S. (9.7.7) and (9.7.8);
further base besselI, etc.
x <- c(1:10, 20, 50, 100^(2:10)) nu <- c(1, 10, 20, 50, 100) r <- lapply(c(0:4,10,20), function(k.) sapply(nu, function(n.) besselIasym(x, nu=n., k.max = k., log = TRUE))) warnings() try( # needs improvement in R [or a local workaround] besselIasym(10000*(1+1i), nu=200, k.max=20, log=TRUE) ) # Error in log1p(-d) : unimplemented complex functionx <- c(1:10, 20, 50, 100^(2:10)) nu <- c(1, 10, 20, 50, 100) r <- lapply(c(0:4,10,20), function(k.) sapply(nu, function(n.) besselIasym(x, nu=n., k.max = k., log = TRUE))) warnings() try( # needs improvement in R [or a local workaround] besselIasym(10000*(1+1i), nu=200, k.max=20, log=TRUE) ) # Error in log1p(-d) : unimplemented complex function
Computes the modified Bessel function, using one of its basic
definitions as an infinite series, e.g. A. & S., p.360, (9.1.10). The
implementation is pure R, working for numeric,
complex, but also e.g., for objects of class
"mpfr" from package Rmpfr.
besselJs(x, nu, nterm = 800, log = FALSE, use.log = log || any(nu * log(x/2) > .Machine$double.max.exp * log(2)), Ceps = if(isNum) 8e-16 else 2^(- [email protected][[1]]@prec))besselJs(x, nu, nterm = 800, log = FALSE, use.log = log || any(nu * log(x/2) > .Machine$double.max.exp * log(2)), Ceps = if(isNum) 8e-16 else 2^(- x.@.Data[[1]]@prec))
x |
numeric or complex vector, or of another |
nu |
non-negative numeric (scalar). |
nterm |
integer indicating the number of terms to be used.
Should be in the order of |
log |
logical indicating if the logarithm |
use.log |
logical indicating if the logarithm should work in
logarithmic scale notably even when |
Ceps |
a relative error tolerance for checking if |
a “numeric” (or complex or "mpfr")
vector of the same class and length as x.
Martin Maechler
Abramowitz, M., and Stegun, I. A. (1964–1972). Handbook of mathematical functions (NBS AMS series 55, U.S. Dept. of Commerce). https://personal.math.ubc.ca/~cbm/aands/page_360.htm
This package BesselJ(), base besselJ(), etc
stopifnot(all.equal(besselJs(1:10, 1), # our R code --> 4 warnings, for x = 4:7 besselJ (1:10, 1)))# internal C code w/ different algorithm ## Large 'nu' ... x <- (0:20)/4 if(interactive()) op <- options(nwarnings = 999) (bx <- besselJ(x, nu=200))# base R's -- gives 19 (mostly wrong) warnings about precision lost ## Visualize: bj <- curve(besselJ(1, x), .001, 2^10, log="xy", n=1001, main=quote(J[nu](1)), xlab = quote(nu), xaxt="n", yaxt="n") # 50+ warnings eaxis <- if(!requireNamespace("sfsmisc")) axis else sfsmisc::eaxis eaxis(1, sub10 = c(-2,3)); eaxis(2) abline(h = (J01 <- besselJ(1, 0)), col=adjustcolor(2, 1/2)) axis(4, at=J01, quote(J[0](1)), las=2, col=2, col.axis=2, line = -1) bj6 <- curve(besselJ(6, x), add=TRUE, n=1001, col=adjustcolor(2, 1/2), lwd=2) plot(y~x, as.data.frame(bj6), log="x", type="l", col=2, lwd=2, main = quote(J[nu](6)), xlab = quote(nu), xaxt="n") eaxis(1, sub10=3); abline(h=0, lty=3) ## large nu (non-log case) -- gave NaN wrongly (typically when it should have given 0): stopifnot(exprs = { besselJs(c(.1, 1:10), 999) == 0 besselJs(c(.1, 1:10), 9999) == 0 besselJs(c(.1, 1:1000), 1e10) == 0 ## besselJs(c(.1, 1:10), 1e20) == 0 -- another *BUG*: Error ... lssum found non-positive sums }) if(require("Rmpfr")) { ## Use high precision, notably large exponent range, numbers: Bx <- besselJs(mpfr(x, 64), nu=200) all.equal(Bx, bx, tol = 1e-15)# TRUE -- warnings were mostly wrong; specifically: cbind(bx, Bx) signif(asNumeric(1 - (bx/Bx)[19:21]), 4) # only [19] had lost accuracy ## With*out* mpfr numbers -- using log -- is accurate (here) lbx <- besselJs( x, nu=200, log=TRUE) lBx <- besselJs(mpfr(x, 64), nu=200, log=TRUE) cbind(x, lbx, lBx) stopifnot(all.equal(asNumeric(log(Bx)), lbx, tol=1e-15), all.equal(lBx, lbx, tol=4e-16)) } # Rmpfr if(interactive()) options(op) # reset 'nwarnings'stopifnot(all.equal(besselJs(1:10, 1), # our R code --> 4 warnings, for x = 4:7 besselJ (1:10, 1)))# internal C code w/ different algorithm ## Large 'nu' ... x <- (0:20)/4 if(interactive()) op <- options(nwarnings = 999) (bx <- besselJ(x, nu=200))# base R's -- gives 19 (mostly wrong) warnings about precision lost ## Visualize: bj <- curve(besselJ(1, x), .001, 2^10, log="xy", n=1001, main=quote(J[nu](1)), xlab = quote(nu), xaxt="n", yaxt="n") # 50+ warnings eaxis <- if(!requireNamespace("sfsmisc")) axis else sfsmisc::eaxis eaxis(1, sub10 = c(-2,3)); eaxis(2) abline(h = (J01 <- besselJ(1, 0)), col=adjustcolor(2, 1/2)) axis(4, at=J01, quote(J[0](1)), las=2, col=2, col.axis=2, line = -1) bj6 <- curve(besselJ(6, x), add=TRUE, n=1001, col=adjustcolor(2, 1/2), lwd=2) plot(y~x, as.data.frame(bj6), log="x", type="l", col=2, lwd=2, main = quote(J[nu](6)), xlab = quote(nu), xaxt="n") eaxis(1, sub10=3); abline(h=0, lty=3) ## large nu (non-log case) -- gave NaN wrongly (typically when it should have given 0): stopifnot(exprs = { besselJs(c(.1, 1:10), 999) == 0 besselJs(c(.1, 1:10), 9999) == 0 besselJs(c(.1, 1:1000), 1e10) == 0 ## besselJs(c(.1, 1:10), 1e20) == 0 -- another *BUG*: Error ... lssum found non-positive sums }) if(require("Rmpfr")) { ## Use high precision, notably large exponent range, numbers: Bx <- besselJs(mpfr(x, 64), nu=200) all.equal(Bx, bx, tol = 1e-15)# TRUE -- warnings were mostly wrong; specifically: cbind(bx, Bx) signif(asNumeric(1 - (bx/Bx)[19:21]), 4) # only [19] had lost accuracy ## With*out* mpfr numbers -- using log -- is accurate (here) lbx <- besselJs( x, nu=200, log=TRUE) lBx <- besselJs(mpfr(x, 64), nu=200, log=TRUE) cbind(x, lbx, lBx) stopifnot(all.equal(asNumeric(log(Bx)), lbx, tol=1e-15), all.equal(lBx, lbx, tol=4e-16)) } # Rmpfr if(interactive()) options(op) # reset 'nwarnings'
for Very Small Order
Use a few terms Taylor approximation for Bessel's for
fixed and .
bessJsmlNu(x, nu, nTrms, m.max)bessJsmlNu(x, nu, nTrms, m.max)
x, nu
|
numeric Bessel J() arguments, see e.g., |
nTrms |
integer, one of 0, 1, or 2, denoting the number of Taylor
series terms to be used, |
m.max |
positive integer specifying the Taylor series order used. |
A. & S. (9.1.10) p. 360 (Ascending Series) (9.1.64) p. 362 (derivative wrt order)
............ show derivation? ...........
The function explicitly calls Vectorize(<fn>, c("x", "nu")),
hence a numeric vector conforming to (e.g., ) x + nu.
Martin Maechler
Abramowitz, M., and Stegun, I. A. (1964–1972). Handbook of mathematical functions (NBS AMS series 55, U.S. Dept. of Commerce). https://personal.math.ubc.ca/~cbm/aands/page_360.htm
This package's BesselJ, base R's besselJ
(nuSml <- 2^-c(seq(30, 53, by=1/2), 75, 100, 300, 800, 1022, 1074, Inf)) ## 9.3e-10 6.6e-10 ... 1.1e-16 .. 2.22e-308 4.94e-324 0.0 ## bug in R (at least up to Jan. 1 2026) : options(digits = 14) # show more digits Jsml <- sapply(nuSml, function(nu) besselJ(pi/2, nu)) tail(cbind(nuSml, Jsml), 20) # complete "divergence" for nu <~ 10^-{16} Jsm.nT.1 <- bessJsmlNu(pi/2, nuSml, nTrms = 1, m.max = 30) Jsm.nT.2 <- bessJsmlNu(pi/2, nuSml, nTrms = 2, m.max = 30) cbind(nuSml, Jsml, Jsm.nT.1, Jsm.nT.2) table(Jsm.nT.2 == Jsm.nT.1) # all TRUE (really ???) all.equal(Jsm.nT.2, Jsm.nT.1, tolerance = 0) # show stopifnot(all.equal(Jsm.nT.2, Jsm.nT.1, tolerance = 4e-16)) ## second example; nu *not* very small x. <- 2 nu2 <- 2^-seq(1, 30, by = 1/8) Jsm2.1 <- bessJsmlNu(x., nu2, nTrms = 1, m.max = 40) Jsm2.2 <- bessJsmlNu(x., nu2, nTrms = 2, m.max = 40) table(Jsm2.2 == Jsm2.1) # now they *do* differ bessJ2 <- besselJ(x., nu2) BessJ2 <- sapply(nu2, function(nu) BesselJ(x., nu)) ## J() function values: --------------------- matplot(nu2, cbind(bessJ2, BessJ2, Jsm2.1, Jsm2.2), log = "x", type = "l") sum(i <- nu2 < 1e-4) stopifnot(all.equal(BessJ2[i], Jsm2.2[i], tolerance = 4e-14), all.equal(BessJ2[i], Jsm2.1[i], tolerance = 4e-9)) ## |error| wrt BesselJ() --------------- matplot(nu2, abs(cbind(bessJ2, Jsm2.1, Jsm2.2) - BessJ2), log = "xy", type = "l", ylim = c(5e-17, 1e-10), xlab = quote("nu" == nu), xaxt = "n", yaxt = "n", main = substitute(list(abs("<bess J>"(x, nu) - BesselJ(x, nu)), x == XX), list(XX = formatC(x.)))) eaxis <- if(!requireNamespace("sfsmisc")) axis else sfsmisc::eaxis eaxis(1, sub10 = c(-2,0)); eaxis(2) legend("topleft", lty = 1:5, lwd = 2, col = 1:6, bty = "n", legend = paste0(c("besselJ(", paste0("bessJsmlNu(*, nTrms = ",1:2)), ")"))(nuSml <- 2^-c(seq(30, 53, by=1/2), 75, 100, 300, 800, 1022, 1074, Inf)) ## 9.3e-10 6.6e-10 ... 1.1e-16 .. 2.22e-308 4.94e-324 0.0 ## bug in R (at least up to Jan. 1 2026) : options(digits = 14) # show more digits Jsml <- sapply(nuSml, function(nu) besselJ(pi/2, nu)) tail(cbind(nuSml, Jsml), 20) # complete "divergence" for nu <~ 10^-{16} Jsm.nT.1 <- bessJsmlNu(pi/2, nuSml, nTrms = 1, m.max = 30) Jsm.nT.2 <- bessJsmlNu(pi/2, nuSml, nTrms = 2, m.max = 30) cbind(nuSml, Jsml, Jsm.nT.1, Jsm.nT.2) table(Jsm.nT.2 == Jsm.nT.1) # all TRUE (really ???) all.equal(Jsm.nT.2, Jsm.nT.1, tolerance = 0) # show stopifnot(all.equal(Jsm.nT.2, Jsm.nT.1, tolerance = 4e-16)) ## second example; nu *not* very small x. <- 2 nu2 <- 2^-seq(1, 30, by = 1/8) Jsm2.1 <- bessJsmlNu(x., nu2, nTrms = 1, m.max = 40) Jsm2.2 <- bessJsmlNu(x., nu2, nTrms = 2, m.max = 40) table(Jsm2.2 == Jsm2.1) # now they *do* differ bessJ2 <- besselJ(x., nu2) BessJ2 <- sapply(nu2, function(nu) BesselJ(x., nu)) ## J() function values: --------------------- matplot(nu2, cbind(bessJ2, BessJ2, Jsm2.1, Jsm2.2), log = "x", type = "l") sum(i <- nu2 < 1e-4) stopifnot(all.equal(BessJ2[i], Jsm2.2[i], tolerance = 4e-14), all.equal(BessJ2[i], Jsm2.1[i], tolerance = 4e-9)) ## |error| wrt BesselJ() --------------- matplot(nu2, abs(cbind(bessJ2, Jsm2.1, Jsm2.2) - BessJ2), log = "xy", type = "l", ylim = c(5e-17, 1e-10), xlab = quote("nu" == nu), xaxt = "n", yaxt = "n", main = substitute(list(abs("<bess J>"(x, nu) - BesselJ(x, nu)), x == XX), list(XX = formatC(x.)))) eaxis <- if(!requireNamespace("sfsmisc")) axis else sfsmisc::eaxis eaxis(1, sub10 = c(-2,0)); eaxis(2) legend("topleft", lty = 1:5, lwd = 2, col = 1:6, bty = "n", legend = paste0(c("besselJ(", paste0("bessJsmlNu(*, nTrms = ",1:2)), ")"))
Computes the modified Bessel function, using one of its basic
definitions as an infinite series. The implementation is pure R,
working for numeric, complex, but also
e.g., for objects of class "mpfr"
from package Rmpfr.
besselIs(x, nu, nterm = 800, expon.scaled = FALSE, log = FALSE, Ceps = if (isNum) 8e-16 else 2^([email protected][[1]]@prec))besselIs(x, nu, nterm = 800, expon.scaled = FALSE, log = FALSE, Ceps = if (isNum) 8e-16 else 2^(-x@.Data[[1]]@prec))
x |
numeric or complex vector, or of another |
nu |
non-negative numeric (scalar). |
nterm |
integer indicating the number of terms to be used.
Should be in the order of |
expon.scaled |
logical indicating if the result should be scaled
by |
log |
logical indicating if the logarithm |
Ceps |
a relative error tolerance for checking if |
a “numeric” (or complex or "mpfr")
vector of the same class and length as x.
Martin Maechler
Abramowitz, M., and Stegun, I. A. (1964,.., 1972). Handbook of mathematical functions (NBS AMS series 55, U.S. Dept. of Commerce).
This package BesselI, base besselI, etc
(nus <- c(outer((0:3)/4, 1:5, `+`))) stopifnot( all.equal(besselIs(1:10, 1), # our R code besselI (1:10, 1)) # internal C code w/ different algorithm , sapply(nus, function(nu) all.equal(besselIs(1:10, nu, expon.scale=TRUE), # our R code BesselI (1:10, nu, expon.scale=TRUE)) # TOMS644 code ) , ## complex argument [gives warnings 'nterm=800' may be too small] sapply(nus, function(nu) all.equal(besselIs((1:10)*(1+1i), nu, expon.scale=TRUE), # our R code BesselI ((1:10)*(1+1i), nu, expon.scale=TRUE)) # TOMS644 code ) ) ## Large 'nu' ... x <- (0:20)/4 (bx <- besselI(x, nu=200))# base R's -- gives (mostly wrong) warnings if(require("Rmpfr")) { ## Use high precision (notably large exponent range) numbers: Bx <- besselIs(mpfr(x, 64), nu=200) all.equal(Bx, bx, tol = 1e-15)# TRUE -- warning were mostly wrong; specifically: cbind(bx, Bx) signif(asNumeric(1 - (bx/Bx)[19:21]), 4) # only [19] had lost accuracy ## With*out* mpfr numbers -- using log -- is accurate (here) (lbx <- besselIs( x, nu=200, log=TRUE)) lBx <- besselIs(mpfr(x, 64), nu=200, log=TRUE) stopifnot(all.equal(asNumeric(log(Bx)), lbx, tol=1e-15), all.equal(lBx, lbx, tol=4e-16)) } # Rmpfr(nus <- c(outer((0:3)/4, 1:5, `+`))) stopifnot( all.equal(besselIs(1:10, 1), # our R code besselI (1:10, 1)) # internal C code w/ different algorithm , sapply(nus, function(nu) all.equal(besselIs(1:10, nu, expon.scale=TRUE), # our R code BesselI (1:10, nu, expon.scale=TRUE)) # TOMS644 code ) , ## complex argument [gives warnings 'nterm=800' may be too small] sapply(nus, function(nu) all.equal(besselIs((1:10)*(1+1i), nu, expon.scale=TRUE), # our R code BesselI ((1:10)*(1+1i), nu, expon.scale=TRUE)) # TOMS644 code ) ) ## Large 'nu' ... x <- (0:20)/4 (bx <- besselI(x, nu=200))# base R's -- gives (mostly wrong) warnings if(require("Rmpfr")) { ## Use high precision (notably large exponent range) numbers: Bx <- besselIs(mpfr(x, 64), nu=200) all.equal(Bx, bx, tol = 1e-15)# TRUE -- warning were mostly wrong; specifically: cbind(bx, Bx) signif(asNumeric(1 - (bx/Bx)[19:21]), 4) # only [19] had lost accuracy ## With*out* mpfr numbers -- using log -- is accurate (here) (lbx <- besselIs( x, nu=200, log=TRUE)) lBx <- besselIs(mpfr(x, 64), nu=200, log=TRUE) stopifnot(all.equal(asNumeric(log(Bx)), lbx, tol=1e-15), all.equal(lBx, lbx, tol=4e-16)) } # Rmpfr