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 org.hipparchus.exception.LocalizedCoreFormats;
20 import org.hipparchus.exception.MathIllegalArgumentException;
21 import org.hipparchus.linear.MatrixDecomposer;
22 import org.hipparchus.linear.MatrixUtils;
23 import org.hipparchus.linear.RealMatrix;
24 import org.hipparchus.util.MathUtils;
25
26 /** Container for a Taylor map.
27 * <p>
28 * A Taylor map is a set of n {@link DerivativeStructure}
29 * \((f_1, f_2, \ldots, f_n)\) depending on m parameters \((p_1, p_2, \ldots, p_m)\),
30 * with positive n and m.
31 * </p>
32 * @since 2.2
33 */
34 public class TaylorMap implements DifferentialAlgebra {
35
36 /** Evaluation point. */
37 private final double[] point;
38
39 /** Mapping functions. */
40 private final DerivativeStructure[] functions;
41
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 TaylorMap(final double[] point, final DerivativeStructure[] 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 DSFactory 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
68 /** Constructor for identity map.
69 * <p>
70 * The identity is considered to be evaluated at origin.
71 * </p>
72 * @param parameters number of free parameters
73 * @param order derivation order
74 * @param nbFunctions number of functions
75 */
76 public TaylorMap(final int parameters, final int order, final int nbFunctions) {
77 this(parameters, nbFunctions);
78 final DSFactory factory = new DSFactory(parameters, order);
79 for (int i = 0; i < nbFunctions; ++i) {
80 functions[i] = factory.variable(i, 0.0);
81 }
82 }
83
84 /** Build an empty map evaluated at origin.
85 * @param parameters number of free parameters
86 * @param nbFunctions number of functions
87 */
88 private TaylorMap(final int parameters, final int nbFunctions) {
89 this.point = new double[parameters];
90 this.functions = new DerivativeStructure[nbFunctions];
91 }
92
93 /** {@inheritDoc} */
94 @Override
95 public int getFreeParameters() {
96 return point.length;
97 }
98
99 /** {@inheritDoc} */
100 @Override
101 public int getOrder() {
102 return functions[0].getOrder();
103 }
104
105 /** Get the number of functions of the map.
106 * @return number of functions of the map
107 */
108 public int getNbFunctions() {
109 return functions.length;
110 }
111
112 /** Get the point at which map is evaluated.
113 * @return point at which map is evaluated
114 */
115 public double[] getPoint() {
116 return point.clone();
117 }
118
119 /** Get a function from the map.
120 * @param i index of the function (must be between 0 included and {@link #getNbFunctions()} excluded
121 * @return function at index i
122 */
123 public DerivativeStructure getFunction(final int i) {
124 return functions[i];
125 }
126
127 /** Subtract two maps.
128 * @param map map to subtract from instance
129 * @return this - map
130 */
131 private TaylorMap subtract(final TaylorMap map) {
132 final TaylorMap result = new TaylorMap(point.length, functions.length);
133 for (int i = 0; i < result.functions.length; ++i) {
134 result.functions[i] = functions[i].subtract(map.functions[i]);
135 }
136 return result;
137 }
138
139 /** Evaluate Taylor expansion of the map at some offset.
140 * @param deltaP parameters offsets \((\Delta p_1, \Delta p_2, \ldots, \Delta p_n)\)
141 * @return value of the Taylor expansion at \((p_1 + \Delta p_1, p_2 + \Delta p_2, \ldots, p_n + \Delta p_n)\)
142 */
143 public double[] value(final double... deltaP) {
144 final double[] value = new double[functions.length];
145 for (int i = 0; i < functions.length; ++i) {
146 value[i] = functions[i].taylor(deltaP);
147 }
148 return value;
149 }
150
151 /** Compose the instance with another Taylor map as \(\mathrm{this} \circ \mathrm{other}\).
152 * @param other map with which instance must be composed
153 * @return composed map \(\mathrm{this} \circ \mathrm{other}\)
154 */
155 public TaylorMap compose(final TaylorMap other) {
156
157 // safety check
158 MathUtils.checkDimension(getFreeParameters(), other.getNbFunctions());
159
160 final DerivativeStructure[] composed = new DerivativeStructure[functions.length];
161 for (int i = 0; i < functions.length; ++i) {
162 composed[i] = functions[i].rebase(other.functions);
163 }
164
165 return new TaylorMap(other.point, composed);
166
167 }
168
169 /** Invert the instance.
170 * <p>
171 * Consider {@link #value(double[]) Taylor expansion} of the map with
172 * small parameters offsets \((\Delta p_1, \Delta p_2, \ldots, \Delta p_n)\)
173 * which leads to evaluation offsets \((f_1 + df_1, f_2 + df_2, \ldots, f_n + df_n)\).
174 * The map inversion defines a Taylor map that computes \((\Delta p_1,
175 * \Delta p_2, \ldots, \Delta p_n)\) from \((df_1, df_2, \ldots, df_n)\).
176 * </p>
177 * <p>
178 * The map must be square to be invertible (i.e. the number of functions and the
179 * number of parameters in the functions must match)
180 * </p>
181 * @param decomposer matrix decomposer to user for inverting the linear part
182 * @return inverted map
183 * @see <a href="https://doi.org/10.1016/S1076-5670(08)70228-3">chapter
184 * 2 of Advances in Imaging and Electron Physics, vol 108
185 * by Martin Berz</a>
186 */
187 public TaylorMap invert(final MatrixDecomposer decomposer) {
188
189 final DSFactory factory = functions[0].getFactory();
190 final DSCompiler compiler = factory.getCompiler();
191 final int n = functions.length;
192
193 // safety check
194 MathUtils.checkDimension(n, functions[0].getFreeParameters());
195
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
205 // separate linear and non-linear terms
206 final RealMatrix linear = MatrixUtils.createRealMatrix(n, n);
207 final TaylorMap nonLinearTM = new TaylorMap(n, n);
208 for (int i = 0; i < n; ++i) {
209 nonLinearTM.functions[i] = factory.build(functions[i].getAllDerivatives());
210 nonLinearTM.functions[i].setDerivativeComponent(0, 0.0);
211 for (int j = 0; j < n; ++j) {
212 final int k = indirection[j];
213 linear.setEntry(i, j, functions[i].getDerivativeComponent(k));
214 nonLinearTM.functions[i].setDerivativeComponent(k, 0.0);
215 }
216 }
217
218 // invert the linear part
219 final RealMatrix linearInvert = decomposer.decompose(linear).getInverse();
220
221 // convert the invert of linear part back to a Taylor map
222 final TaylorMap linearInvertTM = new TaylorMap(n, n);
223 for (int i = 0; i < n; ++i) {
224 linearInvertTM.functions[i] = new DerivativeStructure(factory);
225 for (int j = 0; j < n; ++j) {
226 linearInvertTM.functions[i].setDerivativeComponent(indirection[j], linearInvert.getEntry(i, j));
227 }
228 }
229
230 // perform fixed-point evaluation of the inverse
231 // adding one derivation order at each iteration
232 final TaylorMap identity = new TaylorMap(n, compiler.getOrder(), n);
233 TaylorMap invertTM = linearInvertTM;
234 for (int k = 1; k < compiler.getOrder(); ++k) {
235 invertTM = linearInvertTM.compose(identity.subtract(nonLinearTM.compose(invertTM)));
236 }
237
238 // set the constants
239 for (int i = 0; i < n; ++i) {
240 invertTM.point[i] = functions[i].getValue();
241 invertTM.functions[i].setDerivativeComponent(0, point[i]);
242 }
243
244 return invertTM;
245
246 }
247
248 }