Skip to main content
Version: 🚧 Alpha 🚧

4. Stochastic Analysis

Before applying any sensitivity analysis method, there is a fundamental question to answer: how much of the variability in nb_recovered is due to the random seed, and how many replications are needed to produce robust results?

If stochasticity dominates, then a single run per parameter configuration is meaningless — the output reflects the seed more than the parameters. The stochanalyse method answers this question systematically before committing to the more expensive Morris or Sobol analyses.

In particular, this step presents:

  • What the stochanalyse method measures and how to interpret its report
  • How repeat and sample interact in this context
  • How to use the permanent block to visualise stochastic spread directly in GAMA
  • How the result informs the repeat: value to use in subsequent batch experiments

The model file for this step can be found in: msi.gama.models/models/Tutorials/SIR Analysis/models/SIR Model 04.gaml


How stochanalyse works​

For each of the sample randomly drawn parameter points in the space, the method runs the simulation repeat times with different seeds. It then computes three indices by progressively increasing the number of replications from 2 up to repeat:

IndexMeaning
CorrelationAverage Pearson correlation between pairs of replicate outputs. Close to 1 means replicas agree — few replications needed.
CVCoefficient of variation across replicas. Close to 0 means low stochastic variability.
Neyman-PearsonEstimated minimum number of replications needed to avoid Type I and Type II errors at standard confidence levels.

The key quantity is the Neyman-Pearson estimate — it directly tells you what value to use for repeat: in the Morris, Sobol and Beta^d experiments that follow.

Total runs = sample × repeat = 10 × 20 = 200 runs.


The method stochanalyse statement​

The method is declared exactly like the others. The outputs: facet lists the global variables to analyse. The report: facet writes the human-readable indices to a .txt file. The sample: facet controls how many distinct parameter points are probed.

method stochanalyse
outputs: ["nb_recovered"]
report: "../results/stoch_report.txt"
results: "../results/stoch_raw.csv"
sample: 10;

Note: Unlike Morris and Sobol, stochanalyse has no dedicated sampling algorithm facet — it draws parameter points uniformly at random from the ranges defined by the parameter statements.


The permanent block​

The permanent block defines displays that persist across all simulation runs and are refreshed at the end of each run rather than at each cycle. It is the standard way to visualise aggregate results in a batch experiment directly within GAMA, without needing an external tool.

Here we use it to show the distribution of nb_recovered across all runs as a histogram, giving immediate visual intuition of how wide the stochastic spread is.

permanent {
display "Stochastic spread" type: java2D {
chart "nb_recovered across all runs" type: histogram background: #white {
data "nb_recovered"
value: simulations collect each.nb_recovered
color: #steelblue;
}
}
}

A narrow histogram means stochasticity is low — a single replication per parameter point may be sufficient. A wide, flat histogram means the random seed contributes substantially to output variability and several replications will be needed.


Reading the report​

The report is structured around three complementary measures, each giving a recommended minimum number of replications for the output nb_recovered:

Coefficient of variation and Standard error — both report the minimum, maximum and average number of replications needed across the 150 sampled parameter points, for three decreasing thresholds (0.05, 0.01, 0.001). In our case both measures agree: an average of 5–6 replications is sufficient at the 0.05 threshold, rising to 6 at the stricter 0.001 threshold. The spread (min = 2, max = 9) shows that some parameter configurations are more stochastic than others.

Critical effect size — this test estimates the number of replications needed to reliably detect an effect of a given standardised size, controlling for both Type I (false positive, α = 0.01) and Type II (false negative, β = 0.05) errors. The results are reported for six conventional effect sizes:

Effect sizeLabelReplications needed (average)
0.01ultra-micro> 10 — not enough with current repeat
0.05micro10
0.1small10
0.2medium10
0.4large9
0.8huge9

The theoretical minimum for ultra-micro effects is over 1000 replications — far beyond what is practical. For effects of small size or larger, 9–10 replications suffice.

