babylon.engine.invariants

Invariant protocol and concrete invariants for the simulation engine.

Spec 040, Discipline 1: Invariants as First-Class Objects. Spec 054 extends this with two new bound invariants (ProbabilityInRange, SimplexPreserved) sitting alongside NonNegativeWealth and HeatNonNegativity.

Every invariant in the constitution becomes an object implementing the Invariant protocol. Systems declare which invariants they preserve, and the test harness checks them automatically via Hypothesis.

Usage:

from babylon.engine.invariants import NonNegativeWealth, Invariant

class VolumeOneProduction:
    invariants: ClassVar[list[Invariant]] = [NonNegativeWealth()]

    def step(self, state: WorldState) -> Result[WorldState, TransitionError]:
        ...

Classes

EdgeModeTrajectoryLegal([valid_arcs])

Every edge-mode transition along a tick boundary is in the legal arc set.

HeatNonNegativity()

Territory heat fields must be >= 0 everywhere.

Invariant(*args, **kwargs)

Protocol for simulation invariants.

InvariantResult(passed[, msg])

Outcome of an invariant check.

NoCommunityFanOut()

No EdgeType.MEMBERSHIP edge has a community-node source.

NonNegativeWealth()

No entity has negative wealth after a system step.

ProbabilityInRange([field_pairs, tolerance])

Every Probability-typed field on every entity stays in [0, 1].

SimplexPreserved([tolerance])

Every TernaryConsciousness on every entity stays on the (r,l,f) simplex.

class babylon.engine.invariants.InvariantResult(passed, msg='')[source]

Bases: object

Outcome of an invariant check.

Use factory methods InvariantResult.ok() and InvariantResult.violated(msg) rather than constructing directly.

Parameters:
passed

True if the invariant holds, False if violated.

msg

Descriptive message (empty on success, diagnosis on failure).

passed: bool
msg: str = ''
property ok: bool

Whether the invariant check passed.

classmethod success()[source]

Create a passing result.

Return type:

InvariantResult

Returns:

InvariantResult with passed=True.

classmethod violated(msg)[source]

Create a failing result with a diagnosis message.

Parameters:

msg (str) – Description of the invariant violation.

Return type:

InvariantResult

Returns:

InvariantResult with passed=False.

__init__(passed, msg='')
Parameters:
Return type:

None

class babylon.engine.invariants.Invariant(*args, **kwargs)[source]

Bases: Protocol

Protocol for simulation invariants.

Every constitution invariant becomes an object implementing this protocol. Systems declare which invariants they preserve via a class-level list. The test harness runs declared invariants on (pre, post) state pairs.

property name: str

Unique identifier for this invariant.

check(pre, post)[source]

Verify the invariant holds between pre and post states.

Parameters:
  • pre (WorldState) – WorldState before system step.

  • post (WorldState) – WorldState after system step.

Return type:

InvariantResult

Returns:

InvariantResult indicating pass or violation.

__init__(*args, **kwargs)
class babylon.engine.invariants.NonNegativeWealth[source]

Bases: object

No entity has negative wealth after a system step.

This invariant checks all social_class entities in the post-state and fails if any have wealth < 0.

property name: str

Invariant identifier.

check(_pre, post)[source]

Check that no entity wealth is negative.

Parameters:
  • _pre (WorldState) – WorldState before step (unused for this invariant).

  • post (WorldState) – WorldState after step.

Return type:

InvariantResult

Returns:

InvariantResult — violated if any entity has wealth < 0.

class babylon.engine.invariants.HeatNonNegativity[source]

Bases: object

Territory heat fields must be >= 0 everywhere.

Heat represents state repressive attention. Negative heat is physically meaningless.

property name: str

Invariant identifier.

check(_pre, post)[source]

Check that no territory has negative heat.

Parameters:
  • _pre (WorldState) – WorldState before step (unused for this invariant).

  • post (WorldState) – WorldState after step.

Return type:

InvariantResult

Returns:

InvariantResult — violated if any territory has heat < 0.

class babylon.engine.invariants.ProbabilityInRange(field_pairs=<factory>, tolerance=0.0)[source]

Bases: object

Every Probability-typed field on every entity stays in [0, 1].

Spec 054 US1 (INV-006). Walks the post-state collections enumerated by _iter_worldstate_collections and asserts 0.0 <= value <= 1.0 on every (ModelClass, field_name) pair listed in field_pairs.

Parameters:
  • field_pairs (Sequence[tuple[type[BaseModel], str]]) – Sequence of (ModelClass, field_name) tuples to check. Defaults to the auto-discovered set from tests.property.harness.probability_discovery.discover_probability_fields() but is parameterized so production code does not import test modules. The test harness instantiates this class with the discovered pairs.

  • tolerance (float) – Absolute slack on the bound. Defaults to 0.0 (exact comparison) per FR-008. The Probability constrained type’s contract is the closed interval [0, 1]; values at the boundary are legal.

