/**
* Copyright (C) 2001-2017 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU Affero General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* This program 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
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.tools.math.optimization.ec.pso;
import com.rapidminer.operator.Operator;
import com.rapidminer.operator.OperatorException;
import com.rapidminer.operator.performance.PerformanceVector;
import com.rapidminer.tools.RandomGenerator;
import com.rapidminer.tools.math.optimization.Optimization;
/**
* This class performs the optimization of a value vector with a particle swarm approach.
*
* @author Ingo Mierswa
*/
public abstract class PSOOptimization implements Optimization {
private int maxGen;
private int maxWithoutImprovement;
private double inertiaWeight;
private double localWeight;
private double globalWeight;
private double minValue;
private double maxValue;
private double inertiaWeightDelta;
private RandomGenerator random;
private Population population;
private Operator operator;
/**
* Creates a new PSO optimization with the given parameters.
*
* @deprecated Please use
* {@link #PSOOptimization(int popSize, int individualSize, int maxGen, int maxWithoutImprovement, double inertiaWeight, double localWeight, double globalWeight, double minValue, double maxValue, boolean dynamicInertiaWeight, RandomGenerator random, Operator op)}
* instead. Creating the PSOOptimization without an operator will not display the
* progress
*/
@Deprecated
public PSOOptimization(int popSize, int individualSize, int maxGen, int maxWithoutImprovement, double inertiaWeight,
double localWeight, double globalWeight, double minValue, double maxValue, boolean dynamicInertiaWeight,
RandomGenerator random) {
this.maxGen = maxGen;
this.maxWithoutImprovement = maxWithoutImprovement;
if (this.maxWithoutImprovement < 1) {
this.maxWithoutImprovement = this.maxGen;
}
this.inertiaWeight = inertiaWeight;
this.localWeight = localWeight;
this.globalWeight = globalWeight;
this.minValue = minValue;
this.maxValue = maxValue;
this.inertiaWeightDelta = 0.0d;
if (dynamicInertiaWeight) {
inertiaWeightDelta = inertiaWeight / maxGen;
}
this.random = random;
this.population = createInitialPopulation(popSize, individualSize);
}
/**
* Creates a new PSO optimization with the given parameters.
*/
public PSOOptimization(int popSize, int individualSize, int maxGen, int maxWithoutImprovement, double inertiaWeight,
double localWeight, double globalWeight, double minValue, double maxValue, boolean dynamicInertiaWeight,
RandomGenerator random, Operator op) {
this(popSize, individualSize, maxGen, maxWithoutImprovement, inertiaWeight, localWeight, globalWeight, minValue,
maxValue, dynamicInertiaWeight, random);
this.operator = op;
}
/**
* Subclasses must implement this method to calculate the fitness of the given individual.
* Please note that null might be returned for non-valid individuals.
*/
public abstract PerformanceVector evaluateIndividual(double[] individual) throws OperatorException;
/**
* This method is invoked after each evaluation. The default implementation does nothing but
* subclasses might implement this method to support online plotting or logging.
*/
public void nextIteration() throws OperatorException {}
public void setMinValue(double minValue) {
this.minValue = minValue;
}
public void setMaxValue(double maxValue) {
this.maxValue = maxValue;
}
/** Creates the initial population. */
protected Population createInitialPopulation(int popSize, int individualSize) {
Population initPop = new Population(popSize, individualSize);
for (int i = 0; i < popSize; i++) {
double[] values = new double[individualSize];
for (int j = 0; j < values.length; j++) {
values[j] = random.nextDoubleInRange(minValue, maxValue);
}
initPop.setValues(i, values);
}
return initPop;
}
public void reinit(int popSize, int individualSize, int maxGen, int maxWithoutImprovement, double inertiaWeight,
double localWeight, double globalWeight, double minValue, double maxValue, boolean dynamicInertiaWeight,
RandomGenerator random) {
this.maxGen = maxGen;
this.maxWithoutImprovement = maxWithoutImprovement;
if (this.maxWithoutImprovement < 1) {
this.maxWithoutImprovement = this.maxGen;
}
this.inertiaWeight = inertiaWeight;
this.localWeight = localWeight;
this.globalWeight = globalWeight;
this.minValue = minValue;
this.maxValue = maxValue;
this.inertiaWeightDelta = 0.0d;
if (dynamicInertiaWeight) {
inertiaWeightDelta = inertiaWeight / maxGen;
}
this.random = random;
this.population = createInitialPopulation(popSize, individualSize);
}
/** Invoke this method for optimization. */
@Override
public void optimize() throws OperatorException {
// velocities
double[][] velocities = new double[population.getNumberOfIndividuals()][population.getIndividualSize()];
for (int p = 0; p < velocities.length; p++) {
for (int a = 0; a < velocities[p].length; a++) {
velocities[p][a] = 0.0d;
}
}
evaluate(population);
population.nextGeneration();
nextIteration();
if (operator != null) {
operator.getProgress().setTotal(maxGen);
}
while (!(population.getGeneration() >= maxGen
|| population.getGenerationsWithoutImprovement() >= maxWithoutImprovement || population.getBestFitnessEver() >= population
.getBestPerformanceEver().getMainCriterion().getMaxFitness())) {
if (operator != null) {
operator.getProgress().setCompleted(population.getGeneration());
}
// update velocities
double[] globalBest = population.getGlobalBestValues();
for (int i = 0; i < velocities.length; i++) {
double[] localBest = population.getLocalBestValues(i);
double[] current = population.getValues(i);
for (int d = 0; d < velocities[i].length; d++) {
velocities[i][d] = inertiaWeight * velocities[i][d] + localWeight * (random.nextGaussian() + 1.0d)
* (localBest[d] - current[d]) + globalWeight * (random.nextGaussian() + 1.0d)
* (globalBest[d] - current[d]);
}
}
// update positions
for (int i = 0; i < velocities.length; i++) {
double[] current = population.getValues(i);
double[] newValues = new double[current.length];
for (int d = 0; d < velocities[i].length; d++) {
newValues[d] = current[d] + velocities[i][d];
if (newValues[d] < minValue) {
newValues[d] = minValue;
}
if (newValues[d] > maxValue) {
newValues[d] = maxValue;
}
}
population.setValues(i, newValues);
}
inertiaWeight -= inertiaWeightDelta;
evaluate(population);
population.nextGeneration();
nextIteration();
}
}
/**
* Calculates the fitness for all individuals and gives the fitness values to the population.
*/
private void evaluate(Population population) throws OperatorException {
PerformanceVector[] fitnessValues = new PerformanceVector[population.getNumberOfIndividuals()];
for (int i = 0; i < fitnessValues.length; i++) {
double[] individual = population.getValues(i);
fitnessValues[i] = evaluateIndividual(individual);
}
population.setFitnessVector(fitnessValues);
}
/** Returns the current generation. */
@Override
public int getGeneration() {
return population.getGeneration();
}
/** Returns the best fitness in the current generation. */
@Override
public double getBestFitnessInGeneration() {
return population.getBestFitnessInGeneration();
}
/** Returns the best fitness ever. */
@Override
public double getBestFitnessEver() {
return population.getBestFitnessEver();
}
/** Returns the best performance vector ever. */
@Override
public PerformanceVector getBestPerformanceEver() {
return population.getBestPerformanceEver();
}
/**
* Returns the best values ever. Use this method after optimization to get the best result.
*/
@Override
public double[] getBestValuesEver() {
return population.getGlobalBestValues();
}
}