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.math3.analysis.integration.gauss;

  18. import org.apache.commons.math3.exception.DimensionMismatchException;
  19. import org.apache.commons.math3.util.Pair;
  20. import org.apache.commons.math3.util.FastMath;

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

  57.     /** {@inheritDoc} */
  58.     @Override
  59.     protected Pair<Double[], Double[]> computeRule(int numberOfPoints)
  60.         throws DimensionMismatchException {

  61.         if (numberOfPoints == 1) {
  62.             // Break recursion.
  63.             return new Pair<Double[], Double[]>(new Double[] { 0d },
  64.                                                 new Double[] { SQRT_PI });
  65.         }

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

  71.         // Compute next rule.
  72.         final Double[] points = new Double[numberOfPoints];
  73.         final Double[] weights = new Double[numberOfPoints];

  74.         final double sqrtTwoTimesLastNumPoints = FastMath.sqrt(2 * lastNumPoints);
  75.         final double sqrtTwoTimesNumPoints = FastMath.sqrt(2 * numberOfPoints);

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

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

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

  140.             points[i] = c;
  141.             weights[i] = w;

  142.             final int idx = lastNumPoints - i;
  143.             points[idx] = -c;
  144.             weights[idx] = w;
  145.         }

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

  158.             points[iMax] = 0d;
  159.             weights[iMax] = w;
  160.         }

  161.         return new Pair<Double[], Double[]>(points, weights);
  162.     }
  163. }