TaylorMap.java

  1. /*
  2.  * Licensed to the Hipparchus project 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 Hipparchus project 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. package org.hipparchus.analysis.differentiation;

  18. import org.hipparchus.exception.LocalizedCoreFormats;
  19. import org.hipparchus.exception.MathIllegalArgumentException;
  20. import org.hipparchus.linear.MatrixDecomposer;
  21. import org.hipparchus.linear.MatrixUtils;
  22. import org.hipparchus.linear.RealMatrix;
  23. import org.hipparchus.util.MathUtils;

  24. /** Container for a Taylor map.
  25.  * <p>
  26.  * A Taylor map is a set of n {@link DerivativeStructure}
  27.  * \((f_1, f_2, \ldots, f_n)\) depending on m parameters \((p_1, p_2, \ldots, p_m)\),
  28.  * with positive n and m.
  29.  * </p>
  30.  * @since 2.2
  31.  */
  32. public class TaylorMap implements DifferentialAlgebra {

  33.     /** Evaluation point. */
  34.     private final double[] point;

  35.     /** Mapping functions. */
  36.     private final DerivativeStructure[] functions;

  37.     /** Simple constructor.
  38.      * <p>
  39.      * The number of number of parameters and derivation orders of all
  40.      * functions must match.
  41.      * </p>
  42.      * @param point point at which map is evaluated
  43.      * @param functions functions composing the map (must contain at least one element)
  44.      */
  45.     public TaylorMap(final double[] point, final DerivativeStructure[] functions) {
  46.         if (point == null || point.length == 0) {
  47.             throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_OF_ELEMENTS_SHOULD_BE_POSITIVE,
  48.                                                    point == null ? 0 : point.length);
  49.         }
  50.         if (functions == null || functions.length == 0) {
  51.             throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_OF_ELEMENTS_SHOULD_BE_POSITIVE,
  52.                                                    functions == null ? 0 : functions.length);
  53.         }
  54.         this.point     = point.clone();
  55.         this.functions = functions.clone();
  56.         final DSFactory factory0 = functions[0].getFactory();
  57.         MathUtils.checkDimension(point.length, factory0.getCompiler().getFreeParameters());
  58.         for (int i = 1; i < functions.length; ++i) {
  59.             factory0.checkCompatibility(functions[i].getFactory());
  60.         }
  61.     }

  62.     /** Constructor for identity map.
  63.      * <p>
  64.      * The identity is considered to be evaluated at origin.
  65.      * </p>
  66.      * @param parameters number of free parameters
  67.      * @param order derivation order
  68.      * @param nbFunctions number of functions
  69.      */
  70.     public TaylorMap(final int parameters, final int order, final int nbFunctions) {
  71.         this(parameters, nbFunctions);
  72.         final DSFactory factory = new DSFactory(parameters, order);
  73.         for (int i = 0; i < nbFunctions; ++i) {
  74.             functions[i] = factory.variable(i, 0.0);
  75.         }
  76.     }

  77.     /** Build an empty map evaluated at origin.
  78.      * @param parameters number of free parameters
  79.      * @param nbFunctions number of functions
  80.      */
  81.     private TaylorMap(final int parameters, final int nbFunctions) {
  82.         this.point     = new double[parameters];
  83.         this.functions = new DerivativeStructure[nbFunctions];
  84.     }

  85.     /** {@inheritDoc} */
  86.     @Override
  87.     public int getFreeParameters() {
  88.         return point.length;
  89.     }

  90.     /** {@inheritDoc} */
  91.     @Override
  92.     public int getOrder() {
  93.         return functions[0].getOrder();
  94.     }

  95.     /** Get the number of functions of the map.
  96.      * @return number of functions of the map
  97.      */
  98.     public int getNbFunctions() {
  99.         return functions.length;
  100.     }

  101.     /** Get the point at which map is evaluated.
  102.      * @return point at which map is evaluated
  103.      */
  104.     public double[] getPoint() {
  105.         return point.clone();
  106.     }

  107.     /** Get a function from the map.
  108.      * @param i index of the function (must be between 0 included and {@link #getNbFunctions()} excluded
  109.      * @return function at index i
  110.      */
  111.     public DerivativeStructure getFunction(final int i) {
  112.         return functions[i];
  113.     }

  114.     /** Subtract two maps.
  115.      * @param map map to subtract from instance
  116.      * @return this - map
  117.      */
  118.     private TaylorMap subtract(final TaylorMap map) {
  119.         final TaylorMap result = new TaylorMap(point.length, functions.length);
  120.         for (int i = 0; i < result.functions.length; ++i) {
  121.             result.functions[i] = functions[i].subtract(map.functions[i]);
  122.         }
  123.         return result;
  124.     }

  125.     /** Evaluate Taylor expansion of the map at some offset.
  126.      * @param deltaP parameters offsets \((\Delta p_1, \Delta p_2, \ldots, \Delta p_n)\)
  127.      * @return value of the Taylor expansion at \((p_1 + \Delta p_1, p_2 + \Delta p_2, \ldots, p_n + \Delta p_n)\)
  128.      */
  129.     public double[] value(final double... deltaP) {
  130.         final double[] value = new double[functions.length];
  131.         for (int i = 0; i < functions.length; ++i) {
  132.             value[i] = functions[i].taylor(deltaP);
  133.         }
  134.         return value;
  135.     }

  136.     /** Compose the instance with another Taylor map as \(\mathrm{this} \circ \mathrm{other}\).
  137.      * @param other map with which instance must be composed
  138.      * @return composed map \(\mathrm{this} \circ \mathrm{other}\)
  139.      */
  140.     public TaylorMap compose(final TaylorMap other) {

  141.         // safety check
  142.         MathUtils.checkDimension(getFreeParameters(), other.getNbFunctions());

  143.         final DerivativeStructure[] composed = new DerivativeStructure[functions.length];
  144.         for (int i = 0; i < functions.length; ++i) {
  145.             composed[i] = functions[i].rebase(other.functions);
  146.         }

  147.         return new TaylorMap(other.point, composed);

  148.     }

  149.     /** Invert the instance.
  150.      * <p>
  151.      * Consider {@link #value(double[]) Taylor expansion} of the map with
  152.      * small parameters offsets \((\Delta p_1, \Delta p_2, \ldots, \Delta p_n)\)
  153.      * which leads to evaluation offsets \((f_1 + df_1, f_2 + df_2, \ldots, f_n + df_n)\).
  154.      * The map inversion defines a Taylor map that computes \((\Delta p_1,
  155.      * \Delta p_2, \ldots, \Delta p_n)\) from \((df_1, df_2, \ldots, df_n)\).
  156.      * </p>
  157.      * <p>
  158.      * The map must be square to be invertible (i.e. the number of functions and the
  159.      * number of parameters in the functions must match)
  160.      * </p>
  161.      * @param decomposer matrix decomposer to user for inverting the linear part
  162.      * @return inverted map
  163.      * @see <a href="https://doi.org/10.1016/S1076-5670(08)70228-3">chapter
  164.      * 2 of Advances in Imaging and Electron Physics, vol 108
  165.      * by Martin Berz</a>
  166.      */
  167.     public TaylorMap invert(final MatrixDecomposer decomposer) {

  168.         final DSFactory  factory  = functions[0].getFactory();
  169.         final DSCompiler compiler = factory.getCompiler();
  170.         final int        n        = functions.length;

  171.         // safety check
  172.         MathUtils.checkDimension(n, functions[0].getFreeParameters());

  173.         // set up an indirection array between linear terms and complete derivatives arrays
  174.         final int[] indirection    = new int[n];
  175.         int linearIndex = 0;
  176.         for (int k = 1; linearIndex < n; ++k) {
  177.             if (compiler.getPartialDerivativeOrdersSum(k) == 1) {
  178.                 indirection[linearIndex++] = k;
  179.             }
  180.         }

  181.         // separate linear and non-linear terms
  182.         final RealMatrix linear      = MatrixUtils.createRealMatrix(n, n);
  183.         final TaylorMap  nonLinearTM = new TaylorMap(n, n);
  184.         for (int i = 0; i < n; ++i) {
  185.             nonLinearTM.functions[i] = factory.build(functions[i].getAllDerivatives());
  186.             nonLinearTM.functions[i].setDerivativeComponent(0, 0.0);
  187.             for (int j = 0; j < n; ++j) {
  188.                 final int k = indirection[j];
  189.                 linear.setEntry(i, j, functions[i].getDerivativeComponent(k));
  190.                 nonLinearTM.functions[i].setDerivativeComponent(k, 0.0);
  191.             }
  192.         }

  193.         // invert the linear part
  194.         final RealMatrix linearInvert = decomposer.decompose(linear).getInverse();

  195.         // convert the invert of linear part back to a Taylor map
  196.         final TaylorMap  linearInvertTM = new TaylorMap(n, n);
  197.         for (int i = 0; i < n; ++i) {
  198.             linearInvertTM.functions[i] = new DerivativeStructure(factory);
  199.             for (int j = 0; j < n; ++j) {
  200.                 linearInvertTM.functions[i].setDerivativeComponent(indirection[j], linearInvert.getEntry(i, j));
  201.             }
  202.         }

  203.         // perform fixed-point evaluation of the inverse
  204.         // adding one derivation order at each iteration
  205.         final TaylorMap identity = new TaylorMap(n, compiler.getOrder(), n);
  206.         TaylorMap invertTM = linearInvertTM;
  207.         for (int k = 1; k < compiler.getOrder(); ++k) {
  208.             invertTM = linearInvertTM.compose(identity.subtract(nonLinearTM.compose(invertTM)));
  209.         }

  210.         // set the constants
  211.         for (int i = 0; i < n; ++i) {
  212.             invertTM.point[i] = functions[i].getValue();
  213.             invertTM.functions[i].setDerivativeComponent(0, point[i]);
  214.         }

  215.         return invertTM;

  216.     }

  217. }