zymera.comms
comms
Comms — potential topology + delivery channels.
Two layers, deliberately separated (see docs/specs/2026-06-11-zymera-design.md):
- :class:
Topology— potential adjacency: who COULD talk this step. Reward terms and connectivity metrics read this unless they explicitly opt into delivered edges. - Channels (:class:
GossipChannel, :class:NullChannel) — realized delivery: which edges actually carried payload (post-dropout). The delivered adjacency is whatWorld.comm_graphrecords.
v1 scope (committed): payloads are OR-reducible (N, H, W) bool grids
only. A payload type zoo is the fork-drift failure mode this design exists
to prevent — extend the payload, don't fork the channel.
GossipChannel(delay=1, dropout=0.0) reproduces zymera v0's gossip
bit-for-bit (examples/comm_coverage.py step): delivery is each
neighbour's PREVIOUS cumulative shared map over NEW-position adjacency —
the diagonal self-loop keeps shared monotone, and the 1-step delay is
what makes information flood multi-hop over time.
Key protocol: deliver consumes its key ONLY when dropout > 0
(trace-time gate), so the no-dropout path is deterministic given state —
exactly like v0, which had no channel randomness at all.
Topology
Bases: Protocol
Who COULD talk this step. adjacency(world) -> (N, N) bool,
symmetric, diagonal True.
DiskTopology
dataclass
DiskTopology(radius, metric='chebyshev')
Agents within radius of each other (in metric) are adjacent.
Matches v0 _adjacency: Chebyshev distance, symmetric, diagonal True
(an agent always "hears" itself — that self-loop is what keeps the
gossip belief monotone).
ChannelState
Gossip channel state. Lives at World.channel.
GossipChannel
dataclass
GossipChannel(topology, delay=1, dropout=0.0, bandwidth=None)
Delayed, lossy flooding of bool grids over a potential topology.
Per deliver: every agent broadcasts its delay-steps-old shared
map to current neighbours; each edge (off-diagonal) survives an
independent symmetric Bernoulli(1 - dropout) draw; the diagonal always
delivers. delay=1, dropout=0.0 ≡ v0 gossip exactly.
bandwidth (top-k most-recent cells) needs int32 step-stamp maps the
payload doesn't carry yet — reserved, must be None for now.
init
init(world, outbox0)
Reset-time state: belief = own outbox; ring buffer pre-filled
with it (so the first delay payloads are the reset snapshot).
Source code in zymera/comms.py
120 121 122 123 124 125 126 127 | |
deliver
deliver(world, outbox, st, key)
One gossip tick.
Returns (incoming, st', delivered_adj):
incoming— (N, H, W) bool, OR of delivering neighbours'delay-old shared maps (self included via the diagonal).st'—shared' = outbox | incoming; buffer rolled.delivered_adj— (N, N) bool realized edges (→comm_graph).
Source code in zymera/comms.py
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | |
NullChannel
dataclass
NullChannel()
No comms: every agent hears only itself. For non-comm missions.