Structured tokens

Structured/agentic tokens are first-class entities: a token (a "project") carries typed attributes (its phase, npv, cost-to-date), custom behavior, and its own history, and can be instantiated, selected by predicate, and advanced through lifecycle phases. A structured place — one whose tokens are distinguishable, a colour set in Coloured-Petri-net terms — is declared with @structured_token and registered against a network before tokens are added to the initial marking.

ReactiveDynamics.register_token_kind!Function
register_token_kind!(net, type)

Register the structured-token kind type (a Symbol) as a place of the static network net, adding a :S row named type if one does not already exist and flagging it placeStructured = true. This is what tells the engine that occupants of that place are first-class token agents (counted from the "structured" container), not a plain scalar count. Returns nothing. The @register sugar and @structured_token (which defines the host struct) are the usual companions; a kind must be registered before instances can be added to a ReactionNetworkProblem.

source
ReactiveDynamics.add_structured_token!Function
add_structured_token!(problem, agent) -> agent

Add a structured-token instance agent to the live problem: entangle it under the "structured" AA container and assign it a per-place monotonic creation index (ADR 0006 §E), so the deterministic (place, creation_index) selection order every token operation relies on is well-defined. Returns the added agent. This is the imperative counterpart to declaring the token in the PopulationEntry initial marking; both funnel through here so creation indices are contiguous and reproducible under (model, seed).

source
ReactiveDynamics.AbstractStructuredTokenType
AbstractStructuredToken <: AbstractAlgebraicAgent

Abstract supertype of every structured (agentic) token kind (ADR 0006/0008). A structured token is a first-class entity — it carries its own attributes, a stable identity, and a history, and moves through a lifecycle (created → bound to a transition as a consumed resource → advanced/retired) rather than being an anonymous unit of a plain-place count. Each concrete kind is an AlgebraicAgents.@aagent, so a token is a genuine node in the AA hierarchy under the problem's "structured" container. Define a kind with @structured_token, register it with register_token_kind!, and add instances with add_structured_token! or the declarative PopulationEntry initial marking. Kinds get their behavior for free (no evolution rule by default — the orchestrator advances them); override log_token_fields to record fields into the per-token trajectory log.

source
ReactiveDynamics.BaseStructuredTokenType
BaseStructuredToken <: AbstractStructuredToken

The base structured-token layout every @structured_token kind inherits (via @aagent FreeAgent). It supplies the protocol fields the engine relies on: place (the :S row the token currently occupies, or :removed once soft-retired), bound_transition (the Transition instance the token is currently committed to as a consumed resource, or nothing when free), and past_bonds (the token's audit history — the (place, t, transition) triples of every transition it has been used in). A concrete kind adds its own modeling attributes (e.g. phase, npv) on top of these; the base layout is what makes a token bindable, selectable, and auditable without per-kind boilerplate.

source
ReactiveDynamics.PopulationEntryType
PopulationEntry(place, kind; count = 1, attributes = Dict{Symbol, Any}())

A declarative initial-marking entry (ADR 0007 §B): count instances of the structured kind (a registry key resolved to a host constructor), each token placed on place and built from attributes — a Dict of field => Expr-or-literal evaluated once at t=0 through the state's seeded rng (§4 D5). It is the structured-token analogue of placeInitVal for plain place: put PopulationEntrys in the population= vector of ReactionNetworkProblem to declare the t=0 marking declaratively, so it re-instantiates cleanly on reinit! / across ensemble members. The alternative form — an already-constructed host token agent — is placed in the population vector directly (no PopulationEntry, and the registry is not consulted).

source
ReactiveDynamics.log_token_fieldsFunction
log_token_fields(token) -> NamedTuple

Per-token trajectory-log hook (ADR 0013 §A1 / CONTRACT §14.1). A host token KIND overrides this to declare WHICH fields the orchestrator records into state.token_trajectory each tick, e.g.

ReactiveDynamics.log_token_fields(t::ProjectToken) = (; t.phase, t.npv_peak, t.pos_remaining)

The default logs NOTHING — the trajectory log is bounded by per-kind opt-in (Invariant 2), so it does not grow for kinds that don't opt in (load-bearing because retired tokens are KEPT under the Milestone-1 soft-:removed decision). The hook MUST be 𝓕ₜ-measurable: a pure field read, no RNG, no future (Invariant 1) — it returns the snapshot rather than holding it, keeping storage central.

source

Runtime token instances

A Transition is a live in-flight instance of a transition recipe; the tokens it occupies are its bound structured agents. ArcSpec is the promoted transition↔place incidence row (an arc) — the typed, foreign-key-exact form of a reaction line that structured-token binding and place merges repoint against.

ReactiveDynamics.TransitionType

One in-flight transition instance — an AlgebraicAgents @aagent, so a live transition is itself a node in the AA hierarchy. Spawned when a transition fires and held in the state's ongoing_transitions until its cycle time elapses, whereupon it completes (with its terminal probability-of-success) and emits its RHS products. i is the originating :T row; trans is the per-instance attribute dict (cycle time, priority, …); bound_structured_agents/nonblock_structured_agents/structured_to_agents hold the tokens this instance occupies; t is its spawn time, q its allocated quantity, and state its progress through the cycle.

source
ReactiveDynamics.ArcSpecType
ArcSpec

One row of the promoted transition↔place incidence relation (ADR 0003 Phase 2): a transition trans (FK → a :T row) consumes/produces place (FK → an :S row) with stoich stoichiometry on side (:lhs or :rhs), under a modality set. Promoting the relation from the re-parsed :trans Expr into a typed record with INTEGER foreign keys makes the model's defining relation FK-checkable and — the headline win — lets equalize! merge places by structurally REPOINTING the place FK instead of doing string surgery on names. A legitimately dynamic arc (a @choose/@move/@structured/@advance/@select term, or expression-valued stoichiometry) carries the sentinel place = 0 and stashes its original term in expr; a static arc has place ≥ 1 and expr === nothing. The table is DERIVED from the authoritative :trans column (see populate_arcs!) and is additive/behavior-preserving — the runtime still parses :trans per tick. Read it via arcs.

source
ReactiveDynamics.arcsFunction
arcs(net) -> Vector{ArcSpec}

The promoted transition↔place incidence table of net (ADR 0003 Phase 2) — the FK-exact ArcSpec rows. populate_arcs! fills it from the :trans column and equalize! keeps it FK-exact across a place merge; it is empty for a freshly-constructed or not-yet-promoted model, so a caller wanting the table on such a model calls populate_arcs!(net) first (as equalize! does).

source
ReactiveDynamics.placenameFunction
placename(net, i) -> Symbol

The :S place name at row index i — the inverse of find_index, used to resolve/check an ArcSpec FK target back to a name.

source