ParameterJacobianWrapper.java

  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.  *      https://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.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.ode;

  22. import java.util.Arrays;
  23. import java.util.HashMap;
  24. import java.util.List;
  25. import java.util.Map;

  26. import org.hipparchus.exception.MathIllegalArgumentException;
  27. import org.hipparchus.exception.MathIllegalStateException;

  28. /** Wrapper class to compute Jacobian matrices by finite differences for ODE
  29.  *  which do not compute them by themselves.
  30.  *
  31.  */
  32. class ParameterJacobianWrapper implements ODEJacobiansProvider {

  33.     /** ode base ordinary differential equation for which Jacobians
  34.      * matrices are requested. */
  35.     private final OrdinaryDifferentialEquation ode;

  36.     /** Steps for finite difference computation of the jacobian df/dy w.r.t. state. */
  37.     private final double[] hY;

  38.     /** Controller to change parameters. */
  39.     private final ParametersController controller;

  40.     /** Steps for finite difference computation of the Jacobian df/dp w.r.t. parameters. */
  41.     private final Map<String, Double> hParam;

  42.     /** Wrap a {@link ParametersController} into a {@link NamedParameterJacobianProvider}.
  43.      * @param ode ode base ordinary differential equation for which Jacobians
  44.      * matrices are requested
  45.      * @param hY step used for finite difference computation with respect to state vector
  46.      * @param controller controller to change parameters
  47.      * @param paramsAndSteps parameters and steps to compute the Jacobians df/dp
  48.      */
  49.     ParameterJacobianWrapper(final OrdinaryDifferentialEquation ode, final double[] hY,
  50.                              final ParametersController controller,
  51.                              final ParameterConfiguration[] paramsAndSteps) {
  52.         this.ode        = ode;
  53.         this.hY         = hY.clone();
  54.         this.controller = controller;
  55.         this.hParam     = new HashMap<>();

  56.         // set up parameters for jacobian computation
  57.         for (final ParameterConfiguration param : paramsAndSteps) {
  58.             final String name = param.getParameterName();
  59.             if (controller.isSupported(name)) {
  60.                 hParam.put(name, param.getHP());
  61.             }
  62.         }
  63.     }

  64.     /** Get the underlying ode.
  65.      * @return underlying ode
  66.      */
  67.     public OrdinaryDifferentialEquation getODE() {
  68.         return ode;
  69.     }

  70.     /** {@inheritDoc} */
  71.     @Override
  72.     public int getDimension() {
  73.         return ode.getDimension();
  74.     }

  75.     /** {@inheritDoc} */
  76.     @Override
  77.     public double[] computeDerivatives(double t, double[] y)
  78.         throws MathIllegalArgumentException, MathIllegalStateException {
  79.         return ode.computeDerivatives(t, y);
  80.     }

  81.     /** {@inheritDoc} */
  82.     @Override
  83.     public double[][] computeMainStateJacobian(double t, double[] y, double[] yDot)
  84.         throws MathIllegalArgumentException, MathIllegalStateException {

  85.         final int n = ode.getDimension();
  86.         final double[][] dFdY = new double[n][n];
  87.         for (int j = 0; j < n; ++j) {
  88.             final double savedYj = y[j];
  89.             y[j] += hY[j];
  90.             final double[] tmpDot = ode.computeDerivatives(t, y);
  91.             for (int i = 0; i < n; ++i) {
  92.                 dFdY[i][j] = (tmpDot[i] - yDot[i]) / hY[j];
  93.             }
  94.             y[j] = savedYj;
  95.         }
  96.         return dFdY;
  97.     }

  98.     /** {@inheritDoc} */
  99.     @Override
  100.     public List<String> getParametersNames() {
  101.         return controller.getParametersNames();
  102.     }

  103.     /** {@inheritDoc} */
  104.     @Override
  105.     public boolean isSupported(String name) {
  106.         return controller.isSupported(name);
  107.     }

  108.     /** {@inheritDoc} */
  109.     @Override
  110.     public double[] computeParameterJacobian(final double t, final double[] y,
  111.                                              final double[] yDot, final String paramName)
  112.         throws MathIllegalArgumentException, MathIllegalStateException {

  113.         final int n = ode.getDimension();
  114.         final double[] dFdP = new double[n];
  115.         if (controller.isSupported(paramName)) {

  116.             // compute the jacobian df/dp w.r.t. parameter
  117.             final double p  = controller.getParameter(paramName);
  118.             final double hP = hParam.get(paramName);
  119.             controller.setParameter(paramName, p + hP);
  120.             final double[] tmpDot = ode.computeDerivatives(t, y);
  121.             for (int i = 0; i < n; ++i) {
  122.                 dFdP[i] = (tmpDot[i] - yDot[i]) / hP;
  123.             }
  124.             controller.setParameter(paramName, p);
  125.         } else {
  126.             Arrays.fill(dFdP, 0, n, 0.0);
  127.         }

  128.         return dFdP;

  129.     }

  130. }