ezmsg-sigproc 2.13.1__tar.gz → 2.15.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/PKG-INFO +2 -2
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/pyproject.toml +1 -1
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/__version__.py +2 -2
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/affinetransform.py +22 -5
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/rollingscaler.py +2 -4
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/sampler.py +9 -18
- ezmsg_sigproc-2.15.0/src/ezmsg/sigproc/util/array.py +59 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/integration/ezmsg/test_sampler_system.py +4 -2
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_rollingscaler.py +2 -3
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_sampler.py +3 -3
- ezmsg_sigproc-2.13.1/tests/unit/test_base.py +0 -1100
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/.github/workflows/docs.yml +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/.github/workflows/python-publish.yml +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/.github/workflows/python-tests.yml +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/.gitignore +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/.pre-commit-config.yaml +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/LICENSE +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/README.md +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/Makefile +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/make.bat +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/source/_templates/autosummary/module.rst +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/source/api/index.rst +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/source/conf.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/source/guides/HybridBuffer.md +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/source/guides/explanations/array_api.rst +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/source/guides/explanations/sigproc.rst +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/source/guides/how-tos/signalprocessing/adaptive.rst +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/source/guides/how-tos/signalprocessing/checkpoint.rst +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/source/guides/how-tos/signalprocessing/composite.rst +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/source/guides/how-tos/signalprocessing/content-signalprocessing.rst +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/source/guides/how-tos/signalprocessing/processor.rst +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/source/guides/how-tos/signalprocessing/standalone.rst +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/source/guides/how-tos/signalprocessing/stateful.rst +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/source/guides/how-tos/signalprocessing/unit.rst +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/source/guides/img/HybridBufferBasic.svg +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/source/guides/img/HybridBufferOverflow.svg +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/source/guides/sigproc/architecture.rst +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/source/guides/sigproc/base.rst +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/source/guides/sigproc/content-sigproc.rst +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/source/guides/sigproc/processors.rst +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/source/guides/sigproc/units.rst +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/source/guides/tutorials/signalprocessing.rst +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/docs/source/index.md +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/__init__.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/activation.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/adaptive_lattice_notch.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/aggregate.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/bandpower.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/base.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/butterworthfilter.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/butterworthzerophase.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/cheby.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/combfilter.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/coordinatespaces.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/decimate.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/denormalize.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/detrend.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/diff.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/downsample.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/ewma.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/ewmfilter.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/extract_axis.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/fbcca.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/filter.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/filterbank.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/filterbankdesign.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/fir_hilbert.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/fir_pmc.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/firfilter.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/gaussiansmoothing.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/kaiser.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/linear.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/math/__init__.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/math/abs.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/math/add.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/math/clip.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/math/difference.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/math/invert.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/math/log.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/math/pow.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/math/scale.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/merge.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/messages.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/quantize.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/resample.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/scaler.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/signalinjector.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/singlebandpow.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/slicer.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/spectral.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/spectrogram.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/spectrum.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/transpose.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/util/__init__.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/util/asio.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/util/axisarray_buffer.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/util/buffer.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/util/message.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/util/profile.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/util/sparse.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/util/typeresolution.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/wavelets.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/src/ezmsg/sigproc/window.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/__init__.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/conftest.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/helpers/__init__.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/helpers/synth.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/helpers/util.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/integration/bytewax/test_spectrum_bytewax.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/integration/bytewax/test_window_bytewax.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/integration/ezmsg/test_add_system.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/integration/ezmsg/test_butterworth_system.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/integration/ezmsg/test_butterworthzerophase_system.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/integration/ezmsg/test_coordinatespaces_system.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/integration/ezmsg/test_decimate_system.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/integration/ezmsg/test_difference_system.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/integration/ezmsg/test_downsample_system.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/integration/ezmsg/test_filter_system.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/integration/ezmsg/test_fir_hilbert_system.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/integration/ezmsg/test_fir_pmc_system.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/integration/ezmsg/test_rollingscaler_system.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/integration/ezmsg/test_scaler_system.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/integration/ezmsg/test_spectrum_system.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/integration/ezmsg/test_window_system.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/resources/xform.csv +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/test_profile.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/buffer/test_axisarray_buffer.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/buffer/test_buffer.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/buffer/test_buffer_overflow.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_activation.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_adaptive_lattice_notch.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_affine_transform.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_aggregate.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_bandpower.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_butter.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_butterworthzerophase.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_combfilter.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_coordinatespaces.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_denormalize.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_diff.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_downsample.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_ewma.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_extract_axis.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_fbcca.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_filter.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_filterbank.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_filterbankdesign.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_fir_hilbert.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_fir_pmc.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_firfilter.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_gaussian_smoothing_filter.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_kaiser.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_linear.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_math.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_math_add.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_math_difference.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_merge.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_quantize.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_resample.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_scaler.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_singlebandpow.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_slicer.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_spectrogram.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_spectrum.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_transpose.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_util.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_wavelets.py +0 -0
- {ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/unit/test_window.py +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ezmsg-sigproc
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.15.0
|
|
4
4
|
Summary: Timeseries signal processing implementations in ezmsg
|
|
5
5
|
Author-email: Griffin Milsap <griffin.milsap@gmail.com>, Preston Peranich <pperanich@gmail.com>, Chadwick Boulay <chadwick.boulay@gmail.com>, Kyle McGraw <kmcgraw@blackrockneuro.com>
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
License-File: LICENSE
|
|
8
8
|
Requires-Python: >=3.10.15
|
|
9
9
|
Requires-Dist: array-api-compat>=1.11.1
|
|
10
|
-
Requires-Dist: ezmsg-baseproc>=1.
|
|
10
|
+
Requires-Dist: ezmsg-baseproc>=1.3.0
|
|
11
11
|
Requires-Dist: ezmsg>=3.6.0
|
|
12
12
|
Requires-Dist: numba>=0.61.0
|
|
13
13
|
Requires-Dist: numpy>=1.26.0
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '2.
|
|
32
|
-
__version_tuple__ = version_tuple = (2,
|
|
31
|
+
__version__ = version = '2.15.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (2, 15, 0)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -23,6 +23,8 @@ from ezmsg.baseproc import (
|
|
|
23
23
|
from ezmsg.util.messages.axisarray import AxisArray, AxisBase
|
|
24
24
|
from ezmsg.util.messages.util import replace
|
|
25
25
|
|
|
26
|
+
from ezmsg.sigproc.util.array import array_device, is_float_dtype, xp_asarray, xp_create
|
|
27
|
+
|
|
26
28
|
|
|
27
29
|
def _find_block_diagonal_clusters(weights: np.ndarray) -> list[tuple[np.ndarray, np.ndarray]] | None:
|
|
28
30
|
"""Detect block-diagonal structure in a weight matrix.
|
|
@@ -245,13 +247,25 @@ class AffineTransformTransformer(
|
|
|
245
247
|
|
|
246
248
|
self._state.new_axis = replace(message.axes[axis], data=np.array(new_labels))
|
|
247
249
|
|
|
248
|
-
# Convert to match message.data namespace
|
|
250
|
+
# Convert to match message.data namespace and device for _process.
|
|
251
|
+
# Weights are stored as numpy float64 after cluster detection; some
|
|
252
|
+
# devices (e.g. MPS) don't support float64, so we downcast weight
|
|
253
|
+
# arrays to the message's dtype when the message is floating-point.
|
|
249
254
|
xp = get_namespace(message.data)
|
|
255
|
+
dev = array_device(message.data)
|
|
256
|
+
msg_dt = message.data.dtype
|
|
257
|
+
# Downcast weights dtype only for float message data (avoids casting
|
|
258
|
+
# float weights to integer when message data happens to be int).
|
|
259
|
+
w_dt = msg_dt if is_float_dtype(xp, msg_dt) else None
|
|
250
260
|
if self._state.weights is not None:
|
|
251
|
-
self._state.weights = xp
|
|
261
|
+
self._state.weights = xp_asarray(xp, self._state.weights, dtype=w_dt, device=dev)
|
|
252
262
|
if self._state.clusters is not None:
|
|
253
263
|
self._state.clusters = [
|
|
254
|
-
(
|
|
264
|
+
(
|
|
265
|
+
xp_asarray(xp, in_idx, device=dev),
|
|
266
|
+
xp_asarray(xp, out_idx, device=dev),
|
|
267
|
+
xp_asarray(xp, sub_w, dtype=w_dt, device=dev),
|
|
268
|
+
)
|
|
255
269
|
for in_idx, out_idx, sub_w in self._state.clusters
|
|
256
270
|
]
|
|
257
271
|
|
|
@@ -345,7 +359,7 @@ class AffineTransformTransformer(
|
|
|
345
359
|
|
|
346
360
|
# Pre-allocate output (omitted channels stay zero)
|
|
347
361
|
out_shape = data.shape[:-1] + (self._state.n_out,)
|
|
348
|
-
result = xp.zeros
|
|
362
|
+
result = xp_create(xp.zeros, out_shape, dtype=data.dtype, device=array_device(data))
|
|
349
363
|
|
|
350
364
|
for in_idx, out_idx, sub_weights in self._state.clusters:
|
|
351
365
|
chunk = xp.take(data, in_idx, axis=data.ndim - 1)
|
|
@@ -371,7 +385,10 @@ class AffineTransformTransformer(
|
|
|
371
385
|
# The weights are stacked A|B where A is the transform and B is a single row
|
|
372
386
|
# in the equation y = Ax + B. This supports NeuroKey's weights matrices.
|
|
373
387
|
sample_shape = data.shape[:axis_idx] + (1,) + data.shape[axis_idx + 1 :]
|
|
374
|
-
data = xp.concat(
|
|
388
|
+
data = xp.concat(
|
|
389
|
+
(data, xp_create(xp.ones, sample_shape, dtype=data.dtype, device=array_device(data))),
|
|
390
|
+
axis=axis_idx,
|
|
391
|
+
)
|
|
375
392
|
|
|
376
393
|
if axis_idx in [-1, len(message.dims) - 1]:
|
|
377
394
|
data = xp.matmul(data, self._state.weights)
|
|
@@ -12,8 +12,6 @@ from ezmsg.baseproc import (
|
|
|
12
12
|
from ezmsg.util.messages.axisarray import AxisArray
|
|
13
13
|
from ezmsg.util.messages.util import replace
|
|
14
14
|
|
|
15
|
-
from ezmsg.sigproc.sampler import SampleMessage
|
|
16
|
-
|
|
17
15
|
|
|
18
16
|
class RollingScalerSettings(ez.Settings):
|
|
19
17
|
axis: str = "time"
|
|
@@ -168,8 +166,8 @@ class RollingScalerProcessor(BaseAdaptiveTransformer[RollingScalerSettings, Axis
|
|
|
168
166
|
|
|
169
167
|
self._state.samples.append((n_b, mean_b, M2_b))
|
|
170
168
|
|
|
171
|
-
def partial_fit(self, message:
|
|
172
|
-
x = message.
|
|
169
|
+
def partial_fit(self, message: AxisArray) -> None:
|
|
170
|
+
x = message.data
|
|
173
171
|
self._add_batch_stats(x)
|
|
174
172
|
|
|
175
173
|
def _process(self, message: AxisArray) -> AxisArray:
|
|
@@ -21,7 +21,7 @@ from ezmsg.util.messages.util import replace
|
|
|
21
21
|
|
|
22
22
|
from .util.axisarray_buffer import HybridAxisArrayBuffer
|
|
23
23
|
from .util.buffer import UpdateStrategy
|
|
24
|
-
from .util.message import
|
|
24
|
+
from .util.message import SampleTriggerMessage
|
|
25
25
|
from .util.profile import profile_subpub
|
|
26
26
|
|
|
27
27
|
|
|
@@ -75,17 +75,7 @@ class SamplerState:
|
|
|
75
75
|
|
|
76
76
|
|
|
77
77
|
class SamplerTransformer(BaseStatefulTransformer[SamplerSettings, AxisArray, AxisArray, SamplerState]):
|
|
78
|
-
def __call__(self, message: AxisArray | SampleTriggerMessage) -> list[
|
|
79
|
-
# TODO: Currently we have a single entry point that accepts both
|
|
80
|
-
# data and trigger messages and we choose a code path based on
|
|
81
|
-
# the message type. However, in the future we will likely replace
|
|
82
|
-
# SampleTriggerMessage with an agumented form of AxisArray,
|
|
83
|
-
# leveraging its attrs field, which makes this a bit harder.
|
|
84
|
-
# We should probably force callers of this object to explicitly
|
|
85
|
-
# call `push_trigger` for trigger messages. This will also
|
|
86
|
-
# simplify typing somewhat because `push_trigger` should not
|
|
87
|
-
# return anything yet we currently have it returning an empty
|
|
88
|
-
# list just to be compatible with __call__.
|
|
78
|
+
def __call__(self, message: AxisArray | SampleTriggerMessage) -> list[AxisArray]:
|
|
89
79
|
if isinstance(message, AxisArray):
|
|
90
80
|
return super().__call__(message)
|
|
91
81
|
else:
|
|
@@ -109,7 +99,7 @@ class SamplerTransformer(BaseStatefulTransformer[SamplerSettings, AxisArray, Axi
|
|
|
109
99
|
self._state.triggers = deque()
|
|
110
100
|
self._state.triggers.clear()
|
|
111
101
|
|
|
112
|
-
def _process(self, message: AxisArray) -> list[
|
|
102
|
+
def _process(self, message: AxisArray) -> list[AxisArray]:
|
|
113
103
|
self._state.buffer.write(message)
|
|
114
104
|
|
|
115
105
|
# How much data in the buffer?
|
|
@@ -119,7 +109,7 @@ class SamplerTransformer(BaseStatefulTransformer[SamplerSettings, AxisArray, Axi
|
|
|
119
109
|
)
|
|
120
110
|
|
|
121
111
|
# Process in reverse order so that we can remove triggers safely as we iterate.
|
|
122
|
-
msgs_out: list[
|
|
112
|
+
msgs_out: list[AxisArray] = []
|
|
123
113
|
for trig_ix in range(len(self._state.triggers) - 1, -1, -1):
|
|
124
114
|
trig = self._state.triggers[trig_ix]
|
|
125
115
|
if trig.period is None:
|
|
@@ -152,13 +142,13 @@ class SamplerTransformer(BaseStatefulTransformer[SamplerSettings, AxisArray, Axi
|
|
|
152
142
|
# Note: buffer will trim itself as needed based on buffer_dur.
|
|
153
143
|
|
|
154
144
|
# Prepare output and drop trigger
|
|
155
|
-
msgs_out.append(
|
|
145
|
+
msgs_out.append(replace(buff_axarr, attrs={**buff_axarr.attrs, "trigger": copy.copy(trig)}))
|
|
156
146
|
del self._state.triggers[trig_ix]
|
|
157
147
|
|
|
158
148
|
msgs_out.reverse() # in-place
|
|
159
149
|
return msgs_out
|
|
160
150
|
|
|
161
|
-
def push_trigger(self, message: SampleTriggerMessage) -> list[
|
|
151
|
+
def push_trigger(self, message: SampleTriggerMessage) -> list[AxisArray]:
|
|
162
152
|
# Input is a trigger message that we will use to sample the buffer.
|
|
163
153
|
|
|
164
154
|
if self._state.buffer is None:
|
|
@@ -198,7 +188,7 @@ class Sampler(BaseTransformerUnit[SamplerSettings, AxisArray, AxisArray, Sampler
|
|
|
198
188
|
SETTINGS = SamplerSettings
|
|
199
189
|
|
|
200
190
|
INPUT_TRIGGER = ez.InputStream(SampleTriggerMessage)
|
|
201
|
-
OUTPUT_SIGNAL = ez.OutputStream(
|
|
191
|
+
OUTPUT_SIGNAL = ez.OutputStream(AxisArray)
|
|
202
192
|
|
|
203
193
|
@ez.subscriber(INPUT_TRIGGER)
|
|
204
194
|
async def on_trigger(self, msg: SampleTriggerMessage) -> None:
|
|
@@ -228,7 +218,8 @@ def sampler(
|
|
|
228
218
|
|
|
229
219
|
Returns:
|
|
230
220
|
A generator that expects `.send` either an :obj:`AxisArray` containing streaming data messages,
|
|
231
|
-
or a :obj:`SampleTriggerMessage` containing a trigger, and yields the list of :obj:`
|
|
221
|
+
or a :obj:`SampleTriggerMessage` containing a trigger, and yields the list of :obj:`AxisArray` s
|
|
222
|
+
with trigger info stored in ``attrs["trigger"]``.
|
|
232
223
|
"""
|
|
233
224
|
return SamplerTransformer(
|
|
234
225
|
settings=SamplerSettings(
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Portable helpers for Array API interoperability.
|
|
2
|
+
|
|
3
|
+
These utilities smooth over differences between Array API libraries
|
|
4
|
+
(NumPy, PyTorch, MLX, CuPy, etc.) — in particular around ``device``
|
|
5
|
+
placement and ``dtype`` introspection, which are not uniformly supported.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def array_device(x):
|
|
12
|
+
"""Return the device of an array, or ``None`` for device-less libraries."""
|
|
13
|
+
try:
|
|
14
|
+
from array_api_compat import device
|
|
15
|
+
|
|
16
|
+
return device(x)
|
|
17
|
+
except (AttributeError, TypeError):
|
|
18
|
+
return None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def xp_asarray(xp, obj, *, dtype=None, device=None):
|
|
22
|
+
"""Portable ``xp.asarray`` that omits unsupported kwargs.
|
|
23
|
+
|
|
24
|
+
Some Array API libraries (e.g. MLX) don't accept a ``device`` keyword.
|
|
25
|
+
This helper builds the kwargs dict dynamically so that only supported
|
|
26
|
+
arguments are forwarded.
|
|
27
|
+
"""
|
|
28
|
+
kwargs = {}
|
|
29
|
+
if dtype is not None:
|
|
30
|
+
kwargs["dtype"] = dtype
|
|
31
|
+
if device is not None:
|
|
32
|
+
kwargs["device"] = device
|
|
33
|
+
return xp.asarray(obj, **kwargs)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def xp_create(fn, *args, dtype=None, device=None, **extra):
|
|
37
|
+
"""Call a creation function (``zeros``, ``ones``, ``eye``) portably.
|
|
38
|
+
|
|
39
|
+
Omits ``device`` if it is ``None`` (for libraries that don't support it).
|
|
40
|
+
"""
|
|
41
|
+
kwargs = dict(extra)
|
|
42
|
+
if dtype is not None:
|
|
43
|
+
kwargs["dtype"] = dtype
|
|
44
|
+
if device is not None:
|
|
45
|
+
kwargs["device"] = device
|
|
46
|
+
return fn(*args, **kwargs)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def is_float_dtype(xp, dtype) -> bool:
|
|
50
|
+
"""Check whether *dtype* is a real floating-point type, portably."""
|
|
51
|
+
try:
|
|
52
|
+
return xp.isdtype(dtype, "real floating")
|
|
53
|
+
except AttributeError:
|
|
54
|
+
pass
|
|
55
|
+
# Fallback for libraries without isdtype (e.g. MLX).
|
|
56
|
+
try:
|
|
57
|
+
return xp.issubdtype(dtype, xp.floating)
|
|
58
|
+
except (AttributeError, TypeError):
|
|
59
|
+
return np.issubdtype(np.dtype(dtype), np.floating)
|
{ezmsg_sigproc-2.13.1 → ezmsg_sigproc-2.15.0}/tests/integration/ezmsg/test_sampler_system.py
RENAMED
|
@@ -90,8 +90,10 @@ def test_sampler_system(test_name: str | None = None):
|
|
|
90
90
|
os.remove(test_filename)
|
|
91
91
|
ez.logger.info(f"Analyzing recording of {len(messages)} messages...")
|
|
92
92
|
assert len(messages) >= n_msgs
|
|
93
|
-
assert all([_.
|
|
93
|
+
assert all([_.data.shape == (int(freq * sample_dur), 1) for _ in messages])
|
|
94
94
|
# Test the sample window slice vs the trigger timestamps
|
|
95
|
-
latencies = [
|
|
95
|
+
latencies = [
|
|
96
|
+
_.axes["time"].offset - (_.attrs["trigger"].timestamp + _.attrs["trigger"].period[0]) for _ in messages
|
|
97
|
+
]
|
|
96
98
|
assert all([0 <= _ < 1 / freq for _ in latencies])
|
|
97
99
|
# Given that the input is a pure sinusoid, we could test that the signal has expected characteristics.
|
|
@@ -6,7 +6,6 @@ from ezmsg.sigproc.rollingscaler import (
|
|
|
6
6
|
RollingScalerProcessor,
|
|
7
7
|
RollingScalerSettings,
|
|
8
8
|
)
|
|
9
|
-
from ezmsg.sigproc.sampler import SampleMessage
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
def _axisarray_from_ndarray(x: np.ndarray, fs: float = 100.0, t0: float = 0.0) -> AxisArray:
|
|
@@ -47,7 +46,7 @@ def test_partial_fit_updates_and_process_uses_snapshot():
|
|
|
47
46
|
[5.0, 3.0],
|
|
48
47
|
]
|
|
49
48
|
)
|
|
50
|
-
proc.partial_fit(
|
|
49
|
+
proc.partial_fit(_axisarray_from_ndarray(epoch))
|
|
51
50
|
|
|
52
51
|
x = np.array([[1.0, 3.0], [3.0, 3.0], [5.0, 3.0]])
|
|
53
52
|
msg = _axisarray_from_ndarray(x)
|
|
@@ -68,7 +67,7 @@ def test_rolling_window_drops_oldest():
|
|
|
68
67
|
|
|
69
68
|
def add_epoch(vals):
|
|
70
69
|
arr = np.array(vals, dtype=float).reshape(-1, 1)
|
|
71
|
-
proc.partial_fit(
|
|
70
|
+
proc.partial_fit(_axisarray_from_ndarray(arr))
|
|
72
71
|
|
|
73
72
|
add_epoch([1, 1, 1, 1])
|
|
74
73
|
add_epoch([3, 3])
|
|
@@ -70,9 +70,9 @@ def test_sampler():
|
|
|
70
70
|
|
|
71
71
|
assert len(samples) == n_trigs
|
|
72
72
|
# Check sample data size. Note: sampler puts the time axis first.
|
|
73
|
-
assert all([_.
|
|
73
|
+
assert all([_.data.shape == (int(fs * period_dur), n_chans) for _ in samples])
|
|
74
74
|
# Compare the sample window slice against the trigger timestamps
|
|
75
|
-
latencies = [_.
|
|
75
|
+
latencies = [_.axes["time"].offset - (_.attrs["trigger"].timestamp + _.attrs["trigger"].period[0]) for _ in samples]
|
|
76
76
|
assert all([0 <= _ < 1 / fs for _ in latencies])
|
|
77
77
|
# Check the sample trigger value matches the trigger input.
|
|
78
|
-
assert all([_.trigger.value == ["Start", "Stop"][ix % 2] for ix, _ in enumerate(samples)])
|
|
78
|
+
assert all([_.attrs["trigger"].value == ["Start", "Stop"][ix % 2] for ix, _ in enumerate(samples)])
|