ezmsg-sigproc 2.7.0__tar.gz → 2.8.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.
Files changed (163) hide show
  1. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/PKG-INFO +1 -1
  2. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/source/guides/explanations/sigproc.rst +1 -1
  3. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/source/guides/tutorials/signalprocessing.rst +1 -1
  4. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/__version__.py +2 -2
  5. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/affinetransform.py +23 -1
  6. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/butterworthzerophase.py +1 -1
  7. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/denormalize.py +3 -4
  8. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/detrend.py +1 -1
  9. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/diff.py +3 -4
  10. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/ewma.py +44 -7
  11. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/extract_axis.py +1 -2
  12. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/filter.py +3 -4
  13. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/fir_hilbert.py +1 -1
  14. ezmsg_sigproc-2.8.0/src/ezmsg/sigproc/linear.py +118 -0
  15. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/rollingscaler.py +4 -4
  16. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/scaler.py +52 -3
  17. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_activation.py +8 -3
  18. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_affine_transform.py +33 -26
  19. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_aggregate.py +18 -13
  20. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_bandpower.py +16 -9
  21. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_base.py +3 -3
  22. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_butter.py +29 -28
  23. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_butterworthzerophase.py +2 -2
  24. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_coordinatespaces.py +12 -12
  25. ezmsg_sigproc-2.8.0/tests/unit/test_ewma.py +162 -0
  26. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_filterbank.py +3 -3
  27. ezmsg_sigproc-2.8.0/tests/unit/test_linear.py +307 -0
  28. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_math.py +21 -21
  29. ezmsg_sigproc-2.8.0/tests/unit/test_scaler.py +233 -0
  30. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_slicer.py +17 -18
  31. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_spectrogram.py +1 -1
  32. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_spectrum.py +17 -14
  33. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_wavelets.py +10 -8
  34. ezmsg_sigproc-2.7.0/tests/unit/test_ewma.py +0 -47
  35. ezmsg_sigproc-2.7.0/tests/unit/test_scaler.py +0 -70
  36. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/.github/workflows/docs.yml +0 -0
  37. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/.github/workflows/python-publish.yml +0 -0
  38. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/.github/workflows/python-tests.yml +0 -0
  39. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/.gitignore +0 -0
  40. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/.pre-commit-config.yaml +0 -0
  41. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/LICENSE +0 -0
  42. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/README.md +0 -0
  43. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/Makefile +0 -0
  44. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/make.bat +0 -0
  45. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/source/_templates/autosummary/module.rst +0 -0
  46. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/source/api/index.rst +0 -0
  47. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/source/conf.py +0 -0
  48. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/source/guides/HybridBuffer.md +0 -0
  49. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/source/guides/how-tos/signalprocessing/adaptive.rst +0 -0
  50. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/source/guides/how-tos/signalprocessing/checkpoint.rst +0 -0
  51. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/source/guides/how-tos/signalprocessing/composite.rst +0 -0
  52. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/source/guides/how-tos/signalprocessing/content-signalprocessing.rst +0 -0
  53. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/source/guides/how-tos/signalprocessing/processor.rst +0 -0
  54. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/source/guides/how-tos/signalprocessing/standalone.rst +0 -0
  55. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/source/guides/how-tos/signalprocessing/stateful.rst +0 -0
  56. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/source/guides/how-tos/signalprocessing/unit.rst +0 -0
  57. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/source/guides/img/HybridBufferBasic.svg +0 -0
  58. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/source/guides/img/HybridBufferOverflow.svg +0 -0
  59. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/source/guides/sigproc/architecture.rst +0 -0
  60. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/source/guides/sigproc/base.rst +0 -0
  61. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/source/guides/sigproc/content-sigproc.rst +0 -0
  62. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/source/guides/sigproc/processors.rst +0 -0
  63. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/source/guides/sigproc/units.rst +0 -0
  64. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/docs/source/index.md +0 -0
  65. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/pyproject.toml +0 -0
  66. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/__init__.py +0 -0
  67. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/activation.py +0 -0
  68. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/adaptive_lattice_notch.py +0 -0
  69. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/aggregate.py +0 -0
  70. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/bandpower.py +0 -0
  71. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/base.py +0 -0
  72. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/butterworthfilter.py +0 -0
  73. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/cheby.py +0 -0
  74. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/combfilter.py +0 -0
  75. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/coordinatespaces.py +0 -0
  76. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/decimate.py +0 -0
  77. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/downsample.py +0 -0
  78. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/ewmfilter.py +0 -0
  79. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/fbcca.py +0 -0
  80. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/filterbank.py +0 -0
  81. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/filterbankdesign.py +0 -0
  82. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/fir_pmc.py +0 -0
  83. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/firfilter.py +0 -0
  84. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/gaussiansmoothing.py +0 -0
  85. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/kaiser.py +0 -0
  86. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/math/__init__.py +0 -0
  87. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/math/abs.py +0 -0
  88. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/math/add.py +0 -0
  89. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/math/clip.py +0 -0
  90. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/math/difference.py +0 -0
  91. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/math/invert.py +0 -0
  92. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/math/log.py +0 -0
  93. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/math/scale.py +0 -0
  94. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/messages.py +0 -0
  95. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/quantize.py +0 -0
  96. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/resample.py +0 -0
  97. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/sampler.py +0 -0
  98. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/signalinjector.py +0 -0
  99. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/slicer.py +0 -0
  100. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/spectral.py +0 -0
  101. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/spectrogram.py +0 -0
  102. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/spectrum.py +0 -0
  103. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/transpose.py +0 -0
  104. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/util/__init__.py +0 -0
  105. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/util/asio.py +0 -0
  106. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/util/axisarray_buffer.py +0 -0
  107. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/util/buffer.py +0 -0
  108. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/util/message.py +0 -0
  109. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/util/profile.py +0 -0
  110. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/util/sparse.py +0 -0
  111. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/util/typeresolution.py +0 -0
  112. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/wavelets.py +0 -0
  113. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/src/ezmsg/sigproc/window.py +0 -0
  114. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/__init__.py +0 -0
  115. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/conftest.py +0 -0
  116. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/helpers/__init__.py +0 -0
  117. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/helpers/synth.py +0 -0
  118. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/helpers/util.py +0 -0
  119. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/integration/bytewax/test_spectrum_bytewax.py +0 -0
  120. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/integration/bytewax/test_window_bytewax.py +0 -0
  121. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/integration/ezmsg/test_add_system.py +0 -0
  122. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/integration/ezmsg/test_butterworth_system.py +0 -0
  123. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/integration/ezmsg/test_butterworthzerophase_system.py +0 -0
  124. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/integration/ezmsg/test_coordinatespaces_system.py +0 -0
  125. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/integration/ezmsg/test_decimate_system.py +0 -0
  126. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/integration/ezmsg/test_difference_system.py +0 -0
  127. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/integration/ezmsg/test_downsample_system.py +0 -0
  128. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/integration/ezmsg/test_filter_system.py +0 -0
  129. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/integration/ezmsg/test_fir_hilbert_system.py +0 -0
  130. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/integration/ezmsg/test_fir_pmc_system.py +0 -0
  131. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/integration/ezmsg/test_rollingscaler_system.py +0 -0
  132. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/integration/ezmsg/test_sampler_system.py +0 -0
  133. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/integration/ezmsg/test_scaler_system.py +0 -0
  134. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/integration/ezmsg/test_spectrum_system.py +0 -0
  135. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/integration/ezmsg/test_window_system.py +0 -0
  136. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/resources/xform.csv +0 -0
  137. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/test_profile.py +0 -0
  138. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/buffer/test_axisarray_buffer.py +0 -0
  139. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/buffer/test_buffer.py +0 -0
  140. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/buffer/test_buffer_overflow.py +0 -0
  141. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_adaptive_lattice_notch.py +0 -0
  142. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_combfilter.py +0 -0
  143. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_denormalize.py +0 -0
  144. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_diff.py +0 -0
  145. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_downsample.py +0 -0
  146. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_extract_axis.py +0 -0
  147. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_fbcca.py +0 -0
  148. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_filter.py +0 -0
  149. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_filterbankdesign.py +0 -0
  150. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_fir_hilbert.py +0 -0
  151. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_fir_pmc.py +0 -0
  152. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_firfilter.py +0 -0
  153. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_gaussian_smoothing_filter.py +0 -0
  154. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_kaiser.py +0 -0
  155. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_math_add.py +0 -0
  156. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_math_difference.py +0 -0
  157. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_quantize.py +0 -0
  158. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_resample.py +0 -0
  159. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_rollingscaler.py +0 -0
  160. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_sampler.py +0 -0
  161. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_transpose.py +0 -0
  162. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.0}/tests/unit/test_util.py +0 -0
  163. {ezmsg_sigproc-2.7.0 → ezmsg_sigproc-2.8.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.7.0
3
+ Version: 2.8.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
@@ -329,7 +329,7 @@ Often, all that is required is the following (e.g., for a custom transformer):
329
329
 
330
330
  import ezmsg.core as ez
331
331
  from ezmsg.util.messages.axisarray import AxisArray
332
- from ezmsg.sigproc.base import BaseTransformer, BaseTransformerUnit
332
+ from ezmsg.baseproc import BaseTransformer, BaseTransformerUnit
333
333
 
334
334
 
335
335
  class CustomTransformerSettings(ez.Settings):
@@ -132,7 +132,7 @@ Add the following import statements to the top of the `downsample.py` file:
132
132
  )
