Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import gama.core.common.interfaces.IKeyword;
import gama.core.common.util.FileUtils;
import gama.core.kernel.batch.IExploration;
import gama.core.kernel.batch.exploration.betadistribution.BetaExploration;
import gama.core.kernel.batch.exploration.morris.Morris;
import gama.core.kernel.batch.exploration.morris.MorrisExploration;
import gama.core.kernel.batch.exploration.sampling.LatinhypercubeSampling;
Expand All @@ -54,14 +55,18 @@
import gama.gaml.compilation.Symbol;
import gama.gaml.descriptions.IDescription;
import gama.gaml.expressions.IExpression;
import gama.gaml.expressions.data.ListExpression;
import gama.gaml.expressions.data.MapExpression;
import gama.gaml.operators.Cast;
import gama.gaml.operators.Containers;
import gama.gaml.operators.Strings;
import gama.gaml.types.GamaDateType;
import gama.gaml.types.GamaFloatType;
import gama.gaml.types.GamaPointType;
import gama.gaml.types.IType;
import gama.gaml.types.Types;
import one.util.streamex.IntStreamEx;
import one.util.streamex.StreamEx;

/**
* The Class AExplorationAlgorithm.
Expand Down Expand Up @@ -112,6 +117,44 @@ public Object value() {
}

});

exp.add(new ParameterAdapter("Sampling method", BatchAgent.EXPLORATION_EXPERIMENT, IType.STRING) {
@Override
public Object value() {
if (hasFacet(IKeyword.FROM)) return Exploration.FROM_FILE;
if (hasFacet(IKeyword.WITH)) return Exploration.FROM_LIST;
final String methodName = IKeyword.METHODS[Arrays.asList(CLASSES).indexOf(AExplorationAlgorithm.this.getClass())];
if (!hasFacet(Exploration.METHODS)) {
if (methodName==IKeyword.MORRIS) { return IKeyword.MORRIS; }
if (methodName==IKeyword.SOBOL) {return IKeyword.SALTELLI; }
return Exploration.DEFAULT_SAMPLING;
}
return hasFacet(Exploration.METHODS)
? Cast.asString(agent.getScope(), getFacet(Exploration.METHODS).value(agent.getScope())) :
Exploration.DEFAULT_SAMPLING;
}
});

exp.add(new ParameterAdapter("Sampled points", BatchAgent.EXPLORATION_EXPERIMENT, IType.STRING) {
@Override
public Object value() { return estimateSamples(agent); }
});

exp.add(new ParameterAdapter("Simulation runs", BatchAgent.EXPLORATION_EXPERIMENT, IType.STRING) {
@Override
public Object value() {
int res = estimateSamples(agent);
final String methodName = IKeyword.METHODS[Arrays.asList(CLASSES).indexOf(AExplorationAlgorithm.this.getClass())];
if (Arrays.asList(IKeyword.SOBOL,IKeyword.MORRIS,IKeyword.BETAD).contains(methodName)) {
return res;
}
return res *
(agent.getSpecies().hasFacet(IKeyword.REPEAT) ?
Cast.asInt(agent.getScope(),
agent.getSpecies().getFacet(IKeyword.REPEAT).value(agent.getScope()))
: 1); }
});

if (getOutputs() != null) {
exp.add(new ParameterAdapter("Outputs of interest", BatchAgent.EXPLORATION_EXPERIMENT, IType.STRING) {
@Override
Expand Down Expand Up @@ -156,6 +199,17 @@ public void run(final IScope scope) {
*/
@Override
public IExpression getOutputs() { return outputsExpression; }

/**
* Return the name of variables / or string representation entered in BATCH_OUTPUT_VAR facet
* @return
*/
@SuppressWarnings("unchecked")
public IList<String> getLitteralOutputs() {;
return StreamEx.of(((ListExpression) outputsExpression)
.getElements()).map(IExpression::getName).toCollection(
Containers.listOf(Types.STRING));
}