Practical conclusion for this model: the CV and standard error tests suggest that 6 replications provide robust aggregated outputs at strict thresholds. If you are only interested in detecting medium to large effects, 9–10 replications cover the critical effect size requirements within the repeat: 10 budget. For the Morris, Sobol and Beta^d experiments that follow, setting repeat: 5 is a reasonable compromise between robustness and computational cost.


Complete model​

model SIR

global {
int nb_agents <- 200 min: 50 max: 500;
int nb_infected_init <- 5 min: 1 max: 50;
float infection_rate <- 0.5 min: 0.0 max: 1.0;
int infection_distance <- 5 min: 1 max: 20;
int recovery_time <- 50 min: 10 max: 200;
int max_steps <- 1000;

int nb_susceptible <- 0;
int nb_infected <- 0;
int nb_recovered <- 0;

init {
create person number: nb_agents;
ask nb_infected_init among (person as list) {
status <- "infected";
infection_timer <- recovery_time;
}
}

reflex update_counts {
nb_susceptible <- person count (each.status = "susceptible");
nb_infected <- person count (each.status = "infected");
nb_recovered <- person count (each.status = "recovered");
}

reflex stop when: cycle >= max_steps or nb_infected = 0 {
do halt;
}
}

species person skills: [moving] {
string status <- "susceptible";
int infection_timer <- 0;
float speed <- 1.0;

reflex move {
do wander();
}

reflex infect when: status = "infected" {
ask (person at_distance infection_distance) where (each.status = "susceptible") {
if flip(infection_rate) {
status <- "infected";
infection_timer <- recovery_time;
}
}
infection_timer <- infection_timer - 1;
if infection_timer <= 0 {
status <- "recovered";
}
}

aspect base {
draw circle(1) at: location color:
(status = "infected") ? #red : ((status = "recovered") ? #blue : #green);
}
}

// ── GUI experiment ─────────────────────────────────────────────────────────────
experiment SIR_gui type: gui {
parameter "Number of agents" var: nb_agents;
parameter "Initially infected" var: nb_infected_init;
parameter "Infection rate" var: infection_rate;
parameter "Infection distance" var: infection_distance;
parameter "Recovery time" var: recovery_time;

output {
display "Population" type: java2D {
species person aspect: base;
}
display "SIR Chart" type: java2D {
chart "SIR dynamics" type: series {
data "Susceptible" value: nb_susceptible color: #green;
data "Infected" value: nb_infected color: #red;
data "Recovered" value: nb_recovered color: #blue;
}
}
monitor "Susceptible" value: nb_susceptible;
monitor "Infected" value: nb_infected;
monitor "Recovered" value: nb_recovered;
monitor "Cycle" value: cycle;
}
}

// ── Stochastic analysis experiment ────────────────────────────────────────────
// Total runs = repeat × sample = 20 × 10 = 200
experiment SIR_Stochanalyse type: batch
repeat: 20
keep_seed: false
until: (cycle >= max_steps or nb_infected = 0) {

parameter "Number of agents" var: nb_agents min: 50 max: 500;
parameter "Initially infected" var: nb_infected_init min: 1 max: 50;
parameter "Infection rate" var: infection_rate min: 0.0 max: 1.0;
parameter "Infection distance" var: infection_distance min: 1 max: 20;
parameter "Recovery time" var: recovery_time min: 10 max: 200;

method stochanalyse
outputs: ["nb_recovered"]
report: "../results/stoch_report.txt"
results: "../results/stoch_raw.csv"
sample: 10;

permanent {
display "Stochastic spread" type: java2D {
chart "nb_recovered across all runs" type: histogram background: #white {
data "nb_recovered"
value: simulations collect each.nb_recovered
color: #steelblue;
}
}
}
}

What's next?​

Now that we know the stochastic contribution of the model, we can move on to sensitivity analysis. Step 5 introduces the Morris method, which screens parameters by their influence on nb_recovered at a low computational cost.