summaryrefslogtreecommitdiff
path: root/src/main/java/org/apache/commons/math3/special/BesselJ.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/apache/commons/math3/special/BesselJ.java')
-rw-r--r--src/main/java/org/apache/commons/math3/special/BesselJ.java661
1 files changed, 661 insertions, 0 deletions
diff --git a/src/main/java/org/apache/commons/math3/special/BesselJ.java b/src/main/java/org/apache/commons/math3/special/BesselJ.java
new file mode 100644
index 0000000..61aa4ff
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/special/BesselJ.java
@@ -0,0 +1,661 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.special;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * This class provides computation methods related to Bessel functions of the first kind. Detailed
+ * descriptions of these functions are available in <a
+ * href="http://en.wikipedia.org/wiki/Bessel_function">Wikipedia</a>, <a
+ * href="http://en.wikipedia.org/wiki/Abramowitz_and_Stegun">Abrabowitz and Stegun</a> (Ch. 9-11),
+ * and <a href="http://dlmf.nist.gov/">DLMF</a> (Ch. 10).
+ *
+ * <p>This implementation is based on the rjbesl Fortran routine at <a
+ * href="http://www.netlib.org/specfun/rjbesl">Netlib</a>.
+ *
+ * <p>From the Fortran code:
+ *
+ * <p>This program is based on a program written by David J. Sookne (2) that computes values of the
+ * Bessel functions J or I of real argument and integer order. Modifications include the restriction
+ * of the computation to the J Bessel function of non-negative real argument, the extension of the
+ * computation to arbitrary positive order, and the elimination of most underflow.
+ *
+ * <p>References:
+ *
+ * <ul>
+ * <li>"A Note on Backward Recurrence Algorithms," Olver, F. W. J., and Sookne, D. J., Math. Comp.
+ * 26, 1972, pp 941-947.
+ * <li>"Bessel Functions of Real Argument and Integer Order," Sookne, D. J., NBS Jour. of Res. B.
+ * 77B, 1973, pp 125-132.
+ * </ul>
+ *
+ * @since 3.4
+ */
+public class BesselJ implements UnivariateFunction {
+
+ // ---------------------------------------------------------------------
+ // Mathematical constants
+ // ---------------------------------------------------------------------
+
+ /** -2 / pi */
+ private static final double PI2 = 0.636619772367581343075535;
+
+ /** first few significant digits of 2pi */
+ private static final double TOWPI1 = 6.28125;
+
+ /** 2pi - TWOPI1 to working precision */
+ private static final double TWOPI2 = 1.935307179586476925286767e-3;
+
+ /** TOWPI1 + TWOPI2 */
+ private static final double TWOPI = TOWPI1 + TWOPI2;
+
+ // ---------------------------------------------------------------------
+ // Machine-dependent parameters
+ // ---------------------------------------------------------------------
+
+ /**
+ * 10.0^K, where K is the largest integer such that ENTEN is machine-representable in working
+ * precision
+ */
+ private static final double ENTEN = 1.0e308;
+
+ /**
+ * Decimal significance desired. Should be set to (INT(log_{10}(2) * (it)+1)). Setting NSIG
+ * lower will result in decreased accuracy while setting NSIG higher will increase CPU time
+ * without increasing accuracy. The truncation error is limited to a relative error of
+ * T=.5(10^(-NSIG)).
+ */
+ private static final double ENSIG = 1.0e16;
+
+ /** 10.0 ** (-K) for the smallest integer K such that K >= NSIG/4 */
+ private static final double RTNSIG = 1.0e-4;
+
+ /** Smallest ABS(X) such that X/4 does not underflow */
+ private static final double ENMTEN = 8.90e-308;
+
+ /** Minimum acceptable value for x */
+ private static final double X_MIN = 0.0;
+
+ /**
+ * Upper limit on the magnitude of x. If abs(x) = n, then at least n iterations of the backward
+ * recursion will be executed. The value of 10.0 ** 4 is used on every machine.
+ */
+ private static final double X_MAX = 1.0e4;
+
+ /** First 25 factorials as doubles */
+ private static final double[] FACT = {
+ 1.0,
+ 1.0,
+ 2.0,
+ 6.0,
+ 24.0,
+ 120.0,
+ 720.0,
+ 5040.0,
+ 40320.0,
+ 362880.0,
+ 3628800.0,
+ 39916800.0,
+ 479001600.0,
+ 6227020800.0,
+ 87178291200.0,
+ 1.307674368e12,
+ 2.0922789888e13,
+ 3.55687428096e14,
+ 6.402373705728e15,
+ 1.21645100408832e17,
+ 2.43290200817664e18,
+ 5.109094217170944e19,
+ 1.12400072777760768e21,
+ 2.585201673888497664e22,
+ 6.2044840173323943936e23
+ };
+
+ /** Order of the function computed when {@link #value(double)} is used */
+ private final double order;
+
+ /**
+ * Create a new BesselJ with the given order.
+ *
+ * @param order order of the function computed when using {@link #value(double)}.
+ */
+ public BesselJ(double order) {
+ this.order = order;
+ }
+
+ /**
+ * Returns the value of the constructed Bessel function of the first kind, for the passed
+ * argument.
+ *
+ * @param x Argument
+ * @return Value of the Bessel function at x
+ * @throws MathIllegalArgumentException if {@code x} is too large relative to {@code order}
+ * @throws ConvergenceException if the algorithm fails to converge
+ */
+ public double value(double x) throws MathIllegalArgumentException, ConvergenceException {
+ return BesselJ.value(order, x);
+ }
+
+ /**
+ * Returns the first Bessel function, \(J_{order}(x)\).
+ *
+ * @param order Order of the Bessel function
+ * @param x Argument
+ * @return Value of the Bessel function of the first kind, \(J_{order}(x)\)
+ * @throws MathIllegalArgumentException if {@code x} is too large relative to {@code order}
+ * @throws ConvergenceException if the algorithm fails to converge
+ */
+ public static double value(double order, double x)
+ throws MathIllegalArgumentException, ConvergenceException {
+ final int n = (int) order;
+ final double alpha = order - n;
+ final int nb = n + 1;
+ final BesselJResult res = rjBesl(x, alpha, nb);
+
+ if (res.nVals >= nb) {
+ return res.vals[n];
+ } else if (res.nVals < 0) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.BESSEL_FUNCTION_BAD_ARGUMENT, order, x);
+ } else if (FastMath.abs(res.vals[res.nVals - 1]) < 1e-100) {
+ return res.vals[n]; // underflow; return value (will be zero)
+ }
+ throw new ConvergenceException(
+ LocalizedFormats.BESSEL_FUNCTION_FAILED_CONVERGENCE, order, x);
+ }
+
+ /**
+ * Encapsulates the results returned by {@link BesselJ#rjBesl(double, double, int)}.
+ *
+ * <p>{@link #getVals()} returns the computed function values. {@link #getnVals()} is the number
+ * of values among those returned by {@link #getnVals()} that can be considered accurate.
+ *
+ * <p>
+ *
+ * <ul>
+ * <li>nVals < 0: An argument is out of range. For example, nb <= 0, alpha < 0 or > 1, or x is
+ * too large. In this case, b(0) is set to zero, the remainder of the b-vector is not
+ * calculated, and nVals is set to MIN(nb,0) - 1 so that nVals != nb.
+ * <li>nb > nVals > 0: Not all requested function values could be calculated accurately. This
+ * usually occurs because nb is much larger than abs(x). In this case, b(n) is calculated
+ * to the desired accuracy for n < nVals, but precision is lost for nVals < n <= nb. If
+ * b(n) does not vanish for n > nVals (because it is too small to be represented), and
+ * b(n)/b(nVals) = \(10^{-k}\), then only the first NSIG-k significant figures of b(n) can
+ * be trusted.
+ * </ul>
+ */
+ public static class BesselJResult {
+
+ /** Bessel function values */
+ private final double[] vals;
+
+ /** Valid value count */
+ private final int nVals;
+
+ /**
+ * Create a new BesselJResult with the given values and valid value count.
+ *
+ * @param b values
+ * @param n count of valid values
+ */
+ public BesselJResult(double[] b, int n) {
+ vals = MathArrays.copyOf(b, b.length);
+ nVals = n;
+ }
+
+ /**
+ * @return the computed function values
+ */
+ public double[] getVals() {
+ return MathArrays.copyOf(vals, vals.length);
+ }
+
+ /**
+ * @return the number of valid function values (normally the same as the length of the array
+ * returned by {@link #getnVals()})
+ */
+ public int getnVals() {
+ return nVals;
+ }
+ }
+
+ /**
+ * Calculates Bessel functions \(J_{n+alpha}(x)\) for non-negative argument x, and non-negative
+ * order n + alpha.
+ *
+ * <p>Before using the output vector, the user should check that nVals = nb, i.e., all orders
+ * have been calculated to the desired accuracy. See BesselResult class javadoc for details on
+ * return values.
+ *
+ * @param x non-negative real argument for which J's are to be calculated
+ * @param alpha fractional part of order for which J's or exponentially scaled J's (\(J\cdot
+ * e^{x}\)) are to be calculated. 0 <= alpha < 1.0.
+ * @param nb integer number of functions to be calculated, nb > 0. The first function calculated
+ * is of order alpha, and the last is of order nb - 1 + alpha.
+ * @return BesselJResult a vector of the functions \(J_{alpha}(x)\) through
+ * \(J_{nb-1+alpha}(x)\), or the corresponding exponentially scaled functions and an integer
+ * output variable indicating possible errors
+ */
+ public static BesselJResult rjBesl(double x, double alpha, int nb) {
+ final double[] b = new double[nb];
+
+ int ncalc = 0;
+ double alpem = 0;
+ double alp2em = 0;
+
+ // ---------------------------------------------------------------------
+ // Check for out of range arguments.
+ // ---------------------------------------------------------------------
+ final int magx = (int) x;
+ if ((nb > 0) && (x >= X_MIN) && (x <= X_MAX) && (alpha >= 0) && (alpha < 1)) {
+ // ---------------------------------------------------------------------
+ // Initialize result array to zero.
+ // ---------------------------------------------------------------------
+ ncalc = nb;
+ for (int i = 0; i < nb; ++i) {
+ b[i] = 0;
+ }
+
+ // ---------------------------------------------------------------------
+ // Branch to use 2-term ascending series for small X and asymptotic
+ // form for large X when NB is not too large.
+ // ---------------------------------------------------------------------
+ double tempa;
+ double tempb;
+ double tempc;
+ double tover;
+ if (x < RTNSIG) {
+ // ---------------------------------------------------------------------
+ // Two-term ascending series for small X.
+ // ---------------------------------------------------------------------
+ tempa = 1;
+ alpem = 1 + alpha;
+ double halfx = 0;
+ if (x > ENMTEN) {
+ halfx = 0.5 * x;
+ }
+ if (alpha != 0) {
+ tempa = FastMath.pow(halfx, alpha) / (alpha * Gamma.gamma(alpha));
+ }
+ tempb = 0;
+ if (x + 1 > 1) {
+ tempb = -halfx * halfx;
+ }
+ b[0] = tempa + (tempa * tempb / alpem);
+ if ((x != 0) && (b[0] == 0)) {
+ ncalc = 0;
+ }
+ if (nb != 1) {
+ if (x <= 0) {
+ for (int n = 1; n < nb; ++n) {
+ b[n] = 0;
+ }
+ } else {
+ // ---------------------------------------------------------------------
+ // Calculate higher order functions.
+ // ---------------------------------------------------------------------
+ tempc = halfx;
+ tover = tempb != 0 ? ENMTEN / tempb : 2 * ENMTEN / x;
+ for (int n = 1; n < nb; ++n) {
+ tempa /= alpem;
+ alpem += 1;
+ tempa *= tempc;
+ if (tempa <= tover * alpem) {
+ tempa = 0;
+ }
+ b[n] = tempa + (tempa * tempb / alpem);
+ if ((b[n] == 0) && (ncalc > n)) {
+ ncalc = n;
+ }
+ }
+ }
+ }
+ } else if ((x > 25.0) && (nb <= magx + 1)) {
+ // ---------------------------------------------------------------------
+ // Asymptotic series for X > 25
+ // ---------------------------------------------------------------------
+ final double xc = FastMath.sqrt(PI2 / x);
+ final double mul = 0.125 / x;
+ final double xin = mul * mul;
+ int m = 0;
+ if (x >= 130.0) {
+ m = 4;
+ } else if (x >= 35.0) {
+ m = 8;
+ } else {
+ m = 11;
+ }
+
+ final double xm = 4.0 * m;
+ // ---------------------------------------------------------------------
+ // Argument reduction for SIN and COS routines.
+ // ---------------------------------------------------------------------
+ double t = (double) ((int) ((x / TWOPI) + 0.5));
+ final double z = x - t * TOWPI1 - t * TWOPI2 - (alpha + 0.5) / PI2;
+ double vsin = FastMath.sin(z);
+ double vcos = FastMath.cos(z);
+ double gnu = 2 * alpha;
+ double capq;
+ double capp;
+ double s;
+ double t1;
+ double xk;
+ for (int i = 1; i <= 2; i++) {
+ s = (xm - 1 - gnu) * (xm - 1 + gnu) * xin * 0.5;
+ t = (gnu - (xm - 3.0)) * (gnu + (xm - 3.0));
+ capp = (s * t) / FACT[2 * m];
+ t1 = (gnu - (xm + 1)) * (gnu + (xm + 1));
+ capq = (s * t1) / FACT[2 * m + 1];
+ xk = xm;
+ int k = 2 * m;
+ t1 = t;
+
+ for (int j = 2; j <= m; j++) {
+ xk -= 4.0;
+ s = (xk - 1 - gnu) * (xk - 1 + gnu);
+ t = (gnu - (xk - 3.0)) * (gnu + (xk - 3.0));
+ capp = (capp + 1 / FACT[k - 2]) * s * t * xin;
+ capq = (capq + 1 / FACT[k - 1]) * s * t1 * xin;
+ k -= 2;
+ t1 = t;
+ }
+
+ capp += 1;
+ capq = (capq + 1) * ((gnu * gnu) - 1) * (0.125 / x);
+ b[i - 1] = xc * (capp * vcos - capq * vsin);
+ if (nb == 1) {
+ return new BesselJResult(MathArrays.copyOf(b, b.length), ncalc);
+ }
+ t = vsin;
+ vsin = -vcos;
+ vcos = t;
+ gnu += 2.0;
+ }
+
+ // ---------------------------------------------------------------------
+ // If NB > 2, compute J(X,ORDER+I) I = 2, NB-1
+ // ---------------------------------------------------------------------
+ if (nb > 2) {
+ gnu = 2 * alpha + 2.0;
+ for (int j = 2; j < nb; ++j) {
+ b[j] = gnu * b[j - 1] / x - b[j - 2];
+ gnu += 2.0;
+ }
+ }
+ } else {
+ // ---------------------------------------------------------------------
+ // Use recurrence to generate results. First initialize the
+ // calculation of P*S.
+ // ---------------------------------------------------------------------
+ final int nbmx = nb - magx;
+ int n = magx + 1;
+ int nstart = 0;
+ int nend = 0;
+ double en = 2 * (n + alpha);
+ double plast = 1;
+ double p = en / x;
+ double pold;
+ // ---------------------------------------------------------------------
+ // Calculate general significance test.
+ // ---------------------------------------------------------------------
+ double test = 2 * ENSIG;
+ boolean readyToInitialize = false;
+ if (nbmx >= 3) {
+ // ---------------------------------------------------------------------
+ // Calculate P*S until N = NB-1. Check for possible
+ // overflow.
+ // ---------------------------------------------------------------------
+ tover = ENTEN / ENSIG;
+ nstart = magx + 2;
+ nend = nb - 1;
+ en = 2 * (nstart - 1 + alpha);
+ double psave;
+ double psavel;
+ for (int k = nstart; k <= nend; k++) {
+ n = k;
+ en += 2.0;
+ pold = plast;
+ plast = p;
+ p = (en * plast / x) - pold;
+ if (p > tover) {
+ // ---------------------------------------------------------------------
+ // To avoid overflow, divide P*S by TOVER. Calculate
+ // P*S until
+ // ABS(P) > 1.
+ // ---------------------------------------------------------------------
+ tover = ENTEN;
+ p /= tover;
+ plast /= tover;
+ psave = p;
+ psavel = plast;
+ nstart = n + 1;
+ do {
+ n += 1;
+ en += 2.0;
+ pold = plast;
+ plast = p;
+ p = (en * plast / x) - pold;
+ } while (p <= 1);
+ tempb = en / x;
+ // ---------------------------------------------------------------------
+ // Calculate backward test and find NCALC, the
+ // highest N such that
+ // the test is passed.
+ // ---------------------------------------------------------------------
+ test = pold * plast * (0.5 - 0.5 / (tempb * tempb));
+ test /= ENSIG;
+ p = plast * tover;
+ n -= 1;
+ en -= 2.0;
+ nend = FastMath.min(nb, n);
+ for (int l = nstart; l <= nend; l++) {
+ pold = psavel;
+ psavel = psave;
+ psave = (en * psavel / x) - pold;
+ if (psave * psavel > test) {
+ ncalc = l - 1;
+ readyToInitialize = true;
+ break;
+ }
+ }
+ ncalc = nend;
+ readyToInitialize = true;
+ break;
+ }
+ }
+ if (!readyToInitialize) {
+ n = nend;
+ en = 2 * (n + alpha);
+ // ---------------------------------------------------------------------
+ // Calculate special significance test for NBMX > 2.
+ // ---------------------------------------------------------------------
+ test =
+ FastMath.max(
+ test, FastMath.sqrt(plast * ENSIG) * FastMath.sqrt(2 * p));
+ }
+ }
+ // ---------------------------------------------------------------------
+ // Calculate P*S until significance test passes.
+ // ---------------------------------------------------------------------
+ if (!readyToInitialize) {
+ do {
+ n += 1;
+ en += 2.0;
+ pold = plast;
+ plast = p;
+ p = (en * plast / x) - pold;
+ } while (p < test);
+ }
+ // ---------------------------------------------------------------------
+ // Initialize the backward recursion and the normalization sum.
+ // ---------------------------------------------------------------------
+ n += 1;
+ en += 2.0;
+ tempb = 0;
+ tempa = 1 / p;
+ int m = (2 * n) - 4 * (n / 2);
+ double sum = 0;
+ double em = (double) (n / 2);
+ alpem = em - 1 + alpha;
+ alp2em = 2 * em + alpha;
+ if (m != 0) {
+ sum = tempa * alpem * alp2em / em;
+ }
+ nend = n - nb;
+
+ boolean readyToNormalize = false;
+ boolean calculatedB0 = false;
+
+ // ---------------------------------------------------------------------
+ // Recur backward via difference equation, calculating (but not
+ // storing) B(N), until N = NB.
+ // ---------------------------------------------------------------------
+ for (int l = 1; l <= nend; l++) {
+ n -= 1;
+ en -= 2.0;
+ tempc = tempb;
+ tempb = tempa;
+ tempa = (en * tempb / x) - tempc;
+ m = 2 - m;
+ if (m != 0) {
+ em -= 1;
+ alp2em = 2 * em + alpha;
+ if (n == 1) {
+ break;
+ }
+ alpem = em - 1 + alpha;
+ if (alpem == 0) {
+ alpem = 1;
+ }
+ sum = (sum + tempa * alp2em) * alpem / em;
+ }
+ }
+
+ // ---------------------------------------------------------------------
+ // Store B(NB).
+ // ---------------------------------------------------------------------
+ b[n - 1] = tempa;
+ if (nend >= 0) {
+ if (nb <= 1) {
+ alp2em = alpha;
+ if (alpha + 1 == 1) {
+ alp2em = 1;
+ }
+ sum += b[0] * alp2em;
+ readyToNormalize = true;
+ } else {
+ // ---------------------------------------------------------------------
+ // Calculate and store B(NB-1).
+ // ---------------------------------------------------------------------
+ n -= 1;
+ en -= 2.0;
+ b[n - 1] = (en * tempa / x) - tempb;
+ if (n == 1) {
+ calculatedB0 = true;
+ } else {
+ m = 2 - m;
+ if (m != 0) {
+ em -= 1;
+ alp2em = 2 * em + alpha;
+ alpem = em - 1 + alpha;
+ if (alpem == 0) {
+ alpem = 1;
+ }
+
+ sum = (sum + (b[n - 1] * alp2em)) * alpem / em;
+ }
+ }
+ }
+ }
+ if (!readyToNormalize && !calculatedB0) {
+ nend = n - 2;
+ if (nend != 0) {
+ // ---------------------------------------------------------------------
+ // Calculate via difference equation and store B(N),
+ // until N = 2.
+ // ---------------------------------------------------------------------
+
+ for (int l = 1; l <= nend; l++) {
+ n -= 1;
+ en -= 2.0;
+ b[n - 1] = (en * b[n] / x) - b[n + 1];
+ m = 2 - m;
+ if (m != 0) {
+ em -= 1;
+ alp2em = 2 * em + alpha;
+ alpem = em - 1 + alpha;
+ if (alpem == 0) {
+ alpem = 1;
+ }
+
+ sum = (sum + b[n - 1] * alp2em) * alpem / em;
+ }
+ }
+ }
+ }
+ // ---------------------------------------------------------------------
+ // Calculate b[0]
+ // ---------------------------------------------------------------------
+ if (!readyToNormalize) {
+ if (!calculatedB0) {
+ b[0] = 2.0 * (alpha + 1) * b[1] / x - b[2];
+ }
+ em -= 1;
+ alp2em = 2 * em + alpha;
+ if (alp2em == 0) {
+ alp2em = 1;
+ }
+ sum += b[0] * alp2em;
+ }
+ // ---------------------------------------------------------------------
+ // Normalize. Divide all B(N) by sum.
+ // ---------------------------------------------------------------------
+
+ if (FastMath.abs(alpha) > 1e-16) {
+ sum *= Gamma.gamma(alpha) * FastMath.pow(x * 0.5, -alpha);
+ }
+ tempa = ENMTEN;
+ if (sum > 1) {
+ tempa *= sum;
+ }
+
+ for (n = 0; n < nb; n++) {
+ if (FastMath.abs(b[n]) < tempa) {
+ b[n] = 0;
+ }
+ b[n] /= sum;
+ }
+ }
+ // ---------------------------------------------------------------------
+ // Error return -- X, NB, or ALPHA is out of range.
+ // ---------------------------------------------------------------------
+ } else {
+ if (b.length > 0) {
+ b[0] = 0;
+ }
+ ncalc = FastMath.min(nb, 0) - 1;
+ }
+ return new BesselJResult(MathArrays.copyOf(b, b.length), ncalc);
+ }
+}