133
133
  import ezmsg.core as ez
134
134
 
135
- from ezmsg.sigproc.base import (
135
+ from ezmsg.baseproc import (
136
136
  BaseStatefulTransformer,
137
137
  BaseTransformerUnit,
138
138
  processor_state,
@@ -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.7.0'
32
- __version_tuple__ = version_tuple = (2, 7, 0)
31
+ __version__ = version = '2.8.0'
32
+ __version_tuple__ = version_tuple = (2, 8, 0)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -1,3 +1,13 @@
1
+ """Affine transformations via matrix multiplication: y = Ax or y = Ax + B.
2
+
3
+ For full matrix transformations where channels are mixed (off-diagonal weights),
4
+ use :obj:`AffineTransformTransformer` or the `AffineTransform` unit.
5
+
6
+ For simple per-channel scaling and offset (diagonal weights only), use
7
+ :obj:`LinearTransformTransformer` from :mod:`ezmsg.sigproc.linear` instead,
8
+ which is more efficient as it avoids matrix multiplication.
9
+ """
10
+
1
11
  import os
2
12
  from pathlib import Path
3
13
 
@@ -17,7 +27,6 @@ from ezmsg.util.messages.util import replace
17
27
  class AffineTransformSettings(ez.Settings):
18
28
  """
19
29
  Settings for :obj:`AffineTransform`.
20
- See :obj:`affine_transform` for argument details.
21
30
  """
22
31
 
23
32
  weights: np.ndarray | str | Path
@@ -39,6 +48,19 @@ class AffineTransformState:
39
48
  class AffineTransformTransformer(
40
49
  BaseStatefulTransformer[AffineTransformSettings, AxisArray, AxisArray, AffineTransformState]
41
50
  ):
51
+ """Apply affine transformation via matrix multiplication: y = Ax or y = Ax + B.
52
+
53
+ Use this transformer when you need full matrix transformations that mix
54
+ channels (off-diagonal weights), such as spatial filters or projections.
55
+
56
+ For simple per-channel scaling and offset where each output channel depends
57
+ only on its corresponding input channel (diagonal weight matrix), use
58
+ :obj:`LinearTransformTransformer` instead, which is more efficient.
59
+
60
+ The weights matrix can include an offset row (stacked as [A|B]) where the
61
+ input is automatically augmented with a column of ones to compute y = Ax + B.
62
+ """
63
+
42
64
  def __call__(self, message: AxisArray) -> AxisArray:
43
65
  # Override __call__ so we can shortcut if weights are None.
44
66
  if self.settings.weights is None or (
@@ -4,10 +4,10 @@ import typing
4
4
  import ezmsg.core as ez
5
5
  import numpy as np
6
6
  import scipy.signal
7
+ from ezmsg.baseproc import SettingsType
7
8
  from ezmsg.util.messages.axisarray import AxisArray
8
9
  from ezmsg.util.messages.util import replace
9
10
 
10
- from ezmsg.sigproc.base import SettingsType
11
11
  from ezmsg.sigproc.butterworthfilter import ButterworthFilterSettings, butter_design_fun
12
12
  from ezmsg.sigproc.filter import (
13
13
  BACoeffs,
@@ -1,14 +1,13 @@
1
1
  import ezmsg.core as ez
2
2
  import numpy as np
3
3
  import numpy.typing as npt
4
- from ezmsg.util.messages.axisarray import AxisArray
5
- from ezmsg.util.messages.util import replace
6
-
7
- from ezmsg.sigproc.base import (
4
+ from ezmsg.baseproc import (
8
5
  BaseStatefulTransformer,
9
6
  BaseTransformerUnit,
10
7
  processor_state,
11
8
  )
9
+ from ezmsg.util.messages.axisarray import AxisArray
10
+ from ezmsg.util.messages.util import replace
12
11
 
13
12
 
14
13
  class DenormalizeSettings(ez.Settings):
@@ -1,7 +1,7 @@
1
1
  import scipy.signal as sps
2
+ from ezmsg.baseproc import BaseTransformerUnit
2
3
  from ezmsg.util.messages.axisarray import AxisArray, replace
3
4
 
4
- from ezmsg.sigproc.base import BaseTransformerUnit
5
5
  from ezmsg.sigproc.ewma import EWMASettings, EWMATransformer
6
6
 
7
7
 
@@ -1,14 +1,13 @@
1
1
  import ezmsg.core as ez
2
2
  import numpy as np
3
3
  import numpy.typing as npt
4
- from ezmsg.util.messages.axisarray import AxisArray, slice_along_axis
5
- from ezmsg.util.messages.util import replace
6
-
7
- from ezmsg.sigproc.base import (
4
+ from ezmsg.baseproc import (
8
5
  BaseStatefulTransformer,
9
6
  BaseTransformerUnit,
10
7
  processor_state,
11
8
  )
9
+ from ezmsg.util.messages.axisarray import AxisArray, slice_along_axis
10
+ from ezmsg.util.messages.util import replace
12
11
 
13
12
 
14
13
  class DiffSettings(ez.Settings):
@@ -139,8 +139,15 @@ class EWMA_Deprecated:
139
139
 
140
140
  class EWMASettings(ez.Settings):
141
141
  time_constant: float = 1.0
142
+ """The amount of time for the smoothed response of a unit step function to reach 1 - 1/e approx-eq 63.2%."""
143
+
142
144
  axis: str | None = None
143
145
 
146
+ accumulate: bool = True
147
+ """If True, update the EWMA state with each sample. If False, only apply
148
+ the current EWMA estimate without updating state (useful for inference
149
+ periods where you don't want to adapt statistics)."""
150
+
144
151
 
145
152
  @processor_state
146
153
  class EWMAState:
@@ -166,15 +173,45 @@ class EWMATransformer(BaseStatefulTransformer[EWMASettings, AxisArray, AxisArray
166
173
  return message
167
174
  axis = self.settings.axis or message.dims[0]
168
175
  axis_idx = message.get_axis_idx(axis)
169
- expected, self._state.zi = sps.lfilter(
170
- [self._state.alpha],
171
- [1.0, self._state.alpha - 1.0],
172
- message.data,
173
- axis=axis_idx,
174
- zi=self._state.zi,
175
- )
176
+ if self.settings.accumulate:
177
+ # Normal behavior: update state with new samples
178
+ expected, self._state.zi = sps.lfilter(
179
+ [self._state.alpha],
180
+ [1.0, self._state.alpha - 1.0],
181
+ message.data,
182
+ axis=axis_idx,
183
+ zi=self._state.zi,
184
+ )
185
+ else:
186
+ # Process-only: compute output without updating state
187
+ expected, _ = sps.lfilter(
188
+ [self._state.alpha],
189
+ [1.0, self._state.alpha - 1.0],
190
+ message.data,
191
+ axis=axis_idx,
192
+ zi=self._state.zi,
193
+ )
176
194
  return replace(message, data=expected)
177
195
 
178
196
 
179
197
  class EWMAUnit(BaseTransformerUnit[EWMASettings, AxisArray, AxisArray, EWMATransformer]):
180
198
  SETTINGS = EWMASettings
199
+
200
+ @ez.subscriber(BaseTransformerUnit.INPUT_SETTINGS)
201
+ async def on_settings(self, msg: EWMASettings) -> None:
202
+ """
203
+ Handle settings updates with smart reset behavior.
204
+
205
+ Only resets state if `axis` changes (structural change).
206
+ Changes to `time_constant` or `accumulate` are applied without
207
+ resetting accumulated state.
208
+ """
209
+ old_axis = self.SETTINGS.axis
210
+ self.apply_settings(msg)
211
+
212
+ if msg.axis != old_axis:
213
+ # Axis changed - need full reset
214
+ self.create_processor()
215
+ else:
216
+ # Only accumulate or time_constant changed - keep state
217
+ self.processor.settings = msg
@@ -1,9 +1,8 @@
1
1
  import ezmsg.core as ez
2
2
  import numpy as np
3
+ from ezmsg.baseproc import BaseTransformer, BaseTransformerUnit
3
4
  from ezmsg.util.messages.axisarray import AxisArray, replace
4
5
 
5
- from ezmsg.sigproc.base import BaseTransformer, BaseTransformerUnit
6
-
7
6
 
8
7
  class ExtractAxisSettings(ez.Settings):
9
8
  axis: str = "freq"
@@ -6,10 +6,7 @@ import ezmsg.core as ez
6
6
  import numpy as np
7
7
  import numpy.typing as npt
8
8
  import scipy.signal
9
- from ezmsg.util.messages.axisarray import AxisArray
10
- from ezmsg.util.messages.util import replace
11
-
12
- from ezmsg.sigproc.base import (
9
+ from ezmsg.baseproc import (
13
10
  BaseConsumerUnit,
14
11
  BaseStatefulTransformer,
15
12
  BaseTransformerUnit,
@@ -17,6 +14,8 @@ from ezmsg.sigproc.base import (
17
14
  TransformerType,
18
15
  processor_state,
19
16
  )
17
+ from ezmsg.util.messages.axisarray import AxisArray
18
+ from ezmsg.util.messages.util import replace
20
19
 
21
20
 
22
21
  @dataclass
@@ -4,10 +4,10 @@ import typing
4
4
  import ezmsg.core as ez
5
5
  import numpy as np
6
6
  import scipy.signal as sps
7
+ from ezmsg.baseproc import BaseStatefulTransformer, processor_state
7
8
  from ezmsg.util.messages.axisarray import AxisArray
8
9
  from ezmsg.util.messages.util import replace
9
10
 
10
- from ezmsg.sigproc.base import BaseStatefulTransformer, processor_state
11
11
  from ezmsg.sigproc.filter import (
12
12
  BACoeffs,
13
13
  BaseFilterByDesignTransformerUnit,
@@ -0,0 +1,118 @@
1
+ """Apply a linear transformation: output = scale * input + offset.
2
+
3
+ Supports per-element scale and offset along a specified axis.
4
+ Uses Array API for compatibility with numpy, cupy, torch, etc.
5
+
6
+ For full matrix transformations, use :obj:`AffineTransformTransformer` instead.
7
+ """
8
+
9
+ import ezmsg.core as ez
10
+ import numpy as np
11
+ import numpy.typing as npt
12
+ from array_api_compat import get_namespace
13
+ from ezmsg.util.messages.axisarray import AxisArray
14
+ from ezmsg.util.messages.util import replace
15
+
16
+ from .base import BaseStatefulTransformer, BaseTransformerUnit, processor_state
17
+
18
+
19
+ class LinearTransformSettings(ez.Settings):
20
+ scale: float | list[float] | npt.ArrayLike = 1.0
21
+ """Scale factor(s). Can be a scalar (applied to all elements) or an array
22
+ matching the size of the specified axis for per-element scaling."""
23
+
24
+ offset: float | list[float] | npt.ArrayLike = 0.0
25
+ """Offset value(s). Can be a scalar (applied to all elements) or an array
26
+ matching the size of the specified axis for per-element offset."""
27
+
28
+ axis: str | None = None
29
+ """Axis along which to apply per-element scale/offset. If None, scalar
30
+ scale/offset are broadcast to all elements."""
31
+
32
+
33
+ @processor_state
34
+ class LinearTransformState:
35
+ scale: npt.NDArray = None
36
+ """Prepared scale array for broadcasting."""
37
+
38
+ offset: npt.NDArray = None
39
+ """Prepared offset array for broadcasting."""
40
+
41
+
42
+ class LinearTransformTransformer(
43
+ BaseStatefulTransformer[LinearTransformSettings, AxisArray, AxisArray, LinearTransformState]
44
+ ):
45
+ """Apply linear transformation: output = scale * input + offset.
46
+
47
+ This transformer is optimized for element-wise linear operations with
48
+ optional per-channel (or per-axis) coefficients. For full matrix
49
+ transformations, use :obj:`AffineTransformTransformer` instead.
50
+
51
+ Examples:
52
+ # Uniform scaling and offset
53
+ >>> transformer = LinearTransformTransformer(LinearTransformSettings(scale=2.0, offset=1.0))
54
+
55
+ # Per-channel scaling (e.g., for 3-channel data along "ch" axis)
56
+ >>> transformer = LinearTransformTransformer(LinearTransformSettings(
57
+ ... scale=[0.5, 1.0, 2.0],
58
+ ... offset=[0.0, 0.1, 0.2],
59
+ ... axis="ch"
60
+ ... ))
61
+ """
62
+
63
+ def _hash_message(self, message: AxisArray) -> int:
64
+ """Hash based on shape and axis to detect when broadcast shapes need recalculation."""
65
+ axis = self.settings.axis
66
+ if axis is not None:
67
+ axis_idx = message.get_axis_idx(axis)
68
+ return hash((message.data.ndim, axis_idx, message.data.shape[axis_idx]))
69
+ return hash(message.data.ndim)
70
+
71
+ def _reset_state(self, message: AxisArray) -> None:
72
+ """Prepare scale/offset arrays with proper broadcast shapes."""
73
+ xp = get_namespace(message.data)
74
+ ndim = message.data.ndim
75
+
76
+ scale = self.settings.scale
77
+ offset = self.settings.offset
78
+
79
+ # Convert settings to arrays
80
+ if isinstance(scale, (list, np.ndarray)):
81
+ scale = xp.asarray(scale, dtype=xp.float64)
82
+ else:
83
+ # Scalar: create a 0-d array
84
+ scale = xp.asarray(float(scale), dtype=xp.float64)
85
+
86
+ if isinstance(offset, (list, np.ndarray)):
87
+ offset = xp.asarray(offset, dtype=xp.float64)
88
+ else:
89
+ # Scalar: create a 0-d array
90
+ offset = xp.asarray(float(offset), dtype=xp.float64)
91
+
92
+ # If axis is specified and we have 1-d arrays, reshape for proper broadcasting
93
+ if self.settings.axis is not None and ndim > 0:
94
+ axis_idx = message.get_axis_idx(self.settings.axis)
95
+
96
+ if scale.ndim == 1:
97
+ # Create shape for broadcasting: all 1s except at axis_idx
98
+ broadcast_shape = [1] * ndim
99
+ broadcast_shape[axis_idx] = scale.shape[0]
100
+ scale = xp.reshape(scale, broadcast_shape)
101
+
102
+ if offset.ndim == 1:
103
+ broadcast_shape = [1] * ndim
104
+ broadcast_shape[axis_idx] = offset.shape[0]
105
+ offset = xp.reshape(offset, broadcast_shape)
106
+
107
+ self._state.scale = scale
108
+ self._state.offset = offset
109
+
110
+ def _process(self, message: AxisArray) -> AxisArray:
111
+ result = message.data * self._state.scale + self._state.offset
112
+ return replace(message, data=result)
113
+
114
+
115
+ class LinearTransform(BaseTransformerUnit[LinearTransformSettings, AxisArray, AxisArray, LinearTransformTransformer]):
116
+ """Unit wrapper for LinearTransformTransformer."""
117
+
118
+ SETTINGS = LinearTransformSettings
@@ -3,14 +3,14 @@ from collections import deque
3
3
  import ezmsg.core as ez
4
4
  import numpy as np
5
5
  import numpy.typing as npt
6
- from ezmsg.util.messages.axisarray import AxisArray
7
- from ezmsg.util.messages.util import replace
8
-
9
- from ezmsg.sigproc.base import (
6
+ from ezmsg.baseproc import (
10
7
  BaseAdaptiveTransformer,
11
8
  BaseAdaptiveTransformerUnit,
12
9
  processor_state,
13
10
  )
11
+ from ezmsg.util.messages.axisarray import AxisArray
12
+ from ezmsg.util.messages.util import replace
13
+
14
14
  from ezmsg.sigproc.sampler import SampleMessage
15
15
 
16
16
 
@@ -1,5 +1,6 @@
1
1
  import typing
2
2
 
3
+ import ezmsg.core as ez
3
4
  import numpy as np
4
5
  from ezmsg.baseproc import (
5
6
  BaseStatefulTransformer,
@@ -82,11 +83,38 @@ class AdaptiveStandardScalerTransformer(
82
83
  ]
83
84
  ):
84
85
  def _reset_state(self, message: AxisArray) -> None:
85
- self._state.samps_ewma = EWMATransformer(time_constant=self.settings.time_constant, axis=self.settings.axis)
86
- self._state.vars_sq_ewma = EWMATransformer(time_constant=self.settings.time_constant, axis=self.settings.axis)
86
+ self._state.samps_ewma = EWMATransformer(
87
+ time_constant=self.settings.time_constant,
88
+ axis=self.settings.axis,
89
+ accumulate=self.settings.accumulate,
90
+ )
91
+ self._state.vars_sq_ewma = EWMATransformer(
92
+ time_constant=self.settings.time_constant,
93
+ axis=self.settings.axis,
94
+ accumulate=self.settings.accumulate,
95
+ )
96
+
97
+ @property
98
+ def accumulate(self) -> bool:
99
+ """Whether to accumulate statistics from incoming samples."""
100
+ return self.settings.accumulate
101
+
102
+ @accumulate.setter
103
+ def accumulate(self, value: bool) -> None:
104
+ """
105
+ Set the accumulate mode and propagate to child EWMA transformers.
106
+
107
+ Args:
108
+ value: If True, update statistics with each sample.
109
+ If False, only apply current statistics without updating.
110
+ """
111
+ if self._state.samps_ewma is not None:
112
+ self._state.samps_ewma.settings = replace(self._state.samps_ewma.settings, accumulate=value)
113
+ if self._state.vars_sq_ewma is not None:
114
+ self._state.vars_sq_ewma.settings = replace(self._state.vars_sq_ewma.settings, accumulate=value)
87
115
 
88
116
  def _process(self, message: AxisArray) -> AxisArray:
89
- # Update step
117
+ # Update step (respects accumulate setting via child EWMAs)
90
118
  mean_message = self._state.samps_ewma(message)
91
119
  var_sq_message = self._state.vars_sq_ewma(replace(message, data=message.data**2))
92
120
 
@@ -108,6 +136,27 @@ class AdaptiveStandardScaler(
108
136
  ):
109
137
  SETTINGS = AdaptiveStandardScalerSettings
110
138
 
139
+ @ez.subscriber(BaseTransformerUnit.INPUT_SETTINGS)
140
+ async def on_settings(self, msg: AdaptiveStandardScalerSettings) -> None:
141
+ """
142
+ Handle settings updates with smart reset behavior.
143
+
144
+ Only resets state if `axis` changes (structural change).
145
+ Changes to `time_constant` or `accumulate` are applied without
146
+ resetting accumulated statistics.
147
+ """
148
+ old_axis = self.SETTINGS.axis
149
+ self.apply_settings(msg)
150
+
151
+ if msg.axis != old_axis:
152
+ # Axis changed - need full reset
153
+ self.create_processor()
154
+ else:
155
+ # Update accumulate on processor (propagates to child EWMAs)
156
+ self.processor.accumulate = msg.accumulate
157
+ # Also update own settings reference
158
+ self.processor.settings = msg
159
+
111
160
 
112
161
  # Backwards compatibility...
113
162
  def scaler_np(time_constant: float = 1.0, axis: str | None = None) -> AdaptiveStandardScalerTransformer:
@@ -4,7 +4,12 @@ import scipy.special
4
4
  from ezmsg.util.messages.axisarray import AxisArray
5
5
  from frozendict import frozendict
6
6
 
7
- from ezmsg.sigproc.activation import ACTIVATIONS, ActivationFunction, activation
7
+ from ezmsg.sigproc.activation import (
8
+ ACTIVATIONS,
9
+ ActivationFunction,
10
+ ActivationSettings,
11
+ ActivationTransformer,
12
+ )
8
13
 
9
14
 
10
15
  @pytest.mark.parametrize("function", [_ for _ in ActivationFunction] + ActivationFunction.options())
@@ -25,8 +30,8 @@ def test_activation(function: str):
25
30
  )
26
31
  yield msg
27
32
 
28
- proc = activation(function=function)
29
- out_msgs = [proc.send(_) for _ in msg_generator()]
33
+ proc = ActivationTransformer(ActivationSettings(function=function))
34
+ out_msgs = [proc(_) for _ in msg_generator()]
30
35
  out_dat = AxisArray.concatenate(*out_msgs, dim="time").data
31
36
 
32
37
  if function in ACTIVATIONS:
@@ -4,11 +4,16 @@ from pathlib import Path
4
4
  import numpy as np
5
5
  from ezmsg.util.messages.axisarray import AxisArray
6
6
 
7
- from ezmsg.sigproc.affinetransform import affine_transform, common_rereference
7
+ from ezmsg.sigproc.affinetransform import (
8
+ AffineTransformSettings,
9
+ AffineTransformTransformer,
10
+ CommonRereferenceSettings,
11
+ CommonRereferenceTransformer,
12
+ )
8
13
  from tests.helpers.util import assert_messages_equal
9
14
 
10
15
 
11
- def test_affine_generator():
16
+ def test_affine_transform():
12
17
  n_times = 13
13
18
  n_chans = 64
14
19
  in_dat = np.arange(n_times * n_chans).reshape(n_times, n_chans)
@@ -20,16 +25,16 @@ def test_affine_generator():
20
25
 
21
26
  backup = [copy.deepcopy(msg_in)]
22
27
 
23
- gen = affine_transform(weights=np.eye(n_chans), axis="ch")
24
- msg_out = gen.send(msg_in)
28
+ xformer = AffineTransformTransformer(AffineTransformSettings(weights=np.eye(n_chans), axis="ch"))
29
+ msg_out = xformer(msg_in)
25
30
  assert msg_out.data.shape == in_dat.shape
26
31
  assert np.allclose(msg_out.data, in_dat)
27
32
  assert not np.may_share_memory(msg_out.data, in_dat)
28
33
 
29
34
  assert_messages_equal([msg_in], backup)
30
35
 
31
- # Send again just to make sure the generator doesn't crash
32
- _ = gen.send(msg_in)
36
+ # Call again just to make sure the transformer doesn't crash
37
+ _ = xformer(msg_in)
33
38
 
34
39
  # Test with weights from a CSV file.
35
40
  csv_path = Path(__file__).parents[1] / "resources" / "xform.csv"
@@ -37,27 +42,29 @@ def test_affine_generator():
37
42
  expected_out = in_dat @ weights.T
38
43
  # Same result: expected_out = np.vstack([(step[None, :] * weights).sum(axis=1) for step in in_dat])
39
44
 
40
- gen = affine_transform(weights=csv_path, axis="ch", right_multiply=False)
41
- msg_out = gen.send(msg_in)
45
+ xformer = AffineTransformTransformer(AffineTransformSettings(weights=csv_path, axis="ch", right_multiply=False))
46
+ msg_out = xformer(msg_in)
42
47
  assert np.allclose(msg_out.data, expected_out)
43
48
  assert len(msg_out.axes["ch"].data) == weights.shape[0]
44
49
  assert (msg_out.axes["ch"].data[:-1] == msg_in.axes["ch"].data).all()
45
50
 
46
51
  # Try again as str, not Path
47
- gen = affine_transform(weights=str(csv_path), axis="ch", right_multiply=False)
48
- msg_out = gen.send(msg_in)
52
+ xformer = AffineTransformTransformer(
53
+ AffineTransformSettings(weights=str(csv_path), axis="ch", right_multiply=False)
54
+ )
55
+ msg_out = xformer(msg_in)
49
56
  assert np.allclose(msg_out.data, expected_out)
50
57
  assert len(msg_out.axes["ch"].data) == weights.shape[0]
51
58
 
52
59
  # Try again as direct ndarray
53
- gen = affine_transform(weights=weights, axis="ch", right_multiply=False)
54
- msg_out = gen.send(msg_in)
60
+ xformer = AffineTransformTransformer(AffineTransformSettings(weights=weights, axis="ch", right_multiply=False))
61
+ msg_out = xformer(msg_in)
55
62
  assert np.allclose(msg_out.data, expected_out)
56
63
  assert len(msg_out.axes["ch"].data) == weights.shape[0]
57
64
 
58
65
  # One more time, but we pre-transpose the weights and do not override right_multiply
59
- gen = affine_transform(weights=weights.T, axis="ch", right_multiply=True)
60
- msg_out = gen.send(msg_in)
66
+ xformer = AffineTransformTransformer(AffineTransformSettings(weights=weights.T, axis="ch", right_multiply=True))
67
+ msg_out = xformer(msg_in)
61
68
  assert np.allclose(msg_out.data, expected_out)
62
69
  assert len(msg_out.axes["ch"].data) == weights.shape[0]
63
70
 
@@ -70,9 +77,9 @@ def test_affine_passthrough():
70
77
 
71
78
  backup = [copy.deepcopy(msg_in)]
72
79
 
73
- gen = affine_transform(weights="passthrough", axis="does not matter")
74
- msg_out = gen.send(msg_in)
75
- # We wouldn't want out_data is in_dat ezmsg pipeline but it's fine for the generator
80
+ xformer = AffineTransformTransformer(AffineTransformSettings(weights="passthrough", axis="does not matter"))
81
+ msg_out = xformer(msg_in)
82
+ # We wouldn't want out_data is in_dat ezmsg pipeline but it's fine for the transformer
76
83
  assert msg_out.data is in_dat
77
84
  assert_messages_equal([msg_out], backup)
78
85
 
@@ -85,8 +92,8 @@ def test_common_rereference():
85
92
 
86
93
  backup = [copy.deepcopy(msg_in)]
87
94
 
88
- gen = common_rereference(mode="mean", axis="ch", include_current=True)
89
- msg_out = gen.send(msg_in)
95
+ xformer = CommonRereferenceTransformer(CommonRereferenceSettings(mode="mean", axis="ch", include_current=True))
96
+ msg_out = xformer(msg_in)
90
97
  assert np.array_equal(
91
98
  msg_out.data,
92
99
  msg_in.data - np.mean(msg_in.data, axis=1, keepdims=True),
@@ -103,17 +110,17 @@ def test_common_rereference():
103
110
  expected_out.append(msg_in.data[..., ch_ix] - np.mean(msg_in.data[..., idx], axis=1))
104
111
  expected_out = np.stack(expected_out).T
105
112
 
106
- gen = common_rereference(mode="mean", axis="ch", include_current=False)
107
- msg_out = gen.send(msg_in) # 41 us
113
+ xformer = CommonRereferenceTransformer(CommonRereferenceSettings(mode="mean", axis="ch", include_current=False))
114
+ msg_out = xformer(msg_in) # 41 us
108
115
  assert np.allclose(msg_out.data, expected_out)
109
116
 
110
- # Instead of CAR, we could use affine_transform with weights that reproduce CAR.
117
+ # Instead of CAR, we could use AffineTransformTransformer with weights that reproduce CAR.
111
118
  # However, this method is 30x slower than above. (Actual difference varies depending on data shape).
112
119
  if False:
113
120
  weights = -np.ones((n_chans, n_chans)) / (n_chans - 1)
114
121
  np.fill_diagonal(weights, 1)
115
- gen = affine_transform(weights=weights, axis="ch")
116
- msg_out = gen.send(msg_in)
122
+ xformer = AffineTransformTransformer(AffineTransformSettings(weights=weights, axis="ch"))
123
+ msg_out = xformer(msg_in)
117
124
  assert np.allclose(msg_out.data, expected_out)
118
125
 
119
126
 
@@ -123,7 +130,7 @@ def test_car_passthrough():
123
130
  in_dat = np.arange(n_times * n_chans).reshape(n_times, n_chans)
124
131
  msg_in = AxisArray(in_dat, dims=["time", "ch"])
125
132
 
126
- gen = common_rereference(mode="passthrough")
127
- msg_out = gen.send(msg_in)
133
+ xformer = CommonRereferenceTransformer(CommonRereferenceSettings(mode="passthrough"))
134
+ msg_out = xformer(msg_in)
128
135
  assert np.array_equal(msg_out.data, in_dat)
129
136
  assert np.may_share_memory(msg_out.data, in_dat)