View Javadoc
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  
19  import java.lang.reflect.Array;
20  
21  import org.hipparchus.CalculusFieldElement;
22  import org.hipparchus.Field;
23  import org.hipparchus.exception.LocalizedCoreFormats;
24  import org.hipparchus.exception.MathIllegalArgumentException;
25  import org.hipparchus.linear.FieldMatrix;
26  import org.hipparchus.linear.FieldMatrixDecomposer;
27  import org.hipparchus.linear.MatrixUtils;
28  import org.hipparchus.util.MathArrays;
29  import org.hipparchus.util.MathUtils;
30  
31  /** Container for a Taylor map.
32   * <p>
33   * A Taylor map is a set of n {@link DerivativeStructure}
34   * \((f_1, f_2, \ldots, f_n)\) depending on m parameters \((p_1, p_2, \ldots, p_m)\),
35   * with positive n and m.
36   * </p>
37   * @param <T> the type of the function parameters and value
38   * @since 2.2
39   */
40  public class FieldTaylorMap<T extends CalculusFieldElement<T>> implements DifferentialAlgebra {
41  
42      /** Evaluation point. */
43      private final T[] point;
44  
45      /** Mapping functions. */
46      private final FieldDerivativeStructure<T>[] functions;
47  
48      /** Simple constructor.
49       * <p>
50       * The number of number of parameters and derivation orders of all
51       * functions must match.
52       * </p>
53       * @param point point at which map is evaluated
54       * @param functions functions composing the map (must contain at least one element)
55       */
56      public FieldTaylorMap(final T[] point, final FieldDerivativeStructure<T>[] functions) {
57          if (point == null || point.length == 0) {
58              throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_OF_ELEMENTS_SHOULD_BE_POSITIVE,
59                                                     point == null ? 0 : point.length);
60          }
61          if (functions == null || functions.length == 0) {
62              throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_OF_ELEMENTS_SHOULD_BE_POSITIVE,
63                                                     functions == null ? 0 : functions.length);
64          }
65          this.point     = point.clone();
66          this.functions = functions.clone();
67          final FDSFactory<T> factory0 = functions[0].getFactory();
68          MathUtils.checkDimension(point.length, factory0.getCompiler().getFreeParameters());
69          for (int i = 1; i < functions.length; ++i) {
70              factory0.checkCompatibility(functions[i].getFactory());
71          }
72      }
73  
74      /** Constructor for identity map.
75       * <p>
76       * The identity is considered to be evaluated at origin.
77       * </p>
78       * @param valueField field for the function parameters and value
79       * @param parameters number of free parameters
80       * @param order derivation order
81       * @param nbFunctions number of functions
82       */
83      public FieldTaylorMap(final Field<T> valueField, final int parameters, final int order, final int nbFunctions) {
84          this(valueField, parameters, nbFunctions);
85          final FDSFactory<T> factory = new FDSFactory<>(valueField, parameters, order);
86          for (int i = 0; i < nbFunctions; ++i) {
87              functions[i] = factory.variable(i, 0.0);
88          }
89      }
90  
91      /** Build an empty map evaluated at origin.
92       * @param valueField field for the function parameters and value
93       * @param parameters number of free parameters
94       * @param nbFunctions number of functions
95       */
96      @SuppressWarnings("unchecked")
97      private FieldTaylorMap(final Field<T> valueField, final int parameters, final int nbFunctions) {
98          this.point     = MathArrays.buildArray(valueField, parameters);
99          this.functions = (FieldDerivativeStructure<T>[]) Array.newInstance(FieldDerivativeStructure.class, nbFunctions);
100     }
101 
102     /** {@inheritDoc} */
103     @Override
104     public int getFreeParameters() {
105         return point.length;
106     }
107 
108     /** {@inheritDoc} */
109     @Override
110     public int getOrder() {
111         return functions[0].getOrder();
112     }
113 
114     /** Get the number of functions of the map.
115      * @return number of functions of the map
116      */
117     public int getNbFunctions() {
118         return functions.length;
119     }
120 
121     /** Get the point at which map is evaluated.
122      * @return point at which map is evaluated
123      */
124     public T[] getPoint() {
125         return point.clone();
126     }
127 
128     /** Get a function from the map.
129      * @param i index of the function (must be between 0 included and {@link #getNbFunctions()} excluded
130      * @return function at index i
131      */
132     public FieldDerivativeStructure<T> getFunction(final int i) {
133         return functions[i];
134     }
135 
136     /** Subtract two maps.
137      * @param map map to subtract from instance
138      * @return this - map
139      */
140     private FieldTaylorMap<T> subtract(final FieldTaylorMap<T> map) {
141         final FieldTaylorMap<T> result = new FieldTaylorMap<>(functions[0].getFactory().getValueField(),
142                                                               point.length, functions.length);
143         for (int i = 0; i < result.functions.length; ++i) {
144             result.functions[i] = functions[i].subtract(map.functions[i]);
145         }
146         return result;
147     }
148 
149     /** Evaluate Taylor expansion of the map at some offset.
150      * @param deltaP parameters offsets \((\Delta p_1, \Delta p_2, \ldots, \Delta p_n)\)
151      * @return value of the Taylor expansion at \((p_1 + \Delta p_1, p_2 + \Delta p_2, \ldots, p_n + \Delta p_n)\)
152      */
153     public T[] value(final double... deltaP) {
154         final T[] value = MathArrays.buildArray(functions[0].getFactory().getValueField(), functions.length);
155         for (int i = 0; i < functions.length; ++i) {
156             value[i] = functions[i].taylor(deltaP);
157         }
158         return value;
159     }
160 
161     /** Evaluate Taylor expansion of the map at some offset.
162      * @param deltaP parameters offsets \((\Delta p_1, \Delta p_2, \ldots, \Delta p_n)\)
163      * @return value of the Taylor expansion at \((p_1 + \Delta p_1, p_2 + \Delta p_2, \ldots, p_n + \Delta p_n)\)
164      */
165     public T[] value(@SuppressWarnings("unchecked") final T... deltaP) {
166         final T[] value = MathArrays.buildArray(functions[0].getFactory().getValueField(), functions.length);
167         for (int i = 0; i < functions.length; ++i) {
168             value[i] = functions[i].taylor(deltaP);
169         }
170         return value;
171     }
172 
173     /** Compose the instance with another Taylor map as \(\mathrm{this} \circ \mathrm{other}\).
174      * @param other map with which instance must be composed
175      * @return composed map \(\mathrm{this} \circ \mathrm{other}\)
176      */
177     public FieldTaylorMap<T> compose(final FieldTaylorMap<T> other) {
178 
179         // safety check
180         MathUtils.checkDimension(getFreeParameters(), other.getNbFunctions());
181 
182         @SuppressWarnings("unchecked")
183         final FieldDerivativeStructure<T>[] composed = (FieldDerivativeStructure<T>[]) Array.newInstance(FieldDerivativeStructure.class,
184                                                                                                          functions.length);
185         for (int i = 0; i < functions.length; ++i) {
186             composed[i] = functions[i].rebase(other.functions);
187         }
188 
189         return new FieldTaylorMap<>(other.point, composed);
190 
191     }
192 
193     /** Invert the instance.
194      * <p>
195      * Consider {@link #value(double[]) Taylor expansion} of the map with
196      * small parameters offsets \((\Delta p_1, \Delta p_2, \ldots, \Delta p_n)\)
197      * which leads to evaluation offsets \((f_1 + df_1, f_2 + df_2, \ldots, f_n + df_n)\).
198      * The map inversion defines a Taylor map that computes \((\Delta p_1,
199      * \Delta p_2, \ldots, \Delta p_n)\) from \((df_1, df_2, \ldots, df_n)\).
200      * </p>
201      * <p>
202      * The map must be square to be invertible (i.e. the number of functions and the
203      * number of parameters in the functions must match)
204      * </p>
205      * @param decomposer matrix decomposer to user for inverting the linear part
206      * @return inverted map
207      * @see <a href="https://doi.org/10.1016/S1076-5670(08)70228-3">chapter
208      * 2 of Advances in Imaging and Electron Physics, vol 108
209      * by Martin Berz</a>
210      */
211     public FieldTaylorMap<T> invert(final FieldMatrixDecomposer<T> decomposer) {
212 
213         final FDSFactory<T>  factory  = functions[0].getFactory();
214         final Field<T>       field    = factory.getValueField();
215         final DSCompiler     compiler = factory.getCompiler();
216         final int            n        = functions.length;
217 
218         // safety check
219         MathUtils.checkDimension(n, functions[0].getFreeParameters());
220 
221         // set up an indirection array between linear terms and complete derivatives arrays
222         final int[] indirection    = new int[n];
223         int linearIndex = 0;
224         for (int k = 1; linearIndex < n; ++k) {
225             if (compiler.getPartialDerivativeOrdersSum(k) == 1) {
226                 indirection[linearIndex++] = k;
227             }
228         }
229 
230         // separate linear and non-linear terms
231         final FieldMatrix<T> linear      = MatrixUtils.createFieldMatrix(field, n, n);
232         final FieldTaylorMap<T>  nonLinearTM = new FieldTaylorMap<>(field, n, n);
233         for (int i = 0; i < n; ++i) {
234             nonLinearTM.functions[i] = factory.build(functions[i].getAllDerivatives());
235             nonLinearTM.functions[i].setDerivativeComponent(0, field.getZero());
236             for (int j = 0; j < n; ++j) {
237                 final int k = indirection[j];
238                 linear.setEntry(i, j, functions[i].getDerivativeComponent(k));
239                 nonLinearTM.functions[i].setDerivativeComponent(k, field.getZero());
240             }
241         }
242 
243         // invert the linear part
244         final FieldMatrix<T> linearInvert = decomposer.decompose(linear).getInverse();
245 
246         // convert the invert of linear part back to a Taylor map
247         final FieldTaylorMap<T>  linearInvertTM = new FieldTaylorMap<>(field, n, n);
248         for (int i = 0; i < n; ++i) {
249             linearInvertTM.functions[i] = new FieldDerivativeStructure<>(factory);
250             for (int j = 0; j < n; ++j) {
251                 linearInvertTM.functions[i].setDerivativeComponent(indirection[j], linearInvert.getEntry(i, j));
252             }
253         }
254 
255         // perform fixed-point evaluation of the inverse
256         // adding one derivation order at each iteration
257         final FieldTaylorMap<T> identity = new FieldTaylorMap<>(field, n, compiler.getOrder(), n);
258         FieldTaylorMap<T> invertTM = linearInvertTM;
259         for (int k = 1; k < compiler.getOrder(); ++k) {
260             invertTM = linearInvertTM.compose(identity.subtract(nonLinearTM.compose(invertTM)));
261         }
262 
263         // set the constants
264         for (int i = 0; i < n; ++i) {
265             invertTM.point[i] = functions[i].getValue();
266             invertTM.functions[i].setDerivativeComponent(0, point[i]);
267         }
268 
269         return invertTM;
270 
271     }
272 
273 }