Skip to content

Add util.rereference: deterministic cluster-aware rereference matrices - #186

Merged
cboulay merged 1 commit into
devfrom
feat/rereference-matrix-util
Jul 23, 2026
Merged

Add util.rereference: deterministic cluster-aware rereference matrices#186
cboulay merged 1 commit into
devfrom
feat/rereference-matrix-util

Conversation

@cboulay

@cboulay cboulay commented Jul 23, 2026

Copy link
Copy Markdown
Member

Summary

  • New ezmsg.sigproc.util.rereference module:
    • RereferenceKind str-enum (identity | car) — round-trips through config as plain strings.
    • car_matrix(n_channels, *, clusters=None, include_current=True, min_reref_size=1, dtype=float64) — per-cluster common-average reference as a symmetric weight matrix, supporting standard and leave-one-out CAR; clusters below min_reref_size stay identity (named distinctly from AffineTransformSettings.min_cluster_size, the block-diagonal matmul merge threshold).
    • rereference_matrix(kind, ...) dispatcher.
    • Matrices are host-side numpy: AffineTransformTransformer already converts weights to the message's array namespace/dtype/device on first use, so this stays Array-API/GPU friendly without any device-side construction code.
  • AffineTransformSettings.weights now also accepts a RereferenceKind or its string value (e.g. weights: car in config), built over channel_clusters at reset — the same cluster spec drives both matrix construction and the block-diagonal matmul optimization. weights=None remains the zero-cost passthrough.
    • Docstrings note that users who simply want streaming CAR are better served by CommonRereference in the same module (per-sample mean subtraction, median support); kind-based weights exist for discoverability of deterministic transforms and for workflows that start from such a matrix and later replace it via set_weights().
  • Extracted shared validate_channel_clusters() into util.channels, replacing duplicated cluster bounds checks (also to be consumed by ezmsg-learn's SSR module).

Motivation

ezmsg-learn PR ezmsg-org/ezmsg-learn#19 adds a CAR cold-start default to LRR; this PR hoists the deterministic-rereference matrix construction into ezmsg-sigproc so LRR (and any affine-transform user) can share one implementation. Once released, the learn PR reduces to a single rereference_matrix(...) call.

Notes

  • Minor edge case: a weights file literally named car or identity (bare relative path, no extension) is now interpreted as a kind rather than a path — same precedent as the existing "passthrough" special value.
  • Error message for out-of-range clusters is unified as "channel_clusters contains out-of-range indices…" (two test regexes updated accordingly).

Testing

  • New tests/unit/test_rereference.py: 19 tests covering both CAR flavors, per-cluster independence, small/singleton-cluster guards, bipolar pair case, symmetry, bounds validation, string dispatch, and kind-based weights through AffineTransformTransformer (incl. rebuild on channel-count change).
  • New validator tests in tests/unit/test_util_channels.py.
  • Full affected suites pass: 79 tests across rereference/affine/channels; ezmsg-learn's unit suite (308 tests) also passes against this branch.

🤖 Generated with Claude Code

New ezmsg.sigproc.util.rereference module with RereferenceKind
(identity|car), car_matrix() (standard and leave-one-out CAR, per-cluster,
with min_reref_size guard), and a rereference_matrix() dispatcher.
Matrices are built as host-side numpy; AffineTransformTransformer already
converts weights to the message's namespace/dtype/device on first use.

AffineTransformSettings.weights now also accepts a RereferenceKind or its
string value (e.g. "car" from config), built over channel_clusters at
reset so the same cluster spec drives both matrix construction and the
block-diagonal matmul optimization. weights=None remains passthrough.
Docstrings point users wanting plain streaming CAR to CommonRereference;
kind-based weights serve discoverability and init-then-set_weights
workflows (e.g. the LRR cold start in ezmsg-learn, which will build on
this).

Also extract shared validate_channel_clusters() into util.channels,
replacing duplicate bounds checks in AffineTransformTransformer and
downstream consumers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cboulay
cboulay merged commit 8609094 into dev Jul 23, 2026
14 checks passed
@cboulay
cboulay deleted the feat/rereference-matrix-util branch July 23, 2026 23:07
cboulay added a commit to ezmsg-org/ezmsg-learn that referenced this pull request Jul 23, 2026
ezmsg-sigproc 2.34.0 (ezmsg-org/ezmsg-sigproc#186) hoists deterministic
cluster-aware rereference matrix construction into
ezmsg.sigproc.util.rereference. Use it here:

- Replace the local RereferenceInit enum with the imported RereferenceKind
  (re-exported from this module; same "identity"/"car" config values).
- Delete _car_effective_matrix: the cold-start branch in _process is now a
  single rereference_matrix(...) call. The matrix is built as host-side
  numpy; AffineTransformTransformer converts it to the message's
  namespace/dtype/device on first use, so the on-device selection-matrix
  scatter is no longer needed.
- Use the shared validate_channel_clusters() from util.channels for
  cluster bounds checking (empty-list policy stays here).
- Bump ezmsg-sigproc floor to 2.34.0 (and test dep ezmsg-simbiophys to
  1.8.0).

Behavior is unchanged for float streams; integer-dtype streams now get
float64 output from the cold-start transform (weights stay float64
instead of being cast to the message's integer dtype).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant