Code release accompanying a research-paper submission on adaptive swarm-based surveillance of invasive predators. The repository contains a spatially-explicit discrete-time simulation framework and the SWARM-IS search strategy described in the paper, together with the baseline strategies (Random, Systematic) and the experimental configurations used in the paper's sensitivity sweep, re-infestation, and scaling experiments.
Python 3.12+. Install runtime dependencies:
pip install -r requirements.txtsrc/ Simulation framework + SWARM-IS implementation
estimation/ Belief-surface estimators
sensor_planning/ Sensor planners (incl. info-gain Steiner-tree)
control_planning/ Control planners (probabilistic removal)
simulation/ Main loop and Hungarian matching
experiment/ Config loading, experiment runner
tuning/ Ray Tune + Ax integration
metrics/ Loss and distance metrics
observation/ Per-node detection broker
utils/ Visualisation, video, IO helpers
experiments/ Strategy + sweep + sub-config JSONs (see below)
risk_maps/ Pre-computed CSV risk maps
scripts/ run_all_experiments.sh, run_from_tuning.py, concat_*.py
docs/design.md Architecture, component interfaces, extension points
cli.py Single entry point (single-run, tune, resume, video)
Run one ready-to-execute example per strategy:
python cli.py -c experiments/example_dual_steiner.json # SWARM-IS
python cli.py -c experiments/example_random.json # Random baseline
python cli.py -c experiments/example_systematic.json # Systematic baselineEach example combines a strategy with one representative sensitivity-sweep
cell (the paper's headline operating point: g0_intercept = 5.5,
false_positive_probability = 0.15, predator_elimination_probability = 0.8,
initial_density_pha = 5, prior_risk_estimate = "Patchy"). Strategy
hyperparameters are the Ax-best configurations extracted from our actual
tuning runs at this cell, so a single-seed example invocation reproduces
the per-condition tuned performance (within seed noise): SWARM-IS
Results are written to results/<config.name>/<timestamp>/:
results.csv— per-iterationlossanddistance_moved.config.json— exact config used.- Optional artefacts (video, plots, distribution frames) per CLI flag.
The strategy templates experiments/strategy_{dual_steiner,random,systematic}.json
encode the same strategies with sample directives ("uniform(...)",
"loguniform(...)", "randint(...)") in place of literal hyperparameter
values. They are the inputs to --tune; they are not runnable directly.
The paper experiments use a tune-then-evaluate workflow because each strategy's optimal hyperparameters depend on the experimental condition.
Run Bayesian optimisation (Ax via Ray Tune) over each strategy's
hyperparameter space, across the full grid of environmental conditions
defined in experiments/sensitivity_sweep.json:
for strat in random systematic dual_steiner; do
python cli.py --tune \
-c experiments/strategy_${strat}.json \
--sensitivity_combinations experiments/sensitivity_sweep.json \
--n_samples 100
doneEach tuning run produces per-condition trial CSVs under
results/<strategy-name>/<timestamp>/. The strategy name is taken from the
"name" field of the strategy config (random, systematic,
dual_steiner).
Useful tuning flags:
--n_samples N— number of Ax trials per sensitivity cell.--max_concurrent K— number of trials evaluated concurrently (1 = fully sequential Bayesian).--cpus_per_trial C— Ray per-trial CPU budget.--contextual— single joint GP across tuning + sensitivity dimensions instead of independent per-cell searches.--resume <path>— resume a partial tuning run.--extract_bests --resume <path>— extract empirical and model-predicted best configs per cell from a completed contextual run.
After tuning, the driver script reads the tuning result CSVs, picks the
best configuration per sensitivity cell, and runs n_repeats evaluation
simulations for each (strategy, sweep cell, sub-experiment) combination:
bash scripts/run_all_experiments.sh results/all results/experimentsArguments:
-
$1(defaultresults/all) — root directory containing one subdir per strategy. The driver expectsresults/all/{random,systematic,dual_steiner}/, each holding the tuning CSVs from Phase 1. Symlink or move your tuning outputs accordingly. -
$2(defaultresults/experiments) — destination for evaluation summaries.
Environment overrides:
N_REPEATS(default10) — number of evaluation seeds per cell.N_WORKERS(default1) — parallel workers for base-experiment cells.
The driver runs three sub-experiments per strategy, each defined by a
sub-config in experiments/:
base_experiment.json— full 432-cell sensitivity grid, no overrides.reinfestation_d{0.5,2.5,5,7.5}.json— per-density re-infestation scenarios; concatenated post-hoc byscripts/concat_reinfestation_summaries.py.scaling_experiment.json— swarm-size scaling sweep.
Sub-configs have the shape:
{
"tuning_filters": { /* fix some dimensions of the tuning run */ },
"sweep": { /* Cartesian sweep over result selection */ },
"overrides": { /* per-run overrides applied after best-config lookup */ }
}Outputs:
results/experiments/<strategy>/<sub-experiment>/summary.csv
results/experiments/<strategy>/reinfestation_experiment/summary.csv (concat)
cli.py supports several output artefacts (single-run mode only):
--make_video— render an MP4 of the simulation. Saved to the run's output directory. Requiresopencv-python.--make_plots— emit PNGs of node trajectories and the final belief / posterior surface.--render_distributions— save per-iteration frames of the belief surface as PNGs for offline animation.--track_simulation_progression/-k— record per-iteration belief surfaces and predator positions in the result file (off by default to keep file sizes small).--tag <str>— prefix the run's timestamp directory with a custom tag.
- New estimator: subclass
BaseEstimator(src/estimation/base.py), implementupdate(reports), export fromsrc/estimation/__init__.py, set"detection_surface.name"in config. - New sensor planner: subclass
BasePlanner(src/sensor_planning/base.py), implementget_initial_locationsandget_new_detector_locations, export fromsrc/sensor_planning/__init__.py, set"planner.name"in config. - New control planner: subclass
BaseControlPlanner, implementdecide(...), wire intobuild_components()insrc/experiment/config.py.
See docs/design.md for the full interface specification and the main
loop step ordering.
This release uses pre-computed risk maps produced by the third-party
landscape generator flsgen (GPLv3). See CREDITS.md.
Copyright (c) 2026 Hugh Parsons.
This project is licensed under the GNU Affero General Public License
v3.0 (AGPL-3.0). See the LICENSE file for the full text.
If you are interested in using the code under a different license, please contact us.