FieldTaylorMap.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 java.lang.reflect.Array;

  19. import org.hipparchus.CalculusFieldElement;
  20. import org.hipparchus.Field;
  21. import org.hipparchus.exception.LocalizedCoreFormats;
  22. import org.hipparchus.exception.MathIllegalArgumentException;
  23. import org.hipparchus.linear.FieldMatrix;
  24. import org.hipparchus.linear.FieldMatrixDecomposer;
  25. import org.hipparchus.linear.MatrixUtils;
  26. import org.hipparchus.util.MathArrays;
  27. import org.hipparchus.util.MathUtils;

  28. /** Container for a Taylor map.
  29.  * <p>
  30.  * A Taylor map is a set of n {@link DerivativeStructure}
  31.  * \((f_1, f_2, \ldots, f_n)\) depending on m parameters \((p_1, p_2, \ldots, p_m)\),
  32.  * with positive n and m.
  33.  * </p>
  34.  * @param <T> the type of the function parameters and value
  35.  * @since 2.2
  36.  */
  37. public class FieldTaylorMap<T extends CalculusFieldElement<T>> implements DifferentialAlgebra {

  38.     /** Evaluation point. */
  39.     private final T[] point;

  40.     /** Mapping functions. */
  41.     private final FieldDerivativeStructure<T>[] functions;

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

  67.     /** Constructor for identity map.
  68.      * <p>
  69.      * The identity is considered to be evaluated at origin.
  70.      * </p>
  71.      * @param valueField field for the function parameters and value
  72.      * @param parameters number of free parameters
  73.      * @param order derivation order
  74.      * @param nbFunctions number of functions
  75.      */
  76.     public FieldTaylorMap(final Field<T> valueField, final int parameters, final int order, final int nbFunctions) {
  77.         this(valueField, parameters, nbFunctions);
  78.         final FDSFactory<T> factory = new FDSFactory<>(valueField, parameters, order);
  79.         for (int i = 0; i < nbFunctions; ++i) {
  80.             functions[i] = factory.variable(i, 0.0);
  81.         }
  82.     }

  83.     /** Build an empty map evaluated at origin.
  84.      * @param valueField field for the function parameters and value
  85.      * @param parameters number of free parameters
  86.      * @param nbFunctions number of functions
  87.      */
  88.     @SuppressWarnings("unchecked")
  89.     private FieldTaylorMap(final Field<T> valueField, final int parameters, final int nbFunctions) {
  90.         this.point     = MathArrays.buildArray(valueField, parameters);
  91.         this.functions = (FieldDerivativeStructure<T>[]) Array.newInstance(FieldDerivativeStructure.class, nbFunctions);
  92.     }

  93.     /** {@inheritDoc} */
  94.     @Override
  95.     public int getFreeParameters() {
  96.         return point.length;
  97.     }

  98.     /** {@inheritDoc} */
  99.     @Override
  100.     public int getOrder() {
  101.         return functions[0].getOrder();
  102.     }

  103.     /** Get the number of functions of the map.
  104.      * @return number of functions of the map
  105.      */
  106.     public int getNbFunctions() {
  107.         return functions.length;
  108.     }

  109.     /** Get the point at which map is evaluated.
  110.      * @return point at which map is evaluated
  111.      */
  112.     public T[] getPoint() {
  113.         return point.clone();
  114.     }

  115.     /** Get a function from the map.
  116.      * @param i index of the function (must be between 0 included and {@link #getNbFunctions()} excluded
  117.      * @return function at index i
  118.      */
  119.     public FieldDerivativeStructure<T> getFunction(final int i) {
  120.         return functions[i];
  121.     }

  122.     /** Subtract two maps.
  123.      * @param map map to subtract from instance
  124.      * @return this - map
  125.      */
  126.     private FieldTaylorMap<T> subtract(final FieldTaylorMap<T> map) {
  127.         final FieldTaylorMap<T> result = new FieldTaylorMap<>(functions[0].getFactory().getValueField(),
  128.                                                               point.length, functions.length);
  129.         for (int i = 0; i < result.functions.length; ++i) {
  130.             result.functions[i] = functions[i].subtract(map.functions[i]);
  131.         }
  132.         return result;
  133.     }

  134.     /** Evaluate Taylor expansion of the map at some offset.
  135.      * @param deltaP parameters offsets \((\Delta p_1, \Delta p_2, \ldots, \Delta p_n)\)
  136.      * @return value of the Taylor expansion at \((p_1 + \Delta p_1, p_2 + \Delta p_2, \ldots, p_n + \Delta p_n)\)
  137.      */
  138.     public T[] value(final double... deltaP) {
  139.         final T[] value = MathArrays.buildArray(functions[0].getFactory().getValueField(), functions.length);
  140.         for (int i = 0; i < functions.length; ++i) {
  141.             value[i] = functions[i].taylor(deltaP);
  142.         }
  143.         return value;
  144.     }

  145.     /** Evaluate Taylor expansion of the map at some offset.
  146.      * @param deltaP parameters offsets \((\Delta p_1, \Delta p_2, \ldots, \Delta p_n)\)
  147.      * @return value of the Taylor expansion at \((p_1 + \Delta p_1, p_2 + \Delta p_2, \ldots, p_n + \Delta p_n)\)
  148.      */
  149.     public T[] value(@SuppressWarnings("unchecked") final T... deltaP) {
  150.         final T[] value = MathArrays.buildArray(functions[0].getFactory().getValueField(), functions.length);
  151.         for (int i = 0; i < functions.length; ++i) {
  152.             value[i] = functions[i].taylor(deltaP);
  153.         }
  154.         return value;
  155.     }

  156.     /** Compose the instance with another Taylor map as \(\mathrm{this} \circ \mathrm{other}\).
  157.      * @param other map with which instance must be composed
  158.      * @return composed map \(\mathrm{this} \circ \mathrm{other}\)
  159.      */
  160.     public FieldTaylorMap<T> compose(final FieldTaylorMap<T> other) {

  161.         // safety check
  162.         MathUtils.checkDimension(getFreeParameters(), other.getNbFunctions());

  163.         @SuppressWarnings("unchecked")
  164.         final FieldDerivativeStructure<T>[] composed = (FieldDerivativeStructure<T>[]) Array.newInstance(FieldDerivativeStructure.class,
  165.                                                                                                          functions.length);
  166.         for (int i = 0; i < functions.length; ++i) {
  167.             composed[i] = functions[i].rebase(other.functions);
  168.         }

  169.         return new FieldTaylorMap<>(other.point, composed);

  170.     }

  171.     /** Invert the instance.
  172.      * <p>
  173.      * Consider {@link #value(double[]) Taylor expansion} of the map with
  174.      * small parameters offsets \((\Delta p_1, \Delta p_2, \ldots, \Delta p_n)\)
  175.      * which leads to evaluation offsets \((f_1 + df_1, f_2 + df_2, \ldots, f_n + df_n)\).
  176.      * The map inversion defines a Taylor map that computes \((\Delta p_1,
  177.      * \Delta p_2, \ldots, \Delta p_n)\) from \((df_1, df_2, \ldots, df_n)\).
  178.      * </p>
  179.      * <p>
  180.      * The map must be square to be invertible (i.e. the number of functions and the
  181.      * number of parameters in the functions must match)
  182.      * </p>
  183.      * @param decomposer matrix decomposer to user for inverting the linear part
  184.      * @return inverted map
  185.      * @see <a href="https://doi.org/10.1016/S1076-5670(08)70228-3">chapter
  186.      * 2 of Advances in Imaging and Electron Physics, vol 108
  187.      * by Martin Berz</a>
  188.      */
  189.     public FieldTaylorMap<T> invert(final FieldMatrixDecomposer<T> decomposer) {

  190.         final FDSFactory<T>  factory  = functions[0].getFactory();
  191.         final Field<T>       field    = factory.getValueField();
  192.         final DSCompiler     compiler = factory.getCompiler();
  193.         final int            n        = functions.length;

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

  196.         // set up an indirection array between linear terms and complete derivatives arrays
  197.         final int[] indirection    = new int[n];
  198.         int linearIndex = 0;
  199.         for (int k = 1; linearIndex < n; ++k) {
  200.             if (compiler.getPartialDerivativeOrdersSum(k) == 1) {
  201.                 indirection[linearIndex++] = k;
  202.             }
  203.         }

  204.         // separate linear and non-linear terms
  205.         final FieldMatrix<T> linear      = MatrixUtils.createFieldMatrix(field, n, n);
  206.         final FieldTaylorMap<T>  nonLinearTM = new FieldTaylorMap<>(field, n, n);
  207.         for (int i = 0; i < n; ++i) {
  208.             nonLinearTM.functions[i] = factory.build(functions[i].getAllDerivatives());
  209.             nonLinearTM.functions[i].setDerivativeComponent(0, field.getZero());
  210.             for (int j = 0; j < n; ++j) {
  211.                 final int k = indirection[j];
  212.                 linear.setEntry(i, j, functions[i].getDerivativeComponent(k));
  213.                 nonLinearTM.functions[i].setDerivativeComponent(k, field.getZero());
  214.             }
  215.         }

  216.         // invert the linear part
  217.         final FieldMatrix<T> linearInvert = decomposer.decompose(linear).getInverse();

  218.         // convert the invert of linear part back to a Taylor map
  219.         final FieldTaylorMap<T>  linearInvertTM = new FieldTaylorMap<>(field, n, n);
  220.         for (int i = 0; i < n; ++i) {
  221.             linearInvertTM.functions[i] = new FieldDerivativeStructure<>(factory);
  222.             for (int j = 0; j < n; ++j) {
  223.                 linearInvertTM.functions[i].setDerivativeComponent(indirection[j], linearInvert.getEntry(i, j));
  224.             }
  225.         }

  226.         // perform fixed-point evaluation of the inverse
  227.         // adding one derivation order at each iteration
  228.         final FieldTaylorMap<T> identity = new FieldTaylorMap<>(field, n, compiler.getOrder(), n);
  229.         FieldTaylorMap<T> invertTM = linearInvertTM;
  230.         for (int k = 1; k < compiler.getOrder(); ++k) {
  231.             invertTM = linearInvertTM.compose(identity.subtract(nonLinearTM.compose(invertTM)));
  232.         }

  233.         // set the constants
  234.         for (int i = 0; i < n; ++i) {
  235.             invertTM.point[i] = functions[i].getValue();
  236.             invertTM.functions[i].setDerivativeComponent(0, point[i]);
  237.         }

  238.         return invertTM;

  239.     }

  240. }