/* Copyright 2009-2016 David Hadka
*
* This file is part of the MOEA Framework.
*
* The MOEA Framework is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* The MOEA Framework is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the MOEA Framework. If not, see <http://www.gnu.org/licenses/>.
*/
package org.moeaframework.util.weights;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.math3.stat.StatUtils;
import org.apache.commons.math3.util.MathArrays;
import org.moeaframework.core.PRNG;
/**
* Generates weights randomly. This is the method proposed in [1] to replace
* the normal boundary intersection method in the original MOEA/D. If {@code N}
* weights are requested, this method first generates {@code 50*N} random
* weights. From these weights, {@code N} are selected that are maximally
* distant from all other weights.
* <p>
* References:
* <ol>
* <li>Zhang, Q., et al. "The Performance of a New Version of MOEA/D on
* CEC09 Unconstrained MOP Test Instances." IEEE Congress on Evolutionary
* Computation, 2009.
* </ol>
*/
public class RandomGenerator implements WeightGenerator {
/**
* The number of objectives.
*/
private final int numberOfObjectives;
/**
* The number of weights to generate.
*/
private final int numberOfPoints;
/**
* Constructs a new weight generator that generates randomly-sampled
* weights.
*
* @param numberOfObjectives the number of objectives
* @param numberOfPoints the number of weights to generate
*/
public RandomGenerator(int numberOfObjectives, int numberOfPoints) {
super();
this.numberOfObjectives = numberOfObjectives;
this.numberOfPoints = numberOfPoints;
}
/**
* Returns the weights for 2D problems.
*
* @return the weights
*/
private List<double[]> initializeWeights2D() {
List<double[]> weights = new ArrayList<double[]>();
// add boundary weights
weights.add(new double[] { 0.0, 1.0 });
weights.add(new double[] { 1.0, 0.0 });
for (int i = 1; i < numberOfPoints - 1; i++) {
double a = i / (double)(numberOfPoints - 1);
weights.add(new double[] { a, 1 - a });
}
return weights;
}
/**
* Returns the weights for problems of arbitrary dimension.
*
* @return the weights
*/
private List<double[]> initializeWeightsND() {
int N = 50;
List<double[]> candidates = new ArrayList<double[]>(numberOfPoints * N);
// create random weights
for (int i = 0; i < numberOfPoints * N; i++) {
double[] weight = new double[numberOfObjectives];
for (int j = 0; j < numberOfObjectives; j++) {
weight[j] = PRNG.nextDouble();
}
double sum = StatUtils.sum(weight);
for (int j = 0; j < numberOfObjectives; j++) {
weight[j] /= sum;
}
candidates.add(weight);
}
List<double[]> weights = new ArrayList<double[]>(numberOfPoints * N);
// add boundary weights (1,0,...,0), (0,1,...,0), ..., (0,...,0,1)
for (int i = 0; i < numberOfObjectives; i++) {
double[] weight = new double[numberOfObjectives];
weight[i] = 1.0;
weights.add(weight);
}
// fill in remaining weights with the weight vector with the largest
// distance from the assigned weights
while (weights.size() < numberOfPoints) {
double[] weight = null;
double distance = Double.NEGATIVE_INFINITY;
for (int i = 0; i < candidates.size(); i++) {
double d = Double.POSITIVE_INFINITY;
for (int j = 0; j < weights.size(); j++) {
d = Math.min(d, MathArrays.distance(candidates.get(i),
weights.get(j)));
}
if (d > distance) {
weight = candidates.get(i);
distance = d;
}
}
weights.add(weight);
candidates.remove(weight);
}
return weights;
}
@Override
public int size() {
return numberOfPoints;
}
@Override
public List<double[]> generate() {
if (numberOfObjectives == 2) {
return initializeWeights2D();
} else {
return initializeWeightsND();
}
}
}