HermiteRuleFactory.java

  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. package org.apache.commons.math4.legacy.analysis.integration.gauss;

  18. import org.apache.commons.math4.core.jdkmath.JdkMath;
  19. import org.apache.commons.math4.legacy.core.Pair;

  20. /**
  21.  * Factory that creates a
  22.  * <a href="http://en.wikipedia.org/wiki/Gauss-Hermite_quadrature">
  23.  * Gauss-type quadrature rule using Hermite polynomials</a>
  24.  * of the first kind.
  25.  * Such a quadrature rule allows the calculation of improper integrals
  26.  * of a function
  27.  * <p>
  28.  *  \(f(x) e^{-x^2}\)
  29.  * </p><p>
  30.  * Recurrence relation and weights computation follow
  31.  * <a href="http://en.wikipedia.org/wiki/Abramowitz_and_Stegun">
  32.  * Abramowitz and Stegun, 1964</a>.
  33.  * </p><p>
  34.  * The coefficients of the standard Hermite polynomials grow very rapidly.
  35.  * In order to avoid overflows, each Hermite polynomial is normalized with
  36.  * respect to the underlying scalar product.
  37.  * The initial interval for the application of the bisection method is
  38.  * based on the roots of the previous Hermite polynomial (interlacing).
  39.  * Upper and lower bounds of these roots are provided by </p>
  40.  * <blockquote>
  41.  *  I. Krasikov,
  42.  *  <em>Nonnegative quadratic forms and bounds on orthogonal polynomials</em>,
  43.  *  Journal of Approximation theory <b>111</b>, 31-49
  44.  * </blockquote>
  45.  *
  46.  * @since 3.3
  47.  */
  48. public class HermiteRuleFactory extends BaseRuleFactory<Double> {
  49.     /** &pi;<sup>1/2</sup>. */
  50.     private static final double SQRT_PI = 1.77245385090551602729;
  51.     /** &pi;<sup>-1/4</sup>. */
  52.     private static final double H0 = 7.5112554446494248286e-1;
  53.     /** &pi;<sup>-1/4</sup> &radic;2. */
  54.     private static final double H1 = 1.0622519320271969145;

  55.     /** {@inheritDoc} */
  56.     @Override
  57.     protected Pair<Double[], Double[]> computeRule(int numberOfPoints) {
  58.         if (numberOfPoints == 1) {
  59.             // Break recursion.
  60.             return new Pair<>(new Double[] { 0d },
  61.                                                 new Double[] { SQRT_PI });
  62.         }

  63.         // Get previous rule.
  64.         // If it has not been computed yet it will trigger a recursive call
  65.         // to this method.
  66.         final int lastNumPoints = numberOfPoints - 1;
  67.         final Double[] previousPoints = getRuleInternal(lastNumPoints).getFirst();

  68.         // Compute next rule.
  69.         final Double[] points = new Double[numberOfPoints];
  70.         final Double[] weights = new Double[numberOfPoints];

  71.         final double sqrtTwoTimesLastNumPoints = JdkMath.sqrt(2.0 * lastNumPoints);
  72.         final double sqrtTwoTimesNumPoints = JdkMath.sqrt(2.0 * numberOfPoints);

  73.         // Find i-th root of H[n+1] by bracketing.
  74.         final int iMax = numberOfPoints / 2;
  75.         for (int i = 0; i < iMax; i++) {
  76.             // Lower-bound of the interval.
  77.             double a = (i == 0) ? -sqrtTwoTimesLastNumPoints : previousPoints[i - 1].doubleValue();
  78.             // Upper-bound of the interval.
  79.             double b = (iMax == 1) ? -0.5 : previousPoints[i].doubleValue();

  80.             // H[j-1](a)
  81.             double hma = H0;
  82.             // H[j](a)
  83.             double ha = H1 * a;
  84.             // H[j-1](b)
  85.             double hmb = H0;
  86.             // H[j](b)
  87.             double hb = H1 * b;
  88.             for (int j = 1; j < numberOfPoints; j++) {
  89.                 // Compute H[j+1](a) and H[j+1](b)
  90.                 final double jp1 = j + 1.0;
  91.                 final double s = JdkMath.sqrt(2 / jp1);
  92.                 final double sm = JdkMath.sqrt(j / jp1);
  93.                 final double hpa = s * a * ha - sm * hma;
  94.                 final double hpb = s * b * hb - sm * hmb;
  95.                 hma = ha;
  96.                 ha = hpa;
  97.                 hmb = hb;
  98.                 hb = hpb;
  99.             }

  100.             // Now ha = H[n+1](a), and hma = H[n](a) (same holds for b).
  101.             // Middle of the interval.
  102.             double c = 0.5 * (a + b);
  103.             // P[j-1](c)
  104.             double hmc = H0;
  105.             // P[j](c)
  106.             double hc = H1 * c;
  107.             boolean done = false;
  108.             while (!done) {
  109.                 done = b - a <= Math.ulp(c);
  110.                 hmc = H0;
  111.                 hc = H1 * c;
  112.                 for (int j = 1; j < numberOfPoints; j++) {
  113.                     // Compute H[j+1](c)
  114.                     final double jp1 = j + 1.0;
  115.                     final double s = JdkMath.sqrt(2 / jp1);
  116.                     final double sm = JdkMath.sqrt(j / jp1);
  117.                     final double hpc = s * c * hc - sm * hmc;
  118.                     hmc = hc;
  119.                     hc = hpc;
  120.                 }
  121.                 // Now h = H[n+1](c) and hm = H[n](c).
  122.                 if (!done) {
  123.                     if (ha * hc < 0) {
  124.                         b = c;
  125.                         hmb = hmc;
  126.                         hb = hc;
  127.                     } else {
  128.                         a = c;
  129.                         hma = hmc;
  130.                         ha = hc;
  131.                     }
  132.                     c = 0.5 * (a + b);
  133.                 }
  134.             }
  135.             final double d = sqrtTwoTimesNumPoints * hmc;
  136.             final double w = 2 / (d * d);

  137.             points[i] = c;
  138.             weights[i] = w;

  139.             final int idx = lastNumPoints - i;
  140.             points[idx] = -c;
  141.             weights[idx] = w;
  142.         }

  143.         // If "numberOfPoints" is odd, 0 is a root.
  144.         // Note: as written, the test for oddness will work for negative
  145.         // integers too (although it is not necessary here), preventing
  146.         // a FindBugs warning.
  147.         if ((numberOfPoints & 1) != 0) {
  148.             double hm = H0;
  149.             for (int j = 1; j < numberOfPoints; j += 2) {
  150.                 final double jp1 = j + 1.0;
  151.                 hm = -JdkMath.sqrt(j / jp1) * hm;
  152.             }
  153.             final double d = sqrtTwoTimesNumPoints * hm;
  154.             final double w = 2 / (d * d);

  155.             points[iMax] = 0d;
  156.             weights[iMax] = w;
  157.         }

  158.         return new Pair<>(points, weights);
  159.     }
  160. }