MultiDirectionalSimplex.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.math3.optim.nonlinear.scalar.noderiv;

  18. import java.util.Comparator;

  19. import org.apache.commons.math3.analysis.MultivariateFunction;
  20. import org.apache.commons.math3.optim.PointValuePair;

  21. /**
  22.  * This class implements the multi-directional direct search method.
  23.  *
  24.  * @since 3.0
  25.  */
  26. public class MultiDirectionalSimplex extends AbstractSimplex {
  27.     /** Default value for {@link #khi}: {@value}. */
  28.     private static final double DEFAULT_KHI = 2;
  29.     /** Default value for {@link #gamma}: {@value}. */
  30.     private static final double DEFAULT_GAMMA = 0.5;
  31.     /** Expansion coefficient. */
  32.     private final double khi;
  33.     /** Contraction coefficient. */
  34.     private final double gamma;

  35.     /**
  36.      * Build a multi-directional simplex with default coefficients.
  37.      * The default values are 2.0 for khi and 0.5 for gamma.
  38.      *
  39.      * @param n Dimension of the simplex.
  40.      */
  41.     public MultiDirectionalSimplex(final int n) {
  42.         this(n, 1d);
  43.     }

  44.     /**
  45.      * Build a multi-directional simplex with default coefficients.
  46.      * The default values are 2.0 for khi and 0.5 for gamma.
  47.      *
  48.      * @param n Dimension of the simplex.
  49.      * @param sideLength Length of the sides of the default (hypercube)
  50.      * simplex. See {@link AbstractSimplex#AbstractSimplex(int,double)}.
  51.      */
  52.     public MultiDirectionalSimplex(final int n, double sideLength) {
  53.         this(n, sideLength, DEFAULT_KHI, DEFAULT_GAMMA);
  54.     }

  55.     /**
  56.      * Build a multi-directional simplex with specified coefficients.
  57.      *
  58.      * @param n Dimension of the simplex. See
  59.      * {@link AbstractSimplex#AbstractSimplex(int,double)}.
  60.      * @param khi Expansion coefficient.
  61.      * @param gamma Contraction coefficient.
  62.      */
  63.     public MultiDirectionalSimplex(final int n,
  64.                                    final double khi, final double gamma) {
  65.         this(n, 1d, khi, gamma);
  66.     }

  67.     /**
  68.      * Build a multi-directional simplex with specified coefficients.
  69.      *
  70.      * @param n Dimension of the simplex. See
  71.      * {@link AbstractSimplex#AbstractSimplex(int,double)}.
  72.      * @param sideLength Length of the sides of the default (hypercube)
  73.      * simplex. See {@link AbstractSimplex#AbstractSimplex(int,double)}.
  74.      * @param khi Expansion coefficient.
  75.      * @param gamma Contraction coefficient.
  76.      */
  77.     public MultiDirectionalSimplex(final int n, double sideLength,
  78.                                    final double khi, final double gamma) {
  79.         super(n, sideLength);

  80.         this.khi   = khi;
  81.         this.gamma = gamma;
  82.     }

  83.     /**
  84.      * Build a multi-directional simplex with default coefficients.
  85.      * The default values are 2.0 for khi and 0.5 for gamma.
  86.      *
  87.      * @param steps Steps along the canonical axes representing box edges.
  88.      * They may be negative but not zero. See
  89.      */
  90.     public MultiDirectionalSimplex(final double[] steps) {
  91.         this(steps, DEFAULT_KHI, DEFAULT_GAMMA);
  92.     }

  93.     /**
  94.      * Build a multi-directional simplex with specified coefficients.
  95.      *
  96.      * @param steps Steps along the canonical axes representing box edges.
  97.      * They may be negative but not zero. See
  98.      * {@link AbstractSimplex#AbstractSimplex(double[])}.
  99.      * @param khi Expansion coefficient.
  100.      * @param gamma Contraction coefficient.
  101.      */
  102.     public MultiDirectionalSimplex(final double[] steps,
  103.                                    final double khi, final double gamma) {
  104.         super(steps);

  105.         this.khi   = khi;
  106.         this.gamma = gamma;
  107.     }

  108.     /**
  109.      * Build a multi-directional simplex with default coefficients.
  110.      * The default values are 2.0 for khi and 0.5 for gamma.
  111.      *
  112.      * @param referenceSimplex Reference simplex. See
  113.      * {@link AbstractSimplex#AbstractSimplex(double[][])}.
  114.      */
  115.     public MultiDirectionalSimplex(final double[][] referenceSimplex) {
  116.         this(referenceSimplex, DEFAULT_KHI, DEFAULT_GAMMA);
  117.     }

  118.     /**
  119.      * Build a multi-directional simplex with specified coefficients.
  120.      *
  121.      * @param referenceSimplex Reference simplex. See
  122.      * {@link AbstractSimplex#AbstractSimplex(double[][])}.
  123.      * @param khi Expansion coefficient.
  124.      * @param gamma Contraction coefficient.
  125.      * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException
  126.      * if the reference simplex does not contain at least one point.
  127.      * @throws org.apache.commons.math3.exception.DimensionMismatchException
  128.      * if there is a dimension mismatch in the reference simplex.
  129.      */
  130.     public MultiDirectionalSimplex(final double[][] referenceSimplex,
  131.                                    final double khi, final double gamma) {
  132.         super(referenceSimplex);

  133.         this.khi   = khi;
  134.         this.gamma = gamma;
  135.     }

  136.     /** {@inheritDoc} */
  137.     @Override
  138.     public void iterate(final MultivariateFunction evaluationFunction,
  139.                         final Comparator<PointValuePair> comparator) {
  140.         // Save the original simplex.
  141.         final PointValuePair[] original = getPoints();
  142.         final PointValuePair best = original[0];

  143.         // Perform a reflection step.
  144.         final PointValuePair reflected = evaluateNewSimplex(evaluationFunction,
  145.                                                                 original, 1, comparator);
  146.         if (comparator.compare(reflected, best) < 0) {
  147.             // Compute the expanded simplex.
  148.             final PointValuePair[] reflectedSimplex = getPoints();
  149.             final PointValuePair expanded = evaluateNewSimplex(evaluationFunction,
  150.                                                                    original, khi, comparator);
  151.             if (comparator.compare(reflected, expanded) <= 0) {
  152.                 // Keep the reflected simplex.
  153.                 setPoints(reflectedSimplex);
  154.             }
  155.             // Keep the expanded simplex.
  156.             return;
  157.         }

  158.         // Compute the contracted simplex.
  159.         evaluateNewSimplex(evaluationFunction, original, gamma, comparator);

  160.     }

  161.     /**
  162.      * Compute and evaluate a new simplex.
  163.      *
  164.      * @param evaluationFunction Evaluation function.
  165.      * @param original Original simplex (to be preserved).
  166.      * @param coeff Linear coefficient.
  167.      * @param comparator Comparator to use to sort simplex vertices from best
  168.      * to poorest.
  169.      * @return the best point in the transformed simplex.
  170.      * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
  171.      * if the maximal number of evaluations is exceeded.
  172.      */
  173.     private PointValuePair evaluateNewSimplex(final MultivariateFunction evaluationFunction,
  174.                                                   final PointValuePair[] original,
  175.                                                   final double coeff,
  176.                                                   final Comparator<PointValuePair> comparator) {
  177.         final double[] xSmallest = original[0].getPointRef();
  178.         // Perform a linear transformation on all the simplex points,
  179.         // except the first one.
  180.         setPoint(0, original[0]);
  181.         final int dim = getDimension();
  182.         for (int i = 1; i < getSize(); i++) {
  183.             final double[] xOriginal = original[i].getPointRef();
  184.             final double[] xTransformed = new double[dim];
  185.             for (int j = 0; j < dim; j++) {
  186.                 xTransformed[j] = xSmallest[j] + coeff * (xSmallest[j] - xOriginal[j]);
  187.             }
  188.             setPoint(i, new PointValuePair(xTransformed, Double.NaN, false));
  189.         }

  190.         // Evaluate the simplex.
  191.         evaluate(evaluationFunction, comparator);

  192.         return getPoint(0);
  193.     }
  194. }