Skip to content

Commit

Permalink
fixes several issue related to batch and explicit explo method
Browse files Browse the repository at this point in the history
- Add new exploration method: explicit.
- Fixes PSO
- enables parallel simulation for different parameter sets at the same
time for all method (except simulated annealing).
- update the batch model with all the different search methods.
  • Loading branch information
ptaillandier committed Aug 30, 2021
1 parent bae2754 commit 7a576c4
Show file tree
Hide file tree
Showing 13 changed files with 568 additions and 387 deletions.
126 changes: 126 additions & 0 deletions msi.gama.core/src/msi/gama/kernel/batch/ExplicitExploration.java
@@ -0,0 +1,126 @@
/*******************************************************************************************************
*
* msi.gama.kernel.batch.HillClimbing.java, in plugin msi.gama.core, is part of the source code of the GAMA modeling and
* simulation platform (v. 1.8.1)
*
* (c) 2007-2020 UMI 209 UMMISCO IRD/SU & Partners
*
* Visit https://github.com/gama-platform/gama for license information and contacts.
*
********************************************************************************************************/
package msi.gama.kernel.batch;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import msi.gama.common.interfaces.IKeyword;
import msi.gama.kernel.experiment.ParametersSet;
import msi.gama.precompiler.GamlAnnotations.doc;
import msi.gama.precompiler.GamlAnnotations.example;
import msi.gama.precompiler.GamlAnnotations.facet;
import msi.gama.precompiler.GamlAnnotations.facets;
import msi.gama.precompiler.GamlAnnotations.inside;
import msi.gama.precompiler.GamlAnnotations.symbol;
import msi.gama.precompiler.GamlAnnotations.usage;
import msi.gama.precompiler.IConcept;
import msi.gama.precompiler.ISymbolKind;
import msi.gama.runtime.IScope;
import msi.gama.runtime.concurrent.GamaExecutorService;
import msi.gama.runtime.exceptions.GamaRuntimeException;
import msi.gaml.descriptions.IDescription;
import msi.gaml.expressions.IExpression;
import msi.gaml.operators.Cast;
import msi.gaml.types.IType;
import msi.gaml.types.Types;

@symbol (
name = IKeyword.EXPLICIT,
kind = ISymbolKind.BATCH_METHOD,
with_sequence = false,
concept = { IConcept.BATCH, IConcept.ALGORITHM })
@inside (
kinds = { ISymbolKind.EXPERIMENT })
@facets (
value = { @facet (
name = IKeyword.NAME,
type = IType.ID,
optional = false,
internal = true,
doc = @doc ("The name of the method. For internal use only")),
@facet (
name = ExplicitExploration.PARAMETER_SET,
type = IType.LIST,
of = IType.MAP,
optional = false,
doc = @doc ("the list of parameter sets to explore; a parameter set is defined by a map: key: name of the variable, value: expression for the value of the variable")),
@facet (
name = IKeyword.MAXIMIZE,
type = IType.FLOAT,
optional = true,
doc = @doc ("the value the algorithm tries to maximize")),
@facet (
name = IKeyword.MINIMIZE,
type = IType.FLOAT,
optional = true,
doc = @doc ("the value the algorithm tries to minimize")),
@facet (
name = IKeyword.AGGREGATION,
type = IType.LABEL,
optional = true,
values = { IKeyword.MIN, IKeyword.MAX },
doc = @doc ("the agregation method")) },
omissible = IKeyword.NAME)
@doc (
value = "This algorithm run simulations with the given parameter sets",
usages = { @usage (
value = "As other batch methods, the basic syntax of the `explicit` statement uses `method explicit` instead of the expected `explicit name: id` : ",
examples = { @example (
value = "method explicit [facet: value];",
isExecutable = false) }),
@usage (
value = "For example: ",
examples = { @example (
value = "method explicit parameter_sets:[[\"a\"::0.5, \"b\"::10],[\"a\"::0.1, \"b\"::100]]; ",
isExecutable = false) }) })
public class ExplicitExploration extends ParamSpaceExploAlgorithm {

protected static final String PARAMETER_SET = "parameter_sets";
protected List<Map<String, Object>> parameterSets;
public ExplicitExploration(final IDescription desc) {
super(desc);

}


@Override
public ParametersSet findBestSolution(final IScope scope) throws GamaRuntimeException {
IExpression psexp = getFacet(PARAMETER_SET);
parameterSets = (List<Map<String, Object>>) Cast.asList(scope, psexp.value(scope));
setBestFitness(null);
List<ParametersSet> solutions = new ArrayList<>();
for (Map<String,Object> parameterSet : parameterSets) {
ParametersSet p = new ParametersSet();
for (String v : parameterSet.keySet()) {
Object val = parameterSet.get(v);
p.put(v,(val instanceof IExpression) ? ((IExpression) val).value(scope) : val);
}
solutions.add(p);
}
if (GamaExecutorService.CONCURRENCY_SIMULATIONS_ALL.getValue()) {
solutions.removeIf(a -> testedSolutions.containsKey(a));
Map<ParametersSet,Double> res = currentExperiment.launchSimulationsWithSolution(solutions);
for (ParametersSet sol : res.keySet()) {
updateBestFitness(sol, res.get(sol));
}
} else
for (ParametersSet sol : solutions) {
if (!testedSolutions.containsKey(sol)) {
double fitness = currentExperiment.launchSimulationsWithSolution(sol);
updateBestFitness(sol, fitness);
}
}
return getBestSolution();
}

}
46 changes: 33 additions & 13 deletions msi.gama.core/src/msi/gama/kernel/batch/HillClimbing.java
Expand Up @@ -30,6 +30,7 @@
import msi.gama.precompiler.IConcept;
import msi.gama.precompiler.ISymbolKind;
import msi.gama.runtime.IScope;
import msi.gama.runtime.concurrent.GamaExecutorService;
import msi.gama.runtime.exceptions.GamaRuntimeException;
import msi.gaml.descriptions.IDescription;
import msi.gaml.expressions.IExpression;
Expand Down Expand Up @@ -101,6 +102,14 @@ public HillClimbing(final IDescription species) {

}

