View Javadoc
1   /*
2    * Copyright (C) 2023 Alberto Irurueta Carro (alberto@irurueta.com)
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.irurueta.numerical.integration;
17  
18  import com.irurueta.algebra.Matrix;
19  import com.irurueta.algebra.WrongSizeException;
20  import com.irurueta.numerical.EvaluationException;
21  
22  /**
23   * Implementation of matrix quadrature using mid-point algorithm.
24   * Mid-point algorithm is suitable for improper integrations, which consists of the following
25   * situations:
26   * - integrand goes to a finite limiting value at finite upper and lower limits, but cannot be
27   * evaluated right on one of those limits (e.g. sin(x)/x at x = 0)
28   * - the upper limit is positive infinity, or the lower limit is negative infinity.
29   * - integrand has an integrable singularity at either limit (e.g. x^-0.5 at x = 0)
30   * - integrand has an integrable singularity at a known place between its upper and lower limits.
31   * - integrand has an integrable singularity at an unknown place between its upper and lower limits.
32   * <p>
33   * Impossible integrals, such as those being infinite (e.g. integral from 1 to infinity of x^-1) or
34   * those that have no limiting sense (e.g. integral from -infinity to infinity of cos(x)) cannot
35   * be handled by this implementation.
36   */
37  public class MidPointMatrixQuadrature extends MatrixQuadrature {
38      /**
39       * Lower limit of integration.
40       */
41      private final double a;
42  
43      /**
44       * Upper limit of integration.
45       */
46      private final double b;
47  
48      /**
49       * Number of rows of quadrature result.
50       */
51      private final int rows;
52  
53      /**
54       * Number of columns of quadrature result.
55       */
56      private final int columns;
57  
58      /**
59       * Current value of integral.
60       */
61      private final Matrix s;
62  
63      /**
64       * Listener to evaluate single dimension matrix functions at required points.
65       */
66      protected final MatrixSingleDimensionFunctionEvaluatorListener listener;
67  
68      /**
69       * Temporary value storing evaluation at mid-point.
70       */
71      private final Matrix tmpMid;
72  
73      /**
74       * Temporary value storing summation of evaluations.
75       */
76      private final Matrix sum;
77  
78      /**
79       * Temporary value storing evaluation at point x.
80       */
81      private final Matrix tmpX;
82  
83      /**
84       * Constructor.
85       *
86       * @param a        Lower limit of integration.
87       * @param b        Upper limit of integration.
88       * @param listener listener to evaluate a single dimension matrix function at required points.
89       * @throws WrongSizeException if size notified by provided listener is invalid.
90       */
91      public MidPointMatrixQuadrature(
92              final double a, final double b, final MatrixSingleDimensionFunctionEvaluatorListener listener)
93              throws WrongSizeException {
94          this.n = 0;
95          this.a = a;
96          this.b = b;
97  
98          rows = listener.getRows();
99          columns = listener.getColumns();
100         s = new Matrix(rows, columns);
101         tmpMid = new Matrix(rows, columns);
102         sum = new Matrix(rows, columns);
103         tmpX = new Matrix(rows, columns);
104 
105         this.listener = listener;
106     }
107 
108     /**
109      * Gets lower limit of integration.
110      *
111      * @return lower limit of integration.
112      */
113     public double getA() {
114         return a;
115     }
116 
117     /**
118      * Gets upper limit of integration.
119      *
120      * @return upper limit of integration.
121      */
122     public double getB() {
123         return b;
124     }
125 
126     /**
127      * Gets current value of integral.
128      *
129      * @return current value of integral.
130      */
131     public Matrix getS() {
132         return s;
133     }
134 
135     /**
136      * Returns the value of the integral at the nth stage of refinement.
137      *
138      * @param result instance where the value of the integral at the nth stage of refinement will
139      *               be stored.
140      * @throws EvaluationException Raised if something failed during the evaluation.
141      */
142     @SuppressWarnings("Duplicates")
143     @Override
144     public void next(Matrix result) throws EvaluationException {
145         try {
146             int it;
147             int j;
148             double x;
149             double tnm;
150             double del;
151             double ddel;
152             n++;
153             if (n == 1) {
154                 // (s = (b - a) * func(0.5 * (a + b)))
155                 func(0.5 * (a + b), tmpMid);
156                 s.copyFrom(tmpMid);
157                 s.multiplyByScalar(b - a);
158                 result.copyFrom(s);
159             } else {
160                 for (it = 1, j = 1; j < n - 1; j++) {
161                     it *= 3;
162                 }
163                 tnm = it;
164                 del = (b - a) / (3.0 * tnm);
165                 // The added points alternate in spacing between del and ddel
166                 ddel = del + del;
167                 x = a + 0.5 * del;
168                 sum.initialize(0.0);
169                 for (j = 0; j < it; j++) {
170                     func(x, tmpX);
171                     sum.add(tmpX);
172 
173                     x += ddel;
174                     func(x, tmpX);
175                     sum.add(tmpX);
176                     x += del;
177                 }
178                 // The new sum is combined with the old integral to give a refined integral
179                 // s = (s + (b - a) * sum / tnm) / 3.0
180                 sum.multiplyByScalar((b - a) / tnm);
181                 s.add(sum);
182                 s.multiplyByScalar(1.0 / 3.0);
183                 result.copyFrom(s);
184             }
185         } catch (final WrongSizeException ex) {
186             throw new EvaluationException(ex);
187         }
188     }
189 
190     /**
191      * Gets type of quadrature.
192      *
193      * @return type of quadrature.
194      */
195     @Override
196     public QuadratureType getType() {
197         return QuadratureType.MID_POINT;
198     }
199 
200     /**
201      * Gets number of rows of quadrature result.
202      *
203      * @return number of rows of quadrature result.
204      */
205     @Override
206     protected int getRows() {
207         return rows;
208     }
209 
210     /**
211      * Gets number of columns of quadrature result.
212      *
213      * @return number of columns of quadrature result.
214      */
215     @Override
216     protected int getColumns() {
217         return columns;
218     }
219 
220     /**
221      * Evaluates matrix function at x.
222      *
223      * @param x      point where function is evaluated.
224      * @param result instance where result of evaluation is stored.
225      * @throws EvaluationException if evaluation fails.
226      */
227     protected void func(final double x, final Matrix result) throws EvaluationException {
228         listener.evaluate(x, result);
229     }
230 }