View Javadoc
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    *      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  package org.apache.commons.math4.legacy.optim.nonlinear.scalar;
18  
19  import org.apache.commons.math4.legacy.analysis.MultivariateFunction;
20  import org.apache.commons.math4.legacy.analysis.UnivariateFunction;
21  import org.apache.commons.math4.legacy.optim.BaseMultivariateOptimizer;
22  import org.apache.commons.math4.legacy.optim.ConvergenceChecker;
23  import org.apache.commons.math4.legacy.optim.OptimizationData;
24  import org.apache.commons.math4.legacy.optim.PointValuePair;
25  import org.apache.commons.math4.legacy.optim.MaxEval;
26  import org.apache.commons.math4.legacy.optim.univariate.BracketFinder;
27  import org.apache.commons.math4.legacy.optim.univariate.BrentOptimizer;
28  import org.apache.commons.math4.legacy.optim.univariate.SearchInterval;
29  import org.apache.commons.math4.legacy.optim.univariate.SimpleUnivariateValueChecker;
30  import org.apache.commons.math4.legacy.optim.univariate.UnivariateObjectiveFunction;
31  import org.apache.commons.math4.legacy.optim.univariate.UnivariateOptimizer;
32  import org.apache.commons.math4.legacy.optim.univariate.UnivariatePointValuePair;
33  
34  /**
35   * Base class for a multivariate scalar function optimizer.
36   *
37   * @since 3.1
38   */
39  public abstract class MultivariateOptimizer
40      extends BaseMultivariateOptimizer<PointValuePair> {
41      /** Objective function. */
42      private MultivariateFunction function;
43      /** Type of optimization. */
44      private GoalType goal;
45      /** Line search relative tolerance. */
46      private double lineSearchRelativeTolerance = 1e-8;
47      /** Line search absolute tolerance. */
48      private double lineSearchAbsoluteTolerance = 1e-8;
49      /** Line serach initial bracketing range. */
50      private double lineSearchInitialBracketingRange = 1d;
51      /** Line search. */
52      private LineSearch lineSearch;
53  
54      /**
55       * @param checker Convergence checker.
56       */
57      protected MultivariateOptimizer(ConvergenceChecker<PointValuePair> checker) {
58          super(checker);
59      }
60  
61      /**
62       * {@inheritDoc}
63       *
64       * @param optData Optimization data. In addition to those documented in
65       * {@link BaseMultivariateOptimizer#parseOptimizationData(OptimizationData[])
66       * BaseMultivariateOptimizer}, this method will register the following data:
67       * <ul>
68       *  <li>{@link ObjectiveFunction}</li>
69       *  <li>{@link GoalType}</li>
70       *  <li>{@link LineSearchTolerance}</li>
71       * </ul>
72       * @return {@inheritDoc}
73       * @throws org.apache.commons.math4.legacy.exception.TooManyEvaluationsException
74       * if the maximal number of evaluations is exceeded.
75       */
76      @Override
77      public PointValuePair optimize(OptimizationData... optData) {
78          // Set up base class and perform computation.
79          return super.optimize(optData);
80      }
81  
82      /**
83       * Scans the list of (required and optional) optimization data that
84       * characterize the problem.
85       *
86       * @param optData Optimization data.
87       * The following data will be looked for:
88       * <ul>
89       *  <li>{@link ObjectiveFunction}</li>
90       *  <li>{@link GoalType}</li>
91       *  <li>{@link LineSearchTolerance}</li>
92       * </ul>
93       */
94      @Override
95      protected void parseOptimizationData(OptimizationData... optData) {
96          // Allow base class to register its own data.
97          super.parseOptimizationData(optData);
98  
99          // The existing values (as set by the previous call) are reused if
100         // not provided in the argument list.
101         for (OptimizationData data : optData) {
102             if (data instanceof GoalType) {
103                 goal = (GoalType) data;
104                 continue;
105             }
106             if (data instanceof ObjectiveFunction) {
107                 final MultivariateFunction delegate = ((ObjectiveFunction) data).getObjectiveFunction();
108                 function = new MultivariateFunction() {
109                         @Override
110                         public double value(double[] point) {
111                             incrementEvaluationCount();
112                             return delegate.value(point);
113                         }
114                     };
115                 continue;
116             }
117             if (data instanceof LineSearchTolerance) {
118                 final LineSearchTolerance tol = (LineSearchTolerance) data;
119                 lineSearchRelativeTolerance = tol.getRelativeTolerance();
120                 lineSearchAbsoluteTolerance = tol.getAbsoluteTolerance();
121                 lineSearchInitialBracketingRange = tol.getInitialBracketingRange();
122                 continue;
123             }
124         }
125     }
126 
127     /**
128      * Intantiate the line search implementation.
129      */
130     protected void createLineSearch() {
131         lineSearch = new LineSearch(this,
132                                     lineSearchRelativeTolerance,
133                                     lineSearchAbsoluteTolerance,
134                                     lineSearchInitialBracketingRange);
135     }
136 
137     /**
138      * Finds the number {@code alpha} that optimizes
139      * {@code f(startPoint + alpha * direction)}.
140      *
141      * @param startPoint Starting point.
142      * @param direction Search direction.
143      * @return the optimum.
144      * @throws org.apache.commons.math4.legacy.exception.TooManyEvaluationsException
145      * if the number of evaluations is exceeded.
146      */
147     protected UnivariatePointValuePair lineSearch(final double[] startPoint,
148                                                   final double[] direction) {
149         return lineSearch.search(startPoint, direction);
150     }
151 
152     /**
153      * @return the optimization type.
154      */
155     protected GoalType getGoalType() {
156         return goal;
157     }
158 
159     /**
160      * @return a wrapper that delegates to the user-supplied function,
161      * and counts the number of evaluations.
162      */
163     protected MultivariateFunction getObjectiveFunction() {
164         return function;
165     }
166 
167     /**
168      * Computes the objective function value.
169      * This method <em>must</em> be called by subclasses to enforce the
170      * evaluation counter limit.
171      *
172      * @param params Point at which the objective function must be evaluated.
173      * @return the objective function value at the specified point.
174      * @throws org.apache.commons.math4.legacy.exception.TooManyEvaluationsException
175      * if the maximal number of evaluations is exceeded.
176      *
177      * @deprecated Use {@link #getObjectiveFunction()} instead.
178      */
179     @Deprecated
180     public double computeObjectiveValue(double[] params) {
181         return function.value(params);
182     }
183 
184     /**
185      * Find the minimum of the objective function along a given direction.
186      *
187      * @since 4.0
188      */
189     private static class LineSearch {
190         /**
191          * Value that will pass the precondition check for {@link BrentOptimizer}
192          * but will not pass the convergence check, so that the custom checker
193          * will always decide when to stop the line search.
194          */
195         private static final double REL_TOL_UNUSED = 1e-15;
196         /**
197          * Value that will pass the precondition check for {@link BrentOptimizer}
198          * but will not pass the convergence check, so that the custom checker
199          * will always decide when to stop the line search.
200          */
201         private static final double ABS_TOL_UNUSED = Double.MIN_VALUE;
202         /**
203          * Optimizer used for line search.
204          */
205         private final UnivariateOptimizer lineOptimizer;
206         /**
207          * Automatic bracketing.
208          */
209         private final BracketFinder bracket = new BracketFinder();
210         /**
211          * Extent of the initial interval used to find an interval that
212          * brackets the optimum.
213          */
214         private final double initialBracketingRange;
215         /**
216          * Optimizer on behalf of which the line search must be performed.
217          */
218         private final MultivariateOptimizer mainOptimizer;
219 
220         /**
221          * The {@code BrentOptimizer} default stopping criterion uses the
222          * tolerances to check the domain (point) values, not the function
223          * values.
224          * The {@code relativeTolerance} and {@code absoluteTolerance}
225          * arguments are thus passed to a {@link SimpleUnivariateValueChecker
226          * custom checker} that will use the function values.
227          *
228          * @param optimizer Optimizer on behalf of which the line search
229          * be performed.
230          * Its {@link MultivariateOptimizer#getObjectiveFunction() objective
231          * function} will be called by the {@link #search(double[],double[])
232          * search} method.
233          * @param relativeTolerance Search will stop when the function relative
234          * difference between successive iterations is below this value.
235          * @param absoluteTolerance Search will stop when the function absolute
236          * difference between successive iterations is below this value.
237          * @param initialBracketingRange Extent of the initial interval used to
238          * find an interval that brackets the optimum.
239          * If the optimized function varies a lot in the vicinity of the optimum,
240          * it may be necessary to provide a value lower than the distance between
241          * successive local minima.
242          */
243         /* package-private */ LineSearch(MultivariateOptimizer optimizer,
244                                          double relativeTolerance,
245                                          double absoluteTolerance,
246                                          double initialBracketingRange) {
247             mainOptimizer = optimizer;
248             lineOptimizer = new BrentOptimizer(REL_TOL_UNUSED,
249                                                ABS_TOL_UNUSED,
250                                                new SimpleUnivariateValueChecker(relativeTolerance,
251                                                                                 absoluteTolerance));
252             this.initialBracketingRange = initialBracketingRange;
253         }
254 
255         /**
256          * Finds the number {@code alpha} that optimizes
257          * {@code f(startPoint + alpha * direction)}.
258          *
259          * @param startPoint Starting point.
260          * @param direction Search direction.
261          * @return the optimum.
262          * @throws org.apache.commons.math4.legacy.exception.TooManyEvaluationsException
263          * if the number of evaluations is exceeded.
264          */
265         /* package-private */ UnivariatePointValuePair search(final double[] startPoint,
266                                                               final double[] direction) {
267             final int n = startPoint.length;
268             final MultivariateFunction func = mainOptimizer.getObjectiveFunction();
269             final UnivariateFunction f = new UnivariateFunction() {
270                     /** {@inheritDoc} */
271                     @Override
272                     public double value(double alpha) {
273                         final double[] x = new double[n];
274                         for (int i = 0; i < n; i++) {
275                             x[i] = startPoint[i] + alpha * direction[i];
276                         }
277                         return func.value(x);
278                     }
279                 };
280 
281             final GoalType goal = mainOptimizer.getGoalType();
282             bracket.search(f, goal, 0, initialBracketingRange);
283             // Passing "MAX_VALUE" as a dummy value because it is the enclosing
284             // class that counts the number of evaluations (and will eventually
285             // generate the exception).
286             return lineOptimizer.optimize(new MaxEval(Integer.MAX_VALUE),
287                                           new UnivariateObjectiveFunction(f),
288                                           goal,
289                                           new SearchInterval(bracket.getLo(),
290                                                              bracket.getHi(),
291                                                              bracket.getMid()));
292         }
293     }
294 }