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 }