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  
18  package org.apache.commons.math4.legacy.ode.nonstiff;
19  
20  import java.io.IOException;
21  import java.io.ObjectInput;
22  import java.io.ObjectOutput;
23  
24  import org.apache.commons.math4.legacy.ode.EquationsMapper;
25  import org.apache.commons.math4.legacy.ode.sampling.AbstractStepInterpolator;
26  import org.apache.commons.math4.legacy.ode.sampling.StepInterpolator;
27  import org.apache.commons.math4.core.jdkmath.JdkMath;
28  
29  /**
30   * This class implements an interpolator for the Gragg-Bulirsch-Stoer
31   * integrator.
32   *
33   * <p>This interpolator compute dense output inside the last step
34   * produced by a Gragg-Bulirsch-Stoer integrator.</p>
35   *
36   * <p>
37   * This implementation is basically a reimplementation in Java of the
38   * <a
39   * href="http://www.unige.ch/math/folks/hairer/prog/nonstiff/odex.f">odex</a>
40   * fortran code by E. Hairer and G. Wanner. The redistribution policy
41   * for this code is available <a
42   * href="http://www.unige.ch/~hairer/prog/licence.txt">here</a>, for
43   * convenience, it is reproduced below.</p>
44   *
45   * <table border="" style="text-align: center; background-color: #E0E0E0;">
46   * <caption>odex redistribution policy</caption>
47   * <tr><td>Copyright (c) 2004, Ernst Hairer</td></tr>
48   *
49   * <tr><td>Redistribution and use in source and binary forms, with or
50   * without modification, are permitted provided that the following
51   * conditions are met:
52   * <ul>
53   *  <li>Redistributions of source code must retain the above copyright
54   *      notice, this list of conditions and the following disclaimer.</li>
55   *  <li>Redistributions in binary form must reproduce the above copyright
56   *      notice, this list of conditions and the following disclaimer in the
57   *      documentation and/or other materials provided with the distribution.</li>
58   * </ul></td></tr>
59   *
60   * <tr><td><strong>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
61   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
62   * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
63   * FOR A  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
64   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
65   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
66   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
67   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
68   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
69   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
70   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</strong></td></tr>
71   * </table>
72   *
73   * @see GraggBulirschStoerIntegrator
74   * @since 1.2
75   */
76  
77  class GraggBulirschStoerStepInterpolator
78    extends AbstractStepInterpolator {
79  
80      /** Serializable version identifier. */
81      private static final long serialVersionUID = 20110928L;
82  
83      /** Slope at the beginning of the step. */
84      private double[] y0Dot;
85  
86      /** State at the end of the step. */
87      private double[] y1;
88  
89      /** Slope at the end of the step. */
90      private double[] y1Dot;
91  
92      /** Derivatives at the middle of the step.
93       * element 0 is state at midpoint, element 1 is first derivative ...
94       */
95      private double[][] yMidDots;
96  
97      /** Interpolation polynomials. */
98      private double[][] polynomials;
99  
100     /** Error coefficients for the interpolation. */
101     private double[] errfac;
102 
103     /** Degree of the interpolation polynomials. */
104     private int currentDegree;
105 
106   /** Simple constructor.
107    * This constructor should not be used directly, it is only intended
108    * for the serialization process.
109    */
110     // CHECKSTYLE: stop RedundantModifier
111     // the public modifier here is needed for serialization
112   public GraggBulirschStoerStepInterpolator() {
113     y0Dot    = null;
114     y1       = null;
115     y1Dot    = null;
116     yMidDots = null;
117     resetTables(-1);
118   }
119   // CHECKSTYLE: resume RedundantModifier
120 
121   /** Simple constructor.
122    * @param y reference to the integrator array holding the current state
123    * @param y0Dot reference to the integrator array holding the slope
124    * at the beginning of the step
125    * @param y1 reference to the integrator array holding the state at
126    * the end of the step
127    * @param y1Dot reference to the integrator array holding the slope
128    * at the end of the step
129    * @param yMidDots reference to the integrator array holding the
130    * derivatives at the middle point of the step
131    * @param forward integration direction indicator
132    * @param primaryMapper equations mapper for the primary equations set
133    * @param secondaryMappers equations mappers for the secondary equations sets
134    */
135   GraggBulirschStoerStepInterpolator(final double[] y, final double[] y0Dot,
136                                      final double[] y1, final double[] y1Dot,
137                                      final double[][] yMidDots,
138                                      final boolean forward,
139                                      final EquationsMapper primaryMapper,
140                                      final EquationsMapper[] secondaryMappers) {
141 
142     super(y, forward, primaryMapper, secondaryMappers);
143     this.y0Dot    = y0Dot;
144     this.y1       = y1;
145     this.y1Dot    = y1Dot;
146     this.yMidDots = yMidDots;
147 
148     resetTables(yMidDots.length + 4);
149   }
150 
151   /** Copy constructor.
152    * @param interpolator interpolator to copy from. The copy is a deep
153    * copy: its arrays are separated from the original arrays of the
154    * instance
155    */
156   GraggBulirschStoerStepInterpolator(final GraggBulirschStoerStepInterpolator interpolator) {
157 
158     super(interpolator);
159 
160     final int dimension = currentState.length;
161 
162     // the interpolator has been finalized,
163     // the following arrays are not needed anymore
164     y0Dot    = null;
165     y1       = null;
166     y1Dot    = null;
167     yMidDots = null;
168 
169     // copy the interpolation polynomials (up to the current degree only)
170     if (interpolator.polynomials == null) {
171       polynomials = null;
172       currentDegree = -1;
173     } else {
174       resetTables(interpolator.currentDegree);
175       for (int i = 0; i < polynomials.length; ++i) {
176         polynomials[i] = new double[dimension];
177         System.arraycopy(interpolator.polynomials[i], 0,
178                          polynomials[i], 0, dimension);
179       }
180       currentDegree = interpolator.currentDegree;
181     }
182   }
183 
184   /** Reallocate the internal tables.
185    * Reallocate the internal tables in order to be able to handle
186    * interpolation polynomials up to the given degree
187    * @param maxDegree maximal degree to handle
188    */
189   private void resetTables(final int maxDegree) {
190 
191     if (maxDegree < 0) {
192       polynomials   = null;
193       errfac        = null;
194       currentDegree = -1;
195     } else {
196 
197       final double[][] newPols = new double[maxDegree + 1][];
198       if (polynomials != null) {
199         System.arraycopy(polynomials, 0, newPols, 0, polynomials.length);
200         for (int i = polynomials.length; i < newPols.length; ++i) {
201           newPols[i] = new double[currentState.length];
202         }
203       } else {
204         for (int i = 0; i < newPols.length; ++i) {
205           newPols[i] = new double[currentState.length];
206         }
207       }
208       polynomials = newPols;
209 
210       // initialize the error factors array for interpolation
211       if (maxDegree <= 4) {
212         errfac = null;
213       } else {
214         errfac = new double[maxDegree - 4];
215         for (int i = 0; i < errfac.length; ++i) {
216           final int ip5 = i + 5;
217           errfac[i] = 1.0 / (ip5 * ip5);
218           final double e = 0.5 * JdkMath.sqrt (((double) (i + 1)) / ip5);
219           for (int j = 0; j <= i; ++j) {
220             errfac[i] *= e / (j + 1);
221           }
222         }
223       }
224 
225       currentDegree = 0;
226     }
227   }
228 
229   /** {@inheritDoc} */
230   @Override
231   protected StepInterpolator doCopy() {
232     return new GraggBulirschStoerStepInterpolator(this);
233   }
234 
235 
236   /** Compute the interpolation coefficients for dense output.
237    * @param mu degree of the interpolation polynomial
238    * @param h current step
239    */
240   public void computeCoefficients(final int mu, final double h) {
241 
242     if (polynomials == null || polynomials.length <= (mu + 4)) {
243       resetTables(mu + 4);
244     }
245 
246     currentDegree = mu + 4;
247 
248     for (int i = 0; i < currentState.length; ++i) {
249 
250       final double yp0   = h * y0Dot[i];
251       final double yp1   = h * y1Dot[i];
252       final double ydiff = y1[i] - currentState[i];
253       final double aspl  = ydiff - yp1;
254       final double bspl  = yp0 - ydiff;
255 
256       polynomials[0][i] = currentState[i];
257       polynomials[1][i] = ydiff;
258       polynomials[2][i] = aspl;
259       polynomials[3][i] = bspl;
260 
261       if (mu < 0) {
262         return;
263       }
264 
265       // compute the remaining coefficients
266       final double ph0 = 0.5 * (currentState[i] + y1[i]) + 0.125 * (aspl + bspl);
267       polynomials[4][i] = 16 * (yMidDots[0][i] - ph0);
268 
269       if (mu > 0) {
270         final double ph1 = ydiff + 0.25 * (aspl - bspl);
271         polynomials[5][i] = 16 * (yMidDots[1][i] - ph1);
272 
273         if (mu > 1) {
274           final double ph2 = yp1 - yp0;
275           polynomials[6][i] = 16 * (yMidDots[2][i] - ph2 + polynomials[4][i]);
276 
277           if (mu > 2) {
278             final double ph3 = 6 * (bspl - aspl);
279             polynomials[7][i] = 16 * (yMidDots[3][i] - ph3 + 3 * polynomials[5][i]);
280 
281             for (int j = 4; j <= mu; ++j) {
282               final double fac1 = 0.5 * j * (j - 1);
283               final double fac2 = 2 * fac1 * (j - 2) * (j - 3);
284               polynomials[j+4][i] =
285                   16 * (yMidDots[j][i] + fac1 * polynomials[j+2][i] - fac2 * polynomials[j][i]);
286             }
287           }
288         }
289       }
290     }
291   }
292 
293   /** Estimate interpolation error.
294    * @param scale scaling array
295    * @return estimate of the interpolation error
296    */
297   public double estimateError(final double[] scale) {
298     double error = 0;
299     if (currentDegree >= 5) {
300       for (int i = 0; i < scale.length; ++i) {
301         final double e = polynomials[currentDegree][i] / scale[i];
302         error += e * e;
303       }
304       error = JdkMath.sqrt(error / scale.length) * errfac[currentDegree - 5];
305     }
306     return error;
307   }
308 
309   /** {@inheritDoc} */
310   @Override
311   protected void computeInterpolatedStateAndDerivatives(final double theta,
312                                                         final double oneMinusThetaH) {
313 
314     final int dimension = currentState.length;
315 
316     final double oneMinusTheta = 1.0 - theta;
317     final double theta05       = theta - 0.5;
318     final double tOmT          = theta * oneMinusTheta;
319     final double t4            = tOmT * tOmT;
320     final double t4Dot         = 2 * tOmT * (1 - 2 * theta);
321     final double dot1          = 1.0 / h;
322     final double dot2          = theta * (2 - 3 * theta) / h;
323     final double dot3          = ((3 * theta - 4) * theta + 1) / h;
324 
325     for (int i = 0; i < dimension; ++i) {
326 
327         final double p0 = polynomials[0][i];
328         final double p1 = polynomials[1][i];
329         final double p2 = polynomials[2][i];
330         final double p3 = polynomials[3][i];
331         interpolatedState[i] = p0 + theta * (p1 + oneMinusTheta * (p2 * theta + p3 * oneMinusTheta));
332         interpolatedDerivatives[i] = dot1 * p1 + dot2 * p2 + dot3 * p3;
333 
334         if (currentDegree > 3) {
335             double cDot = 0;
336             double c = polynomials[currentDegree][i];
337             for (int j = currentDegree - 1; j > 3; --j) {
338                 final double d = 1.0 / (j - 3);
339                 cDot = d * (theta05 * cDot + c);
340                 c = polynomials[j][i] + c * d * theta05;
341             }
342             interpolatedState[i]       += t4 * c;
343             interpolatedDerivatives[i] += (t4 * cDot + t4Dot * c) / h;
344         }
345     }
346 
347     if (h == 0) {
348         // in this degenerated case, the previous computation leads to NaN for derivatives
349         // we fix this by using the derivatives at midpoint
350         System.arraycopy(yMidDots[1], 0, interpolatedDerivatives, 0, dimension);
351     }
352   }
353 
354   /** {@inheritDoc} */
355   @Override
356   public void writeExternal(final ObjectOutput out)
357     throws IOException {
358 
359     final int dimension = (currentState == null) ? -1 : currentState.length;
360 
361     // save the state of the base class
362     writeBaseExternal(out);
363 
364     // save the local attributes (but not the temporary vectors)
365     out.writeInt(currentDegree);
366     for (int k = 0; k <= currentDegree; ++k) {
367       for (int l = 0; l < dimension; ++l) {
368         out.writeDouble(polynomials[k][l]);
369       }
370     }
371   }
372 
373   /** {@inheritDoc} */
374   @Override
375   public void readExternal(final ObjectInput in)
376     throws IOException, ClassNotFoundException {
377 
378     // read the base class
379     final double t = readBaseExternal(in);
380     final int dimension = (currentState == null) ? -1 : currentState.length;
381 
382     // read the local attributes
383     final int degree = in.readInt();
384     resetTables(degree);
385     currentDegree = degree;
386 
387     for (int k = 0; k <= currentDegree; ++k) {
388       for (int l = 0; l < dimension; ++l) {
389         polynomials[k][l] = in.readDouble();
390       }
391     }
392 
393     // we can now set the interpolated time and state
394     setInterpolatedTime(t);
395   }
396 }