Skip to content

Resolve the operating dimension from chunk_dim across the processors - #28

Merged
cboulay merged 4 commits into
devfrom
cboulay/chunk-dim-axis
Sep 7, 2026
Merged

Resolve the operating dimension from chunk_dim across the processors#28
cboulay merged 4 commits into
devfrom
cboulay/chunk-dim-axis

Conversation

@cboulay

@cboulay cboulay commented Sep 7, 2026

Copy link
Copy Markdown
Member

Requires ezmsg-baseproc 1.13.0 and ezmsg-sigproc 3.8.0 (both released).

Five distinct cases, not one

The axis settings here don't all mean the same thing, so they don't all get the same treatment:

file what its axis means treatment
ssr.py a channel axis resolve_feature_dim — setting stays
flatten.py which axis to preserve default only — setting stays
slda.py the accumulating axis deprecated, removed in 2.0
adaptive_decomp / incremental_decomp "!X" = iterate over X ! spelling deprecated
sample_adapt_regressor.py resample_axis deprecated
  • ssr.pydims[-1] is the chunk dim on a (ch, time) stream, so the old default would have regressed across time. Naming a channel axis is a real choice, so the setting stays.
  • flatten.py — mirrors sigproc's Flatten: it holds no data between messages, so only the default changes.
  • slda.py — caches an output template keyed to the dimension its samples accumulate along.
  • the decompsaxis="!time" meant "iterate over time, decompose the rest", and the non-! branch guessed the iteration axis with a hand-rolled "win" if "win" in dims else "time". Both are exactly what chunk_dim answers. axis=None is the new default and does what "!time" spelled; naming a target axis (axis="ch") is untouched and stays silent. This also settles incremental_decomp's # TODO: This iter_axis is likely incorrect — there's no message to resolve from inside _initialize_processors, so it defers to Window, which resolves chunk_dim itself.
  • sample_adapt_regressor.py — stops hardcoding axis="time" for its Window, and forwards resample_axis under suppress_axis_deprecation so sigproc doesn't warn about a class the user never touched.

sgd.py had a positional assumption in the math

It flattened everything but dims[0] into the feature vector — assuming the streaming axis came first. It now resolves that axis and moves it to the front.

It also declares STREAMING_DIMS = ("win", "time"), because it is fed windows: a producer that declares no chunk_dim is accumulating along win here, and the base ("time",) default would fold the windows into the feature vector. I found this the hard way — my first attempt called resolve_chunk_dim(message) without threading the class's STREAMING_DIMS through, so the override silently did nothing and the feature count changed from 6 to 4.

Fixes five tests that were already red on dev

IncrementalDecomp unbundles its training windows with iter_over_axis("win"), which slices that dimension away while chunk_dim still names it — and an AxisArray rejects a chunk_dim that isn't among its dims. The declaration is now dropped before the slice.

I verified these were pre-existing by running unmodified dev against sigproc 3.7.0 as well as 3.8.0 — 5 failures either way, so neither this PR nor the sigproc upgrade caused them.

The one test change is an assertion that pinned the hardcode this PR removes (win.settings.axis == "time"is None).

Testing

369 passed, 24 skipped — up from 362 passed / 7 failed. Deprecation behaviour checked by hand across all seven cases: axis=None and axis="ch" silent, "!time" / slda axis / resample_axis each warn once naming our class.

Five distinct cases, not one:

* ssr.py names a *channel* axis, so it skips the chunk dim rather than
  following it (resolve_feature_dim). dims[-1] is the chunk dim on a
  (ch, time) stream, which would have regressed across time. The setting
  stays: naming a channel axis is a real choice.
* flatten.py mirrors ezmsg-sigproc's Flatten -- the setting stays, only the
  default changes, because Flatten holds no data between messages.
* slda.py caches an output template keyed to the dimension its samples
  accumulate along; `axis` deprecated, removed in 2.0.
* adaptive_decomp/incremental_decomp: `axis="!time"` meant "iterate over
  time, decompose the rest", and the non-! branch guessed the iteration axis
  with a hand-rolled `"win" if "win" in dims else "time"`. Both are what
  chunk_dim answers. `axis=None` is now the default and does what "!time"
  spelled; the "!" spelling is deprecated. Naming a target axis is untouched.
  This also settles incremental_decomp's `TODO: This iter_axis is likely
  incorrect` -- there is no message to resolve from in _initialize_processors,
  so it defers to Window, which resolves chunk_dim itself.
* sample_adapt_regressor.py stops hardcoding axis="time" for its Window, and
  deprecates `resample_axis`, forwarding it under suppress_axis_deprecation so
  ezmsg-sigproc does not warn about a class the user never touched.

sgd.py flattened everything but `dims[0]` into the feature vector, which
assumed the streaming axis came first. It now resolves and moves that axis to
the front. It declares STREAMING_DIMS = ("win", "time") because it is fed
windows: a producer that declares no chunk_dim is accumulating along `win`
here, and the base default would have folded the windows into the features.

Fixes five tests that were already failing on dev. IncrementalDecomp unbundles
its training windows with iter_over_axis("win"), which slices the dimension
away while chunk_dim still named it -- and an AxisArray rejects a chunk_dim
that is not among its dims. The declaration is dropped before the slice.
ezmsg-sigproc 3.8.0 shipped its own copy of the deprecation machinery before
it moved to ezmsg-baseproc, so its warnings check a ContextVar that
baseproc's suppress_axis_deprecation() does not set. Import the context
manager from ezmsg.sigproc, which works against 3.8.0 and against later
versions that re-export baseproc's.
ezmsg-sigproc 3.8.1 shares baseproc's deprecation mechanism, so there is a
single ContextVar again and the import that works is the obvious one. Pinned
to >=3.8.1 accordingly.
Slicing "win" away leaves each sub-message no longer a chunk along it.
Clearing the declaration was enough to stop AxisArray rejecting them, but it
left the decomp resolving the iteration axis from STREAMING_DIMS when the
message can simply say it: successive windows advance along the within-window
axis, which is the one the offset fix-up right below re-anchors.

Newer ezmsg clears chunk_dim itself when iter_over_axis consumes the
dimension, so the explicit clear before the loop is only there to keep this
working on versions that do not.
@cboulay
cboulay merged commit b3cc7e1 into dev Sep 7, 2026
9 checks passed
@cboulay
cboulay deleted the cboulay/chunk-dim-axis branch September 7, 2026 05:26
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