Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ on:
- dev
workflow_dispatch:

# A new push to a PR supersedes the run already in flight for that ref, so a
# wedged job cannot sit on a runner while its replacement queues behind it.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
strategy:
Expand All @@ -19,6 +25,7 @@ jobs:
- "windows-latest"
- "macos-latest"
runs-on: ${{matrix.os}}
timeout-minutes: 30

steps:
- uses: actions/checkout@v4
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ requires-python = ">=3.10"
dynamic = ["version"]
dependencies = [
"array-api-compat>=1.11.0",
# 3.10.0b2 for AxisArray.chunk_dim, which every producer here declares, and
# 3.10.0b3 for AxisArray.stream_dim, which every producer here declares, and
# CoordinateAxis.fingerprint, which they prime so downstream consumers do
# not each recompute it.
"ezmsg>=3.10.0b2",
"ezmsg>=3.10.0b3",
"ezmsg-baseproc>=1.12.0", # axis-aware default state hash + hash witness
"ezmsg-sigproc>=2.23.0",
"ezmsg-event>=0.9.0",
Expand Down
2 changes: 1 addition & 1 deletion src/ezmsg/simbiophys/baseline_drift.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ class BaselineDriftTransformer(
"""

# No `_hash_message`: the default already folds in the channel count and the
# chunk axis's gain, which is all this hashed before, and additionally the
# stream axis's gain, which is all this hashed before, and additionally the
# channel *fingerprint* -- one drift process is warmed up per channel, so a
# relabel at a fixed count leaves each channel wearing another's drift.

Expand Down
2 changes: 1 addition & 1 deletion src/ezmsg/simbiophys/dnss/lfp.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ def _reset_state(self, time_axis: LinearAxis) -> None:
data=np.zeros((0, self.settings.n_ch), dtype=np.float64),
dims=["time", "ch"],
axes={"time": time_axis, "ch": ch_axis},
chunk_dim="time",
stream_dim="time",
)

def _produce(self, n_samples: int, time_axis: LinearAxis) -> AxisArray:
Expand Down
2 changes: 1 addition & 1 deletion src/ezmsg/simbiophys/dnss/spike.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ def _reset_state(self, time_axis: LinearAxis) -> None:
),
dims=["time", "ch"],
axes={"time": time_axis, "ch": ch_axis},
chunk_dim="time",
stream_dim="time",
)

def _produce(self, n_samples: int, time_axis: LinearAxis) -> AxisArray:
Expand Down
2 changes: 1 addition & 1 deletion src/ezmsg/simbiophys/dynamic_colored_noise.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ class DynamicColoredNoiseTransformer(
"""

# No `_hash_message`: the default already folds in the channel count and the
# chunk axis's gain, which is all this hashed before, and additionally the
# stream axis's gain, which is all this hashed before, and additionally the
# channel *fingerprint* -- the delay lines and per-channel coefficients are
# tied to specific channels, so a relabel at a fixed count has to reset.

Expand Down
2 changes: 1 addition & 1 deletion src/ezmsg/simbiophys/line_noise.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class LineNoiseTransformer(BaseStatefulTransformer[LineNoiseSettings, AxisArray,

def _chunk_axis(self, message: AxisArray) -> AxisBase | None:
"""The axis the stream grows along, however the producer named it."""
dim = message.chunk_dim or next((d for d in self.STREAMING_DIMS if d in message.dims), None)
dim = message.stream_dim or next((d for d in self.STREAMING_DIMS if d in message.dims), None)
return message.axes.get(dim)

def _hash_message(self, message: AxisArray) -> int:
Expand Down
4 changes: 2 additions & 2 deletions src/ezmsg/simbiophys/noise.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def _reset_state(self, time_axis: LinearAxis) -> None:
dims=["time", "ch"],
axes={"time": time_axis, "ch": ch_axis},
# Messages append along `time`; `ch` describes the stream.
chunk_dim="time",
stream_dim="time",
)

def _produce(self, n_samples: int, time_axis: LinearAxis) -> AxisArray:
Expand Down Expand Up @@ -125,7 +125,7 @@ def _initialize_processors(settings: PinkNoiseSettings) -> dict[str, BaseProcess
scale=settings.scale,
)
),
# No `axis`: WhiteNoiseProducer declares chunk_dim="time" on what it
# No `axis`: WhiteNoiseProducer declares stream_dim="time" on what it
# emits, so the filter resolves to the same dimension it was pinned
# to -- without asking ezmsg-sigproc to warn about a setting no user
# of PinkNoise ever chose.
Expand Down
4 changes: 2 additions & 2 deletions src/ezmsg/simbiophys/oscillator.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def _reset_state(self, time_axis: LinearAxis) -> None:
data=np.zeros((0, 2)),
dims=["time", "ch"],
axes={"time": time_axis, "ch": ch_axis},
chunk_dim="time",
stream_dim="time",
)

def _produce(self, n_samples: int, time_axis: LinearAxis) -> AxisArray:
Expand Down Expand Up @@ -242,7 +242,7 @@ def _reset_state(self, time_axis: LinearAxis) -> None:
data=np.zeros((0, n_ch)),
dims=["time", "ch"],
axes={"time": time_axis, "ch": ch_axis},
chunk_dim="time",
stream_dim="time",
)

