LegendreHighPrecisionRuleFactory.java
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.apache.commons.math4.legacy.analysis.integration.gauss;
- import java.math.BigDecimal;
- import java.math.MathContext;
- import org.apache.commons.math4.legacy.core.Pair;
- /**
- * Factory that creates Gauss-type quadrature rule using Legendre polynomials.
- * In this implementation, the lower and upper bounds of the natural interval
- * of integration are -1 and 1, respectively.
- * The Legendre polynomials are evaluated using the recurrence relation
- * presented in <a href="http://en.wikipedia.org/wiki/Abramowitz_and_Stegun">
- * Abramowitz and Stegun, 1964</a>.
- *
- * @since 3.1
- */
- public class LegendreHighPrecisionRuleFactory extends BaseRuleFactory<BigDecimal> {
- /** Settings for enhanced precision computations. */
- private final MathContext mContext;
- /** The number {@code 2}. */
- private final BigDecimal two;
- /** The number {@code -1}. */
- private final BigDecimal minusOne;
- /** The number {@code 0.5}. */
- private final BigDecimal oneHalf;
- /**
- * Default precision is {@link MathContext#DECIMAL128 DECIMAL128}.
- */
- public LegendreHighPrecisionRuleFactory() {
- this(MathContext.DECIMAL128);
- }
- /**
- * @param mContext Precision setting for computing the quadrature rules.
- */
- public LegendreHighPrecisionRuleFactory(MathContext mContext) {
- this.mContext = mContext;
- two = new BigDecimal("2", mContext);
- minusOne = new BigDecimal("-1", mContext);
- oneHalf = new BigDecimal("0.5", mContext);
- }
- /** {@inheritDoc} */
- @Override
- protected Pair<BigDecimal[], BigDecimal[]> computeRule(int numberOfPoints) {
- if (numberOfPoints == 1) {
- // Break recursion.
- return new Pair<>(new BigDecimal[] { BigDecimal.ZERO },
- new BigDecimal[] { two });
- }
- // Get previous rule.
- // If it has not been computed yet it will trigger a recursive call
- // to this method.
- final BigDecimal[] previousPoints = getRuleInternal(numberOfPoints - 1).getFirst();
- // Compute next rule.
- final BigDecimal[] points = new BigDecimal[numberOfPoints];
- final BigDecimal[] weights = new BigDecimal[numberOfPoints];
- // Find i-th root of P[n+1] by bracketing.
- final int iMax = numberOfPoints / 2;
- for (int i = 0; i < iMax; i++) {
- // Lower-bound of the interval.
- BigDecimal a = (i == 0) ? minusOne : previousPoints[i - 1];
- // Upper-bound of the interval.
- BigDecimal b = (iMax == 1) ? BigDecimal.ONE : previousPoints[i];
- // P[j-1](a)
- BigDecimal pma = BigDecimal.ONE;
- // P[j](a)
- BigDecimal pa = a;
- // P[j-1](b)
- BigDecimal pmb = BigDecimal.ONE;
- // P[j](b)
- BigDecimal pb = b;
- for (int j = 1; j < numberOfPoints; j++) {
- final BigDecimal b_two_j_p_1 = new BigDecimal(2 * j + 1, mContext);
- final BigDecimal b_j = new BigDecimal(j, mContext);
- final BigDecimal b_j_p_1 = new BigDecimal(j + 1, mContext);
- // Compute P[j+1](a)
- // ppa = ((2 * j + 1) * a * pa - j * pma) / (j + 1);
- BigDecimal tmp1 = a.multiply(b_two_j_p_1, mContext);
- tmp1 = pa.multiply(tmp1, mContext);
- BigDecimal tmp2 = pma.multiply(b_j, mContext);
- // P[j+1](a)
- BigDecimal ppa = tmp1.subtract(tmp2, mContext);
- ppa = ppa.divide(b_j_p_1, mContext);
- // Compute P[j+1](b)
- // ppb = ((2 * j + 1) * b * pb - j * pmb) / (j + 1);
- tmp1 = b.multiply(b_two_j_p_1, mContext);
- tmp1 = pb.multiply(tmp1, mContext);
- tmp2 = pmb.multiply(b_j, mContext);
- // P[j+1](b)
- BigDecimal ppb = tmp1.subtract(tmp2, mContext);
- ppb = ppb.divide(b_j_p_1, mContext);
- pma = pa;
- pa = ppa;
- pmb = pb;
- pb = ppb;
- }
- // Now pa = P[n+1](a), and pma = P[n](a). Same holds for b.
- // Middle of the interval.
- BigDecimal c = a.add(b, mContext).multiply(oneHalf, mContext);
- // P[j-1](c)
- BigDecimal pmc = BigDecimal.ONE;
- // P[j](c)
- BigDecimal pc = c;
- boolean done = false;
- while (!done) {
- BigDecimal tmp1 = b.subtract(a, mContext);
- BigDecimal tmp2 = c.ulp().multiply(BigDecimal.TEN, mContext);
- done = tmp1.compareTo(tmp2) <= 0;
- pmc = BigDecimal.ONE;
- pc = c;
- for (int j = 1; j < numberOfPoints; j++) {
- final BigDecimal b_two_j_p_1 = new BigDecimal(2 * j + 1, mContext);
- final BigDecimal b_j = new BigDecimal(j, mContext);
- final BigDecimal b_j_p_1 = new BigDecimal(j + 1, mContext);
- // Compute P[j+1](c)
- tmp1 = c.multiply(b_two_j_p_1, mContext);
- tmp1 = pc.multiply(tmp1, mContext);
- tmp2 = pmc.multiply(b_j, mContext);
- // P[j+1](c)
- BigDecimal ppc = tmp1.subtract(tmp2, mContext);
- ppc = ppc.divide(b_j_p_1, mContext);
- pmc = pc;
- pc = ppc;
- }
- // Now pc = P[n+1](c) and pmc = P[n](c).
- if (!done) {
- if (pa.signum() * pc.signum() <= 0) {
- b = c;
- pmb = pmc;
- pb = pc;
- } else {
- a = c;
- pma = pmc;
- pa = pc;
- }
- c = a.add(b, mContext).multiply(oneHalf, mContext);
- }
- }
- final BigDecimal nP = new BigDecimal(numberOfPoints, mContext);
- BigDecimal tmp1 = pmc.subtract(c.multiply(pc, mContext), mContext);
- tmp1 = tmp1.multiply(nP);
- tmp1 = tmp1.pow(2, mContext);
- BigDecimal tmp2 = c.pow(2, mContext);
- tmp2 = BigDecimal.ONE.subtract(tmp2, mContext);
- tmp2 = tmp2.multiply(two, mContext);
- tmp2 = tmp2.divide(tmp1, mContext);
- points[i] = c;
- weights[i] = tmp2;
- final int idx = numberOfPoints - i - 1;
- points[idx] = c.negate(mContext);
- weights[idx] = tmp2;
- }
- // If "numberOfPoints" is odd, 0 is a root.
- // Note: as written, the test for oddness will work for negative
- // integers too (although it is not necessary here), preventing
- // a FindBugs warning.
- if ((numberOfPoints & 1) != 0) {
- BigDecimal pmc = BigDecimal.ONE;
- for (int j = 1; j < numberOfPoints; j += 2) {
- final BigDecimal b_j = new BigDecimal(j, mContext);
- final BigDecimal b_j_p_1 = new BigDecimal(j + 1, mContext);
- // pmc = -j * pmc / (j + 1);
- pmc = pmc.multiply(b_j, mContext);
- pmc = pmc.divide(b_j_p_1, mContext);
- pmc = pmc.negate(mContext);
- }
- // 2 / pow(numberOfPoints * pmc, 2);
- final BigDecimal nP = new BigDecimal(numberOfPoints, mContext);
- BigDecimal tmp1 = pmc.multiply(nP, mContext);
- tmp1 = tmp1.pow(2, mContext);
- BigDecimal tmp2 = two.divide(tmp1, mContext);
- points[iMax] = BigDecimal.ZERO;
- weights[iMax] = tmp2;
- }
- return new Pair<>(points, weights);
- }
- }