Scheduling
Normative semantics: This page explains how to use the available schedulers. The authoritative description of activation order, agent eligibility, lifecycle visibility, random-stream limitations, and current versus target guarantees is Simulation Semantics V1.
Scheduling controls the order in which agents act.
Activation order is a modelling assumption. In agent-based modelling, changing the scheduler can change the simulation result. ABMForge therefore treats schedulers as explicit model components.
Built-in schedulers
| Scheduler | Activation order | Uses scheduler RNG | Skips dead agents | Typical use |
|---|---|---|---|---|
SequentialActivation |
Insertion order | no | yes | deterministic models |
RandomActivation |
Random permutation | yes | yes | stochastic activation assumptions |
SimultaneousActivation |
all step(), then all advance() |
no | yes | cellular automata and synchronous update models |
StagedActivation |
declared stage order | optional | yes | multi-phase agent behaviour |
All built-in schedulers operate on a snapshot of agents selected at the beginning of the scheduler step. Agents created during a scheduler pass are not activated until a later pass.
SequentialActivation
Activates agents in insertion order.
from abmforge.scheduling import SequentialActivation
self.scheduler = SequentialActivation(self)
Use this when deterministic ordering is part of the model design or when you want a simple teaching example.
RandomActivation
Activates living agents in a deterministic random order using the named scheduler random stream obtained from model.rng_stream("scheduler").
from abmforge.scheduling import RandomActivation
self.scheduler = RandomActivation(self)
Use this when random activation is part of the model assumption.
Given the same candidate history, model seed, and scheduler-stream draw history, RandomActivation produces reproducible activation order. The default model.rng stream remains available to model and agent behavior, so unrelated behavior draws do not change later activation order.
SimultaneousActivation
Calls step() for every eligible agent, then calls advance() for every
agent that remains eligible.
Before any callback runs, the scheduler validates the initial eligible
candidate snapshot. Every participating agent must define callable step()
and advance() methods.
If one or more candidates violate this contract, ABMForge raises one
TypeError that identifies every invalid agent and each missing or
non-callable method. No activation callback is executed.
from abmforge.scheduling import SimultaneousActivation
self.scheduler = SimultaneousActivation(self)
This scheduler is useful when agents should calculate pending state without immediately changing the state observed by other agents.
A typical agent pattern is:
class Cell(Agent):
def step(self):
self.next_state = compute_next_state(self)
def advance(self):
self.state = self.next_state
An agent that intentionally has no commit work must still define a callable
no-op advance() method:
def advance(self) -> None:
pass
Agents that are already ineligible when the scheduler call begins are excluded from strict two-phase capability validation.
The scheduler guarantees phase ordering, but it does not automatically isolate
current and next state. Model authors must avoid directly mutating current
state in step() when later decision callbacks must observe the old state.
StagedActivation
Calls named methods on agents in a specified order.
from abmforge.scheduling import StagedActivation
self.scheduler = StagedActivation(
self,
stages=["sense", "decide", "act"],
shuffle=False,
)
If shuffle=True, the named scheduler random stream is used to shuffle agents within each stage.
Choosing a scheduler
| Modelling need | Recommended scheduler |
|---|---|
| fixed deterministic order | SequentialActivation |
| random order each step | RandomActivation |
| synchronous update | SimultaneousActivation |
| multi-phase agent behaviour | StagedActivation |
Reproducibility notes
For reproducible ABM experiments:
- set the model seed,
- document the scheduler used,
- document whether activation order is deterministic or random,
- avoid relying on accidental dictionary ordering beyond documented insertion-order behaviour,
- include the scheduler choice in scenario or model documentation.
Common pitfalls
Activation order affects results
Two models with the same rules but different schedulers may produce different results.
Newly spawned agents
Built-in schedulers do not activate agents spawned during the same scheduler pass.
Dead agents
Built-in schedulers skip agents with is_alive == False.
Simultaneous update
With SimultaneousActivation, decision calculations belong in step() and pending-state commits belong in advance(). The scheduler enforces the two callable methods and their phase order, but it cannot prevent a model implementation from mutating current state directly in step().
Staged scheduler contract
StagedActivation validates its stage list at construction time. The stage list
must be a non-empty sequence of non-empty strings. Passing a single string is
rejected because it would otherwise be interpreted as a sequence of characters.
Each living agent must provide a callable method for each declared stage. If a
stage method is missing or non-callable, ABMForge raises an AttributeError
that names the agent type, agent id, and missing stage.
Models may define optional hooks:
def before_stage(self, stage: str) -> None:
...
def after_stage(self, stage: str) -> None:
...
These hooks are called before and after each declared stage. They are useful for recording stage-level diagnostics, enforcing invariants, or teaching multi-phase scheduler semantics. Hooks must be callable when defined.
The scheduler still operates on a snapshot of living agents selected at the beginning of the scheduler step. Agents spawned during a staged scheduler pass are not activated until a later pass.
Random-stream separation
Randomized built-in activation paths use the named scheduler random stream:
RandomActivation;AgentCollection.shuffle_do();- shuffled
StagedActivation.
They obtain this generator through model.rng_stream("scheduler").
The default model.rng stream remains available for ordinary model and agent
behavior. Consuming that default stream does not consume the scheduler stream,
so unrelated behavior draws do not change later activation order.
Named streams are cached by name and derived independently of stream creation
order. Custom model components can request their own stream with
model.rng_stream("component-name").
Scheduler metadata
Schedulers expose to_metadata() for JSON-serializable audit metadata. The base
scheduler metadata uses schema version scheduler-metadata-v1 and includes:
schema_version;scheduler_type;module;attached.
StagedActivation.to_metadata() also records:
stages;shuffle.
This metadata is intended for inspection and snapshot audit trails. It is not a scheduler restore contract.
Activation Eligibility
All built-in schedulers use callback-time activation eligibility validation.
A candidate agent receives a callback only when:
- the agent is still present in the model collection;
- the collection still stores the same object under that identifier;
- the candidate remains alive.
Built-in schedulers create a candidate snapshot when the scheduler call begins. As a result:
- agents added during the call are deferred until the next scheduler call;
- agents removed before their turn are skipped;
- agents marked not alive before their turn are skipped;
- replacing an agent with a new object using the same identifier does not activate either object from the stale snapshot position.
SimultaneousActivation first validates the complete initially eligible
candidate snapshot for callable step() and advance() methods. This
preflight validation finishes before any callback is invoked. It then applies
the eligibility check independently before every decision and commit callback.
StagedActivation applies the eligibility check before every stage callback.
Custom schedulers should follow the same contract unless they explicitly declare and document a different experimental execution profile.