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 parameters of the map.
115      * @return number of parameters of the map
116      */
117     @Deprecated
118     public int getNbParameters() {
119         return getFreeParameters();
120     }
121 
122     /** Get the number of functions of the map.
123      * @return number of functions of the map
124      */
125     public int getNbFunctions() {
126         return functions.length;
127     }
128 
129     /** Get the point at which map is evaluated.
130      * @return point at which map is evaluated
131      */
132     public T[] getPoint() {
133         return point.clone();
134     }
135 
136     /** Get a function from the map.
137      * @param i index of the function (must be between 0 included and {@link #getNbFunctions()} excluded
138      * @return function at index i
139      */
140     public FieldDerivativeStructure<T> getFunction(final int i) {
141         return functions[i];
142     }
143 
144     /** Subtract two maps.
145      * @param map map to subtract from instance
146      * @return this - map
147      */
148     private FieldTaylorMap<T> subtract(final FieldTaylorMap<T> map) {
149         final FieldTaylorMap<T> result = new FieldTaylorMap<>(functions[0].getFactory().getValueField(),
150                                                               point.length, functions.length);
151         for (int i = 0; i < result.functions.length; ++i) {
152             result.functions[i] = functions[i].subtract(map.functions[i]);
153         }
154         return result;
155     }
156 
157     /** Evaluate Taylor expansion of the map at some offset.
158      * @param deltaP parameters offsets \((\Delta p_1, \Delta p_2, \ldots, \Delta p_n)\)
159      * @return value of the Taylor expansion at \((p_1 + \Delta p_1, p_2 + \Delta p_2, \ldots, p_n + \Delta p_n)\)
160      */
161     public T[] value(final double... deltaP) {
162         final T[] value = MathArrays.buildArray(functions[0].getFactory().getValueField(), functions.length);
163         for (int i = 0; i < functions.length; ++i) {
164             value[i] = functions[i].taylor(deltaP);
165         }
166         return value;
167     }
168 
169     /** Evaluate Taylor expansion of the map at some offset.
170      * @param deltaP parameters offsets \((\Delta p_1, \Delta p_2, \ldots, \Delta p_n)\)
171      * @return value of the Taylor expansion at \((p_1 + \Delta p_1, p_2 + \Delta p_2, \ldots, p_n + \Delta p_n)\)
172      */
173     public T[] value(@SuppressWarnings("unchecked") final T... deltaP) {
174         final T[] value = MathArrays.buildArray(functions[0].getFactory().getValueField(), functions.length);
175         for (int i = 0; i < functions.length; ++i) {
176             value[i] = functions[i].taylor(deltaP);
177         }
178         return value;
179     }
180 
181     /** Compose the instance with another Taylor map as \(\mathrm{this} \circ \mathrm{other}\).
182      * @param other map with which instance must be composed
183      * @return composed map \(\mathrm{this} \circ \mathrm{other}\)
184      */
185     public FieldTaylorMap<T> compose(final FieldTaylorMap<T> other) {
186 
187         // safety check
188         MathUtils.checkDimension(getFreeParameters(), other.getNbFunctions());
189 
190         @SuppressWarnings("unchecked")
191         final FieldDerivativeStructure<T>[] composed = (FieldDerivativeStructure<T>[]) Array.newInstance(FieldDerivativeStructure.class,
192                                                                                                          functions.length);
193         for (int i = 0; i < functions.length; ++i) {
194             composed[i] = functions[i].rebase(other.functions);
195         }
196 
197         return new FieldTaylorMap<>(other.point, composed);
198 
199     }
200 
201     /** Invert the instance.
202      * <p>
203      * Consider {@link #value(double[]) Taylor expansion} of the map with
204      * small parameters offsets \((\Delta p_1, \Delta p_2, \ldots, \Delta p_n)\)
205      * which leads to evaluation offsets \((f_1 + df_1, f_2 + df_2, \ldots, f_n + df_n)\).
206      * The map inversion defines a Taylor map that computes \((\Delta p_1,
207      * \Delta p_2, \ldots, \Delta p_n)\) from \((df_1, df_2, \ldots, df_n)\).
208      * </p>
209      * <p>
210      * The map must be square to be invertible (i.e. the number of functions and the
211      * number of parameters in the functions must match)
212      * </p>
213      * @param decomposer matrix decomposer to user for inverting the linear part
214      * @return inverted map
215      * @see <a href="https://doi.org/10.1016/S1076-5670(08)70228-3">chapter
216      * 2 of Advances in Imaging and Electron Physics, vol 108
217      * by Martin Berz</a>
218      */
219     public FieldTaylorMap<T> invert(final FieldMatrixDecomposer<T> decomposer) {
220 
221         final FDSFactory<T>  factory  = functions[0].getFactory();
222         final Field<T>       field    = factory.getValueField();
223         final DSCompiler     compiler = factory.getCompiler();
224         final int            n        = functions.length;
225 
226         // safety check
227         MathUtils.checkDimension(n, functions[0].getFreeParameters());
228 
229         // set up an indirection array between linear terms and complete derivatives arrays
230         final int[] indirection    = new int[n];
231         int linearIndex = 0;
232         for (int k = 1; linearIndex < n; ++k) {
233             if (compiler.getPartialDerivativeOrdersSum(k) == 1) {
234                 indirection[linearIndex++] = k;
235             }
236         }
237 
238         // separate linear and non-linear terms
239         final FieldMatrix<T> linear      = MatrixUtils.createFieldMatrix(field, n, n);
240         final FieldTaylorMap<T>  nonLinearTM = new FieldTaylorMap<>(field, n, n);
241         for (int i = 0; i < n; ++i) {
242             nonLinearTM.functions[i] = factory.build(functions[i].getAllDerivatives());
243             nonLinearTM.functions[i].setDerivativeComponent(0, field.getZero());
244             for (int j = 0; j < n; ++j) {
245                 final int k = indirection[j];
246                 linear.setEntry(i, j, functions[i].getDerivativeComponent(k));
247                 nonLinearTM.functions[i].setDerivativeComponent(k, field.getZero());
248             }
249         }
250 
251         // invert the linear part
252         final FieldMatrix<T> linearInvert = decomposer.decompose(linear).getInverse();
253 
254         // convert the invert of linear part back to a Taylor map
255         final FieldTaylorMap<T>  linearInvertTM = new FieldTaylorMap<>(field, n, n);
256         for (int i = 0; i < n; ++i) {
257             linearInvertTM.functions[i] = new FieldDerivativeStructure<>(factory);
258             for (int j = 0; j < n; ++j) {
259                 linearInvertTM.functions[i].setDerivativeComponent(indirection[j], linearInvert.getEntry(i, j));
260             }
261         }
262 
263         // perform fixed-point evaluation of the inverse
264         // adding one derivation order at each iteration
265         final FieldTaylorMap<T> identity = new FieldTaylorMap<>(field, n, compiler.getOrder(), n);
266         FieldTaylorMap<T> invertTM = linearInvertTM;
267         for (int k = 1; k < compiler.getOrder(); ++k) {
268             invertTM = linearInvertTM.compose(identity.subtract(nonLinearTM.compose(invertTM)));
269         }
270 
271         // set the constants
272         for (int i = 0; i < n; ++i) {
273             invertTM.point[i] = functions[i].getValue();
274             invertTM.functions[i].setDerivativeComponent(0, point[i]);
275         }
276 
277         return invertTM;
278 
279     }
280 
281 }