/**
* Construct the experimental plan based on the given the proper modeler input: from sampling methods, from a file
Expand Down Expand Up @@ -258,7 +312,8 @@ public List<ParametersSet> buildParameterSets(final IScope scope, final List<Par
* @param results
*/
public void saveRawResults(final IScope scope, final IMap<ParametersSet, Map<String, List<Object>>> results) {
String path_to = Cast.asString(scope, outputFilePath.value(scope));
String path_to = outputFilePath==null ? FileUtils.constructAbsoluteFilePath(scope, currentExperiment.getName()+"_results.csv", false) :
Cast.asString(scope, outputFilePath.value(scope));
final File fo = new File(FileUtils.constructAbsoluteFilePath(scope, path_to, false));
final File parento = fo.getParentFile();
if (!parento.exists()) {
Expand Down Expand Up @@ -295,14 +350,19 @@ public void saveRawResults(final IScope scope, final IMap<ParametersSet, Map<Str
* @param scope
* @return
*/
@SuppressWarnings ("unchecked")
private List<ParametersSet> buildParameterFromMap(final IScope scope) {
IExpression psexp = getFacet(IKeyword.WITH);
if (psexp.getGamlType().isAssignableFrom(Types.LIST)) throw GamaRuntimeException.error(
"You cannot use " + IKeyword.WITH + " facet without input a list of maps: got "+psexp.getDenotedType(), scope);
List<Map<String, Object>> parameterSets = Cast.asList(scope, psexp.value(scope));

return buildParametersSetList(scope, parameterSets);
List<IMap<IExpression, IExpression>> parameterSets = StreamEx.of( ((ListExpression) psexp).getElements() )
.map(e -> ((MapExpression) e).getElements()).toList();
List<Map<String, Object>> paramSets = new ArrayList<>();
for (IMap<IExpression, IExpression> ps : parameterSets) {
Map<String,Object> lt = new HashMap<>();
for (var e : ps.entrySet()) { lt.put(e.getKey().getName(), e.getValue()); }
paramSets.add(lt);
}
return buildParametersSetList(scope, paramSets);
}

private List<ParametersSet> buildParametersSetList(final IScope scope,
Expand Down Expand Up @@ -330,7 +390,8 @@ private List<ParametersSet> buildParametersFromCSV(final IScope scope, final Str
throws GamaRuntimeException {
List<Map<String, Object>> parameters = new ArrayList<>();

try (FileReader fr = new FileReader(new File(path), StandardCharsets.UTF_8);
try (FileReader fr = new FileReader(
new File(FileUtils.constructAbsoluteFilePath(scope, path, false)), StandardCharsets.UTF_8);
BufferedReader br = new BufferedReader(fr)) {
String line = " ";
String[] tempArr;
Expand Down Expand Up @@ -366,8 +427,7 @@ private String buildSimulationCsv(final IMap<ParametersSet, Map<String, List<Obj
final IScope scope) {
StringBuilder sb = new StringBuilder();

@SuppressWarnings ("unchecked") List<String> outputs =
Cast.asList(scope, getFacet(IKeyword.BATCH_VAR_OUTPUTS).value(scope));
List<String> outputs = getLitteralOutputs();
List<String> inputs = results.getKeys().anyValue(scope).getKeys();
// Write the header
sb.append(String.join(CSV_SEP, inputs));
Expand Down Expand Up @@ -397,6 +457,50 @@ private String buildSimulationCsv(final IMap<ParametersSet, Map<String, List<Obj

return sb.toString();
}

// ##################### Estimate sample size based on method facets

private int estimateSamples(final BatchAgent agent) {
String method = Exploration.DEFAULT_SAMPLING;
if (hasFacet(Exploration.METHODS)) {
method = Cast.asString(agent.getScope(), getFacet(Exploration.METHODS).value(agent.getScope()));
} else {
String xpm =IKeyword.METHODS[Arrays.asList(CLASSES).indexOf(AExplorationAlgorithm.this.getClass())];
if (hasFacet(IKeyword.FROM)) method = Exploration.FROM_FILE;
else if (hasFacet(IKeyword.WITH)) method = Exploration.FROM_LIST;
else if (xpm == IKeyword.MORRIS) method = IKeyword.MORRIS;
else if (xpm == IKeyword.SOBOL) method = IKeyword.SALTELLI;
}
int K = agent.getParametersToExplore().size();
int N = hasFacet(Exploration.SAMPLE_SIZE) ? Cast.asInt(agent.getScope(),
getFacet(Exploration.SAMPLE_SIZE).value(agent.getScope())) : sample_size;
int res = switch (method) {
case IKeyword.MORRIS:
yield N * (K+1);
case IKeyword.SALTELLI:
yield N * (2*K + 2);
case IKeyword.LHS, IKeyword.ORTHOGONAL, IKeyword.UNIFORM:
yield N;
case Exploration.FROM_LIST:
yield buildParameterFromMap(agent.getScope()).size();
case Exploration.FROM_FILE:
yield buildParametersFromCSV(agent.getScope(),
Cast.asString(agent.getScope(), getFacet(IKeyword.FROM).value(agent.getScope()))).size();
default:
yield hasFacet(Exploration.SAMPLE_FACTORIAL) ?
IntStreamEx.of(getFactorial(agent.getScope(), agent.getParametersToExplore()))
.reduce(1, (a,b) -> a*b)
: (hasFacet(Exploration.SAMPLE_SIZE) ? N
: IntStreamEx.of(agent.getParametersToExplore().stream().mapToInt(
b -> getParameterSwip(agent.getScope(), b).size())).reduce(1, (a,b) -> a*b));

};
if (IKeyword.METHODS[Arrays.asList(CLASSES).indexOf(AExplorationAlgorithm.this.getClass())]==IKeyword.BETAD
&& hasFacet(BetaExploration.BOOTSTRAP)) { res = N + N *
Cast.asInt(agent.getScope(), getFacet(BetaExploration.BOOTSTRAP).value(agent.getScope()))
* K;}
return res;
}

// ##################### Methods to determine possible values based on exhaustive ######################

Expand All @@ -413,6 +517,7 @@ private List<Object> getParameterSwip(final IScope scope, final Batch var) {
case IType.FLOAT -> getFloatParameterSwip(scope, var);
case IType.DATE -> getDateParameterSwip(scope, var);
case IType.POINT -> getPointParameterSwip(scope, var);
case IType.BOOL -> Arrays.asList(true,false);
default -> getDefaultParameterSwip(scope, var);
};
}
Expand Down Expand Up @@ -490,8 +595,8 @@ private List<Object> getFloatParameterSwip(final IScope scope, final Batch var)
} else if (stepFloatValue == 0) { stepFloatValue = 0.1; }

while (minFloatValue <= maxFloatValue) {
minFloatValue += stepFloatValue;
res.add(minFloatValue);
minFloatValue += stepFloatValue;
}

return res;
Expand Down
19 changes: 0 additions & 19 deletions gama.core/src/gama/core/kernel/batch/exploration/Exploration.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import gama.core.common.interfaces.IKeyword;
import gama.core.kernel.experiment.BatchAgent;
import gama.core.kernel.experiment.IParameter.Batch;
import gama.core.kernel.experiment.ParameterAdapter;
import gama.core.kernel.experiment.ParametersSet;
import gama.core.runtime.IScope;
import gama.core.runtime.exceptions.GamaRuntimeException;
Expand Down Expand Up @@ -199,24 +198,6 @@ public void explore(final IScope scope) throws GamaRuntimeException {
@Override
public void addParametersTo(final List<Batch> exp, final BatchAgent agent) {
super.addParametersTo(exp, agent);

exp.add(new ParameterAdapter("Sampled points", BatchAgent.EXPLORATION_EXPERIMENT, IType.STRING) {
@Override
public Object value() {
return sample_size;
}
});

exp.add(new ParameterAdapter("Sampling method", BatchAgent.EXPLORATION_EXPERIMENT, IType.STRING) {
@Override
public Object value() {
if (hasFacet(IKeyword.FROM)) return FROM_FILE;
if (hasFacet(IKeyword.WITH)) return FROM_LIST;
return hasFacet(Exploration.METHODS)
? Cast.asString(agent.getScope(), getFacet(METHODS).value(agent.getScope())) : DEFAULT_SAMPLING;
}
});

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import gama.core.kernel.batch.exploration.Exploration;
import gama.core.kernel.experiment.BatchAgent;
import gama.core.kernel.experiment.IParameter.Batch;
import gama.core.kernel.experiment.ParameterAdapter;
import gama.core.kernel.experiment.ParametersSet;
import gama.core.runtime.IScope;
import gama.core.runtime.exceptions.GamaRuntimeException;
Expand Down Expand Up @@ -162,7 +161,7 @@ public void explore(final IScope scope) {
currentExperiment.setKeepSimulations(false);
res_outputs = currentExperiment.runSimulationsAndReturnResults(sets);

outputs = Cast.asList(scope, getFacet(IKeyword.BATCH_VAR_OUTPUTS).value(scope));
outputs = getLitteralOutputs();

Map<String, Map<Batch, Double>> res = new HashMap<>();
for (String out : outputs) {
Expand Down Expand Up @@ -191,35 +190,6 @@ public void explore(final IScope scope) {
@Override
public void addParametersTo(final List<Batch> exp, final BatchAgent agent) {
super.addParametersTo(exp, agent);

exp.add(new ParameterAdapter("Sampled points", IKeyword.BETAD, IType.STRING) {
@Override public Object value() {
if (hasFacet(BOOTSTRAP)) {
return Cast.asInt(agent.getScope(),
getFacet(Exploration.SAMPLE_SIZE).value(agent.getScope()))
+ Cast.asInt(agent.getScope(),
getFacet(Exploration.SAMPLE_SIZE).value(agent.getScope()))
* Cast.asInt(agent.getScope(), getFacet(BOOTSTRAP).value(agent.getScope()))
* getParams(agent).size();
}
if (hasFacet(Exploration.SAMPLE_SIZE)) {
return Cast.asInt(agent.getScope(),
getFacet(Exploration.SAMPLE_SIZE).value(agent.getScope()));
}

return sample_size;
}
});

exp.add(new ParameterAdapter("Sampling method", IKeyword.BETAD, IType.STRING) {
@Override
public Object value() {
return hasFacet(Exploration.METHODS)
? Cast.asString(agent.getScope(), getFacet(Exploration.METHODS).value(agent.getScope()))
: Exploration.DEFAULT_SAMPLING;
}
});

}

// ================================== //
Expand All @@ -231,9 +201,7 @@ public Object value() {
* @return
*/
private List<Batch> getParams(BatchAgent xp) {
return xp.getParametersToExplore().stream()
.filter(p -> p.getMinValue(xp.getScope()) != null && p.getMaxValue(xp.getScope()) != null)
.map(p -> p).toList();
return xp.getParametersToExplore().stream().map(p -> p).toList();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
public final class Morris {

public static final int DEFAULT_LEVELS = 4;
public static final int DEFAULT_TRAJECTORIES = 10;

/**
* Attributes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,22 +69,22 @@
optional = false,
internal = true,
doc = @doc ("The name of the method. For internal use only")),
@facet (
name = Exploration.SAMPLE_SIZE,
type = IType.ID,
optional = false,
doc = @doc ("The size of the sample for Morris samples")),
@facet (
name = MorrisExploration.NB_LEVELS,
type = IType.ID,
optional = false,
doc = @doc ("Number of level for the Morris method, can't be 1")),
optional = true,
doc = @doc ("Number of level each trajectories is made of, can't be 1 / should be even - 4 by default")),
@facet (
name = IKeyword.BATCH_VAR_OUTPUTS,
type = IType.LIST,
of = IType.STRING,
optional = false,
doc = @doc ("The list of output variables to analyze through morris method")),
@facet (
name = Exploration.SAMPLE_SIZE,
type = IType.INT,
optional = true,
doc = @doc ("The number of trajectories , 10 by default - usually between 5 and 15 but should be relative (positive correlation) to the number of parameters")),
@facet (
name = IKeyword.BATCH_REPORT,
type = IType.STRING,
Expand All @@ -106,7 +106,7 @@
usages = { @usage (
value = "For example: ",
examples = { @example (
value = "method morris sample_size:100 nb_levels:4 outputs:['my_var'] report:'../path/to/report.txt;",
value = "method morris nb_levels:4 outputs:['my_var'] report:'../path/to/report.txt;",
isExecutable = false) }) })

public class MorrisExploration extends AExplorationAlgorithm {
Expand Down Expand Up @@ -163,8 +163,9 @@ public void setChildren(final Iterable<? extends ISymbol> children) {}

@Override
public void explore(final IScope scope) {
this.sample = Cast.asInt(scope, getFacet(Exploration.SAMPLE_SIZE).value(scope));
this.nb_levels = Cast.asInt(scope, getFacet(NB_LEVELS).value(scope));
this.sample = hasFacet(Exploration.SAMPLE_SIZE) ?
Cast.asInt(scope, getFacet(Exploration.SAMPLE_SIZE).value(scope)) : Morris.DEFAULT_TRAJECTORIES;
this.nb_levels = hasFacet(NB_LEVELS) ? Cast.asInt(scope, getFacet(NB_LEVELS).value(scope)) : Morris.DEFAULT_LEVELS;
if (hasFacet(PARAMETER_CSV_PATH)) {
IExpression path_facet = getFacet(PARAMETER_CSV_PATH);
String path =
Expand Down Expand Up @@ -222,7 +223,7 @@ public List<ParametersSet> buildParameterSets(final IScope scope, final List<Par
List<String> names = new ArrayList<>();
for (int i = 0; i < parameters.size(); i++) { names.add(parameters.get(i).getName()); }
this.ParametersNames = names;
outputs = Cast.asList(scope, getFacet(IKeyword.BATCH_VAR_OUTPUTS).value(scope));
outputs = getLitteralOutputs();

// Puck Fython
List<Object> morris_samplings = MorrisSampling.makeMorrisSampling(nb_levels, this.sample, parameters, scope);
Expand All @@ -239,20 +240,10 @@ public List<ParametersSet> buildParameterSets(final IScope scope, final List<Par
public void addParametersTo(final List<Batch> exp, final BatchAgent agent) {
super.addParametersTo(exp, agent);

int s = Cast.asInt(agent.getScope(), getFacet(Exploration.SAMPLE_SIZE).value(agent.getScope()));
int l = Cast.asInt(agent.getScope(), getFacet(NB_LEVELS).value(agent.getScope()));

exp.add(new ParameterAdapter("Morris level", IKeyword.MORRIS, IType.STRING) {
@Override
public Object value() {
return l;
}
});

exp.add(new ParameterAdapter("Morris sample", IKeyword.MORRIS, IType.STRING) {
@Override
public Object value() {
return solutions == null ? s * l : solutions.size() == 0 ? sample : solutions.size();
return Cast.asInt(agent.getScope(), getFacet(NB_LEVELS).value(agent.getScope()));
}
});

Expand Down
Loading