summaryrefslogtreecommitdiff
path: root/src/main/java/org/apache/commons/math/ode/jacobians/FirstOrderIntegratorWithJacobians.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/apache/commons/math/ode/jacobians/FirstOrderIntegratorWithJacobians.java')
-rw-r--r--src/main/java/org/apache/commons/math/ode/jacobians/FirstOrderIntegratorWithJacobians.java899
1 files changed, 899 insertions, 0 deletions
diff --git a/src/main/java/org/apache/commons/math/ode/jacobians/FirstOrderIntegratorWithJacobians.java b/src/main/java/org/apache/commons/math/ode/jacobians/FirstOrderIntegratorWithJacobians.java
new file mode 100644
index 0000000..dc5ca18
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/jacobians/FirstOrderIntegratorWithJacobians.java
@@ -0,0 +1,899 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode.jacobians;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.MaxEvaluationsExceededException;
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.ode.ExtendedFirstOrderDifferentialEquations;
+import org.apache.commons.math.ode.FirstOrderIntegrator;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.events.EventException;
+import org.apache.commons.math.ode.events.EventHandler;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.ode.sampling.StepInterpolator;
+
+/** This class enhances a first order integrator for differential equations to
+ * compute also partial derivatives of the solution with respect to initial state
+ * and parameters.
+ * <p>In order to compute both the state and its derivatives, the ODE problem
+ * is extended with jacobians of the raw ODE and the variational equations are
+ * added to form a new compound problem of higher dimension. If the original ODE
+ * problem has dimension n and there are p parameters, the compound problem will
+ * have dimension n &times; (1 + n + p).</p>
+ * @see ParameterizedODE
+ * @see ODEWithJacobians
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 2.1
+ * @deprecated as of 2.2 the complete package is deprecated, it will be replaced
+ * in 3.0 by a completely rewritten implementation
+ */
+@Deprecated
+public class FirstOrderIntegratorWithJacobians {
+
+ /** Underlying integrator for compound problem. */
+ private final FirstOrderIntegrator integrator;
+
+ /** Raw equations to integrate. */
+ private final ODEWithJacobians ode;
+
+ /** Maximal number of evaluations allowed. */
+ private int maxEvaluations;
+
+ /** Number of evaluations already performed. */
+ private int evaluations;
+
+ /** Build an enhanced integrator using internal differentiation to compute jacobians.
+ * @param integrator underlying integrator to solve the compound problem
+ * @param ode original problem (f in the equation y' = f(t, y))
+ * @param p parameters array (may be null if {@link
+ * ParameterizedODE#getParametersDimension()
+ * getParametersDimension()} from original problem is zero)
+ * @param hY step sizes to use for computing the jacobian df/dy, must have the
+ * same dimension as the original problem
+ * @param hP step sizes to use for computing the jacobian df/dp, must have the
+ * same dimension as the original problem parameters dimension
+ * @see #FirstOrderIntegratorWithJacobians(FirstOrderIntegrator,
+ * ODEWithJacobians)
+ */
+ public FirstOrderIntegratorWithJacobians(final FirstOrderIntegrator integrator,
+ final ParameterizedODE ode,
+ final double[] p, final double[] hY, final double[] hP) {
+ checkDimension(ode.getDimension(), hY);
+ checkDimension(ode.getParametersDimension(), p);
+ checkDimension(ode.getParametersDimension(), hP);
+ this.integrator = integrator;
+ this.ode = new FiniteDifferencesWrapper(ode, p, hY, hP);
+ setMaxEvaluations(-1);
+ }
+
+ /** Build an enhanced integrator using ODE builtin jacobian computation features.
+ * @param integrator underlying integrator to solve the compound problem
+ * @param ode original problem, which can compute the jacobians by itself
+ * @see #FirstOrderIntegratorWithJacobians(FirstOrderIntegrator,
+ * ParameterizedODE, double[], double[], double[])
+ */
+ public FirstOrderIntegratorWithJacobians(final FirstOrderIntegrator integrator,
+ final ODEWithJacobians ode) {
+ this.integrator = integrator;
+ this.ode = ode;
+ setMaxEvaluations(-1);
+ }
+
+ /** Add a step handler to this integrator.
+ * <p>The handler will be called by the integrator for each accepted
+ * step.</p>
+ * @param handler handler for the accepted steps
+ * @see #getStepHandlers()
+ * @see #clearStepHandlers()
+ */
+ public void addStepHandler(StepHandlerWithJacobians handler) {
+ final int n = ode.getDimension();
+ final int k = ode.getParametersDimension();
+ integrator.addStepHandler(new StepHandlerWrapper(handler, n, k));
+ }
+
+ /** Get all the step handlers that have been added to the integrator.
+ * @return an unmodifiable collection of the added events handlers
+ * @see #addStepHandler(StepHandlerWithJacobians)
+ * @see #clearStepHandlers()
+ */
+ public Collection<StepHandlerWithJacobians> getStepHandlers() {
+ final Collection<StepHandlerWithJacobians> handlers =
+ new ArrayList<StepHandlerWithJacobians>();
+ for (final StepHandler handler : integrator.getStepHandlers()) {
+ if (handler instanceof StepHandlerWrapper) {
+ handlers.add(((StepHandlerWrapper) handler).getHandler());
+ }
+ }
+ return handlers;
+ }
+
+ /** Remove all the step handlers that have been added to the integrator.
+ * @see #addStepHandler(StepHandlerWithJacobians)
+ * @see #getStepHandlers()
+ */
+ public void clearStepHandlers() {
+ integrator.clearStepHandlers();
+ }
+
+ /** Add an event handler to the integrator.
+ * @param handler event handler
+ * @param maxCheckInterval maximal time interval between switching
+ * function checks (this interval prevents missing sign changes in
+ * case the integration steps becomes very large)
+ * @param convergence convergence threshold in the event time search
+ * @param maxIterationCount upper limit of the iteration count in
+ * the event time search
+ * @see #getEventHandlers()
+ * @see #clearEventHandlers()
+ */
+ public void addEventHandler(EventHandlerWithJacobians handler,
+ double maxCheckInterval,
+ double convergence,
+ int maxIterationCount) {
+ final int n = ode.getDimension();
+ final int k = ode.getParametersDimension();
+ integrator.addEventHandler(new EventHandlerWrapper(handler, n, k),
+ maxCheckInterval, convergence, maxIterationCount);
+ }
+
+ /** Get all the event handlers that have been added to the integrator.
+ * @return an unmodifiable collection of the added events handlers
+ * @see #addEventHandler(EventHandlerWithJacobians, double, double, int)
+ * @see #clearEventHandlers()
+ */
+ public Collection<EventHandlerWithJacobians> getEventHandlers() {
+ final Collection<EventHandlerWithJacobians> handlers =
+ new ArrayList<EventHandlerWithJacobians>();
+ for (final EventHandler handler : integrator.getEventHandlers()) {
+ if (handler instanceof EventHandlerWrapper) {
+ handlers.add(((EventHandlerWrapper) handler).getHandler());
+ }
+ }
+ return handlers;
+ }
+
+ /** Remove all the event handlers that have been added to the integrator.
+ * @see #addEventHandler(EventHandlerWithJacobians, double, double, int)
+ * @see #getEventHandlers()
+ */
+ public void clearEventHandlers() {
+ integrator.clearEventHandlers();
+ }
+
+ /** Integrate the differential equations and the variational equations up to the given time.
+ * <p>This method solves an Initial Value Problem (IVP) and also computes the derivatives
+ * of the solution with respect to initial state and parameters. This can be used as
+ * a basis to solve Boundary Value Problems (BVP).</p>
+ * <p>Since this method stores some internal state variables made
+ * available in its public interface during integration ({@link
+ * #getCurrentSignedStepsize()}), it is <em>not</em> thread-safe.</p>
+ * @param t0 initial time
+ * @param y0 initial value of the state vector at t0
+ * @param dY0dP initial value of the state vector derivative with respect to the
+ * parameters at t0
+ * @param t target time for the integration
+ * (can be set to a value smaller than <code>t0</code> for backward integration)
+ * @param y placeholder where to put the state vector at each successful
+ * step (and hence at the end of integration), can be the same object as y0
+ * @param dYdY0 placeholder where to put the state vector derivative with respect
+ * to the initial state (dy[i]/dy0[j] is in element array dYdY0[i][j]) at each successful
+ * step (and hence at the end of integration)
+ * @param dYdP placeholder where to put the state vector derivative with respect
+ * to the parameters (dy[i]/dp[j] is in element array dYdP[i][j]) at each successful
+ * step (and hence at the end of integration)
+ * @return stop time, will be the same as target time if integration reached its
+ * target, but may be different if some event handler stops it at some point.
+ * @throws IntegratorException if the integrator cannot perform integration
+ * @throws DerivativeException this exception is propagated to the caller if
+ * the underlying user function triggers one
+ */
+ public double integrate(final double t0, final double[] y0, final double[][] dY0dP,
+ final double t, final double[] y,
+ final double[][] dYdY0, final double[][] dYdP)
+ throws DerivativeException, IntegratorException {
+
+ final int n = ode.getDimension();
+ final int k = ode.getParametersDimension();
+ checkDimension(n, y0);
+ checkDimension(n, y);
+ checkDimension(n, dYdY0);
+ checkDimension(n, dYdY0[0]);
+ if (k != 0) {
+ checkDimension(n, dY0dP);
+ checkDimension(k, dY0dP[0]);
+ checkDimension(n, dYdP);
+ checkDimension(k, dYdP[0]);
+ }
+
+ // set up initial state, including partial derivatives
+ // the compound state z contains the raw state y and its derivatives
+ // with respect to initial state y0 and to parameters p
+ // y[i] is stored in z[i]
+ // dy[i]/dy0[j] is stored in z[n + i * n + j]
+ // dy[i]/dp[j] is stored in z[n * (n + 1) + i * k + j]
+ final double[] z = new double[n * (1 + n + k)];
+ System.arraycopy(y0, 0, z, 0, n);
+ for (int i = 0; i < n; ++i) {
+
+ // set diagonal element of dy/dy0 to 1.0 at t = t0
+ z[i * (1 + n) + n] = 1.0;
+
+ // set initial derivatives with respect to parameters
+ System.arraycopy(dY0dP[i], 0, z, n * (n + 1) + i * k, k);
+
+ }
+
+ // integrate the compound state variational equations
+ evaluations = 0;
+ final double stopTime = integrator.integrate(new MappingWrapper(), t0, z, t, z);
+
+ // dispatch the final compound state into the state and partial derivatives arrays
+ dispatchCompoundState(z, y, dYdY0, dYdP);
+
+ return stopTime;
+
+ }
+
+ /** Dispatch a compound state array into state and jacobians arrays.
+ * @param z compound state
+ * @param y raw state array to fill
+ * @param dydy0 jacobian array to fill
+ * @param dydp jacobian array to fill
+ */
+ private static void dispatchCompoundState(final double[] z, final double[] y,
+ final double[][] dydy0, final double[][] dydp) {
+
+ final int n = y.length;
+ final int k = dydp[0].length;
+
+ // state
+ System.arraycopy(z, 0, y, 0, n);
+
+ // jacobian with respect to initial state
+ for (int i = 0; i < n; ++i) {
+ System.arraycopy(z, n * (i + 1), dydy0[i], 0, n);
+ }
+
+ // jacobian with respect to parameters
+ for (int i = 0; i < n; ++i) {
+ System.arraycopy(z, n * (n + 1) + i * k, dydp[i], 0, k);
+ }
+
+ }
+
+ /** Get the current value of the step start time t<sub>i</sub>.
+ * <p>This method can be called during integration (typically by
+ * the object implementing the {@link org.apache.commons.math.ode.FirstOrderDifferentialEquations
+ * differential equations} problem) if the value of the current step that
+ * is attempted is needed.</p>
+ * <p>The result is undefined if the method is called outside of
+ * calls to <code>integrate</code>.</p>
+ * @return current value of the step start time t<sub>i</sub>
+ */
+ public double getCurrentStepStart() {
+ return integrator.getCurrentStepStart();
+ }
+
+ /** Get the current signed value of the integration stepsize.
+ * <p>This method can be called during integration (typically by
+ * the object implementing the {@link org.apache.commons.math.ode.FirstOrderDifferentialEquations
+ * differential equations} problem) if the signed value of the current stepsize
+ * that is tried is needed.</p>
+ * <p>The result is undefined if the method is called outside of
+ * calls to <code>integrate</code>.</p>
+ * @return current signed value of the stepsize
+ */
+ public double getCurrentSignedStepsize() {
+ return integrator.getCurrentSignedStepsize();
+ }
+
+ /** Set the maximal number of differential equations function evaluations.
+ * <p>The purpose of this method is to avoid infinite loops which can occur
+ * for example when stringent error constraints are set or when lots of
+ * discrete events are triggered, thus leading to many rejected steps.</p>
+ * @param maxEvaluations maximal number of function evaluations (negative
+ * values are silently converted to maximal integer value, thus representing
+ * almost unlimited evaluations)
+ */
+ public void setMaxEvaluations(int maxEvaluations) {
+ this.maxEvaluations = (maxEvaluations < 0) ? Integer.MAX_VALUE : maxEvaluations;
+ }
+
+ /** Get the maximal number of functions evaluations.
+ * @return maximal number of functions evaluations
+ */
+ public int getMaxEvaluations() {
+ return maxEvaluations;
+ }
+
+ /** Get the number of evaluations of the differential equations function.
+ * <p>
+ * The number of evaluations corresponds to the last call to the
+ * <code>integrate</code> method. It is 0 if the method has not been called yet.
+ * </p>
+ * @return number of evaluations of the differential equations function
+ */
+ public int getEvaluations() {
+ return evaluations;
+ }
+
+ /** Check array dimensions.
+ * @param expected expected dimension
+ * @param array (may be null if expected is 0)
+ * @throws IllegalArgumentException if the array dimension does not match the expected one
+ */
+ private void checkDimension(final int expected, final Object array)
+ throws IllegalArgumentException {
+ int arrayDimension = (array == null) ? 0 : Array.getLength(array);
+ if (arrayDimension != expected) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, arrayDimension, expected);
+ }
+ }
+
+ /** Wrapper class used to map state and jacobians into compound state. */
+ private class MappingWrapper implements ExtendedFirstOrderDifferentialEquations {
+
+ /** Current state. */
+ private final double[] y;
+
+ /** Time derivative of the current state. */
+ private final double[] yDot;
+
+ /** Derivatives of yDot with respect to state. */
+ private final double[][] dFdY;
+
+ /** Derivatives of yDot with respect to parameters. */
+ private final double[][] dFdP;
+
+ /** Simple constructor.
+ */
+ public MappingWrapper() {
+
+ final int n = ode.getDimension();
+ final int k = ode.getParametersDimension();
+ y = new double[n];
+ yDot = new double[n];
+ dFdY = new double[n][n];
+ dFdP = new double[n][k];
+
+ }
+
+ /** {@inheritDoc} */
+ public int getDimension() {
+ final int n = y.length;
+ final int k = dFdP[0].length;
+ return n * (1 + n + k);
+ }
+
+ /** {@inheritDoc} */
+ public int getMainSetDimension() {
+ return ode.getDimension();
+ }
+
+ /** {@inheritDoc} */
+ public void computeDerivatives(final double t, final double[] z, final double[] zDot)
+ throws DerivativeException {
+
+ final int n = y.length;
+ final int k = dFdP[0].length;
+
+ // compute raw ODE and its jacobians: dy/dt, d[dy/dt]/dy0 and d[dy/dt]/dp
+ System.arraycopy(z, 0, y, 0, n);
+ if (++evaluations > maxEvaluations) {
+ throw new DerivativeException(new MaxEvaluationsExceededException(maxEvaluations));
+ }
+ ode.computeDerivatives(t, y, yDot);
+ ode.computeJacobians(t, y, yDot, dFdY, dFdP);
+
+ // state part of the compound equations
+ System.arraycopy(yDot, 0, zDot, 0, n);
+
+ // variational equations: from d[dy/dt]/dy0 to d[dy/dy0]/dt
+ for (int i = 0; i < n; ++i) {
+ final double[] dFdYi = dFdY[i];
+ for (int j = 0; j < n; ++j) {
+ double s = 0;
+ final int startIndex = n + j;
+ int zIndex = startIndex;
+ for (int l = 0; l < n; ++l) {
+ s += dFdYi[l] * z[zIndex];
+ zIndex += n;
+ }
+ zDot[startIndex + i * n] = s;
+ }
+ }
+
+ // variational equations: from d[dy/dt]/dy0 and d[dy/dt]/dp to d[dy/dp]/dt
+ for (int i = 0; i < n; ++i) {
+ final double[] dFdYi = dFdY[i];
+ final double[] dFdPi = dFdP[i];
+ for (int j = 0; j < k; ++j) {
+ double s = dFdPi[j];
+ final int startIndex = n * (n + 1) + j;
+ int zIndex = startIndex;
+ for (int l = 0; l < n; ++l) {
+ s += dFdYi[l] * z[zIndex];
+ zIndex += k;
+ }
+ zDot[startIndex + i * k] = s;
+ }
+ }
+
+ }
+
+ }
+
+ /** Wrapper class to compute jacobians by finite differences for ODE which do not compute them themselves. */
+ private class FiniteDifferencesWrapper implements ODEWithJacobians {
+
+ /** Raw ODE without jacobians computation. */
+ private final ParameterizedODE ode;
+
+ /** Parameters array (may be null if parameters dimension from original problem is zero) */
+ private final double[] p;
+
+ /** Step sizes to use for computing the jacobian df/dy. */
+ private final double[] hY;
+
+ /** Step sizes to use for computing the jacobian df/dp. */
+ private final double[] hP;
+
+ /** Temporary array for state derivatives used to compute jacobians. */
+ private final double[] tmpDot;
+
+ /** Simple constructor.
+ * @param ode original ODE problem, without jacobians computations
+ * @param p parameters array (may be null if parameters dimension from original problem is zero)
+ * @param hY step sizes to use for computing the jacobian df/dy
+ * @param hP step sizes to use for computing the jacobian df/dp
+ */
+ public FiniteDifferencesWrapper(final ParameterizedODE ode,
+ final double[] p, final double[] hY, final double[] hP) {
+ this.ode = ode;
+ this.p = p.clone();
+ this.hY = hY.clone();
+ this.hP = hP.clone();
+ tmpDot = new double[ode.getDimension()];
+ }
+
+ /** {@inheritDoc} */
+ public int getDimension() {
+ return ode.getDimension();
+ }
+
+ /** {@inheritDoc} */
+ public void computeDerivatives(double t, double[] y, double[] yDot) throws DerivativeException {
+ // this call to computeDerivatives has already been counted,
+ // we must not increment the counter again
+ ode.computeDerivatives(t, y, yDot);
+ }
+
+ /** {@inheritDoc} */
+ public int getParametersDimension() {
+ return ode.getParametersDimension();
+ }
+
+ /** {@inheritDoc} */
+ public void computeJacobians(double t, double[] y, double[] yDot,
+ double[][] dFdY, double[][] dFdP)
+ throws DerivativeException {
+
+ final int n = hY.length;
+ final int k = hP.length;
+
+ evaluations += n + k;
+ if (evaluations > maxEvaluations) {
+ throw new DerivativeException(new MaxEvaluationsExceededException(maxEvaluations));
+ }
+
+ // compute df/dy where f is the ODE and y is the state array
+ for (int j = 0; j < n; ++j) {
+ final double savedYj = y[j];
+ y[j] += hY[j];
+ ode.computeDerivatives(t, y, tmpDot);
+ for (int i = 0; i < n; ++i) {
+ dFdY[i][j] = (tmpDot[i] - yDot[i]) / hY[j];
+ }
+ y[j] = savedYj;
+ }
+
+ // compute df/dp where f is the ODE and p is the parameters array
+ for (int j = 0; j < k; ++j) {
+ ode.setParameter(j, p[j] + hP[j]);
+ ode.computeDerivatives(t, y, tmpDot);
+ for (int i = 0; i < n; ++i) {
+ dFdP[i][j] = (tmpDot[i] - yDot[i]) / hP[j];
+ }
+ ode.setParameter(j, p[j]);
+ }
+
+ }
+
+ }
+
+ /** Wrapper for step handlers. */
+ private static class StepHandlerWrapper implements StepHandler {
+
+ /** Underlying step handler with jacobians. */
+ private final StepHandlerWithJacobians handler;
+
+ /** Dimension of the original ODE. */
+ private final int n;
+
+ /** Number of parameters. */
+ private final int k;
+
+ /** Simple constructor.
+ * @param handler underlying step handler with jacobians
+ * @param n dimension of the original ODE
+ * @param k number of parameters
+ */
+ public StepHandlerWrapper(final StepHandlerWithJacobians handler,
+ final int n, final int k) {
+ this.handler = handler;
+ this.n = n;
+ this.k = k;
+ }
+
+ /** Get the underlying step handler with jacobians.
+ * @return underlying step handler with jacobians
+ */
+ public StepHandlerWithJacobians getHandler() {
+ return handler;
+ }
+
+ /** {@inheritDoc} */
+ public void handleStep(StepInterpolator interpolator, boolean isLast)
+ throws DerivativeException {
+ handler.handleStep(new StepInterpolatorWrapper(interpolator, n, k), isLast);
+ }
+
+ /** {@inheritDoc} */
+ public boolean requiresDenseOutput() {
+ return handler.requiresDenseOutput();
+ }
+
+ /** {@inheritDoc} */
+ public void reset() {
+ handler.reset();
+ }
+
+ }
+
+ /** Wrapper for step interpolators. */
+ private static class StepInterpolatorWrapper
+ implements StepInterpolatorWithJacobians {
+
+ /** Wrapped interpolator. */
+ private StepInterpolator interpolator;
+
+ /** State array. */
+ private double[] y;
+
+ /** Jacobian with respect to initial state dy/dy0. */
+ private double[][] dydy0;
+
+ /** Jacobian with respect to parameters dy/dp. */
+ private double[][] dydp;
+
+ /** Time derivative of the state array. */
+ private double[] yDot;
+
+ /** Time derivative of the sacobian with respect to initial state dy/dy0. */
+ private double[][] dydy0Dot;
+
+ /** Time derivative of the jacobian with respect to parameters dy/dp. */
+ private double[][] dydpDot;
+
+ /** Simple constructor.
+ * <p>This constructor is used only for externalization. It does nothing.</p>
+ */
+ @SuppressWarnings("unused")
+ public StepInterpolatorWrapper() {
+ }
+
+ /** Simple constructor.
+ * @param interpolator wrapped interpolator
+ * @param n dimension of the original ODE
+ * @param k number of parameters
+ */
+ public StepInterpolatorWrapper(final StepInterpolator interpolator,
+ final int n, final int k) {
+ this.interpolator = interpolator;
+ y = new double[n];
+ dydy0 = new double[n][n];
+ dydp = new double[n][k];
+ yDot = new double[n];
+ dydy0Dot = new double[n][n];
+ dydpDot = new double[n][k];
+ }
+
+ /** {@inheritDoc} */
+ public void setInterpolatedTime(double time) {
+ interpolator.setInterpolatedTime(time);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isForward() {
+ return interpolator.isForward();
+ }
+
+ /** {@inheritDoc} */
+ public double getPreviousTime() {
+ return interpolator.getPreviousTime();
+ }
+
+ /** {@inheritDoc} */
+ public double getInterpolatedTime() {
+ return interpolator.getInterpolatedTime();
+ }
+
+ /** {@inheritDoc} */
+ public double[] getInterpolatedY() throws DerivativeException {
+ double[] extendedState = interpolator.getInterpolatedState();
+ System.arraycopy(extendedState, 0, y, 0, y.length);
+ return y;
+ }
+
+ /** {@inheritDoc} */
+ public double[][] getInterpolatedDyDy0() throws DerivativeException {
+ double[] extendedState = interpolator.getInterpolatedState();
+ final int n = y.length;
+ int start = n;
+ for (int i = 0; i < n; ++i) {
+ System.arraycopy(extendedState, start, dydy0[i], 0, n);
+ start += n;
+ }
+ return dydy0;
+ }
+
+ /** {@inheritDoc} */
+ public double[][] getInterpolatedDyDp() throws DerivativeException {
+ double[] extendedState = interpolator.getInterpolatedState();
+ final int n = y.length;
+ final int k = dydp[0].length;
+ int start = n * (n + 1);
+ for (int i = 0; i < n; ++i) {
+ System.arraycopy(extendedState, start, dydp[i], 0, k);
+ start += k;
+ }
+ return dydp;
+ }
+
+ /** {@inheritDoc} */
+ public double[] getInterpolatedYDot() throws DerivativeException {
+ double[] extendedDerivatives = interpolator.getInterpolatedDerivatives();
+ System.arraycopy(extendedDerivatives, 0, yDot, 0, yDot.length);
+ return yDot;
+ }
+
+ /** {@inheritDoc} */
+ public double[][] getInterpolatedDyDy0Dot() throws DerivativeException {
+ double[] extendedDerivatives = interpolator.getInterpolatedDerivatives();
+ final int n = y.length;
+ int start = n;
+ for (int i = 0; i < n; ++i) {
+ System.arraycopy(extendedDerivatives, start, dydy0Dot[i], 0, n);
+ start += n;
+ }
+ return dydy0Dot;
+ }
+
+ /** {@inheritDoc} */
+ public double[][] getInterpolatedDyDpDot() throws DerivativeException {
+ double[] extendedDerivatives = interpolator.getInterpolatedDerivatives();
+ final int n = y.length;
+ final int k = dydpDot[0].length;
+ int start = n * (n + 1);
+ for (int i = 0; i < n; ++i) {
+ System.arraycopy(extendedDerivatives, start, dydpDot[i], 0, k);
+ start += k;
+ }
+ return dydpDot;
+ }
+
+ /** {@inheritDoc} */
+ public double getCurrentTime() {
+ return interpolator.getCurrentTime();
+ }
+
+ /** {@inheritDoc} */
+ public StepInterpolatorWithJacobians copy() throws DerivativeException {
+ final int n = y.length;
+ final int k = dydp[0].length;
+ StepInterpolatorWrapper copied =
+ new StepInterpolatorWrapper(interpolator.copy(), n, k);
+ copyArray(y, copied.y);
+ copyArray(dydy0, copied.dydy0);
+ copyArray(dydp, copied.dydp);
+ copyArray(yDot, copied.yDot);
+ copyArray(dydy0Dot, copied.dydy0Dot);
+ copyArray(dydpDot, copied.dydpDot);
+ return copied;
+ }
+
+ /** {@inheritDoc} */
+ public void writeExternal(ObjectOutput out) throws IOException {
+ out.writeObject(interpolator);
+ out.writeInt(y.length);
+ out.writeInt(dydp[0].length);
+ writeArray(out, y);
+ writeArray(out, dydy0);
+ writeArray(out, dydp);
+ writeArray(out, yDot);
+ writeArray(out, dydy0Dot);
+ writeArray(out, dydpDot);
+ }
+
+ /** {@inheritDoc} */
+ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ interpolator = (StepInterpolator) in.readObject();
+ final int n = in.readInt();
+ final int k = in.readInt();
+ y = new double[n];
+ dydy0 = new double[n][n];
+ dydp = new double[n][k];
+ yDot = new double[n];
+ dydy0Dot = new double[n][n];
+ dydpDot = new double[n][k];
+ readArray(in, y);
+ readArray(in, dydy0);
+ readArray(in, dydp);
+ readArray(in, yDot);
+ readArray(in, dydy0Dot);
+ readArray(in, dydpDot);
+ }
+
+ /** Copy an array.
+ * @param src source array
+ * @param dest destination array
+ */
+ private static void copyArray(final double[] src, final double[] dest) {
+ System.arraycopy(src, 0, dest, 0, src.length);
+ }
+
+ /** Copy an array.
+ * @param src source array
+ * @param dest destination array
+ */
+ private static void copyArray(final double[][] src, final double[][] dest) {
+ for (int i = 0; i < src.length; ++i) {
+ copyArray(src[i], dest[i]);
+ }
+ }
+
+ /** Write an array.
+ * @param out output stream
+ * @param array array to write
+ * @exception IOException if array cannot be read
+ */
+ private static void writeArray(final ObjectOutput out, final double[] array)
+ throws IOException {
+ for (int i = 0; i < array.length; ++i) {
+ out.writeDouble(array[i]);
+ }
+ }
+
+ /** Write an array.
+ * @param out output stream
+ * @param array array to write
+ * @exception IOException if array cannot be read
+ */
+ private static void writeArray(final ObjectOutput out, final double[][] array)
+ throws IOException {
+ for (int i = 0; i < array.length; ++i) {
+ writeArray(out, array[i]);
+ }
+ }
+
+ /** Read an array.
+ * @param in input stream
+ * @param array array to read
+ * @exception IOException if array cannot be read
+ */
+ private static void readArray(final ObjectInput in, final double[] array)
+ throws IOException {
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = in.readDouble();
+ }
+ }
+
+ /** Read an array.
+ * @param in input stream
+ * @param array array to read
+ * @exception IOException if array cannot be read
+ */
+ private static void readArray(final ObjectInput in, final double[][] array)
+ throws IOException {
+ for (int i = 0; i < array.length; ++i) {
+ readArray(in, array[i]);
+ }
+ }
+
+ }
+
+ /** Wrapper for event handlers. */
+ private static class EventHandlerWrapper implements EventHandler {
+
+ /** Underlying event handler with jacobians. */
+ private final EventHandlerWithJacobians handler;
+
+ /** State array. */
+ private double[] y;
+
+ /** Jacobian with respect to initial state dy/dy0. */
+ private double[][] dydy0;
+
+ /** Jacobian with respect to parameters dy/dp. */
+ private double[][] dydp;
+
+ /** Simple constructor.
+ * @param handler underlying event handler with jacobians
+ * @param n dimension of the original ODE
+ * @param k number of parameters
+ */
+ public EventHandlerWrapper(final EventHandlerWithJacobians handler,
+ final int n, final int k) {
+ this.handler = handler;
+ y = new double[n];
+ dydy0 = new double[n][n];
+ dydp = new double[n][k];
+ }
+
+ /** Get the underlying event handler with jacobians.
+ * @return underlying event handler with jacobians
+ */
+ public EventHandlerWithJacobians getHandler() {
+ return handler;
+ }
+
+ /** {@inheritDoc} */
+ public int eventOccurred(double t, double[] z, boolean increasing)
+ throws EventException {
+ dispatchCompoundState(z, y, dydy0, dydp);
+ return handler.eventOccurred(t, y, dydy0, dydp, increasing);
+ }
+
+ /** {@inheritDoc} */
+ public double g(double t, double[] z)
+ throws EventException {
+ dispatchCompoundState(z, y, dydy0, dydp);
+ return handler.g(t, y, dydy0, dydp);
+ }
+
+ /** {@inheritDoc} */
+ public void resetState(double t, double[] z)
+ throws EventException {
+ dispatchCompoundState(z, y, dydy0, dydp);
+ handler.resetState(t, y, dydy0, dydp);
+ }
+
+ }
+
+}