SIRAnalysis_step6 - gama-platform/gama GitHub Wiki
6. Sobol Sensitivity Analysis
Morris told us which parameters matter and which do not. Sobol goes further by quantifying exactly how much of the output variance each parameter explains — both on its own and through interactions with other parameters. It is the reference method for variance-based global sensitivity analysis.
In particular, this step presents:
- The principle of the Sobol method and its three indices S1 and ST (GAMA doesn't compute S2 in its current state)
- The Saltelli sampling scheme and how to calculate the total number of runs
- How to declare
method soboland configure its facets - How to read and interpret the report file produced by GAMA
The model file for this step can be found in:
Library models/Tutorials/SIRAnalysis/models/SIR_Sobol.gaml
How Sobol works
Sobol is a variance-based global sensitivity analysis method. It decomposes the total variance of the output into contributions from each parameter and their interactions:
| Index | Meaning |
|---|---|
| S1 | First-order index: fraction of output variance explained by Xi alone, ignoring all interactions. The sum of all S1 ≤ 1. |
| ST | Total-order index: fraction explained by Xi including all interactions involving Xi. ST ≥ S1 always. |
Three diagnostic rules:
- ST − S1 ≈ 0 → the parameter acts independently, no significant interactions
- ST − S1 > 0 → the parameter interacts with others; the gap quantifies the interaction share
- ST ≈ 0 → the parameter can be fixed at any value without materially affecting output variance
The Saltelli sampling scheme
Sobol uses a specific sampling design due to Saltelli (2002). Two independent base
matrices A and B of shape (sample × k) are drawn, and k+2 derived matrices
are constructed from them. This gives a total run count of:
total runs = sample × (2k + 2)
With sample: 100 and k = 5 parameters:
100 × (2 × 5 + 2) = 100 × 12 = 1200 runs
This is significantly more expensive than Morris. For a first analysis, sample: 100
is sufficient to detect dominant parameters. For publication-quality estimates,
sample: 500 or more is recommended.
Morris vs Sobol
| Morris | Sobol | |
|---|---|---|
| Cost | r × (k+1) | N × (2k+2) |
| Indices | μ, μ*, σ | S1, ST |
| Interactions | Detected via σ (qualitative) | Quantified via ST |
| Best for | Screening — many parameters | Quantification — fewer parameters |
The recommended workflow is to run Morris first to eliminate negligible parameters, then run Sobol on the reduced set.
The method sobol statement
The facets are:
outputs:— list of global variables to analyse. Mandatory.sample:— N in the Saltelli scheme. Total runs = N × (2k+2). Mandatory.report:— path to the output file. Use.txtfor a human-readable report. Mandatory.results:— path to the raw CSV with one row per run. Optional but recommended.
method sobol
outputs: ["nb_recovered"]
sample: 100
report: "../results/sobol_report.txt"
results: "../results/sobol_raw.csv";
Reading the report
Our experiment reulted in the following report :
After the run, sobol_report.txt lists S1 and ST for each parameter. A typical result
on our SIR model shows nb_agents and infection_distance with the highest ST values,
confirming the Morris ranking. Parameters with ST close to 0 — here recovery_time —
can be fixed without loss of output variability. A large ST − S1 gap for a parameter
indicates it contributes substantially through interactions.
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);
}
}
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;
}
}
// Total runs = sample × (2k + 2) = 100 × 12 = 1200 runs
experiment SIR_Sobol type: batch
repeat: 1
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 sobol
outputs: ["nb_recovered"]
sample: 100
report: "../results/sobol_report.txt"
results: "../results/sobol_raw.csv";
}
What's next?
Step 7 introduces Beta^d, the third and final sensitivity analysis method available in GAMA. Unlike Sobol, it does not rely on variance — it measures how much fixing a parameter shifts the entire output distribution, making it complementary to both Morris and Sobol.