Home | History | Annotate | Download | only in nonstiff
      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.math.ode.nonstiff;
     19 
     20 import org.apache.commons.math.ode.DerivativeException;
     21 import org.apache.commons.math.ode.FirstOrderDifferentialEquations;
     22 import org.apache.commons.math.ode.IntegratorException;
     23 import org.apache.commons.math.ode.events.EventHandler;
     24 import org.apache.commons.math.ode.sampling.AbstractStepInterpolator;
     25 import org.apache.commons.math.ode.sampling.DummyStepInterpolator;
     26 import org.apache.commons.math.ode.sampling.StepHandler;
     27 import org.apache.commons.math.util.FastMath;
     28 
     29 /**
     30  * This class implements a Gragg-Bulirsch-Stoer integrator for
     31  * Ordinary Differential Equations.
     32  *
     33  * <p>The Gragg-Bulirsch-Stoer algorithm is one of the most efficient
     34  * ones currently available for smooth problems. It uses Richardson
     35  * extrapolation to estimate what would be the solution if the step
     36  * size could be decreased down to zero.</p>
     37  *
     38  * <p>
     39  * This method changes both the step size and the order during
     40  * integration, in order to minimize computation cost. It is
     41  * particularly well suited when a very high precision is needed. The
     42  * limit where this method becomes more efficient than high-order
     43  * embedded Runge-Kutta methods like {@link DormandPrince853Integrator
     44  * Dormand-Prince 8(5,3)} depends on the problem. Results given in the
     45  * Hairer, Norsett and Wanner book show for example that this limit
     46  * occurs for accuracy around 1e-6 when integrating Saltzam-Lorenz
     47  * equations (the authors note this problem is <i>extremely sensitive
     48  * to the errors in the first integration steps</i>), and around 1e-11
     49  * for a two dimensional celestial mechanics problems with seven
     50  * bodies (pleiades problem, involving quasi-collisions for which
     51  * <i>automatic step size control is essential</i>).
     52  * </p>
     53  *
     54  * <p>
     55  * This implementation is basically a reimplementation in Java of the
     56  * <a
     57  * href="http://www.unige.ch/math/folks/hairer/prog/nonstiff/odex.f">odex</a>
     58  * fortran code by E. Hairer and G. Wanner. The redistribution policy
     59  * for this code is available <a
     60  * href="http://www.unige.ch/~hairer/prog/licence.txt">here</a>, for
     61  * convenience, it is reproduced below.</p>
     62  * </p>
     63  *
     64  * <table border="0" width="80%" cellpadding="10" align="center" bgcolor="#E0E0E0">
     65  * <tr><td>Copyright (c) 2004, Ernst Hairer</td></tr>
     66  *
     67  * <tr><td>Redistribution and use in source and binary forms, with or
     68  * without modification, are permitted provided that the following
     69  * conditions are met:
     70  * <ul>
     71  *  <li>Redistributions of source code must retain the above copyright
     72  *      notice, this list of conditions and the following disclaimer.</li>
     73  *  <li>Redistributions in binary form must reproduce the above copyright
     74  *      notice, this list of conditions and the following disclaimer in the
     75  *      documentation and/or other materials provided with the distribution.</li>
     76  * </ul></td></tr>
     77  *
     78  * <tr><td><strong>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
     79  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
     80  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     81  * FOR A  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
     82  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     83  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     84  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     85  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     86  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     87  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     88  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</strong></td></tr>
     89  * </table>
     90  *
     91  * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 fvr. 2011) $
     92  * @since 1.2
     93  */
     94 
     95 public class GraggBulirschStoerIntegrator extends AdaptiveStepsizeIntegrator {
     96 
     97     /** Integrator method name. */
     98     private static final String METHOD_NAME = "Gragg-Bulirsch-Stoer";
     99 
    100     /** maximal order. */
    101     private int maxOrder;
    102 
    103     /** step size sequence. */
    104     private int[] sequence;
    105 
    106     /** overall cost of applying step reduction up to iteration k+1, in number of calls. */
    107     private int[] costPerStep;
    108 
    109     /** cost per unit step. */
    110     private double[] costPerTimeUnit;
    111 
    112     /** optimal steps for each order. */
    113     private double[] optimalStep;
    114 
    115     /** extrapolation coefficients. */
    116     private double[][] coeff;
    117 
    118     /** stability check enabling parameter. */
    119     private boolean performTest;
    120 
    121     /** maximal number of checks for each iteration. */
    122     private int maxChecks;
    123 
    124     /** maximal number of iterations for which checks are performed. */
    125     private int maxIter;
    126 
    127     /** stepsize reduction factor in case of stability check failure. */
    128     private double stabilityReduction;
    129 
    130     /** first stepsize control factor. */
    131     private double stepControl1;
    132 
    133     /** second stepsize control factor. */
    134     private double stepControl2;
    135 
    136     /** third stepsize control factor. */
    137     private double stepControl3;
    138 
    139     /** fourth stepsize control factor. */
    140     private double stepControl4;
    141 
    142     /** first order control factor. */
    143     private double orderControl1;
    144 
    145     /** second order control factor. */
    146     private double orderControl2;
    147 
    148     /** use interpolation error in stepsize control. */
    149     private boolean useInterpolationError;
    150 
    151     /** interpolation order control parameter. */
    152     private int mudif;
    153 
    154   /** Simple constructor.
    155    * Build a Gragg-Bulirsch-Stoer integrator with the given step
    156    * bounds. All tuning parameters are set to their default
    157    * values. The default step handler does nothing.
    158    * @param minStep minimal step (must be positive even for backward
    159    * integration), the last step can be smaller than this
    160    * @param maxStep maximal step (must be positive even for backward
    161    * integration)
    162    * @param scalAbsoluteTolerance allowed absolute error
    163    * @param scalRelativeTolerance allowed relative error
    164    */
    165   public GraggBulirschStoerIntegrator(final double minStep, final double maxStep,
    166                                       final double scalAbsoluteTolerance,
    167                                       final double scalRelativeTolerance) {
    168     super(METHOD_NAME, minStep, maxStep,
    169           scalAbsoluteTolerance, scalRelativeTolerance);
    170     setStabilityCheck(true, -1, -1, -1);
    171     setStepsizeControl(-1, -1, -1, -1);
    172     setOrderControl(-1, -1, -1);
    173     setInterpolationControl(true, -1);
    174   }
    175 
    176   /** Simple constructor.
    177    * Build a Gragg-Bulirsch-Stoer integrator with the given step
    178    * bounds. All tuning parameters are set to their default
    179    * values. The default step handler does nothing.
    180    * @param minStep minimal step (must be positive even for backward
    181    * integration), the last step can be smaller than this
    182    * @param maxStep maximal step (must be positive even for backward
    183    * integration)
    184    * @param vecAbsoluteTolerance allowed absolute error
    185    * @param vecRelativeTolerance allowed relative error
    186    */
    187   public GraggBulirschStoerIntegrator(final double minStep, final double maxStep,
    188                                       final double[] vecAbsoluteTolerance,
    189                                       final double[] vecRelativeTolerance) {
    190     super(METHOD_NAME, minStep, maxStep,
    191           vecAbsoluteTolerance, vecRelativeTolerance);
    192     setStabilityCheck(true, -1, -1, -1);
    193     setStepsizeControl(-1, -1, -1, -1);
    194     setOrderControl(-1, -1, -1);
    195     setInterpolationControl(true, -1);
    196   }
    197 
    198   /** Set the stability check controls.
    199    * <p>The stability check is performed on the first few iterations of
    200    * the extrapolation scheme. If this test fails, the step is rejected
    201    * and the stepsize is reduced.</p>
    202    * <p>By default, the test is performed, at most during two
    203    * iterations at each step, and at most once for each of these
    204    * iterations. The default stepsize reduction factor is 0.5.</p>
    205    * @param performStabilityCheck if true, stability check will be performed,
    206      if false, the check will be skipped
    207    * @param maxNumIter maximal number of iterations for which checks are
    208    * performed (the number of iterations is reset to default if negative
    209    * or null)
    210    * @param maxNumChecks maximal number of checks for each iteration
    211    * (the number of checks is reset to default if negative or null)
    212    * @param stepsizeReductionFactor stepsize reduction factor in case of
    213    * failure (the factor is reset to default if lower than 0.0001 or
    214    * greater than 0.9999)
    215    */
    216   public void setStabilityCheck(final boolean performStabilityCheck,
    217                                 final int maxNumIter, final int maxNumChecks,
    218                                 final double stepsizeReductionFactor) {
    219 
    220     this.performTest = performStabilityCheck;
    221     this.maxIter     = (maxNumIter   <= 0) ? 2 : maxNumIter;
    222     this.maxChecks   = (maxNumChecks <= 0) ? 1 : maxNumChecks;
    223 
    224     if ((stepsizeReductionFactor < 0.0001) || (stepsizeReductionFactor > 0.9999)) {
    225       this.stabilityReduction = 0.5;
    226     } else {
    227       this.stabilityReduction = stepsizeReductionFactor;
    228     }
    229 
    230   }
    231 
    232   /** Set the step size control factors.
    233 
    234    * <p>The new step size hNew is computed from the old one h by:
    235    * <pre>
    236    * hNew = h * stepControl2 / (err/stepControl1)^(1/(2k+1))
    237    * </pre>
    238    * where err is the scaled error and k the iteration number of the
    239    * extrapolation scheme (counting from 0). The default values are
    240    * 0.65 for stepControl1 and 0.94 for stepControl2.</p>
    241    * <p>The step size is subject to the restriction:
    242    * <pre>
    243    * stepControl3^(1/(2k+1))/stepControl4 <= hNew/h <= 1/stepControl3^(1/(2k+1))
    244    * </pre>
    245    * The default values are 0.02 for stepControl3 and 4.0 for
    246    * stepControl4.</p>
    247    * @param control1 first stepsize control factor (the factor is
    248    * reset to default if lower than 0.0001 or greater than 0.9999)
    249    * @param control2 second stepsize control factor (the factor
    250    * is reset to default if lower than 0.0001 or greater than 0.9999)
    251    * @param control3 third stepsize control factor (the factor is
    252    * reset to default if lower than 0.0001 or greater than 0.9999)
    253    * @param control4 fourth stepsize control factor (the factor
    254    * is reset to default if lower than 1.0001 or greater than 999.9)
    255    */
    256   public void setStepsizeControl(final double control1, final double control2,
    257                                  final double control3, final double control4) {
    258 
    259     if ((control1 < 0.0001) || (control1 > 0.9999)) {
    260       this.stepControl1 = 0.65;
    261     } else {
    262       this.stepControl1 = control1;
    263     }
    264 
    265     if ((control2 < 0.0001) || (control2 > 0.9999)) {
    266       this.stepControl2 = 0.94;
    267     } else {
    268       this.stepControl2 = control2;
    269     }
    270 
    271     if ((control3 < 0.0001) || (control3 > 0.9999)) {
    272       this.stepControl3 = 0.02;
    273     } else {
    274       this.stepControl3 = control3;
    275     }
    276 
    277     if ((control4 < 1.0001) || (control4 > 999.9)) {
    278       this.stepControl4 = 4.0;
    279     } else {
    280       this.stepControl4 = control4;
    281     }
    282 
    283   }
    284 
    285   /** Set the order control parameters.
    286    * <p>The Gragg-Bulirsch-Stoer method changes both the step size and
    287    * the order during integration, in order to minimize computation
    288    * cost. Each extrapolation step increases the order by 2, so the
    289    * maximal order that will be used is always even, it is twice the
    290    * maximal number of columns in the extrapolation table.</p>
    291    * <pre>
    292    * order is decreased if w(k-1) <= w(k)   * orderControl1
    293    * order is increased if w(k)   <= w(k-1) * orderControl2
    294    * </pre>
    295    * <p>where w is the table of work per unit step for each order
    296    * (number of function calls divided by the step length), and k is
    297    * the current order.</p>
    298    * <p>The default maximal order after construction is 18 (i.e. the
    299    * maximal number of columns is 9). The default values are 0.8 for
    300    * orderControl1 and 0.9 for orderControl2.</p>
    301    * @param maximalOrder maximal order in the extrapolation table (the
    302    * maximal order is reset to default if order <= 6 or odd)
    303    * @param control1 first order control factor (the factor is
    304    * reset to default if lower than 0.0001 or greater than 0.9999)
    305    * @param control2 second order control factor (the factor
    306    * is reset to default if lower than 0.0001 or greater than 0.9999)
    307    */
    308   public void setOrderControl(final int maximalOrder,
    309                               final double control1, final double control2) {
    310 
    311     if ((maximalOrder <= 6) || (maximalOrder % 2 != 0)) {
    312       this.maxOrder = 18;
    313     }
    314 
    315     if ((control1 < 0.0001) || (control1 > 0.9999)) {
    316       this.orderControl1 = 0.8;
    317     } else {
    318       this.orderControl1 = control1;
    319     }
    320 
    321     if ((control2 < 0.0001) || (control2 > 0.9999)) {
    322       this.orderControl2 = 0.9;
    323     } else {
    324       this.orderControl2 = control2;
    325     }
    326 
    327     // reinitialize the arrays
    328     initializeArrays();
    329 
    330   }
    331 
    332   /** {@inheritDoc} */
    333   @Override
    334   public void addStepHandler (final StepHandler handler) {
    335 
    336     super.addStepHandler(handler);
    337 
    338     // reinitialize the arrays
    339     initializeArrays();
    340 
    341   }
    342 
    343   /** {@inheritDoc} */
    344   @Override
    345   public void addEventHandler(final EventHandler function,
    346                               final double maxCheckInterval,
    347                               final double convergence,
    348                               final int maxIterationCount) {
    349     super.addEventHandler(function, maxCheckInterval, convergence, maxIterationCount);
    350 
    351     // reinitialize the arrays
    352     initializeArrays();
    353 
    354   }
    355 
    356   /** Initialize the integrator internal arrays. */
    357   private void initializeArrays() {
    358 
    359     final int size = maxOrder / 2;
    360 
    361     if ((sequence == null) || (sequence.length != size)) {
    362       // all arrays should be reallocated with the right size
    363       sequence        = new int[size];
    364       costPerStep     = new int[size];
    365       coeff           = new double[size][];
    366       costPerTimeUnit = new double[size];
    367       optimalStep     = new double[size];
    368     }
    369 
    370     if (requiresDenseOutput()) {
    371       // step size sequence: 2, 6, 10, 14, ...
    372       for (int k = 0; k < size; ++k) {
    373         sequence[k] = 4 * k + 2;
    374       }
    375     } else {
    376       // step size sequence: 2, 4, 6, 8, ...
    377       for (int k = 0; k < size; ++k) {
    378         sequence[k] = 2 * (k + 1);
    379       }
    380     }
    381 
    382     // initialize the order selection cost array
    383     // (number of function calls for each column of the extrapolation table)
    384     costPerStep[0] = sequence[0] + 1;
    385     for (int k = 1; k < size; ++k) {
    386       costPerStep[k] = costPerStep[k-1] + sequence[k];
    387     }
    388 
    389     // initialize the extrapolation tables
    390     for (int k = 0; k < size; ++k) {
    391       coeff[k] = (k > 0) ? new double[k] : null;
    392       for (int l = 0; l < k; ++l) {
    393         final double ratio = ((double) sequence[k]) / sequence[k-l-1];
    394         coeff[k][l] = 1.0 / (ratio * ratio - 1.0);
    395       }
    396     }
    397 
    398   }
    399 
    400   /** Set the interpolation order control parameter.
    401    * The interpolation order for dense output is 2k - mudif + 1. The
    402    * default value for mudif is 4 and the interpolation error is used
    403    * in stepsize control by default.
    404    *
    405    * @param useInterpolationErrorForControl if true, interpolation error is used
    406    * for stepsize control
    407    * @param mudifControlParameter interpolation order control parameter (the parameter
    408    * is reset to default if <= 0 or >= 7)
    409    */
    410   public void setInterpolationControl(final boolean useInterpolationErrorForControl,
    411                                       final int mudifControlParameter) {
    412 
    413     this.useInterpolationError = useInterpolationErrorForControl;
    414 
    415     if ((mudifControlParameter <= 0) || (mudifControlParameter >= 7)) {
    416       this.mudif = 4;
    417     } else {
    418       this.mudif = mudifControlParameter;
    419     }
    420 
    421   }
    422 
    423   /** Update scaling array.
    424    * @param y1 first state vector to use for scaling
    425    * @param y2 second state vector to use for scaling
    426    * @param scale scaling array to update (can be shorter than state)
    427    */
    428   private void rescale(final double[] y1, final double[] y2, final double[] scale) {
    429     if (vecAbsoluteTolerance == null) {
    430       for (int i = 0; i < scale.length; ++i) {
    431         final double yi = FastMath.max(FastMath.abs(y1[i]), FastMath.abs(y2[i]));
    432         scale[i] = scalAbsoluteTolerance + scalRelativeTolerance * yi;
    433       }
    434     } else {
    435       for (int i = 0; i < scale.length; ++i) {
    436         final double yi = FastMath.max(FastMath.abs(y1[i]), FastMath.abs(y2[i]));
    437         scale[i] = vecAbsoluteTolerance[i] + vecRelativeTolerance[i] * yi;
    438       }
    439     }
    440   }
    441 
    442   /** Perform integration over one step using substeps of a modified
    443    * midpoint method.
    444    * @param t0 initial time
    445    * @param y0 initial value of the state vector at t0
    446    * @param step global step
    447    * @param k iteration number (from 0 to sequence.length - 1)
    448    * @param scale scaling array (can be shorter than state)
    449    * @param f placeholder where to put the state vector derivatives at each substep
    450    *          (element 0 already contains initial derivative)
    451    * @param yMiddle placeholder where to put the state vector at the middle of the step
    452    * @param yEnd placeholder where to put the state vector at the end
    453    * @param yTmp placeholder for one state vector
    454    * @return true if computation was done properly,
    455    *         false if stability check failed before end of computation
    456    * @throws DerivativeException this exception is propagated to the caller if the
    457    * underlying user function triggers one
    458    */
    459   private boolean tryStep(final double t0, final double[] y0, final double step, final int k,
    460                           final double[] scale, final double[][] f,
    461                           final double[] yMiddle, final double[] yEnd,
    462                           final double[] yTmp)
    463     throws DerivativeException {
    464 
    465     final int    n        = sequence[k];
    466     final double subStep  = step / n;
    467     final double subStep2 = 2 * subStep;
    468 
    469     // first substep
    470     double t = t0 + subStep;
    471     for (int i = 0; i < y0.length; ++i) {
    472       yTmp[i] = y0[i];
    473       yEnd[i] = y0[i] + subStep * f[0][i];
    474     }
    475     computeDerivatives(t, yEnd, f[1]);
    476 
    477     // other substeps
    478     for (int j = 1; j < n; ++j) {
    479 
    480       if (2 * j == n) {
    481         // save the point at the middle of the step
    482         System.arraycopy(yEnd, 0, yMiddle, 0, y0.length);
    483       }
    484 
    485       t += subStep;
    486       for (int i = 0; i < y0.length; ++i) {
    487         final double middle = yEnd[i];
    488         yEnd[i]       = yTmp[i] + subStep2 * f[j][i];
    489         yTmp[i]       = middle;
    490       }
    491 
    492       computeDerivatives(t, yEnd, f[j+1]);
    493 
    494       // stability check
    495       if (performTest && (j <= maxChecks) && (k < maxIter)) {
    496         double initialNorm = 0.0;
    497         for (int l = 0; l < scale.length; ++l) {
    498           final double ratio = f[0][l] / scale[l];
    499           initialNorm += ratio * ratio;
    500         }
    501         double deltaNorm = 0.0;
    502         for (int l = 0; l < scale.length; ++l) {
    503           final double ratio = (f[j+1][l] - f[0][l]) / scale[l];
    504           deltaNorm += ratio * ratio;
    505         }
    506         if (deltaNorm > 4 * FastMath.max(1.0e-15, initialNorm)) {
    507           return false;
    508         }
    509       }
    510 
    511     }
    512 
    513     // correction of the last substep (at t0 + step)
    514     for (int i = 0; i < y0.length; ++i) {
    515       yEnd[i] = 0.5 * (yTmp[i] + yEnd[i] + subStep * f[n][i]);
    516     }
    517 
    518     return true;
    519 
    520   }
    521 
    522   /** Extrapolate a vector.
    523    * @param offset offset to use in the coefficients table
    524    * @param k index of the last updated point
    525    * @param diag working diagonal of the Aitken-Neville's
    526    * triangle, without the last element
    527    * @param last last element
    528    */
    529   private void extrapolate(final int offset, final int k,
    530                            final double[][] diag, final double[] last) {
    531 
    532     // update the diagonal
    533     for (int j = 1; j < k; ++j) {
    534       for (int i = 0; i < last.length; ++i) {
    535         // Aitken-Neville's recursive formula
    536         diag[k-j-1][i] = diag[k-j][i] +
    537                          coeff[k+offset][j-1] * (diag[k-j][i] - diag[k-j-1][i]);
    538       }
    539     }
    540 
    541     // update the last element
    542     for (int i = 0; i < last.length; ++i) {
    543       // Aitken-Neville's recursive formula
    544       last[i] = diag[0][i] + coeff[k+offset][k-1] * (diag[0][i] - last[i]);
    545     }
    546   }
    547 
    548   /** {@inheritDoc} */
    549   @Override
    550   public double integrate(final FirstOrderDifferentialEquations equations,
    551                           final double t0, final double[] y0, final double t, final double[] y)
    552       throws DerivativeException, IntegratorException {
    553 
    554     sanityChecks(equations, t0, y0, t, y);
    555     setEquations(equations);
    556     resetEvaluations();
    557     final boolean forward = t > t0;
    558 
    559     // create some internal working arrays
    560     final double[] yDot0   = new double[y0.length];
    561     final double[] y1      = new double[y0.length];
    562     final double[] yTmp    = new double[y0.length];
    563     final double[] yTmpDot = new double[y0.length];
    564 
    565     final double[][] diagonal = new double[sequence.length-1][];
    566     final double[][] y1Diag = new double[sequence.length-1][];
    567     for (int k = 0; k < sequence.length-1; ++k) {
    568       diagonal[k] = new double[y0.length];
    569       y1Diag[k] = new double[y0.length];
    570     }
    571 
    572     final double[][][] fk  = new double[sequence.length][][];
    573     for (int k = 0; k < sequence.length; ++k) {
    574 
    575       fk[k]    = new double[sequence[k] + 1][];
    576 
    577       // all substeps start at the same point, so share the first array
    578       fk[k][0] = yDot0;
    579 
    580       for (int l = 0; l < sequence[k]; ++l) {
    581         fk[k][l+1] = new double[y0.length];
    582       }
    583 
    584     }
    585 
    586     if (y != y0) {
    587       System.arraycopy(y0, 0, y, 0, y0.length);
    588     }
    589 
    590     double[] yDot1      = new double[y0.length];
    591     double[][] yMidDots = null;
    592     final boolean denseOutput = requiresDenseOutput();
    593     if (denseOutput) {
    594       yMidDots = new double[1 + 2 * sequence.length][];
    595       for (int j = 0; j < yMidDots.length; ++j) {
    596         yMidDots[j] = new double[y0.length];
    597       }
    598     } else {
    599       yMidDots    = new double[1][];
    600       yMidDots[0] = new double[y0.length];
    601     }
    602 
    603     // initial scaling
    604     final double[] scale = new double[mainSetDimension];
    605     rescale(y, y, scale);
    606 
    607     // initial order selection
    608     final double tol =
    609         (vecRelativeTolerance == null) ? scalRelativeTolerance : vecRelativeTolerance[0];
    610     final double log10R = FastMath.log10(FastMath.max(1.0e-10, tol));
    611     int targetIter = FastMath.max(1,
    612                               FastMath.min(sequence.length - 2,
    613                                        (int) FastMath.floor(0.5 - 0.6 * log10R)));
    614     // set up an interpolator sharing the integrator arrays
    615     AbstractStepInterpolator interpolator = null;
    616     if (denseOutput) {
    617       interpolator = new GraggBulirschStoerStepInterpolator(y, yDot0,
    618                                                             y1, yDot1,
    619                                                             yMidDots, forward);
    620     } else {
    621       interpolator = new DummyStepInterpolator(y, yDot1, forward);
    622     }
    623     interpolator.storeTime(t0);
    624 
    625     stepStart = t0;
    626     double  hNew             = 0;
    627     double  maxError         = Double.MAX_VALUE;
    628     boolean previousRejected = false;
    629     boolean firstTime        = true;
    630     boolean newStep          = true;
    631     boolean firstStepAlreadyComputed = false;
    632     for (StepHandler handler : stepHandlers) {
    633         handler.reset();
    634     }
    635     setStateInitialized(false);
    636     costPerTimeUnit[0] = 0;
    637     isLastStep = false;
    638     do {
    639 
    640       double error;
    641       boolean reject = false;
    642 
    643       if (newStep) {
    644 
    645         interpolator.shift();
    646 
    647         // first evaluation, at the beginning of the step
    648         if (! firstStepAlreadyComputed) {
    649           computeDerivatives(stepStart, y, yDot0);
    650         }
    651 
    652         if (firstTime) {
    653           hNew = initializeStep(equations, forward,
    654                                 2 * targetIter + 1, scale,
    655                                 stepStart, y, yDot0, yTmp, yTmpDot);
    656         }
    657 
    658         newStep = false;
    659 
    660       }
    661 
    662       stepSize = hNew;
    663 
    664       // step adjustment near bounds
    665       if ((forward && (stepStart + stepSize > t)) ||
    666           ((! forward) && (stepStart + stepSize < t))) {
    667         stepSize = t - stepStart;
    668       }
    669       final double nextT = stepStart + stepSize;
    670       isLastStep = forward ? (nextT >= t) : (nextT <= t);
    671 
    672       // iterate over several substep sizes
    673       int k = -1;
    674       for (boolean loop = true; loop; ) {
    675 
    676         ++k;
    677 
    678         // modified midpoint integration with the current substep
    679         if ( ! tryStep(stepStart, y, stepSize, k, scale, fk[k],
    680                        (k == 0) ? yMidDots[0] : diagonal[k-1],
    681                        (k == 0) ? y1 : y1Diag[k-1],
    682                        yTmp)) {
    683 
    684           // the stability check failed, we reduce the global step
    685           hNew   = FastMath.abs(filterStep(stepSize * stabilityReduction, forward, false));
    686           reject = true;
    687           loop   = false;
    688 
    689         } else {
    690 
    691           // the substep was computed successfully
    692           if (k > 0) {
    693 
    694             // extrapolate the state at the end of the step
    695             // using last iteration data
    696             extrapolate(0, k, y1Diag, y1);
    697             rescale(y, y1, scale);
    698 
    699             // estimate the error at the end of the step.
    700             error = 0;
    701             for (int j = 0; j < mainSetDimension; ++j) {
    702               final double e = FastMath.abs(y1[j] - y1Diag[0][j]) / scale[j];
    703               error += e * e;
    704             }
    705             error = FastMath.sqrt(error / mainSetDimension);
    706 
    707             if ((error > 1.0e15) || ((k > 1) && (error > maxError))) {
    708               // error is too big, we reduce the global step
    709               hNew   = FastMath.abs(filterStep(stepSize * stabilityReduction, forward, false));
    710               reject = true;
    711               loop   = false;
    712             } else {
    713 
    714               maxError = FastMath.max(4 * error, 1.0);
    715 
    716               // compute optimal stepsize for this order
    717               final double exp = 1.0 / (2 * k + 1);
    718               double fac = stepControl2 / FastMath.pow(error / stepControl1, exp);
    719               final double pow = FastMath.pow(stepControl3, exp);
    720               fac = FastMath.max(pow / stepControl4, FastMath.min(1 / pow, fac));
    721               optimalStep[k]     = FastMath.abs(filterStep(stepSize * fac, forward, true));
    722               costPerTimeUnit[k] = costPerStep[k] / optimalStep[k];
    723 
    724               // check convergence
    725               switch (k - targetIter) {
    726 
    727               case -1 :
    728                 if ((targetIter > 1) && ! previousRejected) {
    729 
    730                   // check if we can stop iterations now
    731                   if (error <= 1.0) {
    732                     // convergence have been reached just before targetIter
    733                     loop = false;
    734                   } else {
    735                     // estimate if there is a chance convergence will
    736                     // be reached on next iteration, using the
    737                     // asymptotic evolution of error
    738                     final double ratio = ((double) sequence [targetIter] * sequence[targetIter + 1]) /
    739                                          (sequence[0] * sequence[0]);
    740                     if (error > ratio * ratio) {
    741                       // we don't expect to converge on next iteration
    742                       // we reject the step immediately and reduce order
    743                       reject = true;
    744                       loop   = false;
    745                       targetIter = k;
    746                       if ((targetIter > 1) &&
    747                           (costPerTimeUnit[targetIter-1] <
    748                            orderControl1 * costPerTimeUnit[targetIter])) {
    749                         --targetIter;
    750                       }
    751                       hNew = optimalStep[targetIter];
    752                     }
    753                   }
    754                 }
    755                 break;
    756 
    757               case 0:
    758                 if (error <= 1.0) {
    759                   // convergence has been reached exactly at targetIter
    760                   loop = false;
    761                 } else {
    762                   // estimate if there is a chance convergence will
    763                   // be reached on next iteration, using the
    764                   // asymptotic evolution of error
    765                   final double ratio = ((double) sequence[k+1]) / sequence[0];
    766                   if (error > ratio * ratio) {
    767                     // we don't expect to converge on next iteration
    768                     // we reject the step immediately
    769                     reject = true;
    770                     loop = false;
    771                     if ((targetIter > 1) &&
    772                         (costPerTimeUnit[targetIter-1] <
    773                          orderControl1 * costPerTimeUnit[targetIter])) {
    774                       --targetIter;
    775                     }
    776                     hNew = optimalStep[targetIter];
    777                   }
    778                 }
    779                 break;
    780 
    781               case 1 :
    782                 if (error > 1.0) {
    783                   reject = true;
    784                   if ((targetIter > 1) &&
    785                       (costPerTimeUnit[targetIter-1] <
    786                        orderControl1 * costPerTimeUnit[targetIter])) {
    787                     --targetIter;
    788                   }
    789                   hNew = optimalStep[targetIter];
    790                 }
    791                 loop = false;
    792                 break;
    793 
    794               default :
    795                 if ((firstTime || isLastStep) && (error <= 1.0)) {
    796                   loop = false;
    797                 }
    798                 break;
    799 
    800               }
    801 
    802             }
    803           }
    804         }
    805       }
    806 
    807       if (! reject) {
    808           // derivatives at end of step
    809           computeDerivatives(stepStart + stepSize, y1, yDot1);
    810       }
    811 
    812       // dense output handling
    813       double hInt = getMaxStep();
    814       if (denseOutput && ! reject) {
    815 
    816         // extrapolate state at middle point of the step
    817         for (int j = 1; j <= k; ++j) {
    818           extrapolate(0, j, diagonal, yMidDots[0]);
    819         }
    820 
    821         final int mu = 2 * k - mudif + 3;
    822 
    823         for (int l = 0; l < mu; ++l) {
    824 
    825           // derivative at middle point of the step
    826           final int l2 = l / 2;
    827           double factor = FastMath.pow(0.5 * sequence[l2], l);
    828           int middleIndex = fk[l2].length / 2;
    829           for (int i = 0; i < y0.length; ++i) {
    830             yMidDots[l+1][i] = factor * fk[l2][middleIndex + l][i];
    831           }
    832           for (int j = 1; j <= k - l2; ++j) {
    833             factor = FastMath.pow(0.5 * sequence[j + l2], l);
    834             middleIndex = fk[l2+j].length / 2;
    835             for (int i = 0; i < y0.length; ++i) {
    836               diagonal[j-1][i] = factor * fk[l2+j][middleIndex+l][i];
    837             }
    838             extrapolate(l2, j, diagonal, yMidDots[l+1]);
    839           }
    840           for (int i = 0; i < y0.length; ++i) {
    841             yMidDots[l+1][i] *= stepSize;
    842           }
    843 
    844           // compute centered differences to evaluate next derivatives
    845           for (int j = (l + 1) / 2; j <= k; ++j) {
    846             for (int m = fk[j].length - 1; m >= 2 * (l + 1); --m) {
    847               for (int i = 0; i < y0.length; ++i) {
    848                 fk[j][m][i] -= fk[j][m-2][i];
    849               }
    850             }
    851           }
    852 
    853         }
    854 
    855         if (mu >= 0) {
    856 
    857           // estimate the dense output coefficients
    858           final GraggBulirschStoerStepInterpolator gbsInterpolator
    859             = (GraggBulirschStoerStepInterpolator) interpolator;
    860           gbsInterpolator.computeCoefficients(mu, stepSize);
    861 
    862           if (useInterpolationError) {
    863             // use the interpolation error to limit stepsize
    864             final double interpError = gbsInterpolator.estimateError(scale);
    865             hInt = FastMath.abs(stepSize / FastMath.max(FastMath.pow(interpError, 1.0 / (mu+4)),
    866                                                 0.01));
    867             if (interpError > 10.0) {
    868               hNew = hInt;
    869               reject = true;
    870             }
    871           }
    872 
    873         }
    874 
    875       }
    876 
    877       if (! reject) {
    878 
    879         // Discrete events handling
    880         interpolator.storeTime(stepStart + stepSize);
    881         stepStart = acceptStep(interpolator, y1, yDot1, t);
    882 
    883         // prepare next step
    884         interpolator.storeTime(stepStart);
    885         System.arraycopy(y1, 0, y, 0, y0.length);
    886         System.arraycopy(yDot1, 0, yDot0, 0, y0.length);
    887         firstStepAlreadyComputed = true;
    888 
    889         int optimalIter;
    890         if (k == 1) {
    891           optimalIter = 2;
    892           if (previousRejected) {
    893             optimalIter = 1;
    894           }
    895         } else if (k <= targetIter) {
    896           optimalIter = k;
    897           if (costPerTimeUnit[k-1] < orderControl1 * costPerTimeUnit[k]) {
    898             optimalIter = k-1;
    899           } else if (costPerTimeUnit[k] < orderControl2 * costPerTimeUnit[k-1]) {
    900             optimalIter = FastMath.min(k+1, sequence.length - 2);
    901           }
    902         } else {
    903           optimalIter = k - 1;
    904           if ((k > 2) &&
    905               (costPerTimeUnit[k-2] < orderControl1 * costPerTimeUnit[k-1])) {
    906             optimalIter = k - 2;
    907           }
    908           if (costPerTimeUnit[k] < orderControl2 * costPerTimeUnit[optimalIter]) {
    909             optimalIter = FastMath.min(k, sequence.length - 2);
    910           }
    911         }
    912 
    913         if (previousRejected) {
    914           // after a rejected step neither order nor stepsize
    915           // should increase
    916           targetIter = FastMath.min(optimalIter, k);
    917           hNew = FastMath.min(FastMath.abs(stepSize), optimalStep[targetIter]);
    918         } else {
    919           // stepsize control
    920           if (optimalIter <= k) {
    921             hNew = optimalStep[optimalIter];
    922           } else {
    923             if ((k < targetIter) &&
    924                 (costPerTimeUnit[k] < orderControl2 * costPerTimeUnit[k-1])) {
    925               hNew = filterStep(optimalStep[k] * costPerStep[optimalIter+1] / costPerStep[k],
    926                                forward, false);
    927             } else {
    928               hNew = filterStep(optimalStep[k] * costPerStep[optimalIter] / costPerStep[k],
    929                                 forward, false);
    930             }
    931           }
    932 
    933           targetIter = optimalIter;
    934 
    935         }
    936 
    937         newStep = true;
    938 
    939       }
    940 
    941       hNew = FastMath.min(hNew, hInt);
    942       if (! forward) {
    943         hNew = -hNew;
    944       }
    945 
    946       firstTime = false;
    947 
    948       if (reject) {
    949         isLastStep = false;
    950         previousRejected = true;
    951       } else {
    952         previousRejected = false;
    953       }
    954 
    955     } while (!isLastStep);
    956 
    957     final double stopTime = stepStart;
    958     resetInternalState();
    959     return stopTime;
    960 
    961   }
    962 
    963 }
    964