Expand description
§henad
Henad is a parallel agent-based modelling engine, built to run millions of agents at interactive speeds on one machine.
This is the crate that a program depends on. It re-exports the engine, the sweeps and the model authoring API under one module tree, with the example models, the app and the command line behind features.
| Feature | Adds |
|---|---|
example-models | The ten example models, at henad::models |
app | The app as a library, at henad::app |
cli | The command line as a library, at henad::cli, on native targets |
testing | The checks a model’s tests run against its entry, at henad::testing |
No feature is on by default, and no feature changes a result.
§A complete program
The program below builds an example model, runs it, reads its statistics, edits a parameter live, fires an action, runs a small sweep, reads the sweep back, rebuilds one run, and opens the app on it.
// Cargo.toml: henad = { version = "0.3", features = ["example-models", "app"] }
// Copy the template's profile block, `[profile.dev] opt-level = 1`, `[profile.dev.package."*"] opt-level = 2` and
// `[profile.release] opt-level = 2`. Without it a debug build runs the example kernels at opt-level 0, and a release
// build at 3 where Henad measures at 2. A crate that registers its own models also calls `henad_build::stamp_commit()`
// from build.rs, with henad-build under [build-dependencies]. This example registers no models.
use std::io::Write;
use std::ops::ControlFlow;
use std::path::Path;
use henad::prelude::*;
fn main() -> Result<(), Box<dyn std::error::Error>> {
henad::install_panic_hook();
let models = henad::models::example_models();
// A sweep rejects a folder that already holds results. Each run of the program starts from an empty folder.
let folder = std::env::temp_dir().join("sir-rates");
if folder.exists() {
std::fs::remove_dir_all(&folder)?;
}
let replay = study(&models, &folder, &mut std::io::stdout())?;
// Then open the app on the run the study rebuilt.
let options = AppOptions::new(models, "SIR study", henad::build_info!()).opening(AppOpening::Run {
replay,
open_at: OpenAt::Start,
});
henad::app::run_native(options)?;
Ok(())
}
/// Runs SIR from `models`, sweeps it into `folder` and rebuilds one run of the sweep, writes what it finds to `out`,
/// and returns the rebuilt run's replay.
fn study(models: &ModelSet, folder: &Path, out: &mut impl Write) -> Result<Replay, Box<dyn std::error::Error>> {
let sir = models.get("sir").ok_or("the example set lacks SIR")?;
// SIR on a 256 by 256 grid with seed 7. One cell in a thousand starts infected, an outbreak at tick 50 adds more,
// and an infection rate of 0.05 spreads the epidemic slowly up to tick 100.
let mut simulation = sir
.setup()
.set("grid_width", 256u32)?
.set("grid_height", 256u32)?
.set("initial_infected_pct", 0.001f32)?
.set_text("infection_rate", "0.05")?
.with_seed(7)
.act_at("seed_outbreak", 50)?
.build(None)?;
simulation.run_to(100)?;
let stats = simulation.stats()?;
let susceptible = stats.scalar("Susceptible");
writeln!(out, "tick {}: {susceptible:?} susceptible", stats.tick())?;
// A more infectious variant arrives. A live edit raises the infection rate and a second outbreak seeds it. Up to
// 400 more ticks follow, sampled every 20, stopping once nobody is infected.
simulation.set_param("infection_rate", 0.3f32)?;
simulation.act("seed_outbreak")?;
let mut samples = Vec::new();
let flow = simulation.run_sampled(500, 20, |sample| {
samples.push((sample.tick(), sample.scalar("Susceptible"), sample.scalar("Infected")));
match sample.scalar("Infected") {
Some(infected) if infected < 1.0 => ControlFlow::Break(sample.tick()),
_ => ControlFlow::Continue(()),
}
})?;
for (tick, susceptible, infected) in samples {
writeln!(out, "tick {tick}: {susceptible:?} susceptible, {infected:?} infected")?;
}
if let ControlFlow::Break(tick) = flow {
writeln!(out, "the epidemic ended by tick {tick}")?;
}
// Three infection rates, four replicates each, written to `folder`.
let loaded = LoadedSpec::parse(
r#"
model = "sir"
[set]
grid_width = 128
grid_height = 128
[run]
steps = 300
replicates = 4
[[block]]
design = "factorial"
factors = [{ param = "infection_rate", values = [0.2, 0.3, 0.4] }]
"#,
)?;
let mut options = SweepOptions::new(Provenance::new(henad::build_info!(), std::env::args().collect()));
options.spec_source = loaded.spec_source.clone();
options.apply_execution(&loaded.execution);
let record = run_spec(
sir,
None,
&loaded.spec,
SweepOutput::Directory(folder.to_owned()),
&options,
&mut NoProgress,
)?;
let counts = &record.report.counts;
writeln!(out, "{} of {} runs ok", counts.ok, counts.rows)?;
// Read the folder back and rebuild run 5 headlessly.
let results = ResultSet::open_dir(folder, 64 << 20)?;
let replay = results.replay(sir.schema(), 5)?;
let mut rebuilt = RunSetup::from_replay(sir, &replay)?.build(None)?;
rebuilt.run_to(replay.ticks)?;
let infected = rebuilt.stats()?.scalar("Infected");
writeln!(out, "run 5 ends with {infected:?} infected")?;
Ok(replay)
}The user guide covers the app, sweeps and writing your own models.
§License
Licensed under MIT or Apache-2.0, at your option.
Modules§
- action
- Action descriptors and the schedule of actions a run fires.
- app
app - The app, opened over a host’s own model set.
- authoring
- Everything a model is written against: the model traits, their types, the helpers and the primitives.
- benchmark
Non-WebAssembly - Benchmarks that time a model’s steps, as
henad-clidoes. - cli
cliand non-WebAssembly - The command line, run over a host’s own model set.
- engine
- Engine states a test or a host builds directly, without an entry.
- explore
- Sweeps, searches, result folders and replay.
- gpu
- The GPU device a model builds on, its sizing, and the headless device a program acquires.
- models
example-models - The ten example models and the set that registers them.
- params
- Parameter descriptors and values, and the text form that
--setaccepts. - prelude
- Items that a program imports whole to build, run and sweep models.
- runner
- Paced runners that step a model while a host draws it, and the snapshots they publish.
- stats
- Statistic descriptors and values, and the CSV writer for stat series.
- testing
testing - Checks of a model entry’s contracts, for a model’s own tests.
- views
- Views of a CPU model’s state for a host to draw.
Macros§
- actions
- Declares a model’s actions and their indices in one place.
- agent_
lanes - Declares a model’s agent lanes.
- buffers
- Declares a model’s storage buffers and their indices in one place.
- build_
info - Returns the
BuildInfoof the crate the macro expands in. - for_
each_ chunk_ mut - Runs a body over each chunk of a mutable slice, in parallel.
- include_
shaders - Brings in the Rust that henad-build generated from the crate’s WGSL, as the modules
shader_bindingsandbinding_decls. - params
- Declares a model’s parameters and their indices in one place.
Structs§
- Build
Info - Identity of one compiled crate.
- Fault
- A failure Henad caught.
- Lane
Spec - One lane of a CPU agent model’s struct-of-arrays storage.
- Model
Entry - One model a host can offer: its declarations, and the factory that builds it on any device.
- Model
Metadata - Model metadata that the UI displays.
- Model
Set - Models a host offers, each id once, in the order the host lists them.
- Model
SetIter - Iterator over the entries of a
ModelSet, in the set’s order. - Model
Source - Origin of a model’s code.
- RunSetup
- Values, seed and schedule for one build of an entry, each checked against its descriptors when set.
- Simulation
- One built model and its schedule, stepped by its caller.
- Simulation
Views - Views of one model, prepared together and borrowed from its state.
- Stat
Sample - One sample of a model’s stats.
- Topology
Hint - Display layers a model presents.
Enums§
- Backend
- Model backend.
- Export
Error - Reason
Simulation::write_statewrote nothing, or stopped part way. - Fault
Kind - Kind of failure a
Faultrecords. - Model
Lookup Error - Reason a host cannot run a model that it requested from a set.
- Model
SetError - Reason a model set rejects an entry.
- Model
State - A freshly built simulation state, tagged with the runner that can drive it.
- Setup
Error - Reason a setup, a live edit or a live action was rejected.
- Structure
- Storage layout of a model, by backend and topology.
Constants§
- ENGINE_
BUILD - Henad’s own build, stamped by henad-explore’s build script over the engine’s crates.
Traits§
- Wasm
NotSend Send, relaxed to nothing on wasm with atomics.- Wasm
NotSync Sync, relaxed to nothing on wasm with atomics.
Functions§
- install_
panic_ hook - Installs a panic hook that records where each panic came from, then calls the hook that was already installed.