Step context#

Every SMC step receives a StepContext alongside the particle state and random-number generator. The context describes the current iteration and carries information that does not belong to an individual particle. The same interface may also be passed to target functions and is passed to callbacks.

You normally do not construct a context yourself. The SequentialMonteCarlo runner creates one at the start of each iteration from the arguments passed to run() or run_functional(). Steps may return updated copies, and callbacks may return an updated copy or None.

Fields#

t

The zero-based index of the current SMC iteration. iteration is a read-only alias for the same value.

observation

The observation selected for the current iteration, or None when no observation is available. If num_steps is specified, a run can continue after its observations are exhausted; those additional iterations receive None. See Using observations for complete iteration and exhaustion semantics.

next_observation_index

The index at which the runner will look for the next observation. When the current iteration consumed an observation at index i, this value is already i + 1. It is primarily useful for checkpointing and resuming a run; use observation to access the current value.

shared

User-supplied, run-wide configuration from shared_context. Treat this mapping as read-only. It is suitable for static data such as bounds, constants, or model configuration.

metadata

Values that steps and callbacks communicate to later steps, callbacks, and iterations. Use with_metadata() to return a context containing additions or replacements.

stop_requested and stop_immediately

Control flags set by the stop-request methods described below. Most code should use those methods rather than setting the fields directly.

Shared data and metadata#

The distinction between shared and metadata is whether a value changes during a run. Supply static dependencies once through shared_context:

result = smc.run(
    initial_state,
    rng=rng,
    shared_context={"bounds": (-5.0, 5.0)},
    metadata={"accepted": 0},
)

A custom step can read both mappings and publish updated metadata:

from sequince.steps.base import SMCStep


class CountCalls(SMCStep):
    def apply(self, state, context, rng):
        bounds = context.shared["bounds"]
        calls = context.metadata.get("calls", 0) + 1
        context = context.with_metadata(calls=calls)
        return state, context, {"bounds_used": bounds}

StepContext is a frozen dataclass. Its helper methods return replacements instead of changing the context object in place. with_metadata performs a shallow copy and merge: keys supplied to the method replace existing keys, and all other keys are retained. Values inside either mapping may still be mutable, so avoid modifying them in place if iteration history or functional execution must remain reproducible.

The runner carries the final metadata mapping into the next iteration. Within an iteration, each step receives the context returned by the preceding step. After the steps finish, callbacks run in order; a callback that returns a context passes it to the next callback, while returning None leaves the context unchanged. The final context is available as result.context.

Stopping a run#

request_stop() requests a deferred stop. The remaining steps, automatic weight normalization, and callbacks for the current iteration still run. The runner then finishes the run.

request_stop_immediately() skips the remaining steps in the current iteration. Automatic normalization and callbacks still run before the runner finishes. Because callbacks execute after the step pipeline, either stop method called from a callback stops after that iteration; an immediate request does not skip later callbacks.

Functional execution is intentionally different: a call to functional_iteration() always completes the fixed step pipeline so that it remains suitable for compilation. In a functional run, both kinds of stop request are handled by the host loop only after the complete iteration.

clear_stop_request() clears both stop flags. This is useful in control-flow steps that conditionally override a stop requested by an inner step.

class StopAtIteration(SMCStep):
    def __init__(self, final_iteration):
        self.final_iteration = final_iteration

    def apply(self, state, context, rng):
        if context.t >= self.final_iteration:
            context = context.request_stop()
        return state, context, None

Context lifecycle#

For each iteration, the runner performs the following operations:

  1. Selects the current observation and creates a fresh context with the current t, observation position, shared data, and metadata.

  2. Passes the state and context through each configured step in order. Eager execution can end this pipeline early for an immediate stop request.

  3. Normalizes particle weights when auto_normalize=True.

  4. Records the state and step outputs, then invokes callbacks in order.

  5. Writes any due checkpoints and either stops or advances to the next iteration with the returned metadata.

When resuming from a checkpoint, the iteration number, observation position, and metadata are restored. shared_context is supplied again when creating the resumed runtime because it is deliberately not stored in the checkpoint.

API reference#

See StepContext in the generated API reference.