ezmsg-sigproc 2.8.0__tar.gz → 2.9.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.8.0 → ezmsg_sigproc-2.9.0}/PKG-INFO +1 -1
- ezmsg_sigproc-2.9.0/docs/source/guides/explanations/array_api.rst +156 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/sigproc/content-sigproc.rst +3 -1
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/__version__.py +2 -2
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/aggregate.py +9 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/coordinatespaces.py +22 -5
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/diff.py +15 -4
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/linear.py +7 -5
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/math/abs.py +11 -6
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/math/add.py +1 -2
- ezmsg_sigproc-2.9.0/src/ezmsg/sigproc/math/clip.py +48 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/math/difference.py +9 -3
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/math/invert.py +8 -3
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/math/log.py +19 -8
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/math/scale.py +8 -3
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/transpose.py +22 -7
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_add_system.py +2 -2
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_difference_system.py +3 -3
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_sampler_system.py +1 -1
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_math.py +6 -6
- ezmsg_sigproc-2.8.0/src/ezmsg/sigproc/math/clip.py +0 -43
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/.github/workflows/docs.yml +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/.github/workflows/python-publish.yml +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/.github/workflows/python-tests.yml +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/.gitignore +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/.pre-commit-config.yaml +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/LICENSE +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/README.md +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/Makefile +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/make.bat +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/_templates/autosummary/module.rst +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/api/index.rst +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/conf.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/HybridBuffer.md +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/explanations/sigproc.rst +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/how-tos/signalprocessing/adaptive.rst +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/how-tos/signalprocessing/checkpoint.rst +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/how-tos/signalprocessing/composite.rst +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/how-tos/signalprocessing/content-signalprocessing.rst +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/how-tos/signalprocessing/processor.rst +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/how-tos/signalprocessing/standalone.rst +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/how-tos/signalprocessing/stateful.rst +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/how-tos/signalprocessing/unit.rst +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/img/HybridBufferBasic.svg +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/img/HybridBufferOverflow.svg +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/sigproc/architecture.rst +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/sigproc/base.rst +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/sigproc/processors.rst +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/sigproc/units.rst +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/tutorials/signalprocessing.rst +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/index.md +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/pyproject.toml +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/__init__.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/activation.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/adaptive_lattice_notch.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/affinetransform.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/bandpower.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/base.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/butterworthfilter.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/butterworthzerophase.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/cheby.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/combfilter.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/decimate.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/denormalize.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/detrend.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/downsample.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/ewma.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/ewmfilter.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/extract_axis.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/fbcca.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/filter.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/filterbank.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/filterbankdesign.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/fir_hilbert.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/fir_pmc.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/firfilter.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/gaussiansmoothing.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/kaiser.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/math/__init__.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/messages.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/quantize.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/resample.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/rollingscaler.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/sampler.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/scaler.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/signalinjector.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/slicer.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/spectral.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/spectrogram.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/spectrum.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/util/__init__.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/util/asio.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/util/axisarray_buffer.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/util/buffer.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/util/message.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/util/profile.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/util/sparse.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/util/typeresolution.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/wavelets.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/window.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/__init__.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/conftest.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/helpers/__init__.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/helpers/synth.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/helpers/util.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/bytewax/test_spectrum_bytewax.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/bytewax/test_window_bytewax.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_butterworth_system.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_butterworthzerophase_system.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_coordinatespaces_system.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_decimate_system.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_downsample_system.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_filter_system.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_fir_hilbert_system.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_fir_pmc_system.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_rollingscaler_system.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_scaler_system.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_spectrum_system.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_window_system.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/resources/xform.csv +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/test_profile.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/buffer/test_axisarray_buffer.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/buffer/test_buffer.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/buffer/test_buffer_overflow.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_activation.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_adaptive_lattice_notch.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_affine_transform.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_aggregate.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_bandpower.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_base.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_butter.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_butterworthzerophase.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_combfilter.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_coordinatespaces.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_denormalize.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_diff.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_downsample.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_ewma.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_extract_axis.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_fbcca.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_filter.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_filterbank.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_filterbankdesign.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_fir_hilbert.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_fir_pmc.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_firfilter.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_gaussian_smoothing_filter.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_kaiser.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_linear.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_math_add.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_math_difference.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_quantize.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_resample.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_rollingscaler.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_sampler.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_scaler.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_slicer.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_spectrogram.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_spectrum.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_transpose.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_util.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_wavelets.py +0 -0
- {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_window.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ezmsg-sigproc
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.9.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
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
Array API Support
|
|
2
|
+
=================
|
|
3
|
+
|
|
4
|
+
ezmsg-sigproc provides support for the `Python Array API standard
|
|
5
|
+
<https://data-apis.org/array-api/>`_, enabling many transformers to work with
|
|
6
|
+
arrays from different backends such as NumPy, CuPy, PyTorch, and JAX.
|
|
7
|
+
|
|
8
|
+
What is the Array API?
|
|
9
|
+
----------------------
|
|
10
|
+
|
|
11
|
+
The Array API is a standardized interface for array operations across different
|
|
12
|
+
Python array libraries. By coding to this standard, ezmsg-sigproc transformers
|
|
13
|
+
can process data regardless of which array library created it, enabling:
|
|
14
|
+
|
|
15
|
+
- **GPU acceleration** via CuPy or PyTorch tensors
|
|
16
|
+
- **Framework interoperability** for integration with ML pipelines
|
|
17
|
+
- **Hardware flexibility** without code changes
|
|
18
|
+
|
|
19
|
+
How It Works
|
|
20
|
+
------------
|
|
21
|
+
|
|
22
|
+
Compatible transformers use `array-api-compat <https://github.com/data-apis/array-api-compat>`_
|
|
23
|
+
to detect the input array's namespace and use the appropriate operations:
|
|
24
|
+
|
|
25
|
+
.. code-block:: python
|
|
26
|
+
|
|
27
|
+
from array_api_compat import get_namespace
|
|
28
|
+
|
|
29
|
+
def _process(self, message: AxisArray) -> AxisArray:
|
|
30
|
+
xp = get_namespace(message.data) # numpy, cupy, torch, etc.
|
|
31
|
+
result = xp.abs(message.data) # Uses the correct backend
|
|
32
|
+
return replace(message, data=result)
|
|
33
|
+
|
|
34
|
+
Usage Example
|
|
35
|
+
-------------
|
|
36
|
+
|
|
37
|
+
Using Array API compatible transformers with CuPy for GPU acceleration:
|
|
38
|
+
|
|
39
|
+
.. code-block:: python
|
|
40
|
+
|
|
41
|
+
import cupy as cp
|
|
42
|
+
from ezmsg.util.messages.axisarray import AxisArray
|
|
43
|
+
from ezmsg.sigproc.math.abs import AbsTransformer
|
|
44
|
+
from ezmsg.sigproc.math.clip import ClipTransformer, ClipSettings
|
|
45
|
+
|
|
46
|
+
# Create data on GPU
|
|
47
|
+
gpu_data = cp.random.randn(1000, 64).astype(cp.float32)
|
|
48
|
+
message = AxisArray(gpu_data, dims=["time", "ch"])
|
|
49
|
+
|
|
50
|
+
# Process entirely on GPU - no data transfer!
|
|
51
|
+
abs_transformer = AbsTransformer()
|
|
52
|
+
clip_transformer = ClipTransformer(ClipSettings(min=0.0, max=1.0))
|
|
53
|
+
|
|
54
|
+
result = clip_transformer(abs_transformer(message))
|
|
55
|
+
# result.data is still a CuPy array on GPU
|
|
56
|
+
|
|
57
|
+
Compatible Modules
|
|
58
|
+
------------------
|
|
59
|
+
|
|
60
|
+
The following transformers fully support the Array API standard:
|
|
61
|
+
|
|
62
|
+
Math Operations
|
|
63
|
+
^^^^^^^^^^^^^^^
|
|
64
|
+
|
|
65
|
+
.. list-table::
|
|
66
|
+
:header-rows: 1
|
|
67
|
+
:widths: 30 70
|
|
68
|
+
|
|
69
|
+
* - Module
|
|
70
|
+
- Description
|
|
71
|
+
* - :mod:`ezmsg.sigproc.math.abs`
|
|
72
|
+
- Absolute value
|
|
73
|
+
* - :mod:`ezmsg.sigproc.math.clip`
|
|
74
|
+
- Clip values to a range
|
|
75
|
+
* - :mod:`ezmsg.sigproc.math.log`
|
|
76
|
+
- Logarithm with configurable base
|
|
77
|
+
* - :mod:`ezmsg.sigproc.math.scale`
|
|
78
|
+
- Multiply by a constant
|
|
79
|
+
* - :mod:`ezmsg.sigproc.math.invert`
|
|
80
|
+
- Compute 1/x
|
|
81
|
+
* - :mod:`ezmsg.sigproc.math.difference`
|
|
82
|
+
- Subtract a constant (ConstDifferenceTransformer)
|
|
83
|
+
|
|
84
|
+
Signal Processing
|
|
85
|
+
^^^^^^^^^^^^^^^^^
|
|
86
|
+
|
|
87
|
+
.. list-table::
|
|
88
|
+
:header-rows: 1
|
|
89
|
+
:widths: 30 70
|
|
90
|
+
|
|
91
|
+
* - Module
|
|
92
|
+
- Description
|
|
93
|
+
* - :mod:`ezmsg.sigproc.diff`
|
|
94
|
+
- Compute differences along an axis
|
|
95
|
+
* - :mod:`ezmsg.sigproc.transpose`
|
|
96
|
+
- Transpose/permute array dimensions
|
|
97
|
+
* - :mod:`ezmsg.sigproc.linear`
|
|
98
|
+
- Per-channel linear transform (scale + offset)
|
|
99
|
+
* - :mod:`ezmsg.sigproc.aggregate`
|
|
100
|
+
- Aggregate operations (AggregateTransformer only)
|
|
101
|
+
|
|
102
|
+
Coordinate Transforms
|
|
103
|
+
^^^^^^^^^^^^^^^^^^^^^
|
|
104
|
+
|
|
105
|
+
.. list-table::
|
|
106
|
+
:header-rows: 1
|
|
107
|
+
:widths: 30 70
|
|
108
|
+
|
|
109
|
+
* - Module
|
|
110
|
+
- Description
|
|
111
|
+
* - :mod:`ezmsg.sigproc.coordinatespaces`
|
|
112
|
+
- Cartesian/polar coordinate conversions
|
|
113
|
+
|
|
114
|
+
Limitations
|
|
115
|
+
-----------
|
|
116
|
+
|
|
117
|
+
Some operations remain NumPy-only due to lack of Array API equivalents:
|
|
118
|
+
|
|
119
|
+
- **Random number generation**: Modules using ``np.random`` (e.g., ``denormalize``)
|
|
120
|
+
- **SciPy operations**: Filtering (``scipy.signal.lfilter``), FFT, wavelets
|
|
121
|
+
- **Advanced indexing**: Some slicing operations for metadata handling
|
|
122
|
+
- **Memory layout**: ``np.require`` for contiguous array optimization (NumPy only)
|
|
123
|
+
|
|
124
|
+
Metadata arrays (axis labels, coordinates) typically remain as NumPy arrays
|
|
125
|
+
since they are not performance-critical.
|
|
126
|
+
|
|
127
|
+
Adding Array API Support
|
|
128
|
+
------------------------
|
|
129
|
+
|
|
130
|
+
When contributing new transformers, follow this pattern:
|
|
131
|
+
|
|
132
|
+
.. code-block:: python
|
|
133
|
+
|
|
134
|
+
from array_api_compat import get_namespace
|
|
135
|
+
from ezmsg.baseproc import BaseTransformer
|
|
136
|
+
from ezmsg.util.messages.axisarray import AxisArray
|
|
137
|
+
from ezmsg.util.messages.util import replace
|
|
138
|
+
|
|
139
|
+
class MyTransformer(BaseTransformer[MySettings, AxisArray, AxisArray]):
|
|
140
|
+
def _process(self, message: AxisArray) -> AxisArray:
|
|
141
|
+
xp = get_namespace(message.data)
|
|
142
|
+
|
|
143
|
+
# Use xp instead of np for array operations
|
|
144
|
+
result = xp.sqrt(xp.abs(message.data))
|
|
145
|
+
|
|
146
|
+
return replace(message, data=result)
|
|
147
|
+
|
|
148
|
+
Key guidelines:
|
|
149
|
+
|
|
150
|
+
1. Call ``get_namespace(message.data)`` at the start of ``_process``
|
|
151
|
+
2. Use ``xp.function_name`` instead of ``np.function_name``
|
|
152
|
+
3. Note that some functions have different names:
|
|
153
|
+
- ``np.concatenate`` → ``xp.concat``
|
|
154
|
+
- ``np.transpose`` → ``xp.permute_dims``
|
|
155
|
+
4. Keep metadata operations (axis labels, etc.) as NumPy
|
|
156
|
+
5. Use in-place operations (``/=``, ``*=``) where possible for efficiency
|
|
@@ -4,7 +4,8 @@ ezmsg-sigproc
|
|
|
4
4
|
Timeseries signal processing implementations in ezmsg, leveraging numpy and scipy.
|
|
5
5
|
Most of the methods and classes in this extension are intended to be used in building signal processing pipelines.
|
|
6
6
|
They use :class:`ezmsg.util.messages.axisarray.AxisArray` as the primary data structure for passing signals between components.
|
|
7
|
-
The message's data are
|
|
7
|
+
The message's data are typically NumPy arrays, though many transformers support the
|
|
8
|
+
:doc:`Array API standard <../explanations/array_api>` for use with CuPy, PyTorch, and other backends.
|
|
8
9
|
|
|
9
10
|
.. note:: Some generators might yield valid :class:`AxisArray` messages with ``.data`` size of 0.
|
|
10
11
|
This may occur when the generator receives inadequate data to produce a valid output, such as when windowing or buffering.
|
|
@@ -21,3 +22,4 @@ This may occur when the generator receives inadequate data to produce a valid ou
|
|
|
21
22
|
base
|
|
22
23
|
units
|
|
23
24
|
processors
|
|
25
|
+
../explanations/array_api
|
|
@@ -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.9.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (2, 9, 0)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Aggregation operations over arrays.
|
|
3
|
+
|
|
4
|
+
.. note::
|
|
5
|
+
:obj:`AggregateTransformer` supports the :doc:`Array API standard </guides/explanations/array_api>`,
|
|
6
|
+
enabling use with NumPy, CuPy, PyTorch, and other compatible array libraries.
|
|
7
|
+
:obj:`RangedAggregateTransformer` currently requires NumPy arrays.
|
|
8
|
+
"""
|
|
9
|
+
|
|
1
10
|
import typing
|
|
2
11
|
|
|
3
12
|
import ezmsg.core as ez
|
|
@@ -3,6 +3,10 @@ Coordinate space transformations for streaming data.
|
|
|
3
3
|
|
|
4
4
|
This module provides utilities and ezmsg nodes for transforming between
|
|
5
5
|
Cartesian (x, y) and polar (r, theta) coordinate systems.
|
|
6
|
+
|
|
7
|
+
.. note::
|
|
8
|
+
This module supports the :doc:`Array API standard </guides/explanations/array_api>`,
|
|
9
|
+
enabling use with NumPy, CuPy, PyTorch, and other compatible array libraries.
|
|
6
10
|
"""
|
|
7
11
|
|
|
8
12
|
from enum import Enum
|
|
@@ -11,6 +15,7 @@ from typing import Tuple
|
|
|
11
15
|
import ezmsg.core as ez
|
|
12
16
|
import numpy as np
|
|
13
17
|
import numpy.typing as npt
|
|
18
|
+
from array_api_compat import get_namespace, is_array_api_obj
|
|
14
19
|
from ezmsg.baseproc import (
|
|
15
20
|
BaseTransformer,
|
|
16
21
|
BaseTransformerUnit,
|
|
@@ -20,14 +25,24 @@ from ezmsg.util.messages.axisarray import AxisArray, replace
|
|
|
20
25
|
# -- Utility functions for coordinate transformations --
|
|
21
26
|
|
|
22
27
|
|
|
28
|
+
def _get_namespace_or_numpy(*args: npt.ArrayLike):
|
|
29
|
+
"""Get array namespace if any arg is an array, otherwise return numpy."""
|
|
30
|
+
for arg in args:
|
|
31
|
+
if is_array_api_obj(arg):
|
|
32
|
+
return get_namespace(arg)
|
|
33
|
+
return np
|
|
34
|
+
|
|
35
|
+
|
|
23
36
|
def polar2z(r: npt.ArrayLike, theta: npt.ArrayLike) -> npt.ArrayLike:
|
|
24
37
|
"""Convert polar coordinates to complex number representation."""
|
|
25
|
-
|
|
38
|
+
xp = _get_namespace_or_numpy(r, theta)
|
|
39
|
+
return r * xp.exp(1j * theta)
|
|
26
40
|
|
|
27
41
|
|
|
28
42
|
def z2polar(z: npt.ArrayLike) -> Tuple[npt.ArrayLike, npt.ArrayLike]:
|
|
29
43
|
"""Convert complex number to polar coordinates (r, theta)."""
|
|
30
|
-
|
|
44
|
+
xp = _get_namespace_or_numpy(z)
|
|
45
|
+
return xp.abs(z), xp.atan2(xp.imag(z), xp.real(z))
|
|
31
46
|
|
|
32
47
|
|
|
33
48
|
def cart2z(x: npt.ArrayLike, y: npt.ArrayLike) -> npt.ArrayLike:
|
|
@@ -37,7 +52,8 @@ def cart2z(x: npt.ArrayLike, y: npt.ArrayLike) -> npt.ArrayLike:
|
|
|
37
52
|
|
|
38
53
|
def z2cart(z: npt.ArrayLike) -> Tuple[npt.ArrayLike, npt.ArrayLike]:
|
|
39
54
|
"""Convert complex number to Cartesian coordinates (x, y)."""
|
|
40
|
-
|
|
55
|
+
xp = _get_namespace_or_numpy(z)
|
|
56
|
+
return xp.real(z), xp.imag(z)
|
|
41
57
|
|
|
42
58
|
|
|
43
59
|
def cart2pol(x: npt.ArrayLike, y: npt.ArrayLike) -> Tuple[npt.ArrayLike, npt.ArrayLike]:
|
|
@@ -90,6 +106,7 @@ class CoordinateSpacesTransformer(BaseTransformer[CoordinateSpacesSettings, Axis
|
|
|
90
106
|
"""
|
|
91
107
|
|
|
92
108
|
def _process(self, message: AxisArray) -> AxisArray:
|
|
109
|
+
xp = get_namespace(message.data)
|
|
93
110
|
axis = self.settings.axis or message.dims[-1]
|
|
94
111
|
axis_idx = message.get_axis_idx(axis)
|
|
95
112
|
|
|
@@ -116,9 +133,9 @@ class CoordinateSpacesTransformer(BaseTransformer[CoordinateSpacesSettings, Axis
|
|
|
116
133
|
out_a, out_b = pol2cart(component_a, component_b)
|
|
117
134
|
|
|
118
135
|
# Stack results back along the same axis
|
|
119
|
-
result =
|
|
136
|
+
result = xp.stack([out_a, out_b], axis=axis_idx)
|
|
120
137
|
|
|
121
|
-
# Update axis labels if present
|
|
138
|
+
# Update axis labels if present (use numpy for string labels)
|
|
122
139
|
axes = message.axes
|
|
123
140
|
if axis in axes and hasattr(axes[axis], "data"):
|
|
124
141
|
if self.settings.mode == CoordinateMode.CART2POL:
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Compute differences along an axis.
|
|
3
|
+
|
|
4
|
+
.. note::
|
|
5
|
+
This module supports the :doc:`Array API standard </guides/explanations/array_api>`,
|
|
6
|
+
enabling use with NumPy, CuPy, PyTorch, and other compatible array libraries.
|
|
7
|
+
"""
|
|
8
|
+
|
|
1
9
|
import ezmsg.core as ez
|
|
2
10
|
import numpy as np
|
|
3
11
|
import numpy.typing as npt
|
|
12
|
+
from array_api_compat import get_namespace
|
|
4
13
|
from ezmsg.baseproc import (
|
|
5
14
|
BaseStatefulTransformer,
|
|
6
15
|
BaseTransformerUnit,
|
|
@@ -39,23 +48,25 @@ class DiffTransformer(BaseStatefulTransformer[DiffSettings, AxisArray, AxisArray
|
|
|
39
48
|
self.state.last_time = ax_info.data[0] - 0.001
|
|
40
49
|
|
|
41
50
|
def _process(self, message: AxisArray) -> AxisArray:
|
|
51
|
+
xp = get_namespace(message.data)
|
|
42
52
|
axis = self.settings.axis or message.dims[0]
|
|
43
53
|
ax_idx = message.get_axis_idx(axis)
|
|
44
54
|
|
|
45
|
-
diffs =
|
|
46
|
-
|
|
55
|
+
diffs = xp.diff(
|
|
56
|
+
xp.concat((self.state.last_dat, message.data), axis=ax_idx),
|
|
47
57
|
axis=ax_idx,
|
|
48
58
|
)
|
|
49
59
|
# Prepare last_dat for next iteration
|
|
50
60
|
self.state.last_dat = slice_along_axis(message.data, slice(-1, None), axis=ax_idx)
|
|
51
|
-
# Scale by fs if requested. This
|
|
61
|
+
# Scale by fs if requested. This converts the diff to a derivative. e.g., diff of position becomes velocity.
|
|
52
62
|
if self.settings.scale_by_fs:
|
|
53
63
|
ax_info = message.get_axis(axis)
|
|
54
64
|
if hasattr(ax_info, "data"):
|
|
65
|
+
# ax_info.data is typically numpy for metadata, so use np.diff here
|
|
55
66
|
dt = np.diff(np.concatenate(([self.state.last_time], ax_info.data)))
|
|
56
67
|
# Expand dt dims to match diffs
|
|
57
68
|
exp_sl = (None,) * ax_idx + (Ellipsis,) + (None,) * (message.data.ndim - ax_idx - 1)
|
|
58
|
-
diffs /= dt[exp_sl]
|
|
69
|
+
diffs /= xp.asarray(dt[exp_sl])
|
|
59
70
|
self.state.last_time = ax_info.data[-1] # For next iteration
|
|
60
71
|
else:
|
|
61
72
|
diffs /= ax_info.gain
|
|
@@ -1,20 +1,22 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Apply a linear transformation: output = scale * input + offset.
|
|
2
3
|
|
|
3
4
|
Supports per-element scale and offset along a specified axis.
|
|
4
|
-
Uses Array API for compatibility with numpy, cupy, torch, etc.
|
|
5
|
-
|
|
6
5
|
For full matrix transformations, use :obj:`AffineTransformTransformer` instead.
|
|
6
|
+
|
|
7
|
+
.. note::
|
|
8
|
+
This module supports the :doc:`Array API standard </guides/explanations/array_api>`,
|
|
9
|
+
enabling use with NumPy, CuPy, PyTorch, and other compatible array libraries.
|
|
7
10
|
"""
|
|
8
11
|
|
|
9
12
|
import ezmsg.core as ez
|
|
10
13
|
import numpy as np
|
|
11
14
|
import numpy.typing as npt
|
|
12
15
|
from array_api_compat import get_namespace
|
|
16
|
+
from ezmsg.baseproc import BaseStatefulTransformer, BaseTransformerUnit, processor_state
|
|
13
17
|
from ezmsg.util.messages.axisarray import AxisArray
|
|
14
18
|
from ezmsg.util.messages.util import replace
|
|
15
19
|
|
|
16
|
-
from .base import BaseStatefulTransformer, BaseTransformerUnit, processor_state
|
|
17
|
-
|
|
18
20
|
|
|
19
21
|
class LinearTransformSettings(ez.Settings):
|
|
20
22
|
scale: float | list[float] | npt.ArrayLike = 1.0
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
1
|
+
"""
|
|
2
|
+
Take the absolute value of the data.
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
.. note::
|
|
5
|
+
This module supports the :doc:`Array API standard </guides/explanations/array_api>`,
|
|
6
|
+
enabling use with NumPy, CuPy, PyTorch, and other compatible array libraries.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from array_api_compat import get_namespace
|
|
10
|
+
from ezmsg.baseproc import BaseTransformer, BaseTransformerUnit
|
|
5
11
|
from ezmsg.util.messages.axisarray import AxisArray
|
|
6
12
|
from ezmsg.util.messages.util import replace
|
|
7
13
|
|
|
8
|
-
from ..base import BaseTransformer, BaseTransformerUnit
|
|
9
|
-
|
|
10
14
|
|
|
11
15
|
class AbsSettings:
|
|
12
16
|
pass
|
|
@@ -14,7 +18,8 @@ class AbsSettings:
|
|
|
14
18
|
|
|
15
19
|
class AbsTransformer(BaseTransformer[None, AxisArray, AxisArray]):
|
|
16
20
|
def _process(self, message: AxisArray) -> AxisArray:
|
|
17
|
-
|
|
21
|
+
xp = get_namespace(message.data)
|
|
22
|
+
return replace(message, data=xp.abs(message.data))
|
|
18
23
|
|
|
19
24
|
|
|
20
25
|
class Abs(BaseTransformerUnit[None, AxisArray, AxisArray, AbsTransformer]): ... # SETTINGS = None
|
|
@@ -5,12 +5,11 @@ import typing
|
|
|
5
5
|
from dataclasses import dataclass, field
|
|
6
6
|
|
|
7
7
|
import ezmsg.core as ez
|
|
8
|
+
from ezmsg.baseproc import BaseTransformer, BaseTransformerUnit
|
|
8
9
|
from ezmsg.baseproc.util.asio import run_coroutine_sync
|
|
9
10
|
from ezmsg.util.messages.axisarray import AxisArray
|
|
10
11
|
from ezmsg.util.messages.util import replace
|
|
11
12
|
|
|
12
|
-
from ..base import BaseTransformer, BaseTransformerUnit
|
|
13
|
-
|
|
14
13
|
# --- Constant Addition (single input) ---
|
|
15
14
|
|
|
16
15
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Clips the data to be within the specified range.
|
|
3
|
+
|
|
4
|
+
.. note::
|
|
5
|
+
This module supports the :doc:`Array API standard </guides/explanations/array_api>`,
|
|
6
|
+
enabling use with NumPy, CuPy, PyTorch, and other compatible array libraries.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import ezmsg.core as ez
|
|
10
|
+
from array_api_compat import get_namespace
|
|
11
|
+
from ezmsg.baseproc import BaseTransformer, BaseTransformerUnit
|
|
12
|
+
from ezmsg.util.messages.axisarray import AxisArray
|
|
13
|
+
from ezmsg.util.messages.util import replace
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ClipSettings(ez.Settings):
|
|
17
|
+
min: float | None = None
|
|
18
|
+
"""Lower clip bound. If None, no lower clipping is applied."""
|
|
19
|
+
|
|
20
|
+
max: float | None = None
|
|
21
|
+
"""Upper clip bound. If None, no upper clipping is applied."""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ClipTransformer(BaseTransformer[ClipSettings, AxisArray, AxisArray]):
|
|
25
|
+
def _process(self, message: AxisArray) -> AxisArray:
|
|
26
|
+
xp = get_namespace(message.data)
|
|
27
|
+
return replace(
|
|
28
|
+
message,
|
|
29
|
+
data=xp.clip(message.data, self.settings.min, self.settings.max),
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Clip(BaseTransformerUnit[ClipSettings, AxisArray, AxisArray, ClipTransformer]):
|
|
34
|
+
SETTINGS = ClipSettings
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def clip(min: float | None = None, max: float | None = None) -> ClipTransformer:
|
|
38
|
+
"""
|
|
39
|
+
Clips the data to be within the specified range.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
min: Lower clip bound. If None, no lower clipping is applied.
|
|
43
|
+
max: Upper clip bound. If None, no upper clipping is applied.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
:obj:`ClipTransformer`.
|
|
47
|
+
"""
|
|
48
|
+
return ClipTransformer(ClipSettings(min=min, max=max))
|
|
@@ -1,16 +1,22 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Take the difference between 2 signals or between a signal and a constant value.
|
|
3
|
+
|
|
4
|
+
.. note::
|
|
5
|
+
:obj:`ConstDifferenceTransformer` supports the :doc:`Array API standard </guides/explanations/array_api>`,
|
|
6
|
+
enabling use with NumPy, CuPy, PyTorch, and other compatible array libraries.
|
|
7
|
+
:obj:`DifferenceProcessor` (two-input difference) currently requires NumPy arrays.
|
|
8
|
+
"""
|
|
2
9
|
|
|
3
10
|
import asyncio
|
|
4
11
|
import typing
|
|
5
12
|
from dataclasses import dataclass, field
|
|
6
13
|
|
|
7
14
|
import ezmsg.core as ez
|
|
15
|
+
from ezmsg.baseproc import BaseTransformer, BaseTransformerUnit
|
|
8
16
|
from ezmsg.baseproc.util.asio import run_coroutine_sync
|
|
9
17
|
from ezmsg.util.messages.axisarray import AxisArray
|
|
10
18
|
from ezmsg.util.messages.util import replace
|
|
11
19
|
|
|
12
|
-
from ..base import BaseTransformer, BaseTransformerUnit
|
|
13
|
-
|
|
14
20
|
|
|
15
21
|
class ConstDifferenceSettings(ez.Settings):
|
|
16
22
|
value: float = 0.0
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Compute the multiplicative inverse (1/x) of the data.
|
|
2
3
|
|
|
4
|
+
.. note::
|
|
5
|
+
This module supports the :doc:`Array API standard </guides/explanations/array_api>`,
|
|
6
|
+
enabling use with NumPy, CuPy, PyTorch, and other compatible array libraries.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from ezmsg.baseproc import BaseTransformer, BaseTransformerUnit
|
|
3
10
|
from ezmsg.util.messages.axisarray import AxisArray
|
|
4
11
|
from ezmsg.util.messages.util import replace
|
|
5
12
|
|
|
6
|
-
from ..base import BaseTransformer, BaseTransformerUnit
|
|
7
|
-
|
|
8
13
|
|
|
9
14
|
class InvertTransformer(BaseTransformer[None, AxisArray, AxisArray]):
|
|
10
15
|
def _process(self, message: AxisArray) -> AxisArray:
|
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Take the logarithm of the data.
|
|
3
|
+
|
|
4
|
+
.. note::
|
|
5
|
+
This module supports the :doc:`Array API standard </guides/explanations/array_api>`,
|
|
6
|
+
enabling use with NumPy, CuPy, PyTorch, and other compatible array libraries.
|
|
7
|
+
"""
|
|
2
8
|
|
|
3
|
-
# TODO: Array API
|
|
4
9
|
import ezmsg.core as ez
|
|
5
|
-
|
|
10
|
+
from array_api_compat import get_namespace
|
|
11
|
+
from ezmsg.baseproc import BaseTransformer, BaseTransformerUnit
|
|
6
12
|
from ezmsg.util.messages.axisarray import AxisArray
|
|
7
13
|
from ezmsg.util.messages.util import replace
|
|
8
14
|
|
|
9
|
-
from ..base import BaseTransformer, BaseTransformerUnit
|
|
10
|
-
|
|
11
15
|
|
|
12
16
|
class LogSettings(ez.Settings):
|
|
13
17
|
base: float = 10.0
|
|
@@ -19,10 +23,17 @@ class LogSettings(ez.Settings):
|
|
|
19
23
|
|
|
20
24
|
class LogTransformer(BaseTransformer[LogSettings, AxisArray, AxisArray]):
|
|
21
25
|
def _process(self, message: AxisArray) -> AxisArray:
|
|
26
|
+
xp = get_namespace(message.data)
|
|
22
27
|
data = message.data
|
|
23
|
-
if self.settings.clip_zero
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
if self.settings.clip_zero:
|
|
29
|
+
# Check if any values are <= 0 and dtype is floating point
|
|
30
|
+
has_non_positive = bool(xp.any(data <= 0))
|
|
31
|
+
is_floating = xp.isdtype(data.dtype, "real floating")
|
|
32
|
+
if has_non_positive and is_floating:
|
|
33
|
+
# Use smallest_normal (Array API equivalent of numpy's finfo.tiny)
|
|
34
|
+
min_val = xp.finfo(data.dtype).smallest_normal
|
|
35
|
+
data = xp.clip(data, min_val, None)
|
|
36
|
+
return replace(message, data=xp.log(data) / xp.log(self.settings.base))
|
|
26
37
|
|
|
27
38
|
|
|
28
39
|
class Log(BaseTransformerUnit[LogSettings, AxisArray, AxisArray, LogTransformer]):
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Scale the data by a constant factor.
|
|
3
|
+
|
|
4
|
+
.. note::
|
|
5
|
+
This module supports the :doc:`Array API standard </guides/explanations/array_api>`,
|
|
6
|
+
enabling use with NumPy, CuPy, PyTorch, and other compatible array libraries.
|
|
7
|
+
"""
|
|
2
8
|
|
|
3
9
|
import ezmsg.core as ez
|
|
10
|
+
from ezmsg.baseproc import BaseTransformer, BaseTransformerUnit
|
|
4
11
|
from ezmsg.util.messages.axisarray import AxisArray
|
|
5
12
|
from ezmsg.util.messages.util import replace
|
|
6
13
|
|
|
7
|
-
from ..base import BaseTransformer, BaseTransformerUnit
|
|
8
|
-
|
|
9
14
|
|
|
10
15
|
class ScaleSettings(ez.Settings):
|
|
11
16
|
scale: float = 1.0
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Transpose or permute array dimensions.
|
|
3
|
+
|
|
4
|
+
.. note::
|
|
5
|
+
This module supports the :doc:`Array API standard </guides/explanations/array_api>`,
|
|
6
|
+
enabling use with NumPy, CuPy, PyTorch, and other compatible array libraries.
|
|
7
|
+
Memory layout optimization (C/F order) only applies to NumPy arrays.
|
|
8
|
+
"""
|
|
9
|
+
|
|
1
10
|
from types import EllipsisType
|
|
2
11
|
|
|
3
12
|
import ezmsg.core as ez
|
|
4
13
|
import numpy as np
|
|
14
|
+
from array_api_compat import get_namespace, is_numpy_array
|
|
5
15
|
from ezmsg.baseproc import (
|
|
6
16
|
BaseStatefulTransformer,
|
|
7
17
|
BaseTransformerUnit,
|
|
@@ -84,6 +94,7 @@ class TransposeTransformer(BaseStatefulTransformer[TransposeSettings, AxisArray,
|
|
|
84
94
|
return super().__call__(message)
|
|
85
95
|
|
|
86
96
|
def _process(self, message: AxisArray) -> AxisArray:
|
|
97
|
+
xp = get_namespace(message.data)
|
|
87
98
|
if self.state.axes_ints is None:
|
|
88
99
|
# No transpose required
|
|
89
100
|
if self.settings.order is None:
|
|
@@ -91,15 +102,19 @@ class TransposeTransformer(BaseStatefulTransformer[TransposeSettings, AxisArray,
|
|
|
91
102
|
# Note: We should not be able to reach here because it should be shortcutted at passthrough.
|
|
92
103
|
msg_out = message
|
|
93
104
|
else:
|
|
94
|
-
#
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
105
|
+
# Memory layout optimization only applies to numpy arrays
|
|
106
|
+
if is_numpy_array(message.data):
|
|
107
|
+
msg_out = replace(
|
|
108
|
+
message,
|
|
109
|
+
data=np.require(message.data, requirements=self.settings.order.upper()[0]),
|
|
110
|
+
)
|
|
111
|
+
else:
|
|
112
|
+
msg_out = message
|
|
99
113
|
else:
|
|
100
114
|
dims_out = [message.dims[ix] for ix in self.state.axes_ints]
|
|
101
|
-
data_out =
|
|
102
|
-
if self.settings.order is not None:
|
|
115
|
+
data_out = xp.permute_dims(message.data, axes=self.state.axes_ints)
|
|
116
|
+
if self.settings.order is not None and is_numpy_array(data_out):
|
|
117
|
+
# Memory layout optimization only applies to numpy arrays
|
|
103
118
|
data_out = np.require(data_out, requirements=self.settings.order.upper()[0])
|
|
104
119
|
msg_out = replace(
|
|
105
120
|
message,
|
|
@@ -76,7 +76,7 @@ def test_add_two_signals_system(
|
|
|
76
76
|
messages: list[AxisArray] = [_ for _ in message_log(test_filename)]
|
|
77
77
|
os.remove(test_filename)
|
|
78
78
|
|
|
79
|
-
assert len(messages)
|
|
79
|
+
assert len(messages) >= n_messages
|
|
80
80
|
|
|
81
81
|
# Verify each message has correct shape
|
|
82
82
|
for msg in messages:
|
|
@@ -137,7 +137,7 @@ def test_const_add_system(
|
|
|
137
137
|
messages: list[AxisArray] = [_ for _ in message_log(test_filename)]
|
|
138
138
|
os.remove(test_filename)
|
|
139
139
|
|
|
140
|
-
assert len(messages)
|
|
140
|
+
assert len(messages) >= n_messages
|
|
141
141
|
|
|
142
142
|
# Verify the constant was added
|
|
143
143
|
data = np.concatenate([_.data for _ in messages]).squeeze()
|
{ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_difference_system.py
RENAMED
|
@@ -74,7 +74,7 @@ def test_difference_two_signals_system(
|
|
|
74
74
|
messages: list[AxisArray] = [_ for _ in message_log(test_filename)]
|
|
75
75
|
os.remove(test_filename)
|
|
76
76
|
|
|
77
|
-
assert len(messages)
|
|
77
|
+
assert len(messages) >= n_messages
|
|
78
78
|
|
|
79
79
|
# Verify each message has correct shape
|
|
80
80
|
for msg in messages:
|
|
@@ -135,7 +135,7 @@ def test_const_difference_system(
|
|
|
135
135
|
messages: list[AxisArray] = [_ for _ in message_log(test_filename)]
|
|
136
136
|
os.remove(test_filename)
|
|
137
137
|
|
|
138
|
-
assert len(messages)
|
|
138
|
+
assert len(messages) >= n_messages
|
|
139
139
|
|
|
140
140
|
# Verify the constant was subtracted
|
|
141
141
|
data = np.concatenate([_.data for _ in messages]).squeeze()
|
|
@@ -191,7 +191,7 @@ def test_const_difference_subtrahend_false_system(
|
|
|
191
191
|
messages: list[AxisArray] = [_ for _ in message_log(test_filename)]
|
|
192
192
|
os.remove(test_filename)
|
|
193
193
|
|
|
194
|
-
assert len(messages)
|
|
194
|
+
assert len(messages) >= n_messages
|
|
195
195
|
|
|
196
196
|
# Verify: value - input
|
|
197
197
|
data = np.concatenate([_.data for _ in messages]).squeeze()
|
|
@@ -89,7 +89,7 @@ def test_sampler_system(test_name: str | None = None):
|
|
|
89
89
|
messages: list[SampleTriggerMessage] = [_ for _ in message_log(test_filename)]
|
|
90
90
|
os.remove(test_filename)
|
|
91
91
|
ez.logger.info(f"Analyzing recording of {len(messages)} messages...")
|
|
92
|
-
assert len(messages)
|
|
92
|
+
assert len(messages) >= n_msgs
|
|
93
93
|
assert all([_.sample.data.shape == (int(freq * sample_dur), 1) for _ in messages])
|
|
94
94
|
# Test the sample window slice vs the trigger timestamps
|
|
95
95
|
latencies = [_.sample.axes["time"].offset - (_.trigger.timestamp + _.trigger.period[0]) for _ in messages]
|