public boolean keepSol(ParametersSet neighborSol, Double neighborFitness ) {
if (isMaximize() && neighborFitness.doubleValue() > getBestFitness()
|| !isMaximize() && neighborFitness.doubleValue() < getBestFitness()) {
setBestFitness(neighborFitness);
return true;
}
return false;
}
@Override
public ParametersSet findBestSolution(final IScope scope) throws GamaRuntimeException {
setBestSolution(this.solutionInit);
Expand All @@ -118,23 +127,34 @@ public ParametersSet findBestSolution(final IScope scope) throws GamaRuntimeExce
}
setBestFitness(currentFitness);
ParametersSet bestNeighbor = null;

for (final ParametersSet neighborSol : neighbors) {
if (neighborSol == null) {
continue;
}
Double neighborFitness = testedSolutions.get(neighborSol);
if (neighborFitness == null) {
neighborFitness = currentExperiment.launchSimulationsWithSolution(neighborSol);

if (GamaExecutorService.CONCURRENCY_SIMULATIONS_ALL.getValue() && ! currentExperiment.getParametersToExplore().isEmpty()) {
Map<ParametersSet,Double> result = testSolutions(neighbors);
for (ParametersSet p : result.keySet()) {
if (keepSol(p, result.get(p))) {
bestNeighbor = p;
}
}
testedSolutions.put(neighborSol, neighborFitness);
} else {
for (final ParametersSet neighborSol : neighbors) {
if (neighborSol == null) {
continue;
}
Double neighborFitness = testedSolutions.get(neighborSol);
if (neighborFitness == null) {
neighborFitness = currentExperiment.launchSimulationsWithSolution(neighborSol);
}
testedSolutions.put(neighborSol, neighborFitness);

if (isMaximize() && neighborFitness.doubleValue() > getBestFitness()
|| !isMaximize() && neighborFitness.doubleValue() < getBestFitness()) {
bestNeighbor = neighborSol;
setBestFitness(neighborFitness);
if (keepSol(neighborSol, neighborFitness)) {
bestNeighbor = neighborSol;
}

}
}



if (bestNeighbor != null) {
setBestSolution(bestNeighbor);
currentFitness = getBestFitness();
Expand Down
20 changes: 20 additions & 0 deletions msi.gama.core/src/msi/gama/kernel/batch/LocalSearchAlgorithm.java
Expand Up @@ -10,6 +10,7 @@
********************************************************************************************************/
package msi.gama.kernel.batch;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

Expand All @@ -18,6 +19,7 @@
import msi.gama.kernel.experiment.ParametersSet;
import msi.gama.runtime.IScope;
import msi.gama.runtime.exceptions.GamaRuntimeException;
import msi.gama.util.GamaMapFactory;
import msi.gaml.descriptions.IDescription;
import msi.gaml.expressions.IExpression;
import msi.gaml.operators.Cast;
Expand All @@ -33,6 +35,24 @@ public abstract class LocalSearchAlgorithm extends ParamSpaceExploAlgorithm {
public LocalSearchAlgorithm(final IDescription species) {
super(species);
}

public Map<ParametersSet, Double> testSolutions(List<ParametersSet> solutions) {
Map<ParametersSet, Double> results = GamaMapFactory.create();
solutions.removeIf(a -> a == null);
List<ParametersSet> solTotest = new ArrayList<>();
for (ParametersSet sol : solutions) {
if (testedSolutions.containsKey(sol)) {
results.put(sol, testedSolutions.get(sol));
} else {
solTotest.add(sol);
}
}
Map<ParametersSet, Double> res = currentExperiment.launchSimulationsWithSolution(solTotest);
testedSolutions.putAll(res);
results.putAll(res);

return results;
}

@Override
public void initializeFor(final IScope scope, final BatchAgent agent) throws GamaRuntimeException {
Expand Down
Expand Up @@ -43,7 +43,7 @@ public abstract class ParamSpaceExploAlgorithm extends Symbol implements IExplor
public final static String[] COMBINATIONS = new String[] { "maximum", "minimum", "average" };
@SuppressWarnings ("rawtypes") public static final Class[] CLASSES =
{ GeneticAlgorithm.class, SimulatedAnnealing.class, HillClimbing.class, TabuSearch.class,
TabuSearchReactive.class, ExhaustiveSearch.class, Swarm.class};
TabuSearchReactive.class, ExhaustiveSearch.class, Swarm.class, ExplicitExploration.class};

static {
AbstractGamlAdditions._constants(COMBINATIONS);
Expand Down Expand Up @@ -97,7 +97,7 @@ public ParamSpaceExploAlgorithm(final IDescription desc) {
isMaximize = hasFacet(IKeyword.MAXIMIZE);
final String ag = getLiteral(IKeyword.AGGREGATION);
combination = IKeyword.MAX.equals(ag) ? C_MAX : IKeyword.MIN.equals(ag) ? C_MIN : C_MEAN;

bestFitness = isMaximize ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
}

@Override
Expand Down Expand Up @@ -183,8 +183,6 @@ public void updateBestFitness(final ParametersSet solution, final Double fitness
if (fitness == null)
return;
Double best = getBestFitness();
if (best == null)
best = 0d;
if (bestSolution == null || (isMaximize() ? fitness > best : fitness < best)) {
setBestFitness(fitness);
setBestSolution(solution);
Expand Down
26 changes: 15 additions & 11 deletions msi.gama.core/src/msi/gama/kernel/batch/Particle.java
Expand Up @@ -27,6 +27,8 @@ class Particle {
final Map<String, GamaPoint> parameters;

ParamSpaceExploAlgorithm algo;

double currentVal;
/**
* Construct a Particle with a random starting position.
* @param beginRange the minimum xyz values of the position (inclusive)
Expand All @@ -36,6 +38,7 @@ class Particle {
currentExperiment = agent;
algo = algorithm;
this.testedSolutions = testedSolutionsMap;
bestEval = algo.isMaximize() ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
final List<IParameter.Batch> v = agent.getParametersToExplore();
parameters = new HashMap<>();
for (IParameter p : v ) {
Expand All @@ -46,34 +49,35 @@ class Particle {
}
position = new ParametersSet(scope, v, true);
velocity = new ParametersSet(scope, v, true);
bestPosition = new ParametersSet(velocity);
bestEval = eval();
for (String key : velocity.keySet()) {
velocity.put(key, Cast.asFloat(scope, velocity.get(key))- Cast.asFloat(scope, position.get(key)) );
}
bestPosition = new ParametersSet(position);
}



/**
* The evaluation of the current position.
* @return the evaluation
*/
private double eval () {
public double eval () {
Double fitness = testedSolutions.get(position);
if (fitness == null) {
fitness = currentExperiment.launchSimulationsWithSolution(position);
testedSolutions.put(position, fitness);
}
testedSolutions.put(position, fitness);

/**/

return fitness.doubleValue();
}

/**
* Update the personal best if the current evaluation is better.
*/
void updatePersonalBest () {
double eval = eval();
if (algo.isMaximize() && eval > bestEval
|| !algo.isMaximize() && eval < bestEval) {
bestEval = eval;
// double eval = eval();
if ((algo.isMaximize() && currentVal > bestEval)
|| (!algo.isMaximize() && currentVal < bestEval)) {
bestEval = currentVal;
bestPosition = new ParametersSet(position);
}
}
Expand Down
12 changes: 8 additions & 4 deletions msi.gama.core/src/msi/gama/kernel/batch/SimulatedAnnealing.java
Expand Up @@ -178,12 +178,16 @@ public ParametersSet findBestSolution(final IScope scope) throws GamaRuntimeExce
testedSolutions.put(neighborSol, neighborFitness);
}

if (isMaximize() && (neighborFitness >= currentFitness
|| scope.getRandom().next() < Math.exp((neighborFitness - currentFitness) / temperature))
|| !isMaximize() && (neighborFitness <= currentFitness || scope.getRandom().next() < Math
.exp((currentFitness - neighborFitness) / temperature))) {
if (isMaximize()) {
if (neighborFitness >= currentFitness || scope.getRandom().next() < Math.exp(Math.abs(neighborFitness - currentFitness) / temperature)) {
bestSolutionAlgo = neighborSol;
currentFitness = neighborFitness;
}

} else if (neighborFitness <= currentFitness || scope.getRandom().next() < Math.exp(Math.abs(currentFitness - neighborFitness) / temperature)) {
bestSolutionAlgo = neighborSol;
currentFitness = neighborFitness;

}
iter++;
}
Expand Down

0 comments on commit 7a576c4

Please sign in to comment.