Skip to content
Draft
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
37 changes: 37 additions & 0 deletions src/arcade/core/sim/Series.java
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,43 @@ protected static void parseParameter(
}
}

/**
* Parses parameter values based on default value.
*
* @param box the parameter map
* @param parameter the parameter name
* @param defaultParameter the default parameter value
* @param values the map of parameter values
* @param scales the map of parameter scaling
* @param ics the map of initial condition parameters
*/
protected static void parseParameter(
MiniBox box,
String parameter,
String defaultParameter,
MiniBox values,
MiniBox scales,
MiniBox ics) {
String value = values.contains(parameter) ? values.get(parameter) : defaultParameter;
Matcher match = Pattern.compile(DISTRIBUTION_REGEX).matcher(value);

if (match.find()) {
box.put("(DISTRIBUTION)" + TAG_SEPARATOR + parameter, match.group(1).toUpperCase());
for (int i = 0; i < (match.groupCount() - 1) / 2; i++) {
int index = 2 * (i + 1);
box.put(parameter + "_" + match.group(index), match.group(index + 1));
}
} else {
box.put(parameter, value);
if (scales.contains(parameter)) {
box.put(parameter, box.getDouble(parameter) * scales.getDouble(parameter));
}
}
if (ics.contains(parameter)) {
box.put(parameter + "_IC", ics.get(parameter));
}
}

/**
* Updates conversion string into a value.
*
Expand Down
8 changes: 6 additions & 2 deletions src/arcade/core/util/Parameters.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.HashMap;
import java.util.HashSet;
import ec.util.MersenneTwisterFast;
import arcade.core.util.distributions.DegenerateDistribution;
import arcade.core.util.distributions.Distribution;

/**
Expand Down Expand Up @@ -39,13 +40,16 @@ public Parameters(
MiniBox distributionsBox = popParameters.filter("(DISTRIBUTION)");
for (String key : distributionsBox.getKeys()) {
Distribution distribution;

if (cellParameters != null && cellParameters.distributions.containsKey(key)) {
distribution = cellParameters.distributions.get(key).rebase(random);
if (distribution instanceof DegenerateDistribution) {
distribution = popParameters.getDistribution(key, random);
}
} else if (popParameters.contains(key + "_IC")) {
distribution = new DegenerateDistribution(key, popParameters, random);
} else {
distribution = popParameters.getDistribution(key, random);
}

distributions.put(key, distribution);
}
}
Expand Down
95 changes: 95 additions & 0 deletions src/arcade/core/util/distributions/DegenerateDistribution.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package arcade.core.util.distributions;

import ec.util.MersenneTwisterFast;
import arcade.core.util.MiniBox;

/** Container class for degenerate distribution (constant value). */
public class DegenerateDistribution implements Distribution {
/** The constant value of the distribution. */
private final double value;

/**
* Creates a degenerate {@code Distribution} from parameters dictionary.
*
* @param name the distribution parameter name
* @param parameters the distribution parameters dictionary
* @param random the random number generator instance (unused for degenerate distribution)
*/
public DegenerateDistribution(String name, MiniBox parameters, MersenneTwisterFast random) {
this(getValueFromParameters(name, parameters));
}

/**
* Helper method to get the value from parameters based on IC type.
*
* @param name the distribution parameter name
* @param parameters the distribution parameters dictionary
* @return the value of the distribution
*/
private static double getValueFromParameters(String name, MiniBox parameters) {
String icType = parameters.get(name + "_IC");
if (icType.equals("MU")) {
return parameters.getDouble(name + "_MU");
} else if (icType.equals("MIN")) {
return parameters.getDouble(name + "_MIN");
} else if (icType.equals("MAX")) {
return parameters.getDouble(name + "_MAX");
} else {
try {
if (icType.contains(".")) {
return Double.parseDouble(icType);
} else {
return Integer.parseInt(icType);
}
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid IC value: " + icType, e);
}
}
}

/**
* Creates a degenerate {@code Distribution} with a constant value.
*
* @param value the constant value of the distribution
*/
public DegenerateDistribution(double value) {
this.value = value;
}

@Override
public MiniBox getParameters() {
MiniBox parameters = new MiniBox();
parameters.put("VALUE", value);
return parameters;
}

@Override
public double getExpected() {
return value;
}

@Override
public double getDoubleValue() {
return value;
}

@Override
public int getIntValue() {
return (int) Math.round(value);
}

@Override
public double nextDouble() {
return value;
}

@Override
public int nextInt() {
return (int) Math.round(value);
}

@Override
public Distribution rebase(MersenneTwisterFast random) {
return new DegenerateDistribution(value);
}
}
3 changes: 0 additions & 3 deletions src/arcade/patch/agent/cell/PatchCellFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ public void createCells(Series series) {
}

