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 sobol and 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 .txt for 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 :

Sobol 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.