# Convert settings to arrays and validate
Expand Down
12 changes: 6 additions & 6 deletions tests/unit/test_state_reset_semantics.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Two facts these simulators now rely on, neither of which anything else notices
if it stops being true:

* Every producer declares :attr:`AxisArray.chunk_dim`, so a downstream processor
* Every producer declares :attr:`AxisArray.stream_dim`, so a downstream processor
knows which dimension grows rather than guessing ``"time"``, and hands its
channel axis over with the fingerprint already computed, so nobody downstream
pays the checksum on every message.
Expand Down Expand Up @@ -48,7 +48,7 @@ def signal(labels: list[str], fs: float = 100.0, n_time: int = 16, dim: str = "t
"ch": CoordinateAxis(data=np.array(labels), dims=["ch"]),
},
key="dev",
chunk_dim=dim,
stream_dim=dim,
)


Expand All @@ -70,10 +70,10 @@ def _produce(self, cls, settings, fs):
producer._reset_state(time_axis)
return producer._produce(10, time_axis)

def test_declares_the_chunk_dim(self, name, cls, settings, fs):
def test_declares_the_stream_dim(self, name, cls, settings, fs):
"""Without this a windowing stage downstream has to guess, and ``"time"``
is present-but-wrong the moment the message becomes ``(win, time, ch)``."""
assert self._produce(cls, settings, fs).chunk_dim == "time"
assert self._produce(cls, settings, fs).stream_dim == "time"

def test_hands_over_a_primed_channel_axis(self, name, cls, settings, fs):
"""The axis is built once per stream, so one checksum covers every
Expand Down Expand Up @@ -114,7 +114,7 @@ def test_a_relabel_at_a_fixed_count_is_a_new_stream(self, cls, settings):
ids=["baseline_drift", "dynamic_colored_noise"],
)
def test_a_longer_chunk_is_the_same_stream(self, cls, settings):
"""The chunk dimension is excluded, so ordinary chunk-size jitter must
"""The stream dimension is excluded, so ordinary chunk-size jitter must
not throw away a warmed-up filter."""
proc = cls(settings)
assert proc._hash_message(signal(["a", "b"], n_time=16)) == proc._hash_message(signal(["a", "b"], n_time=64))
Expand All @@ -132,7 +132,7 @@ def test_a_sample_rate_change_does(self):
proc = LineNoiseTransformer(LineNoiseSettings(freq=60.0))
assert proc._hash_message(signal(["a", "b"], fs=100.0)) != proc._hash_message(signal(["a", "b"], fs=500.0))

def test_it_reads_the_declared_chunk_dim_not_the_name_time(self):
def test_it_reads_the_declared_stream_dim_not_the_name_time(self):
"""A stream that grows along ``samp`` has no ``time`` axis at all; the
old hard-coded lookup silently fell back to a period of zero."""
proc = LineNoiseTransformer(LineNoiseSettings(freq=60.0))
Expand Down
Loading