1   /* Copyright 2013-2019 CS Systèmes d'Information
2    * Licensed to CS Systèmes d'Information (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS 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    *   http://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  
18  package org.orekit.rugged.adjustment;
19  
20  import java.util.ArrayList;
21  import java.util.HashMap;
22  import java.util.HashSet;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Set;
26  
27  import org.hipparchus.analysis.differentiation.DSFactory;
28  import org.hipparchus.analysis.differentiation.DerivativeStructure;
29  import org.hipparchus.optim.ConvergenceChecker;
30  import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresProblem;
31  import org.hipparchus.optim.nonlinear.vector.leastsquares.MultivariateJacobianFunction;
32  import org.hipparchus.optim.nonlinear.vector.leastsquares.ParameterValidator;
33  import org.orekit.rugged.adjustment.measurements.Observables;
34  import org.orekit.rugged.errors.RuggedException;
35  import org.orekit.rugged.errors.RuggedMessages;
36  import org.orekit.rugged.linesensor.LineSensor;
37  import org.orekit.rugged.utils.DSGenerator;
38  import org.orekit.utils.ParameterDriver;
39  
40  /**
41   * Builder for optimization problem.
42   * <p>
43   * Constructs the optimization problem defined by a set of measurement and sensors.
44   * </p>
45   * @author Jonathan Guinet
46   * @author Guylaine Prat
47   * @since 2.0
48   */
49  abstract class OptimizationProblemBuilder {
50  
51      /** Margin used in parameters estimation for the inverse location lines range. */
52      protected static final int ESTIMATION_LINE_RANGE_MARGIN = 100;
53  
54      /** Derivative structure generator.*/
55      private final DSGenerator generator;
56  
57      /** Parameter drivers list. */
58      private final List<ParameterDriver> drivers;
59  
60      /** Number of parameters to refine. */
61      private final int nbParams;
62  
63      /** Measurements. */
64      private Observables measurements;
65  
66      /** Sensors list. */
67      private final List<LineSensor> sensors;
68  
69      /** Constructor.
70       * @param sensors list of sensors to refine
71       * @param measurements set of observables
72       */
73      OptimizationProblemBuilder(final List<LineSensor> sensors, final Observables measurements) {
74  
75          this.generator = this.createGenerator(sensors);
76          this.drivers = this.generator.getSelected();
77          this.nbParams = this.drivers.size();
78          if (this.nbParams == 0) {
79              throw new RuggedException(RuggedMessages.NO_PARAMETERS_SELECTED);
80          }
81          this.measurements = measurements;
82          this.sensors = sensors;
83      }
84  
85      /** Least squares problem builder.
86       * @param maxEvaluations maximum number of evaluations
87       * @param convergenceThreshold convergence threshold
88       * @return the least squares problem
89       */
90  
91      public abstract LeastSquaresProblem build(int maxEvaluations, double convergenceThreshold);
92  
93      /** Create the convergence check.
94       * <p>
95       * check LInf distance of parameters variation between previous and current iteration
96       * </p>
97       * @param parametersConvergenceThreshold convergence threshold
98       * @return the checker
99       */
100     final ConvergenceChecker<LeastSquaresProblem.Evaluation>
101                             createChecker(final double parametersConvergenceThreshold) {
102 
103         final ConvergenceChecker<LeastSquaresProblem.Evaluation> checker = (iteration, previous, current)
104             -> current.getPoint().getLInfDistance(previous.getPoint()) <= parametersConvergenceThreshold;
105 
106         return checker;
107     }
108 
109     /** Create start points for optimization algorithm.
110      * @return start parameters values (normalized)
111      */
112     final double[] createStartTab() {
113 
114         // Get start points (as a normalized value)
115         final double[] start = new double[this.nbParams];
116         int iStart = 0;
117         for (final ParameterDriver driver : this.drivers) {
118             start[iStart++] = driver.getNormalizedValue();
119         }
120         return start;
121     }
122 
123     /** Create targets and weights of optimization problem. */
124     protected abstract void createTargetAndWeight();
125 
126     /** Create the model function value and its Jacobian.
127      * @return the model function value and its Jacobian
128      */
129     protected abstract MultivariateJacobianFunction createFunction();
130 
131     /** Parse the observables to select mapping .*/
132     protected abstract void initMapping();
133 
134     /** Create parameter validator.
135      * @return parameter validator
136      */
137     final ParameterValidator createParameterValidator() {
138 
139         // Prevent parameters to exceed their prescribed bounds
140         final ParameterValidator validator = params -> {
141             int i = 0;
142             for (final ParameterDriver driver : this.drivers) {
143 
144                 // let the parameter handle min/max clipping
145                 driver.setNormalizedValue(params.getEntry(i));
146                 params.setEntry(i++, driver.getNormalizedValue());
147             }
148             return params;
149         };
150 
151         return validator;
152     }
153 
154     /** Create the generator for {@link DerivativeStructure} instances.
155      * @param selectedSensors list of sensors referencing the parameters drivers
156      * @return a new generator
157      */
158     private DSGenerator createGenerator(final List<LineSensor> selectedSensors) {
159 
160         // Initialize set of drivers name
161         final Set<String> names = new HashSet<>();
162 
163         // Get the drivers name
164         for (final LineSensor sensor : selectedSensors) {
165 
166             // Get the drivers name for the sensor
167             sensor.getParametersDrivers().forEach(driver -> {
168 
169                 // Add the name of the driver to the set of drivers name
170                 if (names.contains(driver.getName()) == false) {
171                     names.add(driver.getName());
172                 }
173             });
174         }
175 
176         // Set up generator list and map
177         final List<ParameterDriver> selected = new ArrayList<>();
178         final Map<String, Integer> map = new HashMap<>();
179 
180         // Get the list of selected drivers
181         for (final LineSensor sensor : selectedSensors) {
182 
183             sensor.getParametersDrivers().filter(driver -> driver.isSelected()).forEach(driver -> {
184                 if (map.get(driver.getName()) == null) {
185                     map.put(driver.getName(), map.size());
186                     selected.add(driver);
187                 }
188             });
189         }
190 
191         final DSFactory factory = new DSFactory(map.size(), 1);
192 
193         // Derivative Structure Generator
194         return new DSGenerator() {
195 
196             /** {@inheritDoc} */
197             @Override
198             public List<ParameterDriver> getSelected() {
199                 return selected;
200             }
201 
202             /** {@inheritDoc} */
203             @Override
204             public DerivativeStructure constant(final double value) {
205                 return factory.constant(value);
206             }
207 
208             /** {@inheritDoc} */
209             @Override
210             public DerivativeStructure variable(final ParameterDriver driver) {
211 
212                 final Integer index = map.get(driver.getName());
213                 if (index == null) {
214                     return constant(driver.getValue());
215                 } else {
216                     return factory.variable(index.intValue(), driver.getValue());
217                 }
218             }
219         };
220     }
221 
222     /** Get the sensors list.
223      * @return the sensors list
224      */
225     protected List<LineSensor> getSensors() {
226         return sensors;
227     }
228 
229     /** Get the number of parameters to refine.
230      * @return the number of parameters to refine
231      */
232     protected final int getNbParams() {
233         return this.nbParams;
234     }
235 
236     /**
237      * Get the parameters drivers list.
238      * @return the selected list of parameters driver
239      */
240     protected final List<ParameterDriver> getDrivers() {
241         return this.drivers;
242     }
243 
244     /**
245      * Get the derivative structure generator.
246      * @return the derivative structure generator.
247      */
248     protected final DSGenerator getGenerator() {
249         return this.generator;
250     }
251 
252     /** Get the measurements.
253      * @return the measurements
254      */
255     protected Observables getMeasurements() {
256         return measurements;
257     }
258 }