Home | History | Annotate | Download | only in jacobians
      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.jacobians;
     19 
     20 import java.io.IOException;
     21 import java.io.ObjectInput;
     22 import java.io.ObjectOutput;
     23 import java.lang.reflect.Array;
     24 import java.util.ArrayList;
     25 import java.util.Collection;
     26 
     27 import org.apache.commons.math.MathRuntimeException;
     28 import org.apache.commons.math.MaxEvaluationsExceededException;
     29 import org.apache.commons.math.ode.DerivativeException;
     30 import org.apache.commons.math.exception.util.LocalizedFormats;
     31 import org.apache.commons.math.ode.ExtendedFirstOrderDifferentialEquations;
     32 import org.apache.commons.math.ode.FirstOrderIntegrator;
     33 import org.apache.commons.math.ode.IntegratorException;
     34 import org.apache.commons.math.ode.events.EventException;
     35 import org.apache.commons.math.ode.events.EventHandler;
     36 import org.apache.commons.math.ode.sampling.StepHandler;
     37 import org.apache.commons.math.ode.sampling.StepInterpolator;
     38 
     39 /** This class enhances a first order integrator for differential equations to
     40  * compute also partial derivatives of the solution with respect to initial state
     41  * and parameters.
     42  * <p>In order to compute both the state and its derivatives, the ODE problem
     43  * is extended with jacobians of the raw ODE and the variational equations are
     44  * added to form a new compound problem of higher dimension. If the original ODE
     45  * problem has dimension n and there are p parameters, the compound problem will
     46  * have dimension n &times; (1 + n + p).</p>
     47  * @see ParameterizedODE
     48  * @see ODEWithJacobians
     49  * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 fvr. 2011) $
     50  * @since 2.1
     51  * @deprecated as of 2.2 the complete package is deprecated, it will be replaced
     52  * in 3.0 by a completely rewritten implementation
     53  */
     54 @Deprecated
     55 public class FirstOrderIntegratorWithJacobians {
     56 
     57     /** Underlying integrator for compound problem. */
     58     private final FirstOrderIntegrator integrator;
     59 
     60     /** Raw equations to integrate. */
     61     private final ODEWithJacobians ode;
     62 
     63     /** Maximal number of evaluations allowed. */
     64     private int maxEvaluations;
     65 
     66     /** Number of evaluations already performed. */
     67     private int evaluations;
     68 
     69     /** Build an enhanced integrator using internal differentiation to compute jacobians.
     70      * @param integrator underlying integrator to solve the compound problem
     71      * @param ode original problem (f in the equation y' = f(t, y))
     72      * @param p parameters array (may be null if {@link
     73      * ParameterizedODE#getParametersDimension()
     74      * getParametersDimension()} from original problem is zero)
     75      * @param hY step sizes to use for computing the jacobian df/dy, must have the
     76      * same dimension as the original problem
     77      * @param hP step sizes to use for computing the jacobian df/dp, must have the
     78      * same dimension as the original problem parameters dimension
     79      * @see #FirstOrderIntegratorWithJacobians(FirstOrderIntegrator,
     80      * ODEWithJacobians)
     81      */
     82     public FirstOrderIntegratorWithJacobians(final FirstOrderIntegrator integrator,
     83                                              final ParameterizedODE ode,
     84                                              final double[] p, final double[] hY, final double[] hP) {
     85         checkDimension(ode.getDimension(), hY);
     86         checkDimension(ode.getParametersDimension(), p);
     87         checkDimension(ode.getParametersDimension(), hP);
     88         this.integrator = integrator;
     89         this.ode = new FiniteDifferencesWrapper(ode, p, hY, hP);
     90         setMaxEvaluations(-1);
     91     }
     92 
     93     /** Build an enhanced integrator using ODE builtin jacobian computation features.
     94      * @param integrator underlying integrator to solve the compound problem
     95      * @param ode original problem, which can compute the jacobians by itself
     96      * @see #FirstOrderIntegratorWithJacobians(FirstOrderIntegrator,
     97      * ParameterizedODE, double[], double[], double[])
     98      */
     99     public FirstOrderIntegratorWithJacobians(final FirstOrderIntegrator integrator,
    100                                              final ODEWithJacobians ode) {
    101         this.integrator = integrator;
    102         this.ode = ode;
    103         setMaxEvaluations(-1);
    104     }
    105 
    106     /** Add a step handler to this integrator.
    107      * <p>The handler will be called by the integrator for each accepted
    108      * step.</p>
    109      * @param handler handler for the accepted steps
    110      * @see #getStepHandlers()
    111      * @see #clearStepHandlers()
    112      */
    113     public void addStepHandler(StepHandlerWithJacobians handler) {
    114         final int n = ode.getDimension();
    115         final int k = ode.getParametersDimension();
    116         integrator.addStepHandler(new StepHandlerWrapper(handler, n, k));
    117     }
    118 
    119     /** Get all the step handlers that have been added to the integrator.
    120      * @return an unmodifiable collection of the added events handlers
    121      * @see #addStepHandler(StepHandlerWithJacobians)
    122      * @see #clearStepHandlers()
    123      */
    124     public Collection<StepHandlerWithJacobians> getStepHandlers() {
    125         final Collection<StepHandlerWithJacobians> handlers =
    126             new ArrayList<StepHandlerWithJacobians>();
    127         for (final StepHandler handler : integrator.getStepHandlers()) {
    128             if (handler instanceof StepHandlerWrapper) {
    129                 handlers.add(((StepHandlerWrapper) handler).getHandler());
    130             }
    131         }
    132         return handlers;
    133     }
    134 
    135     /** Remove all the step handlers that have been added to the integrator.
    136      * @see #addStepHandler(StepHandlerWithJacobians)
    137      * @see #getStepHandlers()
    138      */
    139     public void clearStepHandlers() {
    140         integrator.clearStepHandlers();
    141     }
    142 
    143     /** Add an event handler to the integrator.
    144      * @param handler event handler
    145      * @param maxCheckInterval maximal time interval between switching
    146      * function checks (this interval prevents missing sign changes in
    147      * case the integration steps becomes very large)
    148      * @param convergence convergence threshold in the event time search
    149      * @param maxIterationCount upper limit of the iteration count in
    150      * the event time search
    151      * @see #getEventHandlers()
    152      * @see #clearEventHandlers()
    153      */
    154     public void addEventHandler(EventHandlerWithJacobians handler,
    155                                 double maxCheckInterval,
    156                                 double convergence,
    157                                 int maxIterationCount) {
    158         final int n = ode.getDimension();
    159         final int k = ode.getParametersDimension();
    160         integrator.addEventHandler(new EventHandlerWrapper(handler, n, k),
    161                                    maxCheckInterval, convergence, maxIterationCount);
    162     }
    163 
    164     /** Get all the event handlers that have been added to the integrator.
    165      * @return an unmodifiable collection of the added events handlers
    166      * @see #addEventHandler(EventHandlerWithJacobians, double, double, int)
    167      * @see #clearEventHandlers()
    168      */
    169     public Collection<EventHandlerWithJacobians> getEventHandlers() {
    170         final Collection<EventHandlerWithJacobians> handlers =
    171             new ArrayList<EventHandlerWithJacobians>();
    172         for (final EventHandler handler : integrator.getEventHandlers()) {
    173             if (handler instanceof EventHandlerWrapper) {
    174                 handlers.add(((EventHandlerWrapper) handler).getHandler());
    175             }
    176         }
    177         return handlers;
    178     }
    179 
    180     /** Remove all the event handlers that have been added to the integrator.
    181      * @see #addEventHandler(EventHandlerWithJacobians, double, double, int)
    182      * @see #getEventHandlers()
    183      */
    184     public void clearEventHandlers() {
    185         integrator.clearEventHandlers();
    186     }
    187 
    188     /** Integrate the differential equations and the variational equations up to the given time.
    189      * <p>This method solves an Initial Value Problem (IVP) and also computes the derivatives
    190      * of the solution with respect to initial state and parameters. This can be used as
    191      * a basis to solve Boundary Value Problems (BVP).</p>
    192      * <p>Since this method stores some internal state variables made
    193      * available in its public interface during integration ({@link
    194      * #getCurrentSignedStepsize()}), it is <em>not</em> thread-safe.</p>
    195      * @param t0 initial time
    196      * @param y0 initial value of the state vector at t0
    197      * @param dY0dP initial value of the state vector derivative with respect to the
    198      * parameters at t0
    199      * @param t target time for the integration
    200      * (can be set to a value smaller than <code>t0</code> for backward integration)
    201      * @param y placeholder where to put the state vector at each successful
    202      *  step (and hence at the end of integration), can be the same object as y0
    203      * @param dYdY0 placeholder where to put the state vector derivative with respect
    204      * to the initial state (dy[i]/dy0[j] is in element array dYdY0[i][j]) at each successful
    205      *  step (and hence at the end of integration)
    206      * @param dYdP placeholder where to put the state vector derivative with respect
    207      * to the parameters (dy[i]/dp[j] is in element array dYdP[i][j]) at each successful
    208      *  step (and hence at the end of integration)
    209      * @return stop time, will be the same as target time if integration reached its
    210      * target, but may be different if some event handler stops it at some point.
    211      * @throws IntegratorException if the integrator cannot perform integration
    212      * @throws DerivativeException this exception is propagated to the caller if
    213      * the underlying user function triggers one
    214      */
    215     public double integrate(final double t0, final double[] y0, final double[][] dY0dP,
    216                             final double t, final double[] y,
    217                             final double[][] dYdY0, final double[][] dYdP)
    218         throws DerivativeException, IntegratorException {
    219 
    220         final int n = ode.getDimension();
    221         final int k = ode.getParametersDimension();
    222         checkDimension(n, y0);
    223         checkDimension(n, y);
    224         checkDimension(n, dYdY0);
    225         checkDimension(n, dYdY0[0]);
    226         if (k != 0) {
    227             checkDimension(n, dY0dP);
    228             checkDimension(k, dY0dP[0]);
    229             checkDimension(n, dYdP);
    230             checkDimension(k, dYdP[0]);
    231         }
    232 
    233         // set up initial state, including partial derivatives
    234         // the compound state z contains the raw state y and its derivatives
    235         // with respect to initial state y0 and to parameters p
    236         //    y[i]         is stored in z[i]
    237         //    dy[i]/dy0[j] is stored in z[n + i * n + j]
    238         //    dy[i]/dp[j]  is stored in z[n * (n + 1) + i * k + j]
    239         final double[] z = new double[n * (1 + n + k)];
    240         System.arraycopy(y0, 0, z, 0, n);
    241         for (int i = 0; i < n; ++i) {
    242 
    243             // set diagonal element of dy/dy0 to 1.0 at t = t0
    244             z[i * (1 + n) + n] = 1.0;
    245 
    246             // set initial derivatives with respect to parameters
    247             System.arraycopy(dY0dP[i], 0, z, n * (n + 1) + i * k, k);
    248 
    249         }
    250 
    251         // integrate the compound state variational equations
    252         evaluations = 0;
    253         final double stopTime = integrator.integrate(new MappingWrapper(), t0, z, t, z);
    254 
    255         // dispatch the final compound state into the state and partial derivatives arrays
    256         dispatchCompoundState(z, y, dYdY0, dYdP);
    257 
    258         return stopTime;
    259 
    260     }
    261 
    262     /** Dispatch a compound state array into state and jacobians arrays.
    263      * @param z compound state
    264      * @param y raw state array to fill
    265      * @param dydy0 jacobian array to fill
    266      * @param dydp jacobian array to fill
    267      */
    268     private static void dispatchCompoundState(final double[] z, final double[] y,
    269                                               final double[][] dydy0, final double[][] dydp) {
    270 
    271         final int n = y.length;
    272         final int k = dydp[0].length;
    273 
    274         // state
    275         System.arraycopy(z, 0, y, 0, n);
    276 
    277         // jacobian with respect to initial state
    278         for (int i = 0; i < n; ++i) {
    279             System.arraycopy(z, n * (i + 1), dydy0[i], 0, n);
    280         }
    281 
    282         // jacobian with respect to parameters
    283         for (int i = 0; i < n; ++i) {
    284             System.arraycopy(z, n * (n + 1) + i * k, dydp[i], 0, k);
    285         }
    286 
    287     }
    288 
    289     /** Get the current value of the step start time t<sub>i</sub>.
    290      * <p>This method can be called during integration (typically by
    291      * the object implementing the {@link org.apache.commons.math.ode.FirstOrderDifferentialEquations
    292      * differential equations} problem) if the value of the current step that
    293      * is attempted is needed.</p>
    294      * <p>The result is undefined if the method is called outside of
    295      * calls to <code>integrate</code>.</p>
    296      * @return current value of the step start time t<sub>i</sub>
    297      */
    298     public double getCurrentStepStart() {
    299         return integrator.getCurrentStepStart();
    300     }
    301 
    302     /** Get the current signed value of the integration stepsize.
    303      * <p>This method can be called during integration (typically by
    304      * the object implementing the {@link org.apache.commons.math.ode.FirstOrderDifferentialEquations
    305      * differential equations} problem) if the signed value of the current stepsize
    306      * that is tried is needed.</p>
    307      * <p>The result is undefined if the method is called outside of
    308      * calls to <code>integrate</code>.</p>
    309      * @return current signed value of the stepsize
    310      */
    311     public double getCurrentSignedStepsize() {
    312         return integrator.getCurrentSignedStepsize();
    313     }
    314 
    315     /** Set the maximal number of differential equations function evaluations.
    316      * <p>The purpose of this method is to avoid infinite loops which can occur
    317      * for example when stringent error constraints are set or when lots of
    318      * discrete events are triggered, thus leading to many rejected steps.</p>
    319      * @param maxEvaluations maximal number of function evaluations (negative
    320      * values are silently converted to maximal integer value, thus representing
    321      * almost unlimited evaluations)
    322      */
    323     public void setMaxEvaluations(int maxEvaluations) {
    324         this.maxEvaluations = (maxEvaluations < 0) ? Integer.MAX_VALUE : maxEvaluations;
    325     }
    326 
    327     /** Get the maximal number of functions evaluations.
    328      * @return maximal number of functions evaluations
    329      */
    330     public int getMaxEvaluations() {
    331         return maxEvaluations;
    332     }
    333 
    334     /** Get the number of evaluations of the differential equations function.
    335      * <p>
    336      * The number of evaluations corresponds to the last call to the
    337      * <code>integrate</code> method. It is 0 if the method has not been called yet.
    338      * </p>
    339      * @return number of evaluations of the differential equations function
    340      */
    341     public int getEvaluations() {
    342         return evaluations;
    343     }
    344 
    345     /** Check array dimensions.
    346      * @param expected expected dimension
    347      * @param array (may be null if expected is 0)
    348      * @throws IllegalArgumentException if the array dimension does not match the expected one
    349      */
    350     private void checkDimension(final int expected, final Object array)
    351         throws IllegalArgumentException {
    352         int arrayDimension = (array == null) ? 0 : Array.getLength(array);
    353         if (arrayDimension != expected) {
    354             throw MathRuntimeException.createIllegalArgumentException(
    355                   LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, arrayDimension, expected);
    356         }
    357     }
    358 
    359     /** Wrapper class used to map state and jacobians into compound state. */
    360     private class MappingWrapper implements  ExtendedFirstOrderDifferentialEquations {
    361 
    362         /** Current state. */
    363         private final double[]   y;
    364 
    365         /** Time derivative of the current state. */
    366         private final double[]   yDot;
    367 
    368         /** Derivatives of yDot with respect to state. */
    369         private final double[][] dFdY;
    370 
    371         /** Derivatives of yDot with respect to parameters. */
    372         private final double[][] dFdP;
    373 
    374         /** Simple constructor.
    375          */
    376         public MappingWrapper() {
    377 
    378             final int n = ode.getDimension();
    379             final int k = ode.getParametersDimension();
    380             y    = new double[n];
    381             yDot = new double[n];
    382             dFdY = new double[n][n];
    383             dFdP = new double[n][k];
    384 
    385         }
    386 
    387         /** {@inheritDoc} */
    388         public int getDimension() {
    389             final int n = y.length;
    390             final int k = dFdP[0].length;
    391             return n * (1 + n + k);
    392         }
    393 
    394         /** {@inheritDoc} */
    395         public int getMainSetDimension() {
    396             return ode.getDimension();
    397         }
    398 
    399         /** {@inheritDoc} */
    400         public void computeDerivatives(final double t, final double[] z, final double[] zDot)
    401             throws DerivativeException {
    402 
    403             final int n = y.length;
    404             final int k = dFdP[0].length;
    405 
    406             // compute raw ODE and its jacobians: dy/dt, d[dy/dt]/dy0 and d[dy/dt]/dp
    407             System.arraycopy(z,    0, y,    0, n);
    408             if (++evaluations > maxEvaluations) {
    409                 throw new DerivativeException(new MaxEvaluationsExceededException(maxEvaluations));
    410             }
    411             ode.computeDerivatives(t, y, yDot);
    412             ode.computeJacobians(t, y, yDot, dFdY, dFdP);
    413 
    414             // state part of the compound equations
    415             System.arraycopy(yDot, 0, zDot, 0, n);
    416 
    417             // variational equations: from d[dy/dt]/dy0 to d[dy/dy0]/dt
    418             for (int i = 0; i < n; ++i) {
    419                 final double[] dFdYi = dFdY[i];
    420                 for (int j = 0; j < n; ++j) {
    421                     double s = 0;
    422                     final int startIndex = n + j;
    423                     int zIndex = startIndex;
    424                     for (int l = 0; l < n; ++l) {
    425                         s += dFdYi[l] * z[zIndex];
    426                         zIndex += n;
    427                     }
    428                     zDot[startIndex + i * n] = s;
    429                 }
    430             }
    431 
    432             // variational equations: from d[dy/dt]/dy0 and d[dy/dt]/dp to d[dy/dp]/dt
    433             for (int i = 0; i < n; ++i) {
    434                 final double[] dFdYi = dFdY[i];
    435                 final double[] dFdPi = dFdP[i];
    436                 for (int j = 0; j < k; ++j) {
    437                     double s = dFdPi[j];
    438                     final int startIndex = n * (n + 1) + j;
    439                     int zIndex = startIndex;
    440                     for (int l = 0; l < n; ++l) {
    441                         s += dFdYi[l] * z[zIndex];
    442                         zIndex += k;
    443                     }
    444                     zDot[startIndex + i * k] = s;
    445                 }
    446             }
    447 
    448         }
    449 
    450     }
    451 
    452     /** Wrapper class to compute jacobians by finite differences for ODE which do not compute them themselves. */
    453     private class FiniteDifferencesWrapper implements ODEWithJacobians {
    454 
    455         /** Raw ODE without jacobians computation. */
    456         private final ParameterizedODE ode;
    457 
    458         /** Parameters array (may be null if parameters dimension from original problem is zero) */
    459         private final double[] p;
    460 
    461         /** Step sizes to use for computing the jacobian df/dy. */
    462         private final double[] hY;
    463 
    464         /** Step sizes to use for computing the jacobian df/dp. */
    465         private final double[] hP;
    466 
    467         /** Temporary array for state derivatives used to compute jacobians. */
    468         private final double[] tmpDot;
    469 
    470         /** Simple constructor.
    471          * @param ode original ODE problem, without jacobians computations
    472          * @param p parameters array (may be null if parameters dimension from original problem is zero)
    473          * @param hY step sizes to use for computing the jacobian df/dy
    474          * @param hP step sizes to use for computing the jacobian df/dp
    475          */
    476         public FiniteDifferencesWrapper(final ParameterizedODE ode,
    477                                         final double[] p, final double[] hY, final double[] hP) {
    478             this.ode = ode;
    479             this.p  = p.clone();
    480             this.hY = hY.clone();
    481             this.hP = hP.clone();
    482             tmpDot = new double[ode.getDimension()];
    483         }
    484 
    485         /** {@inheritDoc} */
    486         public int getDimension() {
    487             return ode.getDimension();
    488         }
    489 
    490         /** {@inheritDoc} */
    491         public void computeDerivatives(double t, double[] y, double[] yDot) throws DerivativeException {
    492             // this call to computeDerivatives has already been counted,
    493             // we must not increment the counter again
    494             ode.computeDerivatives(t, y, yDot);
    495         }
    496 
    497         /** {@inheritDoc} */
    498         public int getParametersDimension() {
    499             return ode.getParametersDimension();
    500         }
    501 
    502         /** {@inheritDoc} */
    503         public void computeJacobians(double t, double[] y, double[] yDot,
    504                                      double[][] dFdY, double[][] dFdP)
    505             throws DerivativeException {
    506 
    507             final int n = hY.length;
    508             final int k = hP.length;
    509 
    510             evaluations += n + k;
    511             if (evaluations > maxEvaluations) {
    512                 throw new DerivativeException(new MaxEvaluationsExceededException(maxEvaluations));
    513             }
    514 
    515             // compute df/dy where f is the ODE and y is the state array
    516             for (int j = 0; j < n; ++j) {
    517                 final double savedYj = y[j];
    518                 y[j] += hY[j];
    519                 ode.computeDerivatives(t, y, tmpDot);
    520                 for (int i = 0; i < n; ++i) {
    521                     dFdY[i][j] = (tmpDot[i] - yDot[i]) / hY[j];
    522                 }
    523                 y[j] = savedYj;
    524             }
    525 
    526             // compute df/dp where f is the ODE and p is the parameters array
    527             for (int j = 0; j < k; ++j) {
    528                 ode.setParameter(j, p[j] +  hP[j]);
    529                 ode.computeDerivatives(t, y, tmpDot);
    530                 for (int i = 0; i < n; ++i) {
    531                     dFdP[i][j] = (tmpDot[i] - yDot[i]) / hP[j];
    532                 }
    533                 ode.setParameter(j, p[j]);
    534             }
    535 
    536         }
    537 
    538     }
    539 
    540     /** Wrapper for step handlers. */
    541     private static class StepHandlerWrapper implements StepHandler {
    542 
    543         /** Underlying step handler with jacobians. */
    544         private final StepHandlerWithJacobians handler;
    545 
    546         /** Dimension of the original ODE. */
    547         private final int n;
    548 
    549         /** Number of parameters. */
    550         private final int k;
    551 
    552         /** Simple constructor.
    553          * @param handler underlying step handler with jacobians
    554          * @param n dimension of the original ODE
    555          * @param k number of parameters
    556          */
    557         public StepHandlerWrapper(final StepHandlerWithJacobians handler,
    558                                   final int n, final int k) {
    559             this.handler = handler;
    560             this.n       = n;
    561             this.k       = k;
    562         }
    563 
    564         /** Get the underlying step handler with jacobians.
    565          * @return underlying step handler with jacobians
    566          */
    567         public StepHandlerWithJacobians getHandler() {
    568             return handler;
    569         }
    570 
    571         /** {@inheritDoc} */
    572         public void handleStep(StepInterpolator interpolator, boolean isLast)
    573             throws DerivativeException {
    574             handler.handleStep(new StepInterpolatorWrapper(interpolator, n, k), isLast);
    575         }
    576 
    577         /** {@inheritDoc} */
    578         public boolean requiresDenseOutput() {
    579             return handler.requiresDenseOutput();
    580         }
    581 
    582         /** {@inheritDoc} */
    583         public void reset() {
    584             handler.reset();
    585         }
    586 
    587     }
    588 
    589     /** Wrapper for step interpolators. */
    590     private static class StepInterpolatorWrapper
    591         implements StepInterpolatorWithJacobians {
    592 
    593         /** Wrapped interpolator. */
    594         private StepInterpolator interpolator;
    595 
    596         /** State array. */
    597         private double[] y;
    598 
    599         /** Jacobian with respect to initial state dy/dy0. */
    600         private double[][] dydy0;
    601 
    602         /** Jacobian with respect to parameters dy/dp. */
    603         private double[][] dydp;
    604 
    605         /** Time derivative of the state array. */
    606         private double[] yDot;
    607 
    608         /** Time derivative of the sacobian with respect to initial state dy/dy0. */
    609         private double[][] dydy0Dot;
    610 
    611         /** Time derivative of the jacobian with respect to parameters dy/dp. */
    612         private double[][] dydpDot;
    613 
    614         /** Simple constructor.
    615          * <p>This constructor is used only for externalization. It does nothing.</p>
    616          */
    617         @SuppressWarnings("unused")
    618         public StepInterpolatorWrapper() {
    619         }
    620 
    621         /** Simple constructor.
    622          * @param interpolator wrapped interpolator
    623          * @param n dimension of the original ODE
    624          * @param k number of parameters
    625          */
    626         public StepInterpolatorWrapper(final StepInterpolator interpolator,
    627                                        final int n, final int k) {
    628             this.interpolator = interpolator;
    629             y        = new double[n];
    630             dydy0    = new double[n][n];
    631             dydp     = new double[n][k];
    632             yDot     = new double[n];
    633             dydy0Dot = new double[n][n];
    634             dydpDot  = new double[n][k];
    635         }
    636 
    637         /** {@inheritDoc} */
    638         public void setInterpolatedTime(double time) {
    639             interpolator.setInterpolatedTime(time);
    640         }
    641 
    642         /** {@inheritDoc} */
    643         public boolean isForward() {
    644             return interpolator.isForward();
    645         }
    646 
    647         /** {@inheritDoc} */
    648         public double getPreviousTime() {
    649             return interpolator.getPreviousTime();
    650         }
    651 
    652         /** {@inheritDoc} */
    653         public double getInterpolatedTime() {
    654             return interpolator.getInterpolatedTime();
    655         }
    656 
    657         /** {@inheritDoc} */
    658         public double[] getInterpolatedY() throws DerivativeException {
    659             double[] extendedState = interpolator.getInterpolatedState();
    660             System.arraycopy(extendedState, 0, y, 0, y.length);
    661             return y;
    662         }
    663 
    664         /** {@inheritDoc} */
    665         public double[][] getInterpolatedDyDy0() throws DerivativeException {
    666             double[] extendedState = interpolator.getInterpolatedState();
    667             final int n = y.length;
    668             int start = n;
    669             for (int i = 0; i < n; ++i) {
    670                 System.arraycopy(extendedState, start, dydy0[i], 0, n);
    671                 start += n;
    672             }
    673             return dydy0;
    674         }
    675 
    676         /** {@inheritDoc} */
    677         public double[][] getInterpolatedDyDp() throws DerivativeException {
    678             double[] extendedState = interpolator.getInterpolatedState();
    679             final int n = y.length;
    680             final int k = dydp[0].length;
    681             int start = n * (n + 1);
    682             for (int i = 0; i < n; ++i) {
    683                 System.arraycopy(extendedState, start, dydp[i], 0, k);
    684                 start += k;
    685             }
    686             return dydp;
    687         }
    688 
    689         /** {@inheritDoc} */
    690         public double[] getInterpolatedYDot() throws DerivativeException {
    691             double[] extendedDerivatives = interpolator.getInterpolatedDerivatives();
    692             System.arraycopy(extendedDerivatives, 0, yDot, 0, yDot.length);
    693             return yDot;
    694         }
    695 
    696         /** {@inheritDoc} */
    697         public double[][] getInterpolatedDyDy0Dot() throws DerivativeException {
    698             double[] extendedDerivatives = interpolator.getInterpolatedDerivatives();
    699             final int n = y.length;
    700             int start = n;
    701             for (int i = 0; i < n; ++i) {
    702                 System.arraycopy(extendedDerivatives, start, dydy0Dot[i], 0, n);
    703                 start += n;
    704             }
    705             return dydy0Dot;
    706         }
    707 
    708         /** {@inheritDoc} */
    709         public double[][] getInterpolatedDyDpDot() throws DerivativeException {
    710             double[] extendedDerivatives = interpolator.getInterpolatedDerivatives();
    711             final int n = y.length;
    712             final int k = dydpDot[0].length;
    713             int start = n * (n + 1);
    714             for (int i = 0; i < n; ++i) {
    715                 System.arraycopy(extendedDerivatives, start, dydpDot[i], 0, k);
    716                 start += k;
    717             }
    718             return dydpDot;
    719         }
    720 
    721         /** {@inheritDoc} */
    722         public double getCurrentTime() {
    723             return interpolator.getCurrentTime();
    724         }
    725 
    726         /** {@inheritDoc} */
    727         public StepInterpolatorWithJacobians copy() throws DerivativeException {
    728             final int n = y.length;
    729             final int k = dydp[0].length;
    730             StepInterpolatorWrapper copied =
    731                 new StepInterpolatorWrapper(interpolator.copy(), n, k);
    732             copyArray(y,        copied.y);
    733             copyArray(dydy0,    copied.dydy0);
    734             copyArray(dydp,     copied.dydp);
    735             copyArray(yDot,     copied.yDot);
    736             copyArray(dydy0Dot, copied.dydy0Dot);
    737             copyArray(dydpDot,  copied.dydpDot);
    738             return copied;
    739         }
    740 
    741         /** {@inheritDoc} */
    742         public void writeExternal(ObjectOutput out) throws IOException {
    743             out.writeObject(interpolator);
    744             out.writeInt(y.length);
    745             out.writeInt(dydp[0].length);
    746             writeArray(out, y);
    747             writeArray(out, dydy0);
    748             writeArray(out, dydp);
    749             writeArray(out, yDot);
    750             writeArray(out, dydy0Dot);
    751             writeArray(out, dydpDot);
    752         }
    753 
    754         /** {@inheritDoc} */
    755         public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    756             interpolator = (StepInterpolator) in.readObject();
    757             final int n = in.readInt();
    758             final int k = in.readInt();
    759             y        = new double[n];
    760             dydy0    = new double[n][n];
    761             dydp     = new double[n][k];
    762             yDot     = new double[n];
    763             dydy0Dot = new double[n][n];
    764             dydpDot  = new double[n][k];
    765             readArray(in, y);
    766             readArray(in, dydy0);
    767             readArray(in, dydp);
    768             readArray(in, yDot);
    769             readArray(in, dydy0Dot);
    770             readArray(in, dydpDot);
    771         }
    772 
    773         /** Copy an array.
    774          * @param src source array
    775          * @param dest destination array
    776          */
    777         private static void copyArray(final double[] src, final double[] dest) {
    778             System.arraycopy(src, 0, dest, 0, src.length);
    779         }
    780 
    781         /** Copy an array.
    782          * @param src source array
    783          * @param dest destination array
    784          */
    785         private static void copyArray(final double[][] src, final double[][] dest) {
    786             for (int i = 0; i < src.length; ++i) {
    787                 copyArray(src[i], dest[i]);
    788             }
    789         }
    790 
    791         /** Write an array.
    792          * @param out output stream
    793          * @param array array to write
    794          * @exception IOException if array cannot be read
    795          */
    796         private static void writeArray(final ObjectOutput out, final double[] array)
    797             throws IOException {
    798             for (int i = 0; i < array.length; ++i) {
    799                 out.writeDouble(array[i]);
    800             }
    801         }
    802 
    803         /** Write an array.
    804          * @param out output stream
    805          * @param array array to write
    806          * @exception IOException if array cannot be read
    807          */
    808         private static void writeArray(final ObjectOutput out, final double[][] array)
    809             throws IOException {
    810             for (int i = 0; i < array.length; ++i) {
    811                 writeArray(out, array[i]);
    812             }
    813         }
    814 
    815         /** Read an array.
    816          * @param in input stream
    817          * @param array array to read
    818          * @exception IOException if array cannot be read
    819          */
    820         private static void readArray(final ObjectInput in, final double[] array)
    821             throws IOException {
    822             for (int i = 0; i < array.length; ++i) {
    823                 array[i] = in.readDouble();
    824             }
    825         }
    826 
    827         /** Read an array.
    828          * @param in input stream
    829          * @param array array to read
    830          * @exception IOException if array cannot be read
    831          */
    832         private static void readArray(final ObjectInput in, final double[][] array)
    833             throws IOException {
    834             for (int i = 0; i < array.length; ++i) {
    835                 readArray(in, array[i]);
    836             }
    837         }
    838 
    839     }
    840 
    841     /** Wrapper for event handlers. */
    842     private static class EventHandlerWrapper implements EventHandler {
    843 
    844         /** Underlying event handler with jacobians. */
    845         private final EventHandlerWithJacobians handler;
    846 
    847         /** State array. */
    848         private double[] y;
    849 
    850         /** Jacobian with respect to initial state dy/dy0. */
    851         private double[][] dydy0;
    852 
    853         /** Jacobian with respect to parameters dy/dp. */
    854         private double[][] dydp;
    855 
    856         /** Simple constructor.
    857          * @param handler underlying event handler with jacobians
    858          * @param n dimension of the original ODE
    859          * @param k number of parameters
    860          */
    861         public EventHandlerWrapper(final EventHandlerWithJacobians handler,
    862                                    final int n, final int k) {
    863             this.handler = handler;
    864             y        = new double[n];
    865             dydy0    = new double[n][n];
    866             dydp     = new double[n][k];
    867         }
    868 
    869         /** Get the underlying event handler with jacobians.
    870          * @return underlying event handler with jacobians
    871          */
    872         public EventHandlerWithJacobians getHandler() {
    873             return handler;
    874         }
    875 
    876         /** {@inheritDoc} */
    877         public int eventOccurred(double t, double[] z, boolean increasing)
    878             throws EventException {
    879             dispatchCompoundState(z, y, dydy0, dydp);
    880             return handler.eventOccurred(t, y, dydy0, dydp, increasing);
    881         }
    882 
    883         /** {@inheritDoc} */
    884         public double g(double t, double[] z)
    885             throws EventException {
    886             dispatchCompoundState(z, y, dydy0, dydp);
    887             return handler.g(t, y, dydy0, dydp);
    888         }
    889 
    890         /** {@inheritDoc} */
    891         public void resetState(double t, double[] z)
    892             throws EventException {
    893             dispatchCompoundState(z, y, dydy0, dydp);
    894             handler.resetState(t, y, dydy0, dydp);
    895         }
    896 
    897     }
    898 
    899 }
    900