int pop = population.getInt("CODE");

for (int i = 0; i < init; i++) {
PatchCellContainer container = createCellForPopulation(id, pop);
cells.put(id, container);
Expand All @@ -152,9 +151,7 @@ public void createCells(Series series) {
public PatchCellContainer createCellForPopulation(int id, int pop) {
MiniBox population = popToParameters.get(pop);
Parameters parameters = new Parameters(population, null, random);

double compression = parameters.getDouble("COMPRESSION_TOLERANCE");

double volume = parameters.getDouble("CELL_VOLUME");
double height = parameters.getDouble("CELL_HEIGHT");
int age = parameters.getInt("CELL_AGE");
Expand Down
6 changes: 3 additions & 3 deletions src/arcade/patch/sim/PatchSeries.java
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ protected void updatePopulations(
Box parameters = box.filterBoxByTag("PARAMETER");
MiniBox parameterValues = parameters.getIdValForTagAtt("PARAMETER", "value");
MiniBox parameterScales = parameters.getIdValForTagAtt("PARAMETER", "scale");

MiniBox parameterICs = parameters.getIdValForTagAtt("PARAMETER", "ic");
// Apply conversion factors.
for (String convert : populationConversions.getKeys()) {
double conversion = parseConversion(populationConversions.get(convert), ds, dt);
Expand All @@ -194,9 +194,9 @@ protected void updatePopulations(
parameter,
populationDefaults.get(parameter),
parameterValues,
parameterScales);
parameterScales,
parameterICs);
}

// Get list of links, if valid.
MiniBox links = box.filterBoxByTag("LINK").getIdValForTagAtt("LINK", "weight");
for (String link : links.getKeys()) {
Expand Down
30 changes: 30 additions & 0 deletions test/arcade/core/sim/SeriesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,36 @@ public void parseParameter_withTwoParameterDistributionValue_usesValue() {
assertEquals(valueB, box.getDouble(parameter + "_B"), EPSILON);
}

@Test
public void parseParameter_withIC_putsICInBox() {
MiniBox box = new MiniBox();
String parameter = randomString();
double defaultParameter = randomDoubleBetween(0, 100);

MiniBox values = new MiniBox();
MiniBox scales = new MiniBox();
MiniBox ics = new MiniBox();
ics.put(parameter, "MU");
Series.parseParameter(box, parameter, "" + defaultParameter, values, scales, ics);

assertEquals("MU", box.get(parameter + "_IC"));
}

@Test
public void parseParameter_withoutIC_doesNotPutICInBox() {
MiniBox box = new MiniBox();
String parameter = randomString();
double defaultParameter = randomDoubleBetween(0, 100);

MiniBox values = new MiniBox();
MiniBox scales = new MiniBox();
MiniBox ics = new MiniBox();

Series.parseParameter(box, parameter, "" + defaultParameter, values, scales, ics);

assertFalse(box.contains(parameter + "_IC"));
}

@Test
public void parseConversion_invalidConversion_returnsOne() {
assertEquals(1, Series.parseConversion(randomString(), DS, DZ, DT), EPSILON);
Expand Down
117 changes: 117 additions & 0 deletions test/arcade/core/util/distributions/DegenerateDistributionTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package arcade.core.util.distributions;

import org.junit.jupiter.api.Test;
import ec.util.MersenneTwisterFast;
import arcade.core.util.MiniBox;
import static org.junit.jupiter.api.Assertions.*;
import static arcade.core.ARCADETestUtilities.*;

public class DegenerateDistributionTest {

private static final double EPSILON = 1E-5;

private static final MersenneTwisterFast RANDOM = new MersenneTwisterFast();

@Test
public void constructor_withDirectValue_setsValueCorrectly() {
double expected = 42.0;
DegenerateDistribution dist = new DegenerateDistribution(expected);

assertEquals(expected, dist.getDoubleValue(), EPSILON);
assertEquals(expected, dist.getExpected(), EPSILON);
assertEquals((int) Math.round(expected), dist.getIntValue());
}

@Test
public void constructor_withICMU_setsValueFromMU() {
MiniBox parameters = new MiniBox();
String name = randomString().toUpperCase();
double mu = randomDoubleBetween(0, 100);
parameters.put(name + "_IC", "MU");
parameters.put(name + "_MU", mu);

DegenerateDistribution dist = new DegenerateDistribution(name, parameters, RANDOM);

assertEquals(mu, dist.getDoubleValue(), EPSILON);
}

@Test
public void constructor_withICNumber_setsValueFromNumber() {
MiniBox parameters = new MiniBox();
String name = randomString().toUpperCase();
String numberString = "50";
parameters.put(name + "_IC", numberString);

DegenerateDistribution dist = new DegenerateDistribution(name, parameters, RANDOM);

assertEquals(50, dist.getDoubleValue(), EPSILON);
}

@Test
public void constructor_withICMIN_setsValueFromMIN() {
MiniBox parameters = new MiniBox();
String name = randomString().toUpperCase();
double min = randomDoubleBetween(0, 100);
parameters.put(name + "_IC", "MIN");
parameters.put(name + "_MIN", min);

DegenerateDistribution dist = new DegenerateDistribution(name, parameters, RANDOM);

assertEquals(min, dist.getDoubleValue(), EPSILON);
}

@Test
public void constructor_withICMAX_setsValueFromMAX() {
MiniBox parameters = new MiniBox();
String name = randomString().toUpperCase();
double max = randomDoubleBetween(0, 100);
parameters.put(name + "_IC", "MAX");
parameters.put(name + "_MAX", max);

DegenerateDistribution dist = new DegenerateDistribution(name, parameters, RANDOM);

assertEquals(max, dist.getDoubleValue(), EPSILON);
}

@Test
public void constructor_withInvalidIC_throwsException() {
MiniBox parameters = new MiniBox();
String name = randomString().toUpperCase();
parameters.put(name + "_IC", "INVALID");

Exception exception =
assertThrows(
IllegalArgumentException.class,
() -> new DegenerateDistribution(name, parameters, RANDOM));

assertTrue(exception.getMessage().contains("Invalid IC"));
}

@Test
public void getParameters_returnsCorrectMiniBox() {
double expected = randomDoubleBetween(0, 100);
DegenerateDistribution dist = new DegenerateDistribution(expected);
MiniBox parameters = dist.getParameters();

assertEquals(expected, parameters.getDouble("VALUE"), EPSILON);
}

@Test
public void rebase_returnsIdenticalDistribution() {
double value = randomDoubleBetween(0, 100);
DegenerateDistribution dist = new DegenerateDistribution(value);
DegenerateDistribution rebased = (DegenerateDistribution) dist.rebase(RANDOM);

assertEquals(value, rebased.getDoubleValue(), EPSILON);
assertEquals(value, rebased.getExpected(), EPSILON);
}

@Test
public void nextDouble_and_nextInt_returnConstantValue() {
double value = randomDoubleBetween(0, 100);
DegenerateDistribution dist = new DegenerateDistribution(value);

assertEquals(value, dist.nextDouble(), EPSILON);
assertEquals((int) Math.round(value), dist.nextInt());
}
}
Loading
Loading