1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.commons.math4.legacy.special; 19 20 import java.util.Arrays; 21 import org.apache.commons.numbers.gamma.Gamma; 22 import org.apache.commons.math4.legacy.analysis.UnivariateFunction; 23 import org.apache.commons.math4.legacy.exception.ConvergenceException; 24 import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException; 25 import org.apache.commons.math4.legacy.exception.util.LocalizedFormats; 26 import org.apache.commons.math4.core.jdkmath.JdkMath; 27 28 /** 29 * This class provides computation methods related to Bessel 30 * functions of the first kind. Detailed descriptions of these functions are 31 * available in <a 32 * href="http://en.wikipedia.org/wiki/Bessel_function">Wikipedia</a>, <a 33 * href="http://en.wikipedia.org/wiki/Abramowitz_and_Stegun">Abrabowitz and 34 * Stegun</a> (Ch. 9-11), and <a href="http://dlmf.nist.gov/">DLMF</a> (Ch. 10). 35 * <p> 36 * This implementation is based on the rjbesl Fortran routine at 37 * <a href="http://www.netlib.org/specfun/rjbesl">Netlib</a>.</p> 38 * <p> 39 * From the Fortran code: </p> 40 * <p> 41 * This program is based on a program written by David J. Sookne (2) that 42 * computes values of the Bessel functions J or I of real argument and integer 43 * order. Modifications include the restriction of the computation to the J 44 * Bessel function of non-negative real argument, the extension of the 45 * computation to arbitrary positive order, and the elimination of most 46 * underflow.</p> 47 * <p> 48 * References:</p> 49 * <ul> 50 * <li>"A Note on Backward Recurrence Algorithms," Olver, F. W. J., and Sookne, 51 * D. J., Math. Comp. 26, 1972, pp 941-947.</li> 52 * <li>"Bessel Functions of Real Argument and Integer Order," Sookne, D. J., NBS 53 * Jour. of Res. B. 77B, 1973, pp 125-132.</li> 54 * </ul> 55 * @since 3.4 56 */ 57 public class BesselJ 58 implements UnivariateFunction { 59 60 // --------------------------------------------------------------------- 61 // Mathematical constants 62 // --------------------------------------------------------------------- 63 64 /** -2 / pi. */ 65 private static final double PI2 = 0.636619772367581343075535; 66 67 /** first few significant digits of 2pi. */ 68 private static final double TOWPI1 = 6.28125; 69 70 /** 2pi - TWOPI1 to working precision. */ 71 private static final double TWOPI2 = 1.935307179586476925286767e-3; 72 73 /** TOWPI1 + TWOPI2. */ 74 private static final double TWOPI = TOWPI1 + TWOPI2; 75 76 // --------------------------------------------------------------------- 77 // Machine-dependent parameters 78 // --------------------------------------------------------------------- 79 80 /** 81 * 10.0^K, where K is the largest integer such that ENTEN is 82 * machine-representable in working precision. 83 */ 84 private static final double ENTEN = 1.0e308; 85 86 /** 87 * Decimal significance desired. Should be set to (INT(log_{10}(2) * (it)+1)). 88 * Setting NSIG lower will result in decreased accuracy while setting 89 * NSIG higher will increase CPU time without increasing accuracy. 90 * The truncation error is limited to a relative error of 91 * T=.5(10^(-NSIG)). 92 */ 93 private static final double ENSIG = 1.0e16; 94 95 /** 10.0 ** (-K) for the smallest integer K such that K >= NSIG/4. */ 96 private static final double RTNSIG = 1.0e-4; 97 98 /** Smallest ABS(X) such that X/4 does not underflow. */ 99 private static final double ENMTEN = 8.90e-308; 100 101 /** Minimum acceptable value for x. */ 102 private static final double X_MIN = 0.0; 103 104 /** 105 * Upper limit on the magnitude of x. If abs(x) = n, then at least 106 * n iterations of the backward recursion will be executed. The value of 107 * 10.0 ** 4 is used on every machine. 108 */ 109 private static final double X_MAX = 1.0e4; 110 111 /** First 25 factorials as doubles. */ 112 private static final double[] FACT = { 113 1.0, 1.0, 2.0, 6.0, 24.0, 120.0, 720.0, 5040.0, 40320.0, 362880.0, 114 3628800.0, 39916800.0, 479001600.0, 6227020800.0, 87178291200.0, 115 1.307674368e12, 2.0922789888e13, 3.55687428096e14, 6.402373705728e15, 116 1.21645100408832e17, 2.43290200817664e18, 5.109094217170944e19, 117 1.12400072777760768e21, 2.585201673888497664e22, 118 6.2044840173323943936e23 119 }; 120 121 /** Order of the function computed when {@link #value(double)} is used. */ 122 private final double order; 123 124 /** 125 * Create a new BesselJ with the given order. 126 * 127 * @param order order of the function computed when using {@link #value(double)}. 128 */ 129 public BesselJ(double order) { 130 this.order = order; 131 } 132 133 /** 134 * Returns the value of the constructed Bessel function of the first kind, 135 * for the passed argument. 136 * 137 * @param x Argument 138 * @return Value of the Bessel function at x 139 * @throws MathIllegalArgumentException if {@code x} is too large relative to {@code order} 140 * @throws ConvergenceException if the algorithm fails to converge 141 */ 142 @Override 143 public double value(double x) 144 throws MathIllegalArgumentException, ConvergenceException { 145 return BesselJ.value(order, x); 146 } 147 148 /** 149 * Returns the first Bessel function, \(J_{order}(x)\). 150 * 151 * @param order Order of the Bessel function 152 * @param x Argument 153 * @return Value of the Bessel function of the first kind, \(J_{order}(x)\) 154 * @throws MathIllegalArgumentException if {@code x} is too large relative to {@code order} 155 * @throws ConvergenceException if the algorithm fails to converge 156 */ 157 public static double value(double order, double x) 158 throws MathIllegalArgumentException, ConvergenceException { 159 final int n = (int) order; 160 final double alpha = order - n; 161 final int nb = n + 1; 162 final BesselJResult res = rjBesl(x, alpha, nb); 163 164 if (res.nVals >= nb) { 165 return res.vals[n]; 166 } else if (res.nVals < 0) { 167 throw new MathIllegalArgumentException(LocalizedFormats.BESSEL_FUNCTION_BAD_ARGUMENT,order, x); 168 } else if (JdkMath.abs(res.vals[res.nVals - 1]) < 1e-100) { 169 return res.vals[n]; // underflow; return value (will be zero) 170 } 171 throw new ConvergenceException(LocalizedFormats.BESSEL_FUNCTION_FAILED_CONVERGENCE, order, x); 172 } 173 174 /** 175 * Encapsulates the results returned by {@link BesselJ#rjBesl(double, double, int)}. 176 * <p> 177 * {@link #getVals()} returns the computed function values. 178 * {@link #getnVals()} is the number of values among those returned by {@link #getnVals()} 179 * that can be considered accurate. 180 * </p><ul> 181 * <li>{@code nVals < 0}: An argument is out of range. For example, {@code nb <= 0}, 182 * {@code alpha < 0 or > 1}, or x is too large. In this case, b(0) is set to zero, the 183 * remainder of the b-vector is not calculated, and nVals is set to 184 * MIN(nb,0) - 1 so that nVals != nb.</li> 185 * <li>{@code nb > nVals > 0}: Not all requested function values could be calculated 186 * accurately. This usually occurs because nb is much larger than abs(x). In 187 * this case, b(n) is calculated to the desired accuracy for {@code n < nVals}, but 188 * precision is lost for {@code nVals < n <= nb}. If b(n) does not vanish for 189 * {@code n > nVals} (because it is too small to be represented), and b(n)/b(nVals) = 190 * \(10^{-k}\), then only the first NSIG-k significant figures of b(n) can be 191 * trusted.</li></ul> 192 */ 193 public static class BesselJResult { 194 195 /** Bessel function values. */ 196 private final double[] vals; 197 198 /** Valid value count. */ 199 private final int nVals; 200 201 /** 202 * Create a new BesselJResult with the given values and valid value count. 203 * 204 * @param b values 205 * @param n count of valid values 206 */ 207 public BesselJResult(double[] b, int n) { 208 vals = Arrays.copyOf(b, b.length); 209 nVals = n; 210 } 211 212 /** 213 * @return the computed function values 214 */ 215 public double[] getVals() { 216 return Arrays.copyOf(vals, vals.length); 217 } 218 219 /** 220 * @return the number of valid function values (normally the same as the 221 * length of the array returned by {@link #getnVals()}) 222 */ 223 public int getnVals() { 224 return nVals; 225 } 226 } 227 228 /** 229 * Calculates Bessel functions \(J_{n+alpha}(x)\) for 230 * non-negative argument x, and non-negative order n + alpha. 231 * <p> 232 * Before using the output vector, the user should check that 233 * nVals = nb, i.e., all orders have been calculated to the desired accuracy. 234 * See BesselResult class javadoc for details on return values. 235 * </p> 236 * @param x non-negative real argument for which J's are to be calculated 237 * @param alpha fractional part of order for which J's or exponentially 238 * scaled J's (\(J\cdot e^{x}\)) are to be calculated. {@code 0 <= alpha < 1.0} 239 * @param nb integer number of functions to be calculated, {@code nb > 0}. The first 240 * function calculated is of order alpha, and the last is of order 241 * {@code nb - 1 + alpha}. 242 * @return BesselJResult a vector of the functions 243 * \(J_{alpha}(x)\) through \(J_{nb-1+alpha}(x)\), or the corresponding exponentially 244 * scaled functions and an integer output variable indicating possible errors 245 */ 246 public static BesselJResult rjBesl(double x, double alpha, int nb) { 247 final double[] b = new double[nb]; 248 249 int ncalc = 0; 250 double alpem = 0; 251 double alp2em = 0; 252 253 // --------------------------------------------------------------------- 254 // Check for out of range arguments. 255 // --------------------------------------------------------------------- 256 final int magx = (int) x; 257 if (nb > 0 && x >= X_MIN && x <= X_MAX && alpha >= 0 && alpha < 1) { 258 // --------------------------------------------------------------------- 259 // Initialize result array to zero. 260 // --------------------------------------------------------------------- 261 ncalc = nb; 262 for (int i = 0; i < nb; ++i) { 263 b[i] = 0; 264 } 265 266 // --------------------------------------------------------------------- 267 // Branch to use 2-term ascending series for small X and asymptotic 268 // form for large X when NB is not too large. 269 // --------------------------------------------------------------------- 270 double tempa; 271 double tempb; 272 double tempc; 273 double tover; 274 if (x < RTNSIG) { 275 // --------------------------------------------------------------------- 276 // Two-term ascending series for small X. 277 // --------------------------------------------------------------------- 278 tempa = 1; 279 alpem = 1 + alpha; 280 double halfx = 0; 281 if (x > ENMTEN) { 282 halfx = 0.5 * x; 283 } 284 if (alpha != 0) { 285 tempa = JdkMath.pow(halfx, alpha) / 286 (alpha * Gamma.value(alpha)); 287 } 288 tempb = 0; 289 if (x + 1 > 1) { 290 tempb = -halfx * halfx; 291 } 292 b[0] = tempa + (tempa * tempb / alpem); 293 if (x != 0 && b[0] == 0) { 294 ncalc = 0; 295 } 296 if (nb != 1) { 297 if (x <= 0) { 298 for (int n = 1; n < nb; ++n) { 299 b[n] = 0; 300 } 301 } else { 302 // --------------------------------------------------------------------- 303 // Calculate higher order functions. 304 // --------------------------------------------------------------------- 305 tempc = halfx; 306 tover = tempb != 0 ? ENMTEN / tempb : 2 * ENMTEN / x; 307 for (int n = 1; n < nb; ++n) { 308 tempa /= alpem; 309 alpem += 1; 310 tempa *= tempc; 311 if (tempa <= tover * alpem) { 312 tempa = 0; 313 } 314 b[n] = tempa + (tempa * tempb / alpem); 315 if (b[n] == 0 && ncalc > n) { 316 ncalc = n; 317 } 318 } 319 } 320 } 321 } else if (x > 25.0 && nb <= magx + 1) { 322 // --------------------------------------------------------------------- 323 // Asymptotic series for X > 25 324 // --------------------------------------------------------------------- 325 final double xc = JdkMath.sqrt(PI2 / x); 326 final double mul = 0.125 / x; 327 final double xin = mul * mul; 328 int m = 0; 329 if (x >= 130.0) { 330 m = 4; 331 } else if (x >= 35.0) { 332 m = 8; 333 } else { 334 m = 11; 335 } 336 337 final double xm = 4.0 * m; 338 // --------------------------------------------------------------------- 339 // Argument reduction for SIN and COS routines. 340 // --------------------------------------------------------------------- 341 double t = (double) ((int) ((x / TWOPI) + 0.5)); 342 final double z = x - t * TOWPI1 - t * TWOPI2 - (alpha + 0.5) / PI2; 343 double vsin = JdkMath.sin(z); 344 double vcos = JdkMath.cos(z); 345 double gnu = 2 * alpha; 346 double capq; 347 double capp; 348 double s; 349 double t1; 350 double xk; 351 for (int i = 1; i <= 2; i++) { 352 s = (xm - 1 - gnu) * (xm - 1 + gnu) * xin * 0.5; 353 t = (gnu - (xm - 3.0)) * (gnu + (xm - 3.0)); 354 capp = (s * t) / FACT[2 * m]; 355 t1 = (gnu - (xm + 1)) * (gnu + (xm + 1)); 356 capq = (s * t1) / FACT[2 * m + 1]; 357 xk = xm; 358 int k = 2 * m; 359 t1 = t; 360 361 for (int j = 2; j <= m; j++) { 362 xk -= 4.0; 363 s = (xk - 1 - gnu) * (xk - 1 + gnu); 364 t = (gnu - (xk - 3.0)) * (gnu + (xk - 3.0)); 365 capp = (capp + 1 / FACT[k - 2]) * s * t * xin; 366 capq = (capq + 1 / FACT[k - 1]) * s * t1 * xin; 367 k -= 2; 368 t1 = t; 369 } 370 371 capp += 1; 372 capq = (capq + 1) * ((gnu * gnu) - 1) * (0.125 / x); 373 b[i - 1] = xc * (capp * vcos - capq * vsin); 374 if (nb == 1) { 375 return new BesselJResult(Arrays.copyOf(b, b.length), 376 ncalc); 377 } 378 t = vsin; 379 vsin = -vcos; 380 vcos = t; 381 gnu += 2.0; 382 } 383 384 // --------------------------------------------------------------------- 385 // If NB > 2, compute J(X,ORDER+I) I = 2, NB-1 386 // --------------------------------------------------------------------- 387 if (nb > 2) { 388 gnu = 2 * alpha + 2.0; 389 for (int j = 2; j < nb; ++j) { 390 b[j] = gnu * b[j - 1] / x - b[j - 2]; 391 gnu += 2.0; 392 } 393 } 394 } else { 395 // --------------------------------------------------------------------- 396 // Use recurrence to generate results. First initialize the 397 // calculation of P*S. 398 // --------------------------------------------------------------------- 399 final int nbmx = nb - magx; 400 int n = magx + 1; 401 int nstart = 0; 402 int nend = 0; 403 double en = 2 * (n + alpha); 404 double plast = 1; 405 double p = en / x; 406 double pold; 407 // --------------------------------------------------------------------- 408 // Calculate general significance test. 409 // --------------------------------------------------------------------- 410 double test = 2 * ENSIG; 411 boolean readyToInitialize = false; 412 if (nbmx >= 3) { 413 // --------------------------------------------------------------------- 414 // Calculate P*S until N = NB-1. Check for possible 415 // overflow. 416 // --------------------------------------------------------------------- 417 tover = ENTEN / ENSIG; 418 nstart = magx + 2; 419 nend = nb - 1; 420 en = 2 * (nstart - 1 + alpha); 421 double psave; 422 double psavel; 423 for (int k = nstart; k <= nend; k++) { 424 n = k; 425 en += 2.0; 426 pold = plast; 427 plast = p; 428 p = (en * plast / x) - pold; 429 if (p > tover) { 430 // --------------------------------------------------------------------- 431 // To avoid overflow, divide P*S by TOVER. Calculate 432 // P*S until 433 // ABS(P) > 1. 434 // --------------------------------------------------------------------- 435 tover = ENTEN; 436 p /= tover; 437 plast /= tover; 438 psave = p; 439 psavel = plast; 440 nstart = n + 1; 441 do { 442 n += 1; 443 en += 2.0; 444 pold = plast; 445 plast = p; 446 p = (en * plast / x) - pold; 447 } while (p <= 1); 448 tempb = en / x; 449 // --------------------------------------------------------------------- 450 // Calculate backward test and find NCALC, the 451 // highest N such that 452 // the test is passed. 453 // --------------------------------------------------------------------- 454 test = pold * plast * (0.5 - 0.5 / (tempb * tempb)); 455 test /= ENSIG; 456 p = plast * tover; 457 n -= 1; 458 en -= 2.0; 459 nend = JdkMath.min(nb, n); 460 for (int l = nstart; l <= nend; l++) { 461 pold = psavel; 462 psavel = psave; 463 psave = (en * psavel / x) - pold; 464 if (psave * psavel > test) { 465 ncalc = l - 1; 466 readyToInitialize = true; 467 break; 468 } 469 } 470 ncalc = nend; 471 readyToInitialize = true; 472 break; 473 } 474 } 475 if (!readyToInitialize) { 476 n = nend; 477 en = 2 * (n + alpha); 478 // --------------------------------------------------------------------- 479 // Calculate special significance test for NBMX > 2. 480 // --------------------------------------------------------------------- 481 test = JdkMath.max(test, JdkMath.sqrt(plast * ENSIG) * 482 JdkMath.sqrt(2 * p)); 483 } 484 } 485 // --------------------------------------------------------------------- 486 // Calculate P*S until significance test passes. 487 // --------------------------------------------------------------------- 488 if (!readyToInitialize) { 489 do { 490 n += 1; 491 en += 2.0; 492 pold = plast; 493 plast = p; 494 p = (en * plast / x) - pold; 495 } while (p < test); 496 } 497 // --------------------------------------------------------------------- 498 // Initialize the backward recursion and the normalization sum. 499 // --------------------------------------------------------------------- 500 n += 1; 501 en += 2.0; 502 tempb = 0; 503 tempa = 1 / p; 504 int m = (2 * n) - 4 * (n / 2); 505 double sum = 0; 506 double em = (double) (n / 2); 507 alpem = em - 1 + alpha; 508 alp2em = 2 * em + alpha; 509 if (m != 0) { 510 sum = tempa * alpem * alp2em / em; 511 } 512 nend = n - nb; 513 514 boolean readyToNormalize = false; 515 boolean calculatedB0 = false; 516 517 // --------------------------------------------------------------------- 518 // Recur backward via difference equation, calculating (but not 519 // storing) B(N), until N = NB. 520 // --------------------------------------------------------------------- 521 for (int l = 1; l <= nend; l++) { 522 n -= 1; 523 en -= 2.0; 524 tempc = tempb; 525 tempb = tempa; 526 tempa = (en * tempb / x) - tempc; 527 m = 2 - m; 528 if (m != 0) { 529 em -= 1; 530 alp2em = 2 * em + alpha; 531 if (n == 1) { 532 break; 533 } 534 alpem = em - 1 + alpha; 535 if (alpem == 0) { 536 alpem = 1; 537 } 538 sum = (sum + tempa * alp2em) * alpem / em; 539 } 540 } 541 542 // --------------------------------------------------------------------- 543 // Store B(NB). 544 // --------------------------------------------------------------------- 545 b[n - 1] = tempa; 546 if (nend >= 0) { 547 if (nb <= 1) { 548 alp2em = alpha; 549 if (alpha + 1 == 1) { 550 alp2em = 1; 551 } 552 sum += b[0] * alp2em; 553 readyToNormalize = true; 554 } else { 555 // --------------------------------------------------------------------- 556 // Calculate and store B(NB-1). 557 // --------------------------------------------------------------------- 558 n -= 1; 559 en -= 2.0; 560 b[n - 1] = (en * tempa / x) - tempb; 561 if (n == 1) { 562 calculatedB0 = true; 563 } else { 564 m = 2 - m; 565 if (m != 0) { 566 em -= 1; 567 alp2em = 2 * em + alpha; 568 alpem = em - 1 + alpha; 569 if (alpem == 0) { 570 alpem = 1; 571 } 572 573 sum = (sum + (b[n - 1] * alp2em)) * alpem / em; 574 } 575 } 576 } 577 } 578 if (!readyToNormalize && !calculatedB0) { 579 nend = n - 2; 580 if (nend != 0) { 581 // --------------------------------------------------------------------- 582 // Calculate via difference equation and store B(N), 583 // until N = 2. 584 // --------------------------------------------------------------------- 585 586 for (int l = 1; l <= nend; l++) { 587 n -= 1; 588 en -= 2.0; 589 b[n - 1] = (en * b[n] / x) - b[n + 1]; 590 m = 2 - m; 591 if (m != 0) { 592 em -= 1; 593 alp2em = 2 * em + alpha; 594 alpem = em - 1 + alpha; 595 if (alpem == 0) { 596 alpem = 1; 597 } 598 599 sum = (sum + b[n - 1] * alp2em) * alpem / em; 600 } 601 } 602 } 603 } 604 // --------------------------------------------------------------------- 605 // Calculate b[0] 606 // --------------------------------------------------------------------- 607 if (!readyToNormalize) { 608 if (!calculatedB0) { 609 b[0] = 2.0 * (alpha + 1) * b[1] / x - b[2]; 610 } 611 em -= 1; 612 alp2em = 2 * em + alpha; 613 if (alp2em == 0) { 614 alp2em = 1; 615 } 616 sum += b[0] * alp2em; 617 } 618 // --------------------------------------------------------------------- 619 // Normalize. Divide all B(N) by sum. 620 // --------------------------------------------------------------------- 621 622 if (JdkMath.abs(alpha) > 1e-16) { 623 sum *= Gamma.value(alpha) * JdkMath.pow(x * 0.5, -alpha); 624 } 625 tempa = ENMTEN; 626 if (sum > 1) { 627 tempa *= sum; 628 } 629 630 for (n = 0; n < nb; n++) { 631 if (JdkMath.abs(b[n]) < tempa) { 632 b[n] = 0; 633 } 634 b[n] /= sum; 635 } 636 } 637 // --------------------------------------------------------------------- 638 // Error return -- X, NB, or ALPHA is out of range. 639 // --------------------------------------------------------------------- 640 } else { 641 if (b.length > 0) { 642 b[0] = 0; 643 } 644 ncalc = JdkMath.min(nb, 0) - 1; 645 } 646 return new BesselJResult(Arrays.copyOf(b, b.length), ncalc); 647 } 648 }