field_pairs: Sequence[tuple[type[BaseModel], str]]
tolerance: float = 0.0
property name: str

Invariant identifier.

check(_pre, post)[source]

Check that no Probability-typed field escapes [0 - tol, 1 + tol].

Parameters:
  • _pre (WorldState) – WorldState before step (unused for this invariant).

  • post (WorldState) – WorldState after step.

Return type:

InvariantResult

Returns:

InvariantResult — violated on first out-of-range field encountered.

__init__(field_pairs=<factory>, tolerance=0.0)
Parameters:
Return type:

None

class babylon.engine.invariants.SimplexPreserved(tolerance=0.0001)[source]

Bases: object

Every TernaryConsciousness on every entity stays on the (r,l,f) simplex.

Spec 054 US3 (INV-008). Walks every entity in the post-state and, for each one carrying a consciousness attribute that is a TernaryConsciousness instance, asserts:

abs(c.r + c.l + c.f - 1.0) <= tolerance -tolerance <= c.r <= 1.0 + tolerance (same for c.l, c.f)

Parameters:

tolerance (float) – Slack on both the simplex sum and per-component bounds. Defaults to 1e-4 per spec acceptance scenario US3.1.

tolerance: float = 0.0001
property name: str

Invariant identifier.

check(_pre, post)[source]

Check that every ternary consciousness still lies on the simplex.

Parameters:
  • _pre (WorldState) – WorldState before step (unused for this invariant).

  • post (WorldState) – WorldState after step.

Return type:

InvariantResult

Returns:

InvariantResult — violated on first off-simplex consciousness.

__init__(tolerance=0.0001)
Parameters:

tolerance (float)

Return type:

None

class babylon.engine.invariants.EdgeModeTrajectoryLegal(valid_arcs=<factory>)[source]

Bases: object

Every edge-mode transition along a tick boundary is in the legal arc set.

Spec 055 US1 (INV-009). For every relationship in the post-state that carries an edge_mode attribute, the pair (pre_mode, post_mode) is either in valid_arcs (a real transition) OR equal (trivial no-transition). Trivial pairs are not counted as transitions; the explicit (ANTAGONISTIC, ANTAGONISTIC) persistence arc is in valid_arcs already.

Parameters:

valid_arcs (frozenset[tuple[Any, Any]]) – Frozen set of legal (EdgeMode, EdgeMode) arcs. Defaults to _VALID_TRANSITIONS from babylon.engine.systems.edge_transition — single source of truth. Tests that want to constrain the arc set can pass an explicit subset.

valid_arcs: frozenset[tuple[Any, Any]]
property name: str

Invariant identifier.

check(pre, post)[source]

Check that every (pre_mode, post_mode) pair is legal.

Parameters:
  • pre (WorldState) – WorldState before step (provides previous edge modes).

  • post (WorldState) – WorldState after step.

Return type:

InvariantResult

Returns:

InvariantResult — violated on first illegal arc encountered.

__init__(valid_arcs=<factory>)
Parameters:

valid_arcs (frozenset[tuple[Any, Any]])

Return type:

None

class babylon.engine.invariants.NoCommunityFanOut[source]

Bases: object

No EdgeType.MEMBERSHIP edge has a community-node source.

Spec 055 US2 (INV-010). Constitutional commitment II.7 (Edges vs Hyperedges) plus Anti-Pattern VIII.9 (Community as Pairwise Edge): community membership lives in the XGI hyperedge layer; the morphism graph MUST NOT carry community-to-member fan-outs.

Walks every EdgeType.MEMBERSHIP edge in the post-graph (constructed via post.to_graph()) and asserts that the source node’s _node_type != "community".

property name: str

Invariant identifier.

check_graph(graph)[source]

Walk an explicit graph for community-fan-out MEMBERSHIP edges.

Preferred entry point. Tests that inject _node_type='community' markers via _inject_community_markers MUST pass the live marked graph here — calling check(pre, post) instead would round-trip through post.to_graph() and strip the markers because community is not yet a first-class WorldState field.

Parameters:

graph (Any) – NetworkX directed graph carrying the live _node_type markers (typically the post-tick graph from a fixture or test setup).

Return type:

InvariantResult

Returns:

InvariantResult — violated on first community-fan-out edge.

check(_pre, post)[source]

Default Invariant Protocol entry — walks post.to_graph().

Best-effort fallback: WorldState.to_graph() does not preserve test-injected _node_type='community' markers, so this entry point only catches violations that survive the round-trip. For full coverage, callers should use check_graph(live_graph).

Return type:

InvariantResult

Parameters:
__init__()
Return type:

None