1 /*
2 * Licensed to the Apache Software Foundation (ASF) 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 ASF 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
18 /*
19 * This is not the original file distributed by the Apache Software Foundation
20 * It has been modified by the Hipparchus project
21 */
22 package org.hipparchus.optim.nonlinear.scalar;
23
24 import org.hipparchus.analysis.MultivariateFunction;
25 import org.hipparchus.analysis.UnivariateFunction;
26 import org.hipparchus.analysis.function.Logit;
27 import org.hipparchus.analysis.function.Sigmoid;
28 import org.hipparchus.exception.LocalizedCoreFormats;
29 import org.hipparchus.exception.MathIllegalArgumentException;
30 import org.hipparchus.util.FastMath;
31 import org.hipparchus.util.MathUtils;
32
33 /**
34 * <p>Adapter for mapping bounded {@link MultivariateFunction} to unbounded ones.</p>
35 *
36 * <p>
37 * This adapter can be used to wrap functions subject to simple bounds on
38 * parameters so they can be used by optimizers that do <em>not</em> directly
39 * support simple bounds.
40 * </p>
41 * <p>
42 * The principle is that the user function that will be wrapped will see its
43 * parameters bounded as required, i.e when its {@code value} method is called
44 * with argument array {@code point}, the elements array will fulfill requirement
45 * {@code lower[i] <= point[i] <= upper[i]} for all i. Some of the components
46 * may be unbounded or bounded only on one side if the corresponding bound is
47 * set to an infinite value. The optimizer will not manage the user function by
48 * itself, but it will handle this adapter and it is this adapter that will take
49 * care the bounds are fulfilled. The adapter {@link #value(double[])} method will
50 * be called by the optimizer with unbound parameters, and the adapter will map
51 * the unbounded value to the bounded range using appropriate functions like
52 * {@link Sigmoid} for double bounded elements for example.
53 * </p>
54 * <p>
55 * As the optimizer sees only unbounded parameters, it should be noted that the
56 * start point or simplex expected by the optimizer should be unbounded, so the
57 * user is responsible for converting his bounded point to unbounded by calling
58 * {@link #boundedToUnbounded(double[])} before providing them to the optimizer.
59 * For the same reason, the point returned by the {@link
60 * org.hipparchus.optim.BaseMultivariateOptimizer#optimize(org.hipparchus.optim.OptimizationData[])}
61 * method is unbounded. So to convert this point to bounded, users must call
62 * {@link #unboundedToBounded(double[])} by themselves!</p>
63 * <p>
64 * This adapter is only a poor man solution to simple bounds optimization constraints
65 * that can be used with simple optimizers like
66 * {@link org.hipparchus.optim.nonlinear.scalar.noderiv.SimplexOptimizer
67 * SimplexOptimizer}.
68 * A better solution is to use an optimizer that directly supports simple bounds like
69 * {@link org.hipparchus.optim.nonlinear.scalar.noderiv.CMAESOptimizer
70 * CMAESOptimizer} or
71 * {@link org.hipparchus.optim.nonlinear.scalar.noderiv.BOBYQAOptimizer
72 * BOBYQAOptimizer}.
73 * One caveat of this poor-man's solution is that behavior near the bounds may be
74 * numerically unstable as bounds are mapped from infinite values.
75 * Another caveat is that convergence values are evaluated by the optimizer with
76 * respect to unbounded variables, so there will be scales differences when
77 * converted to bounded variables.
78 * </p>
79 *
80 * @see MultivariateFunctionPenaltyAdapter
81 *
82 */
83 public class MultivariateFunctionMappingAdapter
84 implements MultivariateFunction {
85 /** Underlying bounded function. */
86 private final MultivariateFunction bounded;
87 /** Mapping functions. */
88 private final Mapper[] mappers;
89
90 /** Simple constructor.
91 * @param bounded bounded function
92 * @param lower lower bounds for each element of the input parameters array
93 * (some elements may be set to {@code Double.NEGATIVE_INFINITY} for
94 * unbounded values)
95 * @param upper upper bounds for each element of the input parameters array
96 * (some elements may be set to {@code Double.POSITIVE_INFINITY} for
97 * unbounded values)
98 * @exception MathIllegalArgumentException if lower and upper bounds are not
99 * consistent, either according to dimension or to values
100 */
101 public MultivariateFunctionMappingAdapter(final MultivariateFunction bounded,
102 final double[] lower, final double[] upper) {
103 // safety checks
104 MathUtils.checkNotNull(lower);
105 MathUtils.checkNotNull(upper);
106 if (lower.length != upper.length) {
107 throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
108 lower.length, upper.length);
109 }
110 for (int i = 0; i < lower.length; ++i) {
111 if (!(upper[i] >= lower[i])) { // NOPMD - the test is written this way so it also fails for NaN
112 throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL,
113 upper[i], lower[i]);
114 }
115 }
116
117 this.bounded = bounded;
118 this.mappers = new Mapper[lower.length];
119 for (int i = 0; i < mappers.length; ++i) {
120 if (Double.isInfinite(lower[i])) {
121 if (Double.isInfinite(upper[i])) {
122 // element is unbounded, no transformation is needed
123 mappers[i] = new NoBoundsMapper();
124 } else {
125 // element is simple-bounded on the upper side
126 mappers[i] = new UpperBoundMapper(upper[i]);
127 }
128 } else {
129 if (Double.isInfinite(upper[i])) {
130 // element is simple-bounded on the lower side
131 mappers[i] = new LowerBoundMapper(lower[i]);
132 } else {
133 // element is double-bounded
134 mappers[i] = new LowerUpperBoundMapper(lower[i], upper[i]);
135 }
136 }
137 }
138 }
139
140 /**
141 * Maps an array from unbounded to bounded.
142 *
143 * @param point Unbounded values.
144 * @return the bounded values.
145 */
146 public double[] unboundedToBounded(double[] point) {
147 // Map unbounded input point to bounded point.
148 final double[] mapped = new double[mappers.length];
149 for (int i = 0; i < mappers.length; ++i) {
150 mapped[i] = mappers[i].unboundedToBounded(point[i]);
151 }
152
153 return mapped;
154 }
155
156 /**
157 * Maps an array from bounded to unbounded.
158 *
159 * @param point Bounded values.
160 * @return the unbounded values.
161 */
162 public double[] boundedToUnbounded(double[] point) {
163 // Map bounded input point to unbounded point.
164 final double[] mapped = new double[mappers.length];
165 for (int i = 0; i < mappers.length; ++i) {
166 mapped[i] = mappers[i].boundedToUnbounded(point[i]);
167 }
168
169 return mapped;
170 }
171
172 /**
173 * Compute the underlying function value from an unbounded point.
174 * <p>
175 * This method simply bounds the unbounded point using the mappings
176 * set up at construction and calls the underlying function using
177 * the bounded point.
178 * </p>
179 * @param point unbounded value
180 * @return underlying function value
181 * @see #unboundedToBounded(double[])
182 */
183 @Override
184 public double value(double[] point) {
185 return bounded.value(unboundedToBounded(point));
186 }
187
188 /** Mapping interface. */
189 private interface Mapper {
190 /**
191 * Maps a value from unbounded to bounded.
192 *
193 * @param y Unbounded value.
194 * @return the bounded value.
195 */
196 double unboundedToBounded(double y);
197
198 /**
199 * Maps a value from bounded to unbounded.
200 *
201 * @param x Bounded value.
202 * @return the unbounded value.
203 */
204 double boundedToUnbounded(double x);
205 }
206
207 /** Local class for no bounds mapping. */
208 private static class NoBoundsMapper implements Mapper {
209 /** {@inheritDoc} */
210 @Override
211 public double unboundedToBounded(final double y) {
212 return y;
213 }
214
215 /** {@inheritDoc} */
216 @Override
217 public double boundedToUnbounded(final double x) {
218 return x;
219 }
220 }
221
222 /** Local class for lower bounds mapping. */
223 private static class LowerBoundMapper implements Mapper {
224 /** Low bound. */
225 private final double lower;
226
227 /**
228 * Simple constructor.
229 *
230 * @param lower lower bound
231 */
232 LowerBoundMapper(final double lower) {
233 this.lower = lower;
234 }
235
236 /** {@inheritDoc} */
237 @Override
238 public double unboundedToBounded(final double y) {
239 return lower + FastMath.exp(y);
240 }
241
242 /** {@inheritDoc} */
243 @Override
244 public double boundedToUnbounded(final double x) {
245 return FastMath.log(x - lower);
246 }
247
248 }
249
250 /** Local class for upper bounds mapping. */
251 private static class UpperBoundMapper implements Mapper {
252
253 /** Upper bound. */
254 private final double upper;
255
256 /** Simple constructor.
257 * @param upper upper bound
258 */
259 UpperBoundMapper(final double upper) {
260 this.upper = upper;
261 }
262
263 /** {@inheritDoc} */
264 @Override
265 public double unboundedToBounded(final double y) {
266 return upper - FastMath.exp(-y);
267 }
268
269 /** {@inheritDoc} */
270 @Override
271 public double boundedToUnbounded(final double x) {
272 return -FastMath.log(upper - x);
273 }
274
275 }
276
277 /** Local class for lower and bounds mapping. */
278 private static class LowerUpperBoundMapper implements Mapper {
279 /** Function from unbounded to bounded. */
280 private final UnivariateFunction boundingFunction;
281 /** Function from bounded to unbounded. */
282 private final UnivariateFunction unboundingFunction;
283
284 /**
285 * Simple constructor.
286 *
287 * @param lower lower bound
288 * @param upper upper bound
289 */
290 LowerUpperBoundMapper(final double lower, final double upper) {
291 boundingFunction = new Sigmoid(lower, upper);
292 unboundingFunction = new Logit(lower, upper);
293 }
294
295 /** {@inheritDoc} */
296 @Override
297 public double unboundedToBounded(final double y) {
298 return boundingFunction.value(y);
299 }
300
301 /** {@inheritDoc} */
302 @Override
303 public double boundedToUnbounded(final double x) {
304 return unboundingFunction.value(x);
305 }
306 }
307 }