BaseOptimizer.java

  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;

  18. import org.apache.commons.math4.legacy.exception.TooManyEvaluationsException;
  19. import org.apache.commons.math4.legacy.exception.TooManyIterationsException;
  20. import org.apache.commons.math4.legacy.core.IntegerSequence;

  21. /**
  22.  * Base class for implementing optimizers.
  23.  * It contains the boiler-plate code for counting the number of evaluations
  24.  * of the objective function and the number of iterations of the algorithm.
  25.  * It also stores a {@link ConvergenceChecker convergence checker}, as well
  26.  * as {@link Tolerance default tolerances} (how the checker and tolerances
  27.  * are used is determined by subclasses).
  28.  * <em>It is not a "user" class.</em>
  29.  *
  30.  * @param <PAIR> Type of the point/value pair returned by the optimization
  31.  * algorithm.
  32.  *
  33.  * @since 3.1
  34.  */
  35. public abstract class BaseOptimizer<PAIR> {
  36.     /** Callback to use for the evaluation counter. */
  37.     private static final MaxEvalCallback MAX_EVAL_CALLBACK = new MaxEvalCallback();
  38.     /** Callback to use for the iteration counter. */
  39.     private static final MaxIterCallback MAX_ITER_CALLBACK = new MaxIterCallback();

  40.     /** Convergence checker. */
  41.     private ConvergenceChecker<PAIR> checker;
  42.     /** Maximum number of evaluations. */
  43.     private int maxEvaluations;
  44.     /** Maximum number of iterations. */
  45.     private int maxIterations;
  46.     /** Evaluations counter. */
  47.     private IntegerSequence.Incrementor evaluations;
  48.     /** Iterations counter. */
  49.     private IntegerSequence.Incrementor iterations;
  50.     /** Relative tolerance. */
  51.     private double relativeTolerance = 1e-6;
  52.     /** Absolute tolerance. */
  53.     private double absoluteTolerance = 1e-6;

  54.     /**
  55.      * @param checker Convergence checker.
  56.      */
  57.     protected BaseOptimizer(ConvergenceChecker<PAIR> checker) {
  58.         this(checker, 0, Integer.MAX_VALUE);
  59.     }

  60.     /**
  61.      * @param checker Convergence checker.
  62.      * @param maxEval Maximum number of objective function evaluations.
  63.      * @param maxIter Maximum number of algorithm iterations.
  64.      */
  65.     protected BaseOptimizer(ConvergenceChecker<PAIR> checker,
  66.                             int maxEval,
  67.                             int maxIter) {
  68.         this.checker = checker;
  69.         this.maxEvaluations = maxEval;
  70.         this.maxIterations = maxIter;
  71.     }

  72.     /** @return the relative tolerance. */
  73.     protected double getRelativeTolerance() {
  74.         return relativeTolerance;
  75.     }

  76.     /** @return the absolute tolerance. */
  77.     protected double getAbsoluteTolerance() {
  78.         return absoluteTolerance;
  79.     }

  80.     /**
  81.      * Gets the maximal number of function evaluations.
  82.      *
  83.      * @return the maximal number of function evaluations.
  84.      */
  85.     public int getMaxEvaluations() {
  86.         return evaluations.getMaximalCount();
  87.     }

  88.     /**
  89.      * Gets the number of evaluations of the objective function.
  90.      * The number of evaluations corresponds to the last call to the
  91.      * {@code optimize} method. It is 0 if the method has not been
  92.      * called yet.
  93.      *
  94.      * @return the number of evaluations of the objective function.
  95.      */
  96.     public int getEvaluations() {
  97.         return evaluations.getCount();
  98.     }

  99.     /**
  100.      * Gets the maximal number of iterations.
  101.      *
  102.      * @return the maximal number of iterations.
  103.      */
  104.     public int getMaxIterations() {
  105.         return iterations.getMaximalCount();
  106.     }

  107.     /**
  108.      * Gets the number of iterations performed by the algorithm.
  109.      * The number iterations corresponds to the last call to the
  110.      * {@code optimize} method. It is 0 if the method has not been
  111.      * called yet.
  112.      *
  113.      * @return the number of evaluations of the objective function.
  114.      */
  115.     public int getIterations() {
  116.         return iterations.getCount();
  117.     }

  118.     /**
  119.      * Gets the convergence checker.
  120.      *
  121.      * @return the object used to check for convergence.
  122.      */
  123.     public ConvergenceChecker<PAIR> getConvergenceChecker() {
  124.         return checker;
  125.     }

  126.     /**
  127.      * Stores data and performs the optimization.
  128.      * <p>
  129.      * The list of parameters is open-ended so that sub-classes can extend it
  130.      * with arguments specific to their concrete implementations.
  131.      * <p>
  132.      * When the method is called multiple times, instance data is overwritten
  133.      * only when actually present in the list of arguments: when not specified,
  134.      * data set in a previous call is retained (and thus is optional in
  135.      * subsequent calls).
  136.      * <p>
  137.      * Important note: Subclasses <em>must</em> override
  138.      * {@link #parseOptimizationData(OptimizationData[])} if they need to register
  139.      * their own options; but then, they <em>must</em> also call
  140.      * {@code super.parseOptimizationData(optData)} within that method.
  141.      *
  142.      * @param optData Optimization data.
  143.      * This method will register the following data:
  144.      * <ul>
  145.      *  <li>{@link MaxEval}</li>
  146.      *  <li>{@link MaxIter}</li>
  147.      *  <li>{@link ConvergenceChecker}</li>
  148.      *  <li>{@link Tolerance}</li>
  149.      * </ul>
  150.      * @return a point/value pair that satisfies the convergence criteria.
  151.      * @throws TooManyEvaluationsException if the maximal number of
  152.      * evaluations is exceeded.
  153.      * @throws TooManyIterationsException if the maximal number of
  154.      * iterations is exceeded.
  155.      */
  156.     public PAIR optimize(OptimizationData... optData) {
  157.         // Parse options.
  158.         parseOptimizationData(optData);
  159.         // Reset counters.
  160.         resetCounters();
  161.         // Perform optimization.
  162.         return doOptimize();
  163.     }

  164.     /**
  165.      * Performs the optimization.
  166.      *
  167.      * @return a point/value pair that satisfies the convergence criteria.
  168.      * @throws TooManyEvaluationsException if the maximal number of
  169.      * evaluations is exceeded.
  170.      * @throws TooManyIterationsException if the maximal number of
  171.      * iterations is exceeded.
  172.      */
  173.     public PAIR optimize() {
  174.         // Reset counters.
  175.         resetCounters();
  176.         // Perform optimization.
  177.         return doOptimize();
  178.     }

  179.     /**
  180.      * Performs the bulk of the optimization algorithm.
  181.      *
  182.      * @return the point/value pair giving the optimal value of the
  183.      * objective function.
  184.      */
  185.     protected abstract PAIR doOptimize();

  186.     /**
  187.      * Increment the evaluation count.
  188.      *
  189.      * @throws TooManyEvaluationsException if the allowed evaluations
  190.      * have been exhausted.
  191.      */
  192.     protected void incrementEvaluationCount() {
  193.         evaluations.increment();
  194.     }

  195.     /**
  196.      * Increment the iteration count.
  197.      *
  198.      * @throws TooManyIterationsException if the allowed iterations
  199.      * have been exhausted.
  200.      */
  201.     protected void incrementIterationCount() {
  202.         iterations.increment();
  203.     }

  204.     /**
  205.      * Scans the list of (required and optional) optimization data that
  206.      * characterize the problem.
  207.      *
  208.      * @param optData Optimization data.
  209.      * The following data will be looked for:
  210.      * <ul>
  211.      *  <li>{@link MaxEval}</li>
  212.      *  <li>{@link MaxIter}</li>
  213.      *  <li>{@link ConvergenceChecker}</li>
  214.      * </ul>
  215.      */
  216.     protected void parseOptimizationData(OptimizationData... optData) {
  217.         // The existing values (as set by the previous call) are reused if
  218.         // not provided in the argument list.
  219.         for (OptimizationData data : optData) {
  220.             if (data instanceof MaxEval) {
  221.                 maxEvaluations = ((MaxEval) data).getMaxEval();
  222.                 continue;
  223.             }
  224.             if (data instanceof MaxIter) {
  225.                 maxIterations = ((MaxIter) data).getMaxIter();
  226.                 continue;
  227.             }
  228.             if (data instanceof ConvergenceChecker) {
  229.                 checker = (ConvergenceChecker<PAIR>) data;
  230.                 continue;
  231.             }
  232.             if (data instanceof Tolerance) {
  233.                final Tolerance tol = (Tolerance) data;
  234.                relativeTolerance = tol.getRelativeTolerance();
  235.                absoluteTolerance = tol.getAbsoluteTolerance();
  236.                continue;
  237.             }
  238.         }
  239.     }

  240.     /** Reset counters. */
  241.     private void resetCounters() {
  242.         evaluations = IntegerSequence.Incrementor.create()
  243.             .withMaximalCount(maxEvaluations)
  244.             .withCallback(MAX_EVAL_CALLBACK);
  245.         iterations = IntegerSequence.Incrementor.create()
  246.             .withMaximalCount(maxIterations)
  247.             .withCallback(MAX_ITER_CALLBACK);
  248.     }

  249.     /**
  250.      * Defines the action to perform when reaching the maximum number
  251.      * of evaluations.
  252.      */
  253.     private static final class MaxEvalCallback
  254.         implements IntegerSequence.Incrementor.MaxCountExceededCallback {
  255.         /**
  256.          * {@inheritDoc}
  257.          * @throws TooManyEvaluationsException
  258.          */
  259.         @Override
  260.         public void trigger(int max) {
  261.             throw new TooManyEvaluationsException(max);
  262.         }
  263.     }

  264.     /**
  265.      * Defines the action to perform when reaching the maximum number
  266.      * of evaluations.
  267.      */
  268.     private static final class MaxIterCallback
  269.         implements IntegerSequence.Incrementor.MaxCountExceededCallback {
  270.         /**
  271.          * {@inheritDoc}
  272.          * @throws TooManyIterationsException
  273.          */
  274.         @Override
  275.         public void trigger(int max) {
  276.             throw new TooManyIterationsException(max);
  277.         }
  278.     }
  279. }