blob: 778ff3c1390256a51d847bde09aada8eaecd7fc9 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math4.legacy.optim.nonlinear.scalar.noderiv;
import java.util.List;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.function.UnaryOperator;
import java.util.function.DoublePredicate;
import org.apache.commons.math4.legacy.analysis.MultivariateFunction;
import org.apache.commons.math4.legacy.optim.PointValuePair;
/**
* <a href="https://scholarship.rice.edu/handle/1911/16304">Multi-directional</a> search method.
*/
public class MultiDirectionalTransform
implements Simplex.TransformFactory {
/** Reflection coefficient. */
private static final double ALPHA = 1;
/** Default value for {@link #gamma}: {@value}. */
private static final double DEFAULT_GAMMA = 2;
/** Default value for {@link #sigma}: {@value}. */
private static final double DEFAULT_SIGMA = 0.5;
/** Expansion coefficient. */
private final double gamma;
/** Contraction coefficient. */
private final double sigma;
/**
* @param gamma Expansion coefficient.
* @param sigma Shrinkage coefficient.
*/
public MultiDirectionalTransform(double gamma,
double sigma) {
if (gamma < 1) {
throw new IllegalArgumentException("gamma: " + gamma);
}
if (sigma < 0 ||
sigma > 1) {
throw new IllegalArgumentException("sigma: " + sigma);
}
this.gamma = gamma;
this.sigma = sigma;
}
/**
* Transform with default values.
*/
public MultiDirectionalTransform() {
this(DEFAULT_GAMMA,
DEFAULT_SIGMA);
}
/** {@inheritDoc} */
@Override
public UnaryOperator<Simplex> create(final MultivariateFunction evaluationFunction,
final Comparator<PointValuePair> comparator,
final DoublePredicate sa) {
return original -> {
final PointValuePair best = original.get(0);
// Perform a reflection step.
final Simplex reflectedSimplex = transform(original,
ALPHA,
comparator,
evaluationFunction);
final PointValuePair reflectedBest = reflectedSimplex.get(0);
if (comparator.compare(reflectedBest, best) < 0) {
// Compute the expanded simplex.
final Simplex expandedSimplex = transform(original,
gamma,
comparator,
evaluationFunction);
final PointValuePair expandedBest = expandedSimplex.get(0);
if (comparator.compare(expandedBest, reflectedBest) <= 0 ||
(sa != null &&
sa.test(expandedBest.getValue() - reflectedBest.getValue()))) {
return expandedSimplex;
} else {
return reflectedSimplex;
}
} else {
// Compute the contracted simplex.
return original.shrink(sigma, evaluationFunction);
}
};
}
/**
* Computes and evaluates a new simplex.
*
* @param original Original simplex.
* @param coeff Linear coefficient.
* @param comp Fitness comparator.
* @param evalFunc Objective function.
* @return the transformed simplex.
* @throws org.apache.commons.math4.legacy.exception.TooManyEvaluationsException
* if the maximal number of evaluations is exceeded.
*/
private Simplex transform(Simplex original,
double coeff,
Comparator<PointValuePair> comp,
MultivariateFunction evalFunc) {
// Transformed simplex is the result a linear transformation on all
// points except the first one.
final int replSize = original.getSize() - 1;
final List<PointValuePair> replacement = new ArrayList<>();
final double[] bestPoint = original.get(0).getPoint();
for (int i = 0; i < replSize; i++) {
replacement.add(Simplex.newPoint(bestPoint,
-coeff,
original.get(i + 1).getPoint(),
evalFunc));
}
return original.replaceLast(replacement).evaluate(evalFunc, comp);
}
/** {@inheritDoc} */
@Override
public String toString() {
return "Multidirectional [g=" + gamma +
" s=" + sigma + "]";
}
}