Skip to content

Add an 'array' field to the channel-map ch axis - #34

Merged
cboulay merged 1 commit into
devfrom
feat/channel-array-field
Jul 28, 2026
Merged

Add an 'array' field to the channel-map ch axis#34
cboulay merged 1 commit into
devfrom
feat/channel-array-field

Conversation

@cboulay

@cboulay cboulay commented Jul 28, 2026

Copy link
Copy Markdown
Member

Summary

Adds an array field to the structured ch axis, so channels can be grouped by physical electrode array rather than by 32-channel connector bank. This is the grouping wanted for per-array rereferencing (LRR/CAR): a bank is a wiring artifact, the array is the implant.

Two channels share an array iff they share a connector — the label prefix before the first -, which the CMP assigns per 64-channel connector (elec1 in elec1-m1-63) — on the same headstage.

("elec1-m1-63", hs=1) -> "hs1-elec1"
("elec1-m1-63", hs=2) -> "hs2-elec1"
("elec1-m1-63", hs=0) -> "elec1"

Why the headstage prefix

The connector label repeats across headstages. Without the prefix, the same .cmp loaded twice (a 256-ch device built from two 128-ch maps) or a bilateral implant would merge two physically distinct arrays into one cluster.

Why the connector, not the whole label stem

The region token (m1, aip) annotates where an array was implanted; a connector whose channels carry inconsistent region labels is still one array. Grouping on the connector also matches the convention already used offline in intent-pipelines (_array_ids_from_metadata), so weights fitted offline cluster the same way when applied live — verified directly on a real 256-ch Hub1 layout (two CMPs, two headstages): both produce an identical partition of 4 arrays x 64 channels.

Fallback

Labels with no connector structure (chan1, or an auto-grid channel with no label) fall back to the connector bank, e.g. ("chan1", hs=1, bank="A") -> "hs1-bankA". Without this, grouping by array would collapse every such channel into a single cluster spanning the whole device. This repo's own test CMP (128ChannelDefaultMapping.cmp) uses chan<N> labels and exercises the fallback.

Populated in three places

  • CMP overlays — from entry.label + entry.headstage (+ bank for the fallback)
  • Source geometry passthrough — array joins the copied fields when present
  • Auto-grid — via the bank fallback, keeping 32-channel clusters

CereLinkSignalSource._build_ch_info also fills it, so a device that read its map from chaninfo carries the field too.

Tests

Five new tests in tests/test_channel_map.py covering identity derivation, CMP population, array-vs-bank group sizes, the two-headstage disambiguation, and the auto-grid fallback. Full suite: 110 passed, 1 skipped.

Groups channels by physical electrode array rather than by 32-channel
connector bank, which is the grouping wanted for per-array rereferencing
(LRR/CAR): a bank is a wiring artifact, the array is the implant.

Two channels share an array iff they share a connector -- the label prefix
before the first '-', which the CMP assigns per 64-channel connector
('elec1' in 'elec1-m1-63') -- on the same headstage. The headstage prefix
keeps two identically labelled arrays distinct (the same .cmp loaded twice,
or a bilateral implant). This matches intent-pipelines' offline convention
(_array_ids_from_metadata), so weights fitted offline cluster the same way
when applied live.

Labels with no connector structure (chan1, or an auto-grid channel with no
label) fall back to the connector bank, so grouping by 'array' degrades to
bank-level grouping instead of collapsing the device into one cluster.
@cboulay
cboulay merged commit 5b1d332 into dev Jul 28, 2026
16 checks passed
@cboulay
cboulay deleted the feat/channel-array-field branch July 28, 2026 18:25
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