Skip to content

Commit

Permalink
Reorganize Ant Colony Optimization code around a new global/local upd…
Browse files Browse the repository at this point in the history
…ate rule paradigm. SimpleEvolutionState now calls hooks on Evaluator to trigger state updates, which pass calls along to the Species to handle things like Ant Colony System's global update.
  • Loading branch information
SigmaX committed Aug 23, 2019
1 parent b50aa9e commit f188170
Show file tree
Hide file tree
Showing 13 changed files with 224 additions and 125 deletions.
23 changes: 23 additions & 0 deletions ecj/src/main/java/ec/Evaluator.java
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,27 @@ public void closeContacts(EvolutionState state, int result)
{
p_problem.closeContacts(state,result);
}

/** Called to update some state by considering the current population.
*
* This are used by some algorithms to update additional state (stored the population's Species)
* beyond fitness values (ex. ACO's pheromone distributions).
*
* This method will typically just call a similar hook on a Species, where the actually state update occurs.*/
public void postEvaluationGlobalUpdate(EvolutionState state)
{
// Do nothing by default
}


/** Called to update some state by considering a single Individual.
*
* This are used by some algorithms to update additional state (stored the population's Species)
* beyond fitness values (ex. ACO's pheromone distributions).
*
* This method will typically just call a similar hook on a Species, where the actually state update occurs.*/
public void postEvaluationLocalUpdate(EvolutionState state, Individual ind, int subpop)
{
// Do nothing by default
}
}
24 changes: 24 additions & 0 deletions ecj/src/main/java/ec/Species.java
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,30 @@ public void setup(final EvolutionState state, final Parameter base)
f_prototype = (Fitness) state.parameters.getInstanceForParameter( base.push(P_FITNESS),def.push(P_FITNESS), Fitness.class);
f_prototype.setup(state,base.push(P_FITNESS));
}

/** A hook for code that is run on every individual as soon as it is evaluated. This method does nothing unless it
* is override by a subclass.
*
* For example, an implementation of Ant Colony System might use this to apply a
* local pheromone update.
*
* @see ec.simple.SimpleEvaluator
*/
public void updateIndividual(final EvolutionState state, final Individual ind)
{
// Do nothing by default
}

/**
* A hook for code that is run on the entire subpopulation as soon as it has been evaluated.
*
* For example, an implementation of Ant System might use this to apply a global
* pheromone update. You can see how this method is used by having a look at SimpleEvaluator.
*/
public void updateSubpopulation(final EvolutionState state, final Subpopulation subpop)
{
// Do nothing by default
}
}


4 changes: 1 addition & 3 deletions ecj/src/main/java/ec/co/ConstructiveBreeder.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
/*
Copyright 2017 by Sean Luke
Copyright 2019 by Sean Luke
Licensed under the Academic Free License version 3.0
See the file "LICENSE" for more information
*/
package ec.co;

import ec.*;
import ec.co.ConstructiveIndividual;
import ec.co.ConstructiveProblemForm;
import ec.util.Parameter;

/**
Expand Down
71 changes: 46 additions & 25 deletions ecj/src/main/java/ec/co/ant/AntSpecies.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,10 @@
import ec.Fitness;
import ec.Individual;
import ec.Species;
import static ec.Species.P_FITNESS;
import static ec.Species.P_INDIVIDUAL;
import ec.Subpopulation;
import ec.co.ConstructiveIndividual;
import ec.co.ConstructiveProblemForm;
import ec.util.Parameter;
import java.util.ArrayList;
import java.util.List;

/**
*
Expand All @@ -26,15 +22,17 @@ public class AntSpecies extends Species
{
public final static Parameter DEFAULT_BASE = new Parameter("constructive");
public final static String SPECIES_NAME = "constructive-species";

public final static String P_CONSTRUCTION_RULE = "construction-rule";
public final static String P_PHEROMONE_TABLE = "pheromone-table";
public final static String P_UPDATE_RULE = "update-rule";

public final static String P_LOCAL_UPDATE_RULE = "local-update-rule";

private ConstructionRule constructionRule;
private PheromoneTable pheromones;
private UpdateRule updateRule;

private UpdateRule localUpdateRule;

@Override
public void setup(final EvolutionState state, final Parameter base)
{
Expand All @@ -43,15 +41,24 @@ public void setup(final EvolutionState state, final Parameter base)
assert(base != null);
constructionRule = (ConstructionRule) state.parameters.getInstanceForParameter(base.push(P_CONSTRUCTION_RULE), null, ConstructionRule.class);
constructionRule.setup(state, base.push(P_CONSTRUCTION_RULE));

pheromones = (PheromoneTable) state.parameters.getInstanceForParameter(base.push(P_PHEROMONE_TABLE), null, PheromoneTable.class);
pheromones.setup(state, base.push(P_PHEROMONE_TABLE));

updateRule = (UpdateRule) state.parameters.getInstanceForParameter(base.push(P_UPDATE_RULE), null, UpdateRule.class);
updateRule.setup(state, base.push(P_UPDATE_RULE));

if (state.parameters.exists(base.push(P_UPDATE_RULE), null))
{
updateRule = (UpdateRule) state.parameters.getInstanceForParameter(base.push(P_UPDATE_RULE), null, UpdateRule.class);
updateRule.setup(state, base.push(P_UPDATE_RULE));
}

if (state.parameters.exists(base.push(P_LOCAL_UPDATE_RULE), null))
{
localUpdateRule = (UpdateRule) state.parameters.getInstanceForParameter(base.push(P_LOCAL_UPDATE_RULE), null, UpdateRule.class);
localUpdateRule.setup(state, base.push(P_LOCAL_UPDATE_RULE));
}
assert(repOK());
}

/** A custom setup method for Species that skips the initialization of the
* breeding pipeline. We call this in place of super.setup(), since this
* Species doesn't use a pipeline.
Expand All @@ -63,44 +70,58 @@ private void setupSuper(final EvolutionState state, final Parameter base)
Parameter def = defaultBase();
// load our individual prototype
i_prototype = (Individual)(state.parameters.getInstanceForParameter(
base.push(P_INDIVIDUAL),def.push(P_INDIVIDUAL),
Individual. class));
base.push(P_INDIVIDUAL),def.push(P_INDIVIDUAL),
Individual. class));
// set the species to me before setting up the individual, so they know who I am
i_prototype.species = this;
i_prototype.setup(state,base.push(P_INDIVIDUAL));

// load our fitness
f_prototype = (Fitness) state.parameters.getInstanceForParameter(
base.push(P_FITNESS),def.push(P_FITNESS),
Fitness.class);
base.push(P_FITNESS),def.push(P_FITNESS),
Fitness.class);
f_prototype.setup(state,base.push(P_FITNESS));
}

public void updatePheromones(final EvolutionState state, final Subpopulation population)

/** Apply a global update rule to the pheromone table.
*
* @param subpop The Subpopulation to use as the input of the global update. */
@Override
public void updateSubpopulation(final EvolutionState state, final Subpopulation subpop)
{
updateRule.updatePheromones(state, pheromones, population);
updateRule.updatePheromones(state, pheromones, subpop.individuals);
assert(repOK());
}


/** Apply a local update rule to the pheromone table.
*
* @param ind The Individual to use as the input of the local update. */
@Override
public void updateIndividual(final EvolutionState state, final Individual ind)
{
if (localUpdateRule != null)
localUpdateRule.updatePheromones(state, pheromones, new ArrayList() {{ add(ind); }});
}

@Override
public ConstructiveIndividual newIndividual(final EvolutionState state, final int thread)
{
assert(state != null);
assert(thread >= 0);

final ConstructiveIndividual ind = (ConstructiveIndividual)(super.newIndividual(state, thread));
assert(repOK());
return constructionRule.constructSolution(state, ind, pheromones, thread);
}

@Override
public Parameter defaultBase()
{
return DEFAULT_BASE.push(SPECIES_NAME);
}

