Declare chunk_dim, prime channel fingerprints, adopt the axis-aware state hash - #8
Merged
Merged
Conversation
…l identity Every producer here built its channel axis once per stream and reused the object, but never said which dimension the stream grows along and never computed the axis's fingerprint. Both now happen at construction: * `chunk_dim="time"` on all five templates. Without it a consumer has to fall back to guessing `"time"`, which is right for these raw streams and wrong the moment anything windows them into `(win, time, ch)`. * `ch_axis.fingerprint` touched once. It caches on the instance and pickles with it, so one checksum covers the whole stream -- including across a process boundary, where unpickling hands out a fresh axis per message and a cold one is re-checksummed by the first consumer in every receiving process, forever. Requires ezmsg 3.10.0b2 for those two fields and baseproc 1.12.0 for the axis-aware default state hash, which replaces the old reset-once-then-never constant. That changes what the stateful stages here treat as a new stream, so each one now says which it wants: * BaselineDrift and DynamicColoredNoise drop their hand-written hashes. Both hashed `(shape[1], time_gain)`, which the default covers -- plus the channel *fingerprint*, which they were missing: their delay lines and drift anchors are one-per-channel, so a relabel at a fixed count was handing each new channel its predecessor's history. * LineNoise keeps an explicit narrowing. Its state is all (1, 1) and broadcasts, so folding in channel identity would only restart the phase accumulator for a sinusoid that did not change. It now reads the declared chunk dim rather than the literal name "time", which on a stream that grows along anything else was silently yielding a sample period of zero. * CosineEncoder gains a constant-except-for-backend hash. Its tuning parameters come from settings alone, so under the default an upstream relabel would redraw every preferred direction and silently change the simulated population mid-run. tests/unit/test_state_reset_semantics.py pins all of the above; two assertions in test_cosine_encoder.py stop asserting the old constant-0 hash.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Completes the
chunk_dim/fingerprintsweep across the ezmsg source packages. simbiophys was the one I had marked clean on the wrong evidence — its axes are built once per stream and reused, but that says nothing about whether the stream is described.Before this, the package had zero
chunk_dimdeclarations, zero primed fingerprints, and was pinned toezmsg-baseproc>=1.6.1, resolving to 1.10.2 whose default_hash_messagewas a constant0(reset once, then never).Producers describe their stream
All five templates —
noise.py,oscillator.py(spiral and sin),dnss/lfp.py,dnss/spike.py— pluscosine_encoder's two model-init paths now:chunk_dim="time". Without it a consumer falls back to guessing"time", which is right for these raw streams and wrong the moment anything windows them into(win, time, ch).ch_axis.fingerprintat construction. It caches on the instance and pickles with it, so one checksum covers the whole stream — including across a process boundary, where unpickling hands out a fresh axis per message and a cold one is re-checksummed by the first consumer in every receiving process, on every message, forever.Dependencies
ezmsg>=3.9.0→>=3.10.0b2, forAxisArray.chunk_dimandCoordinateAxis.fingerprint.ezmsg-baseproc>=1.6.1→>=1.12.0, for the axis-aware default state hash and the hash witness.The baseproc bump changes reset semantics, so each stage now says what it wants
BaselineDrift(shape[1], time_gain)DynamicColoredNoise(shape[1], time_gain)LineNoise"time"axis gainCosineEncoder0BaselineDrift / DynamicColoredNoise. Their hand-written hashes covered exactly what the default covers, minus the one thing that mattered: the channel fingerprint. Their delay lines, drift anchors and per-channel coefficients are one-per-channel, so a relabel at a fixed channel count was handing each new channel its predecessor's history.
LineNoise. Genuinely common-mode — every state array is
(1, 1)and broadcasts over however many channels arrive — so the default would restart the phase accumulator for a sinusoid that did not change. The narrowing stays, but it now resolves the declared chunk dim instead of the literal name"time"; on a stream growing along anything else the old lookup silently yielded a sample period of zero.CosineEncoder. Its tuning parameters come from
settingsalone (a file, or a seeded draw) and never from the message. Under the default hash, the first upstream relabel would redraw every channel's preferred direction and silently change the simulated population mid-run. The only thing_reset_statereads from the message is the array backend, so that is all the hash folds in.That last one surfaced as two real failures the moment the dependency was bumped:
test_directional_tuningandtest_speed_modulationsettransformer._hash = 0to mean "treat this hand-set state as initialized", which only worked because the old default was0. They now adopttransformer._hash_message(msg)and say so.Tests
New
tests/unit/test_state_reset_semantics.py, 19 tests: every producer declares its chunk dim and hands over a primed axis; the per-channel stages reset on a relabel at fixed count but not on chunk-size jitter; LineNoise does the opposite and reads a non-timechunk dim; CosineEncoder keeps its population and reuses its output axis object.Mutation-tested — 10 mutations, all killed. The first version of the CosineEncoder redraw test was vacuous (
seed=42makes a redraw reproduce the same preferred directions); it usesseed=Noneso a reset is actually visible.161 passed.
🤖 Generated with Claude Code