View Javadoc
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.math4.legacy.analysis.integration.gauss;
18  
19  import java.math.BigDecimal;
20  import java.math.MathContext;
21  
22  import org.apache.commons.math4.legacy.core.Pair;
23  
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   * @since 3.1
33   */
34  public class LegendreHighPrecisionRuleFactory extends BaseRuleFactory<BigDecimal> {
35      /** Settings for enhanced precision computations. */
36      private final MathContext mContext;
37      /** The number {@code 2}. */
38      private final BigDecimal two;
39      /** The number {@code -1}. */
40      private final BigDecimal minusOne;
41      /** The number {@code 0.5}. */
42      private final BigDecimal oneHalf;
43  
44      /**
45       * Default precision is {@link MathContext#DECIMAL128 DECIMAL128}.
46       */
47      public LegendreHighPrecisionRuleFactory() {
48          this(MathContext.DECIMAL128);
49      }
50  
51      /**
52       * @param mContext Precision setting for computing the quadrature rules.
53       */
54      public LegendreHighPrecisionRuleFactory(MathContext mContext) {
55          this.mContext = mContext;
56          two = new BigDecimal("2", mContext);
57          minusOne = new BigDecimal("-1", mContext);
58          oneHalf = new BigDecimal("0.5", mContext);
59      }
60  
61      /** {@inheritDoc} */
62      @Override
63      protected Pair<BigDecimal[], BigDecimal[]> computeRule(int numberOfPoints) {
64          if (numberOfPoints == 1) {
65              // Break recursion.
66              return new Pair<>(new BigDecimal[] { BigDecimal.ZERO },
67                                                          new BigDecimal[] { two });
68          }
69  
70          // Get previous rule.
71          // If it has not been computed yet it will trigger a recursive call
72          // to this method.
73          final BigDecimal[] previousPoints = getRuleInternal(numberOfPoints - 1).getFirst();
74  
75          // Compute next rule.
76          final BigDecimal[] points = new BigDecimal[numberOfPoints];
77          final BigDecimal[] weights = new BigDecimal[numberOfPoints];
78  
79          // Find i-th root of P[n+1] by bracketing.
80          final int iMax = numberOfPoints / 2;
81          for (int i = 0; i < iMax; i++) {
82              // Lower-bound of the interval.
83              BigDecimal a = (i == 0) ? minusOne : previousPoints[i - 1];
84              // Upper-bound of the interval.
85              BigDecimal b = (iMax == 1) ? BigDecimal.ONE : previousPoints[i];
86              // P[j-1](a)
87              BigDecimal pma = BigDecimal.ONE;
88              // P[j](a)
89              BigDecimal pa = a;
90              // P[j-1](b)
91              BigDecimal pmb = BigDecimal.ONE;
92              // P[j](b)
93              BigDecimal pb = b;
94              for (int j = 1; j < numberOfPoints; j++) {
95                  final BigDecimal b_two_j_p_1 = new BigDecimal(2 * j + 1, mContext);
96                  final BigDecimal b_j = new BigDecimal(j, mContext);
97                  final BigDecimal b_j_p_1 = new BigDecimal(j + 1, mContext);
98  
99                  // Compute P[j+1](a)
100                 // ppa = ((2 * j + 1) * a * pa - j * pma) / (j + 1);
101 
102                 BigDecimal tmp1 = a.multiply(b_two_j_p_1, mContext);
103                 tmp1 = pa.multiply(tmp1, mContext);
104                 BigDecimal tmp2 = pma.multiply(b_j, mContext);
105                 // P[j+1](a)
106                 BigDecimal ppa = tmp1.subtract(tmp2, mContext);
107                 ppa = ppa.divide(b_j_p_1, mContext);
108 
109                 // Compute P[j+1](b)
110                 // ppb = ((2 * j + 1) * b * pb - j * pmb) / (j + 1);
111 
112                 tmp1 = b.multiply(b_two_j_p_1, mContext);
113                 tmp1 = pb.multiply(tmp1, mContext);
114                 tmp2 = pmb.multiply(b_j, mContext);
115                 // P[j+1](b)
116                 BigDecimal ppb = tmp1.subtract(tmp2, mContext);
117                 ppb = ppb.divide(b_j_p_1, mContext);
118 
119                 pma = pa;
120                 pa = ppa;
121                 pmb = pb;
122                 pb = ppb;
123             }
124             // Now pa = P[n+1](a), and pma = P[n](a). Same holds for b.
125             // Middle of the interval.
126             BigDecimal c = a.add(b, mContext).multiply(oneHalf, mContext);
127             // P[j-1](c)
128             BigDecimal pmc = BigDecimal.ONE;
129             // P[j](c)
130             BigDecimal pc = c;
131             boolean done = false;
132             while (!done) {
133                 BigDecimal tmp1 = b.subtract(a, mContext);
134                 BigDecimal tmp2 = c.ulp().multiply(BigDecimal.TEN, mContext);
135                 done = tmp1.compareTo(tmp2) <= 0;
136                 pmc = BigDecimal.ONE;
137                 pc = c;
138                 for (int j = 1; j < numberOfPoints; j++) {
139                     final BigDecimal b_two_j_p_1 = new BigDecimal(2 * j + 1, mContext);
140                     final BigDecimal b_j = new BigDecimal(j, mContext);
141                     final BigDecimal b_j_p_1 = new BigDecimal(j + 1, mContext);
142 
143                     // Compute P[j+1](c)
144                     tmp1 = c.multiply(b_two_j_p_1, mContext);
145                     tmp1 = pc.multiply(tmp1, mContext);
146                     tmp2 = pmc.multiply(b_j, mContext);
147                     // P[j+1](c)
148                     BigDecimal ppc = tmp1.subtract(tmp2, mContext);
149                     ppc = ppc.divide(b_j_p_1, mContext);
150 
151                     pmc = pc;
152                     pc = ppc;
153                 }
154                 // Now pc = P[n+1](c) and pmc = P[n](c).
155                 if (!done) {
156                     if (pa.signum() * pc.signum() <= 0) {
157                         b = c;
158                         pmb = pmc;
159                         pb = pc;
160                     } else {
161                         a = c;
162                         pma = pmc;
163                         pa = pc;
164                     }
165                     c = a.add(b, mContext).multiply(oneHalf, mContext);
166                 }
167             }
168             final BigDecimal nP = new BigDecimal(numberOfPoints, mContext);
169             BigDecimal tmp1 = pmc.subtract(c.multiply(pc, mContext), mContext);
170             tmp1 = tmp1.multiply(nP);
171             tmp1 = tmp1.pow(2, mContext);
172             BigDecimal tmp2 = c.pow(2, mContext);
173             tmp2 = BigDecimal.ONE.subtract(tmp2, mContext);
174             tmp2 = tmp2.multiply(two, mContext);
175             tmp2 = tmp2.divide(tmp1, mContext);
176 
177             points[i] = c;
178             weights[i] = tmp2;
179 
180             final int idx = numberOfPoints - i - 1;
181             points[idx] = c.negate(mContext);
182             weights[idx] = tmp2;
183         }
184         // If "numberOfPoints" is odd, 0 is a root.
185         // Note: as written, the test for oddness will work for negative
186         // integers too (although it is not necessary here), preventing
187         // a FindBugs warning.
188         if ((numberOfPoints & 1) != 0) {
189             BigDecimal pmc = BigDecimal.ONE;
190             for (int j = 1; j < numberOfPoints; j += 2) {
191                 final BigDecimal b_j = new BigDecimal(j, mContext);
192                 final BigDecimal b_j_p_1 = new BigDecimal(j + 1, mContext);
193 
194                 // pmc = -j * pmc / (j + 1);
195                 pmc = pmc.multiply(b_j, mContext);
196                 pmc = pmc.divide(b_j_p_1, mContext);
197                 pmc = pmc.negate(mContext);
198             }
199 
200             // 2 / pow(numberOfPoints * pmc, 2);
201             final BigDecimal nP = new BigDecimal(numberOfPoints, mContext);
202             BigDecimal tmp1 = pmc.multiply(nP, mContext);
203             tmp1 = tmp1.pow(2, mContext);
204             BigDecimal tmp2 = two.divide(tmp1, mContext);
205 
206             points[iMax] = BigDecimal.ZERO;
207             weights[iMax] = tmp2;
208         }
209 
210         return new Pair<>(points, weights);
211     }
212 }