/** Representation invariant, used for verification.
*
*
* @return true if the class is found to be in an erroneous state.
*/
public final boolean repOK()
Expand Down
52 changes: 26 additions & 26 deletions ecj/src/main/java/ec/co/ant/AntSystemUpdateRule.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class AntSystemUpdateRule implements UpdateRule
public enum DepositRule { ANT_CYCLE, ANT_DENSITY, ANT_QUANTITY };
private DepositRule depositRule;
private double q;

@Override
public void setup(final EvolutionState state, final Parameter base)
{
Expand All @@ -46,72 +46,72 @@ public void setup(final EvolutionState state, final Parameter base)
if (depositString == null)
state.output.fatal(String.format("%s: missing required parameter '%s'.", this.getClass().getSimpleName(), base.push(P_DEPOSIT_RULE)));
try
{
{
depositString = depositString.replace('-', '_');
depositRule = DepositRule.valueOf(depositString);
}
}
catch (final NullPointerException e)
{
{
state.output.fatal(String.format("%s: invalid value '%s' found for parameter '%s'. Allowed values are %s.", this.getClass().getSimpleName(), depositString, base.push(P_DEPOSIT_RULE), Arrays.asList(DepositRule.values())));
}
}
catch (final IllegalArgumentException e)
{
{
state.output.fatal(String.format("%s: invalid value '%s' found for parameter '%s'. Allowed values are %s.", this.getClass().getSimpleName(), depositString, base.push(P_DEPOSIT_RULE), Arrays.asList(DepositRule.values())));
}
}
assert(repOK());
}

public double getDecayRate()
{
return decayRate;
}

public DepositRule getDepositRule()
{
return depositRule;
}

public double getQ()
{
return q;
}

@Override
public void updatePheromones(final EvolutionState state, final PheromoneTable pheromones, final Subpopulation subpop)
public void updatePheromones(final EvolutionState state, final PheromoneTable pheromones, final List individuals)
{
assert(pheromones != null);
assert(subpop != null);

assert(individuals != null);
assert(!individuals.isEmpty());

decayPheromones(state, pheromones);

final Map<Component, Double> contributions = new HashMap();
// Loop through every individual and record its pheremone contributions (scores) for each edge
for (final Individual o : subpop.individuals)
{
assert(o instanceof ConstructiveIndividual);
for (final Object o : individuals)
{
final ConstructiveIndividual ind = (ConstructiveIndividual) o;
assert(ind.size() > 0);
for (final Object oo : ind)
{
{
assert(oo instanceof Component);
final Component c = (Component) oo;
final double cPheromone = pheromoneContribution(ind, c);
if (contributions.containsKey(c))
contributions.put(c, contributions.get(c) + cPheromone);
else
contributions.put(c, cPheromone);
}
}
}
// Apply the new pheromones
for (final Component c : contributions.keySet())
{
{
final double oldPheromone = pheromones.get(state, c, 0); // Using thread 0 because we are in a single-threaded function
final double newPheromone = oldPheromone + contributions.get(c);
pheromones.set(c, newPheromone);
}
}
assert(repOK());
}

private void decayPheromones(final EvolutionState state, final PheromoneTable pheromones)
{
assert(state != null);
Expand All @@ -120,14 +120,14 @@ private void decayPheromones(final EvolutionState state, final PheromoneTable ph
for (final Component c : components)
pheromones.set(c, (1.0-decayRate)*pheromones.get(state, c, 0)); // Using thread 0 because we are in a single-threaded function
}

private double pheromoneContribution(final ConstructiveIndividual ind, final Component component)
{
assert(ind != null);
assert(component != null);
final double fitness = ind.fitness.fitness();
switch (depositRule)
{
{
case ANT_CYCLE:
assert(fitness > 0);
return q*fitness;
Expand All @@ -137,9 +137,9 @@ private double pheromoneContribution(final ConstructiveIndividual ind, final Com
return q*component.desirability();
default:
throw new IllegalStateException(String.format("%s: no deposit rule logic implemented for %s.", this.getClass().getSimpleName(), depositRule));
}
}
}

public final boolean repOK()
{
return P_DECAY_RATE != null
Expand Down
Loading

0 comments on commit f188170

Please sign in to comment.