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.
Files changed (163) hide show
  1. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/PKG-INFO +1 -1
  2. ezmsg_sigproc-2.9.0/docs/source/guides/explanations/array_api.rst +156 -0
  3. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/sigproc/content-sigproc.rst +3 -1
  4. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/__version__.py +2 -2
  5. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/aggregate.py +9 -0
  6. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/coordinatespaces.py +22 -5
  7. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/diff.py +15 -4
  8. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/linear.py +7 -5
  9. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/math/abs.py +11 -6
  10. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/math/add.py +1 -2
  11. ezmsg_sigproc-2.9.0/src/ezmsg/sigproc/math/clip.py +48 -0
  12. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/math/difference.py +9 -3
  13. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/math/invert.py +8 -3
  14. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/math/log.py +19 -8
  15. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/math/scale.py +8 -3
  16. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/transpose.py +22 -7
  17. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_add_system.py +2 -2
  18. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_difference_system.py +3 -3
  19. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_sampler_system.py +1 -1
  20. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_math.py +6 -6
  21. ezmsg_sigproc-2.8.0/src/ezmsg/sigproc/math/clip.py +0 -43
  22. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/.github/workflows/docs.yml +0 -0
  23. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/.github/workflows/python-publish.yml +0 -0
  24. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/.github/workflows/python-tests.yml +0 -0
  25. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/.gitignore +0 -0
  26. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/.pre-commit-config.yaml +0 -0
  27. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/LICENSE +0 -0
  28. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/README.md +0 -0
  29. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/Makefile +0 -0
  30. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/make.bat +0 -0
  31. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/_templates/autosummary/module.rst +0 -0
  32. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/api/index.rst +0 -0
  33. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/conf.py +0 -0
  34. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/HybridBuffer.md +0 -0
  35. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/explanations/sigproc.rst +0 -0
  36. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/how-tos/signalprocessing/adaptive.rst +0 -0
  37. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/how-tos/signalprocessing/checkpoint.rst +0 -0
  38. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/how-tos/signalprocessing/composite.rst +0 -0
  39. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/how-tos/signalprocessing/content-signalprocessing.rst +0 -0
  40. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/how-tos/signalprocessing/processor.rst +0 -0
  41. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/how-tos/signalprocessing/standalone.rst +0 -0
  42. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/how-tos/signalprocessing/stateful.rst +0 -0
  43. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/how-tos/signalprocessing/unit.rst +0 -0
  44. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/img/HybridBufferBasic.svg +0 -0
  45. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/img/HybridBufferOverflow.svg +0 -0
  46. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/sigproc/architecture.rst +0 -0
  47. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/sigproc/base.rst +0 -0
  48. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/sigproc/processors.rst +0 -0
  49. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/sigproc/units.rst +0 -0
  50. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/guides/tutorials/signalprocessing.rst +0 -0
  51. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/docs/source/index.md +0 -0
  52. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/pyproject.toml +0 -0
  53. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/__init__.py +0 -0
  54. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/activation.py +0 -0
  55. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/adaptive_lattice_notch.py +0 -0
  56. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/affinetransform.py +0 -0
  57. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/bandpower.py +0 -0
  58. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/base.py +0 -0
  59. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/butterworthfilter.py +0 -0
  60. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/butterworthzerophase.py +0 -0
  61. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/cheby.py +0 -0
  62. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/combfilter.py +0 -0
  63. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/decimate.py +0 -0
  64. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/denormalize.py +0 -0
  65. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/detrend.py +0 -0
  66. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/downsample.py +0 -0
  67. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/ewma.py +0 -0
  68. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/ewmfilter.py +0 -0
  69. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/extract_axis.py +0 -0
  70. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/fbcca.py +0 -0
  71. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/filter.py +0 -0
  72. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/filterbank.py +0 -0
  73. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/filterbankdesign.py +0 -0
  74. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/fir_hilbert.py +0 -0
  75. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/fir_pmc.py +0 -0
  76. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/firfilter.py +0 -0
  77. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/gaussiansmoothing.py +0 -0
  78. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/kaiser.py +0 -0
  79. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/math/__init__.py +0 -0
  80. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/messages.py +0 -0
  81. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/quantize.py +0 -0
  82. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/resample.py +0 -0
  83. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/rollingscaler.py +0 -0
  84. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/sampler.py +0 -0
  85. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/scaler.py +0 -0
  86. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/signalinjector.py +0 -0
  87. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/slicer.py +0 -0
  88. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/spectral.py +0 -0
  89. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/spectrogram.py +0 -0
  90. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/spectrum.py +0 -0
  91. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/util/__init__.py +0 -0
  92. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/util/asio.py +0 -0
  93. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/util/axisarray_buffer.py +0 -0
  94. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/util/buffer.py +0 -0
  95. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/util/message.py +0 -0
  96. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/util/profile.py +0 -0
  97. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/util/sparse.py +0 -0
  98. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/util/typeresolution.py +0 -0
  99. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/wavelets.py +0 -0
  100. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/src/ezmsg/sigproc/window.py +0 -0
  101. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/__init__.py +0 -0
  102. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/conftest.py +0 -0
  103. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/helpers/__init__.py +0 -0
  104. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/helpers/synth.py +0 -0
  105. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/helpers/util.py +0 -0
  106. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/bytewax/test_spectrum_bytewax.py +0 -0
  107. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/bytewax/test_window_bytewax.py +0 -0
  108. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_butterworth_system.py +0 -0
  109. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_butterworthzerophase_system.py +0 -0
  110. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_coordinatespaces_system.py +0 -0
  111. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_decimate_system.py +0 -0
  112. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_downsample_system.py +0 -0
  113. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_filter_system.py +0 -0
  114. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_fir_hilbert_system.py +0 -0
  115. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_fir_pmc_system.py +0 -0
  116. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_rollingscaler_system.py +0 -0
  117. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_scaler_system.py +0 -0
  118. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_spectrum_system.py +0 -0
  119. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/integration/ezmsg/test_window_system.py +0 -0
  120. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/resources/xform.csv +0 -0
  121. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/test_profile.py +0 -0
  122. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/buffer/test_axisarray_buffer.py +0 -0
  123. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/buffer/test_buffer.py +0 -0
  124. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/buffer/test_buffer_overflow.py +0 -0
  125. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_activation.py +0 -0
  126. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_adaptive_lattice_notch.py +0 -0
  127. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_affine_transform.py +0 -0
  128. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_aggregate.py +0 -0
  129. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_bandpower.py +0 -0
  130. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_base.py +0 -0
  131. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_butter.py +0 -0
  132. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_butterworthzerophase.py +0 -0
  133. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_combfilter.py +0 -0
  134. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_coordinatespaces.py +0 -0
  135. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_denormalize.py +0 -0
  136. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_diff.py +0 -0
  137. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_downsample.py +0 -0
  138. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_ewma.py +0 -0
  139. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_extract_axis.py +0 -0
  140. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_fbcca.py +0 -0
  141. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_filter.py +0 -0
  142. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_filterbank.py +0 -0
  143. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_filterbankdesign.py +0 -0
  144. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_fir_hilbert.py +0 -0
  145. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_fir_pmc.py +0 -0
  146. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_firfilter.py +0 -0
  147. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_gaussian_smoothing_filter.py +0 -0
  148. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_kaiser.py +0 -0
  149. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_linear.py +0 -0
  150. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_math_add.py +0 -0
  151. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_math_difference.py +0 -0
  152. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_quantize.py +0 -0
  153. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_resample.py +0 -0
  154. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_rollingscaler.py +0 -0
  155. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_sampler.py +0 -0
  156. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_scaler.py +0 -0
  157. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_slicer.py +0 -0
  158. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_spectrogram.py +0 -0
  159. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_spectrum.py +0 -0
  160. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_transpose.py +0 -0
  161. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_util.py +0 -0
  162. {ezmsg_sigproc-2.8.0 → ezmsg_sigproc-2.9.0}/tests/unit/test_wavelets.py +0 -0
  163. {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.8.0
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 expected to be a numpy array.
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.8.0'
32
- __version_tuple__ = version_tuple = (2, 8, 0)
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
- return r * np.exp(1j * theta)
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
- return np.abs(z), np.angle(z)
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
- return np.real(z), np.imag(z)
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 = np.stack([out_a, out_b], axis=axis_idx)
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 = np.diff(
46
- np.concatenate((self.state.last_dat, message.data), axis=ax_idx),
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 convers the diff to a derivative. e.g., diff of position becomes velocity.
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
- """Apply a linear transformation: output = scale * input + offset.
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
- """Take the absolute value of the data."""
2
- # TODO: Array API
1
+ """
2
+ Take the absolute value of the data.
3
3
 
4
- import numpy as np
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
- return replace(message, data=np.abs(message.data))
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
- """Take the difference between 2 signals or between a signal and a constant value."""
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/data transformer."""
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
- """Take the logarithm of the data."""
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
- import numpy as np
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 and np.any(data <= 0) and np.issubdtype(data.dtype, np.floating):
24
- data = np.clip(data, a_min=np.finfo(data.dtype).tiny, a_max=None)
25
- return replace(message, data=np.log(data) / np.log(self.settings.base))
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
- """Scale the data by a constant factor."""
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
- # If the memory is already contiguous in the correct order, np.require won't do anything.
95
- msg_out = replace(
96
- message,
97
- data=np.require(message.data, requirements=self.settings.order.upper()[0]),
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 = np.transpose(message.data, axes=self.state.axes_ints)
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) == n_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) == n_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()
@@ -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) == n_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) == n_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) == n_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) == n_msgs
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]