FieldLegendreRuleFactory.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 Gauss-type quadrature rule using Legendre polynomials.
  25.  * In this implementation, the lower and upper bounds of the natural interval
  26.  * of integration are -1 and 1, respectively.
  27.  * The Legendre polynomials are evaluated using the recurrence relation
  28.  * presented in <a href="http://en.wikipedia.org/wiki/Abramowitz_and_Stegun">
  29.  * Abramowitz and Stegun, 1964</a>.
  30.  *
  31.  * @param <T> Type of the number used to represent the points and weights of
  32.  * the quadrature rules.
  33.  * @since 2.0
  34.  */
  35. public class FieldLegendreRuleFactory<T extends CalculusFieldElement<T>> extends FieldAbstractRuleFactory<T> {

  36.     /** Simple constructor
  37.      * @param field field to which rule coefficients belong
  38.      */
  39.     public FieldLegendreRuleFactory(final Field<T> field) {
  40.         super(field);
  41.     }

  42.     /** {@inheritDoc} */
  43.     @Override
  44.     public Pair<T[], T[]> computeRule(int numberOfPoints)
  45.         throws MathIllegalArgumentException {

  46.         final Field<T> field = getField();

  47.         if (numberOfPoints == 1) {
  48.             // Break recursion.
  49.             final T[] points  = MathArrays.buildArray(field, numberOfPoints);
  50.             final T[] weights = MathArrays.buildArray(field, numberOfPoints);
  51.             points[0]  = field.getZero();
  52.             weights[0] = field.getZero().newInstance(2);
  53.             return new Pair<>(points, weights);
  54.         }

  55.         // find nodes as roots of Legendre polynomial
  56.         final Legendre<T> p      =  new Legendre<>(numberOfPoints);
  57.         final T[]         points = findRoots(numberOfPoints, p::ratio);
  58.         enforceSymmetry(points);

  59.         // compute weights
  60.         final T[] weights = MathArrays.buildArray(field, numberOfPoints);
  61.         for (int i = 0; i <= numberOfPoints / 2; i++) {
  62.             final T c = points[i];
  63.             final T[] pKpKm1 = p.pNpNm1(c);
  64.             final T d = pKpKm1[1].subtract(c.multiply(pKpKm1[0])).multiply(numberOfPoints);
  65.             weights[i] = c.square().subtract(1).multiply(-2).divide(d.multiply(d));

  66.             // symmetrical point
  67.             final int idx = numberOfPoints - i - 1;
  68.             weights[idx]  = weights[i];

  69.         }

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

  71.     }

  72.     /** Legendre polynomial.
  73.      * @param <T> Type of the field elements.
  74.      */
  75.     private static class Legendre<T extends CalculusFieldElement<T>> {

  76.         /** Degree. */
  77.         private int degree;

  78.         /** Simple constructor.
  79.          * @param degree polynomial degree
  80.          */
  81.         Legendre(int degree) {
  82.             this.degree = degree;
  83.         }

  84.         /** Compute ratio P(x)/P'(x).
  85.          * @param x point at which ratio must be computed
  86.          * @return ratio P(x)/P'(x)
  87.          */
  88.         public T ratio(T x) {
  89.             T pm = x.getField().getOne();
  90.             T p  = x;
  91.             T d  = x.getField().getOne();
  92.             for (int n = 1; n < degree; n++) {
  93.                 // apply recurrence relations (n+1) Pₙ₊₁(x)  = (2n+1) x Pₙ(x) - n Pₙ₋₁(x)
  94.                 // and                              P'ₙ₊₁(x) = (n+1) Pₙ(x) + x P'ₙ(x)
  95.                 final T pp = p.multiply(x.multiply(2 * n + 1)).subtract(pm.multiply(n)).divide(n + 1);
  96.                 d  = p.multiply(n + 1).add(d.multiply(x));
  97.                 pm = p;
  98.                 p  = pp;
  99.             }
  100.             return p.divide(d);
  101.         }

  102.         /** Compute Pₙ(x) and Pₙ₋₁(x).
  103.          * @param x point at which polynomials are evaluated
  104.          * @return array containing Pₙ(x) at index 0 and Pₙ₋₁(x) at index 1
  105.          */
  106.         private T[] pNpNm1(final T x) {
  107.             T[] p = MathArrays.buildArray(x.getField(), 2);
  108.             p[0] = x;
  109.             p[1] = x.getField().getOne();
  110.             for (int n = 1; n < degree; n++) {
  111.                 // apply recurrence relation (n+1) Pₙ₊₁(x) = (2n+1) x Pₙ(x) - n Pₙ₋₁(x)
  112.                 final T pp = p[0].multiply(x.multiply(2 * n + 1)).subtract(p[1].multiply(n)).divide(n + 1);
  113.                 p[1] = p[0];
  114.                 p[0] = pp;
  115.             }
  116.             return p;
  117.         }

  118.     }

  119. }