Data Model
The types that make up a loaded experiment, and the columnar store that holds live
entity data while it runs. Everything here is the post-load form from
internal/engine/model/types.go (plus regime.go, planner.go,
distributions.go). The Raw* parse-time mirrors are covered in
Config YAML and Process Flow → loading.
The types fall into six conceptual groups. The first five describe the loaded
Config object graph; the sixth is the runtime store.
The loaded Config object graph
Section titled “The loaded Config object graph”Solid edges are containment (a parent owns a slice of children); the dashed edge is a reference by index. Boxes are coloured by conceptual group.
SimulationConfig
Section titled “SimulationConfig”Config is the root object the loader produces. It holds the machine catalog, the
shared taxonomy dimension catalog, named lookup Catalogs, and two derived members
— the resolved Regime and the ExecutionPlan (both computed at load, neither
declared directly).
| Type | Key fields | Notes |
|---|---|---|
Config | Name, Taxonomy, Catalogs, PolicyTables + PolicyTableIndex, StateMachines + StateMachineIndex, MessageNames, MessageCascadeDepth, Regime, Plan, Timing | Ordered []*StateMachine with a name→index map. PolicyTables follow the same pattern as Catalogs. GetStateMachine(name) is the named lookup. |
PolicyTable | Name, Policies map[string][]ProbabilityDistribution | A named set of probability matrices. Policy keys are taxonomy values; the matrix layout matches the owning state’s events. |
TaxonomyDefinition | Name, Dependency, Distribution, ConditionalDistributions, Weights, Lookups, Options, Values + ValueIndex | One of five sampling strategies. Dependency names another entry, forming a DAG resolved into Config.TaxonomyOrder. |
CategoricalDistribution | map[string]float64 | Implements UnmarshalYAML to coerce mixed int/float and Normalize(). |
Catalog | Name, Entries, AttrNames + AttrIndex, SKUIndex | A named table of attribute rows (e.g. products). Backs catalog fields and field families. |
- Five taxonomy strategies:
Distribution(flat probabilities),ConditionalDistributions(keyed by a dependency value),Lookups(deterministic 1:1),Weights(integer weights keyed by a dependency), orOptions(uniform random). See Config YAML → taxonomy for examples of each. - Value interning: every categorical value is interned into
Values []string+ValueIndex. Taxonomy fields are stored on entities as denseValueIdxintegers (init-1), compared with integer ops, and rendered back to strings only at export. - Catalog-backed fields either sample a
CatalogIdx(func: random) or derive a value from an attribute (func: from_catalog).
Schema
Section titled “Schema”A StateMachine defines the shape of an entity: its fields, its typed references
to other entities, and its states.
| Type | Key fields | Notes |
|---|---|---|
StateMachine | Name, LoadFile, ReferenceSlots + ReferenceIndex, InitState, Fields, Families, States + StateIndex, MessagesAccepted, FieldMapping, FieldIndex, AgeIdx/TurnIdx, IntCount/FloatCount/StringCount | Carries everything the store needs to allocate and address an entity’s columns. |
Field | Name, ValueType, Func, TaxonomyName, CatalogName, InitInt/InitFloat/InitString, IsSystem, IsFamily | The flat unit. ValueType is one of String/Int/Float. |
ReferenceSlot | Name, EntityIdx | EntityIdx is the index into Config.StateMachines of the referenced type (-1 until linkReferences resolves it). |
FieldFamily | Name, CatalogName, BaseIdx, Size, Init | Expands to one field per catalog entry: stock[0], stock[1], … |
FieldSlot | ValueType, SliceIndex | One entry of FieldMapping: maps a flat FieldIdx to its backing column. |
- References, not parent/child: a machine declares typed reference slots (e.g.
Verificationreferencesmerchant). They define connectivity and feed the planner’s hazard graph but do not constrain spawn order. - Field flattening: the YAML categories (
taxonomy,catalog,families,counters,dimensions,measures) flatten into one[]Field. Four system fields are prepended (StateMachine,ID,TurnNumber,Age), then one<ref>_idsystem string field per reference slot.counters→int,dimensions→string,measures→float. - No reflection:
buildFieldMappingprecomputesFieldMappingso the columnar store resolves aFieldIdxto(ValueType, SliceIndex)directly.
Lifecycle
Section titled “Lifecycle”The Markov layer: each State carries the events that may fire from it and a
per-turn probability matrix.
| Type | Key fields | Notes |
|---|---|---|
State | Name, Description, Decision, EventSelectorKind, Events, Probabilities, MessagesAccepted, Policy | Decision is materialized at load ("stochastic" or "first_wins"); EventSelectorKind is the resolved SelectorKind for event dispatch. State-level MessagesAccepted groups take precedence over machine-level ones. Policy (nil when unused) links to a policy table for per-entity probability override. |
ProbabilityDistribution | []float64 | One row of the matrix. Normalize() scales it to sum 1.0 at load. |
Execution
Section titled “Execution”What happens when an event fires: conditions gate it, then actions produce effects.
| Type | Key fields | Notes |
|---|---|---|
Event | Name, Conditions, Actions | All conditions must pass (AND) for the actions to run. |
Condition | Field (FieldIdx), Operator, ValueType, IntValue/FloatValue/StringValue, Bracket, Path | Operators: is, atLeast, atMost (strings support is only). Path is a compiled *PathExpr for indirect field access (self.ref.field); nil for direct fields. |
Action | Type, StateIdx, Field, Operator, Value, EntityIdx, ReferenceIdx, MessageIdx, Payload, Delay, References, Bracket, ApplyAll, Path, ValuePath | One struct covers every action type; unused index fields are -1. Path/ValuePath are compiled *PathExpr for indirect field/reference access. |
BracketExpr | FamilyBaseIdx, FamilySize, Source, SourceIdx | Resolves family[payload.key | self.field | sender.field] to a concrete member index at runtime. |
PathExpr | Hops []PathHop, TerminalFieldIdx, TerminalMachineIdx, TerminalIsRef | A compiled self.ref.ref.field expression: a chain of PathHop dereferences terminating in a field or reference slot on the target machine. Compiled at load by parsePathExpr. |
PathHop | RefSlotIdx, TargetMachineIdx | A single reference-slot dereference. At runtime, GetReference resolves the entity handle at each hop. |
Runtime action types (after loader normalization):
transition— set a new state on self; resets Age to 0.update—set/add/subtracton a self field; optionallyapply: allover a family or a bracket-resolved member.new_entity— spawn another machine, optionally binding the child’s reference slots via aReferencesmap.set_reference— resolve and write a reference slot.emit_message— enqueue aMessageto a reference slot or back to the sender, with an optionaldelay.
Indexed references: actions store integer indices — StateIdx, EntityIdx,
ReferenceIdx, MessageIdx — resolved in linkReferences. update_within_state
is accepted as an alias for update; update_reference is desugared into an
emit_message plus an auto-generated handler (see Scheduler →
messaging).
Messaging & Scheduling
Section titled “Messaging & Scheduling”| Type | Key fields | Notes |
|---|---|---|
Message | NameIdx, Sender, Payload | Immutable; constructed at runtime by emit_message, never parsed from YAML. Payload values are positional. |
MessagesAccepted | Message, MessageIdx, Decision, SelectorKind, Handlers, Probabilities | Groups handlers for a single message type. Decision is always materialized ("stochastic" or "first_wins"). SelectorKind controls dispatch: FirstValidWins or StaylessProbabilistic. Lives on a State (authoritative) or the StateMachine (fallback). Probabilities is an age-indexed matrix with no stay column — each column maps directly to a handler. |
Handler | Name, Conditions, Actions, MessageIdx | A resolved message handler within a MessagesAccepted group. Mirrors Event: named, with conditions gating execution and actions firing when selected. MessageIdx is copied from the parent group. |
Regime | Clock, Work, Update, Order | Four declared scheduling axes. Concurrency is not an axis — the engine derives it. |
ExecutionPlan | SeedTypes, Partitions, Independent | Derived by finalizeSchedule: seed types (no inbound spawn edge), and union-find partitions. |
Typed indices
Section titled “Typed indices”Several named int types prevent accidental transposition between distinct index
spaces. The compiler rejects passing one where another is expected:
| Type | Indexes into |
|---|---|
FieldIdx | a machine’s Fields / FieldMapping |
SlotIdx | a per-type backing slice (ints/floats/strings) |
ValueIdx | TaxonomyDefinition.Values |
ReferenceIdx | a machine’s ReferenceSlots |
MessageIdx | Config.MessageNames |
CatalogIdx | Catalog.Entries |
PolicyTableIdx | Config.PolicyTables |
SelectorKind | enum: FirstValidWins, ProbabilisticWithStay, StaylessProbabilistic |
Runtime store: the archetype store
Section titled “Runtime store: the archetype store”At experiment time the live entities of each machine type are stored together,
column-wise, in an EntityStore (store.go / store_archetype.go). The loaded
Config types above are the read-only schema; the store holds the mutable rows. One
Archetype holds every entity of one machine type as a struct-of-arrays, and code
addresses an individual entity by an EntityHandle{machineIdx, row} — its position
in that storage.
| Type | Role |
|---|---|
EntityStore (interface) | Create/activate entities, Access(handle), ForEach, get/set references and state, export fields. Abstracts the storage layout. |
ArchetypeEntityStore | The only implementation: archetypes []*Archetype + a name→index map. |
Archetype | One per machine type. Parallel IntColumns/FloatColumns/StringColumns, a States column, RefColumns (per slot), a NextWake column, ActiveCount/TotalCount, and an optional snapshot. |
ArchetypeView | A cursor over one row: GetInt/SetInt/AddInt (and float/string), GetReference, GetState, GetNextWake, GetMachine. |
EntityHandle | The {machineIdx, row} coordinate. O(1) access via FieldMapping; never scanned. |