LegendreRuleFactory.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.  *      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.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.analysis.integration.gauss;

  22. import org.hipparchus.exception.MathIllegalArgumentException;
  23. import org.hipparchus.util.Pair;

  24. /**
  25.  * Factory that creates Gauss-type quadrature rule using Legendre polynomials.
  26.  * In this implementation, the lower and upper bounds of the natural interval
  27.  * of integration are -1 and 1, respectively.
  28.  * The Legendre polynomials are evaluated using the recurrence relation
  29.  * presented in <a href="http://en.wikipedia.org/wiki/Abramowitz_and_Stegun">
  30.  * Abramowitz and Stegun, 1964</a>.
  31.  *
  32.  */
  33. public class LegendreRuleFactory extends AbstractRuleFactory {

  34.     /** Empty constructor.
  35.      * <p>
  36.      * This constructor is not strictly necessary, but it prevents spurious
  37.      * javadoc warnings with JDK 18 and later.
  38.      * </p>
  39.      * @since 3.0
  40.      */
  41.     public LegendreRuleFactory() { // NOPMD - unnecessary constructor added intentionally to make javadoc happy
  42.         // nothing to do
  43.     }

  44.     /** {@inheritDoc} */
  45.     @Override
  46.     protected Pair<double[], double[]> computeRule(int numberOfPoints)
  47.         throws MathIllegalArgumentException {

  48.         if (numberOfPoints == 1) {
  49.             // Break recursion.
  50.            return new Pair<>(new double[] { 0 } , new double[] { 2 });
  51.         }

  52.         // find nodes as roots of Legendre polynomial
  53.         final Legendre p      =  new Legendre(numberOfPoints);
  54.         final double[] points = findRoots(numberOfPoints, p::ratio);
  55.         enforceSymmetry(points);

  56.         // compute weights
  57.         final double[] weights = new double[numberOfPoints];
  58.         for (int i = 0; i <= numberOfPoints / 2; i++) {
  59.             final double c = points[i];
  60.             final double[] pKpKm1 = p.pNpNm1(c);
  61.             final double d = numberOfPoints * (pKpKm1[1] - c * pKpKm1[0]);
  62.             weights[i] = 2 * (1 - c * c) / (d * d);

  63.             // symmetrical point
  64.             final int idx = numberOfPoints - i - 1;
  65.             weights[idx]  = weights[i];

  66.         }

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

  68.     }

  69.     /** Legendre polynomial. */
  70.     private static class Legendre {

  71.         /** Degree. */
  72.         private int degree;

  73.         /** Simple constructor.
  74.          * @param degree polynomial degree
  75.          */
  76.         Legendre(int degree) {
  77.             this.degree = degree;
  78.         }

  79.         /** Compute ratio P(x)/P'(x).
  80.          * @param x point at which ratio must be computed
  81.          * @return ratio P(x)/P'(x)
  82.          */
  83.         public double ratio(double x) {
  84.             double pm = 1;
  85.             double p  = x;
  86.             double d  = 1;
  87.             for (int n = 1; n < degree; n++) {
  88.                 // apply recurrence relations (n+1) Pₙ₊₁(x)  = (2n+1) x Pₙ(x) - n Pₙ₋₁(x)
  89.                 // and                              P'ₙ₊₁(x) = (n+1) Pₙ(x) + x P'ₙ(x)
  90.                 final double pp = (p * (x * (2 * n + 1)) - pm * n) / (n + 1);
  91.                 d  = p * (n + 1) + d * x;
  92.                 pm = p;
  93.                 p  = pp;
  94.             }
  95.             return p / d;
  96.         }

  97.         /** Compute Pₙ(x) and Pₙ₋₁(x).
  98.          * @param x point at which polynomials are evaluated
  99.          * @return array containing Pₙ(x) at index 0 and Pₙ₋₁(x) at index 1
  100.          */
  101.         private double[] pNpNm1(final double x) {
  102.             double[] p = { x, 1 };
  103.             for (int n = 1; n < degree; n++) {
  104.                 // apply recurrence relation (n+1) Pₙ₊₁(x) = (2n+1) x Pₙ(x) - n Pₙ₋₁(x)
  105.                 final double pp = (p[0] * (x * (2 * n + 1)) - p[1] * n) / (n + 1);
  106.                 p[1] = p[0];
  107.                 p[0] = pp;
  108.             }
  109.             return p;
  110.         }

  111.     }

  112. }