FieldHermiteRuleFactory.java

  1. /*
  2.  * Licensed to the Hipparchus project 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 Hipparchus project 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. package org.hipparchus.analysis.integration.gauss;

  18. import org.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.Field;
  20. import org.hipparchus.exception.MathIllegalArgumentException;
  21. import org.hipparchus.util.MathArrays;
  22. import org.hipparchus.util.Pair;

  23. /**
  24.  * Factory that creates a
  25.  * <a href="http://en.wikipedia.org/wiki/Gauss-Hermite_quadrature">
  26.  * Gauss-type quadrature rule using Hermite polynomials</a>
  27.  * of the first kind.
  28.  * Such a quadrature rule allows the calculation of improper integrals
  29.  * of a function
  30.  * <p>
  31.  *  \(f(x) e^{-x^2}\)
  32.  * </p><p>
  33.  * Recurrence relation and weights computation follow
  34.  * <a href="http://en.wikipedia.org/wiki/Abramowitz_and_Stegun">
  35.  * Abramowitz and Stegun, 1964</a>.
  36.  * </p><p>
  37.  * The coefficients of the standard Hermite polynomials grow very rapidly.
  38.  * In order to avoid overflows, each Hermite polynomial is normalized with
  39.  * respect to the underlying scalar product.
  40.  * @param <T> Type of the number used to represent the points and weights of
  41.  * the quadrature rules.
  42.  * @since 2.0
  43.  */
  44. public class FieldHermiteRuleFactory<T extends CalculusFieldElement<T>> extends FieldAbstractRuleFactory<T> {

  45.     /** Simple constructor
  46.      * @param field field to which rule coefficients belong
  47.      */
  48.     public FieldHermiteRuleFactory(final Field<T> field) {
  49.         super(field);
  50.     }

  51.     /** {@inheritDoc} */
  52.     @Override
  53.     protected Pair<T[], T[]> computeRule(int numberOfPoints)
  54.         throws MathIllegalArgumentException {

  55.         final Field<T> field  = getField();
  56.         final T        sqrtPi = field.getZero().getPi().sqrt();

  57.         if (numberOfPoints == 1) {
  58.             // Break recursion.
  59.             final T[] points  = MathArrays.buildArray(field, numberOfPoints);
  60.             final T[] weights = MathArrays.buildArray(field, numberOfPoints);
  61.             points[0]  = field.getZero();
  62.             weights[0] = sqrtPi;
  63.             return new Pair<>(points, weights);
  64.         }

  65.         // find nodes as roots of Hermite polynomial
  66.         final T[] points = findRoots(numberOfPoints, new Hermite<>(field, numberOfPoints)::ratio);
  67.         enforceSymmetry(points);

  68.         // compute weights
  69.         final T[] weights = MathArrays.buildArray(field, numberOfPoints);
  70.         final Hermite<T> hm1 = new Hermite<>(field, numberOfPoints - 1);
  71.         for (int i = 0; i < numberOfPoints; i++) {
  72.             final T y = hm1.hNhNm1(points[i])[0];
  73.             weights[i] = sqrtPi.divide(y.square().multiply(numberOfPoints));
  74.         }

  75.         return new Pair<>(points, weights);

  76.     }

  77.     /** Hermite polynomial, normalized to avoid overflow.
  78.      * <p>
  79.      * The regular Hermite polynomials and associated weights are given by:
  80.      *   <pre>
  81.      *     H₀(x)   = 1
  82.      *     H₁(x)   = 2 x
  83.      *     Hₙ₊₁(x) = 2x Hₙ(x) - 2n Hₙ₋₁(x), and H'ₙ(x) = 2n Hₙ₋₁(x)
  84.      *     wₙ(xᵢ) = [2ⁿ⁻¹ n! √π]/[n Hₙ₋₁(xᵢ)]²
  85.      *   </pre>
  86.      * </p>
  87.      * <p>
  88.      * In order to avoid overflow with normalize the polynomials hₙ(x) = Hₙ(x) / √[2ⁿ n!]
  89.      * so the recurrence relations and weights become:
  90.      *   <pre>
  91.      *     h₀(x)   = 1
  92.      *     h₁(x)   = √2 x
  93.      *     hₙ₊₁(x) = [√2 x hₙ(x) - √n hₙ₋₁(x)]/√(n+1), and h'ₙ(x) = 2n hₙ₋₁(x)
  94.      *     uₙ(xᵢ) = √π/[n Nₙ₋₁(xᵢ)²]
  95.      *   </pre>
  96.      * </p>
  97.      * @param <T> Type of the field elements.
  98.      */
  99.     private static class Hermite<T extends CalculusFieldElement<T>> {

  100.         /** √2. */
  101.         private final T sqrt2;

  102.         /** Degree. */
  103.         private final int degree;

  104.         /** Simple constructor.
  105.          * @param field field to which rule coefficients belong
  106.          * @param degree polynomial degree
  107.          */
  108.         Hermite(Field<T> field, int degree) {
  109.             this.sqrt2  = field.getZero().newInstance(2).sqrt();
  110.             this.degree = degree;
  111.         }

  112.         /** Compute ratio H(x)/H'(x).
  113.          * @param x point at which ratio must be computed
  114.          * @return ratio H(x)/H'(x)
  115.          */
  116.         public T ratio(T x) {
  117.             T[] h = hNhNm1(x);
  118.             return h[0].divide(h[1].multiply(2 * degree));
  119.         }

  120.         /** Compute Nₙ(x) and Nₙ₋₁(x).
  121.          * @param x point at which polynomials are evaluated
  122.          * @return array containing Nₙ(x) at index 0 and Nₙ₋₁(x) at index 1
  123.          */
  124.         private T[] hNhNm1(final T x) {
  125.             T[] h = MathArrays.buildArray(x.getField(), 2);
  126.             h[0] = sqrt2.multiply(x);
  127.             h[1] = x.getField().getOne();
  128.             T sqrtN = x.getField().getOne();
  129.             for (int n = 1; n < degree; n++) {
  130.                 // apply recurrence relation hₙ₊₁(x) = [√2 x hₙ(x) - √n hₙ₋₁(x)]/√(n+1)
  131.                 final T sqrtNp = x.getField().getZero().newInstance(n + 1).sqrt();
  132.                 final T hp = (h[0].multiply(x).multiply(sqrt2).subtract(h[1].multiply(sqrtN))).divide(sqrtNp);
  133.                 h[1]  = h[0];
  134.                 h[0]  = hp;
  135.                 sqrtN = sqrtNp;
  136.             }
  137.             return h;
  138.         }

  139.     }

  140. }