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