|
| 1 | +package org.matheclipse.core.numerics.integral; |
| 2 | + |
| 3 | +import java.util.function.DoubleUnaryOperator; |
| 4 | +import org.matheclipse.core.numerics.utils.Constants; |
| 5 | + |
| 6 | +/** |
| 7 | + * Implements an adaptive numerical integrator based on the 7-point |
| 8 | + * Gauss-Lobatto rule, as described in [1]. |
| 9 | + * |
| 10 | + * <p> |
| 11 | + * References: |
| 12 | + * <ul> |
| 13 | + * <li>[1] Gander, W., Gautschi, W. Adaptive Quadrature�Revisited. BIT Numerical |
| 14 | + * Mathematics 40, 84�101 (2000). https://doi.org/10.1023/A:1022318402393</li> |
| 15 | + * </ul> |
| 16 | + * </p> |
| 17 | + */ |
| 18 | +public final class GaussLobatto extends Quadrature { |
| 19 | + |
| 20 | + private static final double ALPHA = Constants.SQRT2 / Constants.SQRT3; |
| 21 | + private static final double BETA = 1.0 / Constants.SQRT5; |
| 22 | + |
| 23 | + private static final double[] X = { 0.94288241569547971906, 0.64185334234578130578, 0.23638319966214988028 }; |
| 24 | + private static final double[] Y = { 0.0158271919734801831, 0.0942738402188500455, 0.1550719873365853963, |
| 25 | + 0.1888215739601824544, 0.1997734052268585268, 0.2249264653333395270, 0.2426110719014077338 }; |
| 26 | + private static final double[] C = { 77.0, 432.0, 625.0, 672.0 }; |
| 27 | + |
| 28 | + private int fev; |
| 29 | + |
| 30 | + public GaussLobatto(final double tolerance, final int maxEvaluations) { |
| 31 | + super(tolerance, maxEvaluations); |
| 32 | + } |
| 33 | + |
| 34 | + @Override |
| 35 | + final QuadratureResult properIntegral(final DoubleUnaryOperator f, final double a, |
| 36 | + final double b) { |
| 37 | + return dlob8e(f, a, b); |
| 38 | + } |
| 39 | + |
| 40 | + @Override |
| 41 | + public final String getName() { |
| 42 | + return "Gauss-Lobatto"; |
| 43 | + } |
| 44 | + |
| 45 | + private final QuadratureResult dlob8e(final DoubleUnaryOperator f, final double a, |
| 46 | + final double b) { |
| 47 | + |
| 48 | + // compute interpolation points |
| 49 | + final double mid = 0.5 * (a + b); |
| 50 | + final double h = 0.5 * (b - a); |
| 51 | + final double y1 = f.applyAsDouble(a); |
| 52 | + final double y3 = f.applyAsDouble(mid - h * ALPHA); |
| 53 | + final double y5 = f.applyAsDouble(mid - h * BETA); |
| 54 | + final double y7 = f.applyAsDouble(mid); |
| 55 | + final double y9 = f.applyAsDouble(mid + h * BETA); |
| 56 | + final double y11 = f.applyAsDouble(mid + h * ALPHA); |
| 57 | + final double y13 = f.applyAsDouble(b); |
| 58 | + final double f1 = f.applyAsDouble(mid - h * X[0]); |
| 59 | + final double f2 = f.applyAsDouble(mid + h * X[0]); |
| 60 | + final double f3 = f.applyAsDouble(mid - h * X[1]); |
| 61 | + final double f4 = f.applyAsDouble(mid + h * X[1]); |
| 62 | + final double f5 = f.applyAsDouble(mid - h * X[2]); |
| 63 | + final double f6 = f.applyAsDouble(mid + h * X[2]); |
| 64 | + fev = 13; |
| 65 | + |
| 66 | + // compute a crude initial estimate of the integral |
| 67 | + final double est1 = (y1 + y13 + 5.0 * (y5 + y9)) * (h / 6.0); |
| 68 | + |
| 69 | + // compute a more refined estimate of the integral |
| 70 | + double est2 = C[0] * (y1 + y13) + C[1] * (y3 + y11) + C[2] * (y5 + y9) + C[3] * y7; |
| 71 | + est2 *= (h / 1470.0); |
| 72 | + |
| 73 | + // compute the error estimate |
| 74 | + double s = Y[0] * (y1 + y13) + Y[1] * (f1 + f2); |
| 75 | + s += Y[2] * (y3 + y11) + Y[3] * (f3 + f4); |
| 76 | + s += Y[4] * (y5 + y9) + Y[5] * (f5 + f6) + Y[6] * y7; |
| 77 | + s *= h; |
| 78 | + double rtol = myTol; |
| 79 | + if (est1 != s) { |
| 80 | + final double r = Math.abs(est2 - s) / Math.abs(est1 - s); |
| 81 | + if (r > 0.0 && r < 1.0) { |
| 82 | + rtol /= r; |
| 83 | + } |
| 84 | + } |
| 85 | + double sign = Math.signum(s); |
| 86 | + if (sign == 0) { |
| 87 | + sign = 1.0; |
| 88 | + } |
| 89 | + double s1 = sign * Math.abs(s) * rtol / Constants.EPSILON; |
| 90 | + if (s == 0) { |
| 91 | + s1 = Math.abs(b - a); |
| 92 | + } |
| 93 | + |
| 94 | + // call the recursive subroutine |
| 95 | + final double result = dlob8(f, a, b, y1, y13, s1, rtol); |
| 96 | + return new QuadratureResult(result, Double.NaN, fev, Double.isFinite(result)); |
| 97 | + } |
| 98 | + |
| 99 | + private final double dlob8(final DoubleUnaryOperator f, final double a, final double b, |
| 100 | + final double fa, final double fb, final double s, final double rtol) { |
| 101 | + |
| 102 | + // check the budget of evaluations |
| 103 | + if (fev >= myMaxEvals) { |
| 104 | + return Double.NaN; |
| 105 | + } |
| 106 | + |
| 107 | + // compute the interpolation points |
| 108 | + final double h = 0.5 * (b - a); |
| 109 | + final double mid = 0.5 * (a + b); |
| 110 | + final double mll = mid - h * ALPHA; |
| 111 | + final double ml = mid - h * BETA; |
| 112 | + final double mr = mid + BETA * h; |
| 113 | + final double mrr = mid + h * ALPHA; |
| 114 | + final double fmll = f.applyAsDouble(mll); |
| 115 | + final double fml = f.applyAsDouble(ml); |
| 116 | + final double fmid = f.applyAsDouble(mid); |
| 117 | + final double fmr = f.applyAsDouble(mr); |
| 118 | + final double fmrr = f.applyAsDouble(mrr); |
| 119 | + fev += 8; |
| 120 | + |
| 121 | + // compute a crude estimate of the integral |
| 122 | + final double est1 = (fa + fb + 5.0 * (fml + fmr)) * (h / 6.0); |
| 123 | + |
| 124 | + // compute a more refined estimate of the integral |
| 125 | + double est2 = C[0] * (fa + fb) + C[1] * (fmll + fmrr) + C[2] * (fml + fmr) + C[3] * fmid; |
| 126 | + est2 *= (h / 1470.0); |
| 127 | + |
| 128 | + // check the convergence |
| 129 | + if (s + (est2 - est1) == s || mll <= a || b <= mrr) { |
| 130 | + return est2; |
| 131 | + } |
| 132 | + |
| 133 | + // subdivide the integration region and repeat |
| 134 | + return dlob8(f, a, mll, fa, fmll, s, rtol) + dlob8(f, mll, ml, fmll, fml, s, rtol) |
| 135 | + + dlob8(f, ml, mid, fml, fmid, s, rtol) + dlob8(f, mid, mr, fmid, fmr, s, rtol) |
| 136 | + + dlob8(f, mr, mrr, fmr, fmrr, s, rtol) + dlob8(f, mrr, b, fmrr, fb, s, rtol); |
| 137 | + } |
| 138 | +} |
0 commit comments