vidmag 0.2.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 (229) hide show
  1. vidmag-0.2.0/.claude/rules/development-practices.md +87 -0
  2. vidmag-0.2.0/.editorconfig +30 -0
  3. vidmag-0.2.0/.gitattributes +35 -0
  4. vidmag-0.2.0/.github/ISSUE_TEMPLATE/bug.md +33 -0
  5. vidmag-0.2.0/.github/ISSUE_TEMPLATE/hardware-result.md +32 -0
  6. vidmag-0.2.0/.github/PULL_REQUEST_TEMPLATE.md +32 -0
  7. vidmag-0.2.0/.github/actionlint.yaml +10 -0
  8. vidmag-0.2.0/.github/dependabot.yml +31 -0
  9. vidmag-0.2.0/.github/workflows/ci.yml +397 -0
  10. vidmag-0.2.0/.github/workflows/deploy-pages.yml +44 -0
  11. vidmag-0.2.0/.github/workflows/gpu.yml +201 -0
  12. vidmag-0.2.0/.github/workflows/lint.yml +63 -0
  13. vidmag-0.2.0/.github/workflows/release.yml +78 -0
  14. vidmag-0.2.0/.gitignore +52 -0
  15. vidmag-0.2.0/.pre-commit-config.yaml +48 -0
  16. vidmag-0.2.0/.readthedocs.yaml +39 -0
  17. vidmag-0.2.0/CHANGELOG.md +137 -0
  18. vidmag-0.2.0/CITATION.cff +50 -0
  19. vidmag-0.2.0/CLAUDE.md +297 -0
  20. vidmag-0.2.0/CONTRIBUTING.md +109 -0
  21. vidmag-0.2.0/LICENSE +58 -0
  22. vidmag-0.2.0/Makefile +138 -0
  23. vidmag-0.2.0/PKG-INFO +336 -0
  24. vidmag-0.2.0/README.md +283 -0
  25. vidmag-0.2.0/SECURITY.md +33 -0
  26. vidmag-0.2.0/benches/apple_m2_max_opencl_2026-08-10.md +93 -0
  27. vidmag-0.2.0/benches/baseline_tests_2026-08-09_macos.txt +3 -0
  28. vidmag-0.2.0/benches/baseline_tests_2026-08-09_osiris_rtx3090.txt +26 -0
  29. vidmag-0.2.0/benches/bench_a100.json +261 -0
  30. vidmag-0.2.0/benches/bench_h100.json +252 -0
  31. vidmag-0.2.0/benches/bench_p100.json +190 -0
  32. vidmag-0.2.0/benches/bench_rtx3090.json +251 -0
  33. vidmag-0.2.0/benches/bench_t4.json +94 -0
  34. vidmag-0.2.0/benches/kaggle_runs/README.md +27 -0
  35. vidmag-0.2.0/benches/kaggle_runs/evm-cuda-baseline.log +356 -0
  36. vidmag-0.2.0/benches/kaggle_runs/evm-cuda-fp16-profile.log +253 -0
  37. vidmag-0.2.0/benches/kaggle_runs/evm-cuda-gpu-comparison.log +462 -0
  38. vidmag-0.2.0/benches/kaggle_runs/evm-cuda-gpu-comparison_2026-08-22_p100.log +105 -0
  39. vidmag-0.2.0/benches/kaggle_runs/evm-cuda-tests.log +393 -0
  40. vidmag-0.2.0/data/.gitkeep +0 -0
  41. vidmag-0.2.0/docs/changelog.md +3 -0
  42. vidmag-0.2.0/docs/comparison.md +48 -0
  43. vidmag-0.2.0/docs/concepts/backends.md +158 -0
  44. vidmag-0.2.0/docs/concepts/how-it-works.md +61 -0
  45. vidmag-0.2.0/docs/concepts/parameters.md +63 -0
  46. vidmag-0.2.0/docs/concepts/phase-based.md +92 -0
  47. vidmag-0.2.0/docs/concepts/pitfalls.md +63 -0
  48. vidmag-0.2.0/docs/contributing.md +3 -0
  49. vidmag-0.2.0/docs/dev/PLAN.md +564 -0
  50. vidmag-0.2.0/docs/dev/archive/README.md +51 -0
  51. vidmag-0.2.0/docs/dev/archive/blog_bandwidth_limit.md +227 -0
  52. vidmag-0.2.0/docs/dev/archive/blog_further_optimizations.md +915 -0
  53. vidmag-0.2.0/docs/dev/archive/blog_speedup.md +685 -0
  54. vidmag-0.2.0/docs/dev/gpu-runner.md +218 -0
  55. vidmag-0.2.0/docs/dev/packaging-notes.md +208 -0
  56. vidmag-0.2.0/docs/dev/release-checklist.md +84 -0
  57. vidmag-0.2.0/docs/dev/torch-backend-notes.md +124 -0
  58. vidmag-0.2.0/docs/getting-started/first-result.md +66 -0
  59. vidmag-0.2.0/docs/getting-started/install.md +88 -0
  60. vidmag-0.2.0/docs/img/baby_demo.gif +0 -0
  61. vidmag-0.2.0/docs/img/face_demo.gif +0 -0
  62. vidmag-0.2.0/docs/img/og_preview.png +0 -0
  63. vidmag-0.2.0/docs/index.md +74 -0
  64. vidmag-0.2.0/docs/internals/design.md +21 -0
  65. vidmag-0.2.0/docs/internals/making-it-fast.md +354 -0
  66. vidmag-0.2.0/docs/licence.md +41 -0
  67. vidmag-0.2.0/docs/performance.md +204 -0
  68. vidmag-0.2.0/docs/recipes/building-blocks.md +103 -0
  69. vidmag-0.2.0/docs/recipes/motion.md +77 -0
  70. vidmag-0.2.0/docs/recipes/pulse.md +67 -0
  71. vidmag-0.2.0/docs/recipes/streaming.md +95 -0
  72. vidmag-0.2.0/docs/recipes/vibration.md +59 -0
  73. vidmag-0.2.0/docs/reference/api.md +22 -0
  74. vidmag-0.2.0/docs/reference/cli.md +63 -0
  75. vidmag-0.2.0/docs/reference/ops.md +33 -0
  76. vidmag-0.2.0/docs/robots.txt +4 -0
  77. vidmag-0.2.0/docs/stability.md +55 -0
  78. vidmag-0.2.0/docs/video/baby_input.mp4 +0 -0
  79. vidmag-0.2.0/docs/video/baby_motion.mp4 +0 -0
  80. vidmag-0.2.0/docs/video/face_input.mp4 +0 -0
  81. vidmag-0.2.0/docs/video/face_pulse.mp4 +0 -0
  82. vidmag-0.2.0/mkdocs.yml +113 -0
  83. vidmag-0.2.0/pyproject.toml +436 -0
  84. vidmag-0.2.0/scripts/cloud/colab_benchmark.ipynb +295 -0
  85. vidmag-0.2.0/scripts/cloud/kaggle/README.md +66 -0
  86. vidmag-0.2.0/scripts/cloud/kaggle/kernel-metadata.json +13 -0
  87. vidmag-0.2.0/scripts/cloud/kaggle/run_gpu_comparison.py +242 -0
  88. vidmag-0.2.0/scripts/dev/make_golden_fixtures.py +139 -0
  89. vidmag-0.2.0/scripts/dev/record_backend_bench.py +158 -0
  90. vidmag-0.2.0/scripts/dev/record_gpu_bench.py +228 -0
  91. vidmag-0.2.0/scripts/dev/verify_install.sh +114 -0
  92. vidmag-0.2.0/scripts/download_samples.py +119 -0
  93. vidmag-0.2.0/scripts/experiments/README.md +19 -0
  94. vidmag-0.2.0/scripts/experiments/bound_microbench.py +245 -0
  95. vidmag-0.2.0/scripts/experiments/measure_fp16_experiments.py +164 -0
  96. vidmag-0.2.0/scripts/experiments/prove_fp16_cost.py +209 -0
  97. vidmag-0.2.0/scripts/profile_full_comparison.py +342 -0
  98. vidmag-0.2.0/scripts/render_cuda_videos.py +101 -0
  99. vidmag-0.2.0/scripts/run_evm.py +36 -0
  100. vidmag-0.2.0/src/vidmag/__init__.py +131 -0
  101. vidmag-0.2.0/src/vidmag/_cli.py +619 -0
  102. vidmag-0.2.0/src/vidmag/api.py +414 -0
  103. vidmag-0.2.0/src/vidmag/backend/__init__.py +58 -0
  104. vidmag-0.2.0/src/vidmag/backend/generic.py +267 -0
  105. vidmag-0.2.0/src/vidmag/backend/ops.py +226 -0
  106. vidmag-0.2.0/src/vidmag/backend/pipelines.py +169 -0
  107. vidmag-0.2.0/src/vidmag/backend/registry.py +265 -0
  108. vidmag-0.2.0/src/vidmag/cpu/__init__.py +70 -0
  109. vidmag-0.2.0/src/vidmag/cpu/backend.py +148 -0
  110. vidmag-0.2.0/src/vidmag/cpu/csp.py +254 -0
  111. vidmag-0.2.0/src/vidmag/cpu/filters.py +143 -0
  112. vidmag-0.2.0/src/vidmag/cpu/magnify.py +648 -0
  113. vidmag-0.2.0/src/vidmag/cpu/ops.py +128 -0
  114. vidmag-0.2.0/src/vidmag/cpu/phase_magnify.py +209 -0
  115. vidmag-0.2.0/src/vidmag/cpu/pyramids.py +256 -0
  116. vidmag-0.2.0/src/vidmag/cuda/CMakeLists.txt +170 -0
  117. vidmag-0.2.0/src/vidmag/cuda/DESIGN.md +400 -0
  118. vidmag-0.2.0/src/vidmag/cuda/__init__.py +128 -0
  119. vidmag-0.2.0/src/vidmag/cuda/_common.py +59 -0
  120. vidmag-0.2.0/src/vidmag/cuda/array.py +215 -0
  121. vidmag-0.2.0/src/vidmag/cuda/batched.py +723 -0
  122. vidmag-0.2.0/src/vidmag/cuda/benchmark.py +342 -0
  123. vidmag-0.2.0/src/vidmag/cuda/bindings.cpp +2120 -0
  124. vidmag-0.2.0/src/vidmag/cuda/include/evm_check.cuh +40 -0
  125. vidmag-0.2.0/src/vidmag/cuda/include/evm_common.cuh +118 -0
  126. vidmag-0.2.0/src/vidmag/cuda/include/evm_dlpack.cuh +66 -0
  127. vidmag-0.2.0/src/vidmag/cuda/kernels/amplify_render.cu +430 -0
  128. vidmag-0.2.0/src/vidmag/cuda/kernels/blur_dn.cu +70 -0
  129. vidmag-0.2.0/src/vidmag/cuda/kernels/butter_bandpass.cu +67 -0
  130. vidmag-0.2.0/src/vidmag/cuda/kernels/color_cvt.cu +123 -0
  131. vidmag-0.2.0/src/vidmag/cuda/kernels/fp16_cvt.cu +52 -0
  132. vidmag-0.2.0/src/vidmag/cuda/kernels/ideal_bandpass.cu +109 -0
  133. vidmag-0.2.0/src/vidmag/cuda/kernels/iir_bandpass.cu +297 -0
  134. vidmag-0.2.0/src/vidmag/cuda/kernels/lpyr.cu +287 -0
  135. vidmag-0.2.0/src/vidmag/cuda/kernels/spatial.cu +956 -0
  136. vidmag-0.2.0/src/vidmag/cuda/kernels/transpose.cu +220 -0
  137. vidmag-0.2.0/src/vidmag/cuda/ops.py +299 -0
  138. vidmag-0.2.0/src/vidmag/cuda/pipelines.py +429 -0
  139. vidmag-0.2.0/src/vidmag/cuda/runtime.py +56 -0
  140. vidmag-0.2.0/src/vidmag/io/__init__.py +30 -0
  141. vidmag-0.2.0/src/vidmag/io/capture.py +102 -0
  142. vidmag-0.2.0/src/vidmag/io/h264.py +60 -0
  143. vidmag-0.2.0/src/vidmag/io/video.py +142 -0
  144. vidmag-0.2.0/src/vidmag/metal/__init__.py +25 -0
  145. vidmag-0.2.0/src/vidmag/metal/array.py +107 -0
  146. vidmag-0.2.0/src/vidmag/metal/kernels.metal +346 -0
  147. vidmag-0.2.0/src/vidmag/metal/ops.py +342 -0
  148. vidmag-0.2.0/src/vidmag/metal/runtime.py +139 -0
  149. vidmag-0.2.0/src/vidmag/notebook.py +34 -0
  150. vidmag-0.2.0/src/vidmag/opencl/__init__.py +29 -0
  151. vidmag-0.2.0/src/vidmag/opencl/array.py +109 -0
  152. vidmag-0.2.0/src/vidmag/opencl/kernels.cl +368 -0
  153. vidmag-0.2.0/src/vidmag/opencl/ops.py +373 -0
  154. vidmag-0.2.0/src/vidmag/opencl/runtime.py +149 -0
  155. vidmag-0.2.0/src/vidmag/presets.py +171 -0
  156. vidmag-0.2.0/src/vidmag/stream.py +297 -0
  157. vidmag-0.2.0/src/vidmag/torch_backend/__init__.py +40 -0
  158. vidmag-0.2.0/src/vidmag/torch_backend/ops.py +319 -0
  159. vidmag-0.2.0/src/vidmag/torch_backend/runtime.py +70 -0
  160. vidmag-0.2.0/src/vidmag/vulkan/__init__.py +28 -0
  161. vidmag-0.2.0/src/vidmag/vulkan/array.py +112 -0
  162. vidmag-0.2.0/src/vidmag/vulkan/ops.py +370 -0
  163. vidmag-0.2.0/src/vidmag/vulkan/runtime.py +351 -0
  164. vidmag-0.2.0/src/vidmag/vulkan/shaders/_common.glsl +16 -0
  165. vidmag-0.2.0/src/vidmag/vulkan/shaders/add_and_quantize.comp +32 -0
  166. vidmag-0.2.0/src/vidmag/vulkan/shaders/add_and_quantize.spv +0 -0
  167. vidmag-0.2.0/src/vidmag/vulkan/shaders/add_into.comp +11 -0
  168. vidmag-0.2.0/src/vidmag/vulkan/shaders/add_into.spv +0 -0
  169. vidmag-0.2.0/src/vidmag/vulkan/shaders/apply_gain.comp +12 -0
  170. vidmag-0.2.0/src/vidmag/vulkan/shaders/apply_gain.spv +0 -0
  171. vidmag-0.2.0/src/vidmag/vulkan/shaders/band_project.comp +16 -0
  172. vidmag-0.2.0/src/vidmag/vulkan/shaders/band_project.spv +0 -0
  173. vidmag-0.2.0/src/vidmag/vulkan/shaders/bgr_u8_to_ntsc.comp +25 -0
  174. vidmag-0.2.0/src/vidmag/vulkan/shaders/bgr_u8_to_ntsc.spv +0 -0
  175. vidmag-0.2.0/src/vidmag/vulkan/shaders/build.py +82 -0
  176. vidmag-0.2.0/src/vidmag/vulkan/shaders/butter_bandpass.comp +22 -0
  177. vidmag-0.2.0/src/vidmag/vulkan/shaders/butter_bandpass.spv +0 -0
  178. vidmag-0.2.0/src/vidmag/vulkan/shaders/corr_dn_cols.comp +39 -0
  179. vidmag-0.2.0/src/vidmag/vulkan/shaders/corr_dn_cols.spv +0 -0
  180. vidmag-0.2.0/src/vidmag/vulkan/shaders/corr_dn_rows.comp +39 -0
  181. vidmag-0.2.0/src/vidmag/vulkan/shaders/corr_dn_rows.spv +0 -0
  182. vidmag-0.2.0/src/vidmag/vulkan/shaders/iir_bandpass.comp +19 -0
  183. vidmag-0.2.0/src/vidmag/vulkan/shaders/iir_bandpass.spv +0 -0
  184. vidmag-0.2.0/src/vidmag/vulkan/shaders/iir_step.comp +16 -0
  185. vidmag-0.2.0/src/vidmag/vulkan/shaders/iir_step.spv +0 -0
  186. vidmag-0.2.0/src/vidmag/vulkan/shaders/resize_bilinear.comp +40 -0
  187. vidmag-0.2.0/src/vidmag/vulkan/shaders/resize_bilinear.spv +0 -0
  188. vidmag-0.2.0/src/vidmag/vulkan/shaders/subtract.comp +12 -0
  189. vidmag-0.2.0/src/vidmag/vulkan/shaders/subtract.spv +0 -0
  190. vidmag-0.2.0/src/vidmag/vulkan/shaders/up_conv_cols.comp +43 -0
  191. vidmag-0.2.0/src/vidmag/vulkan/shaders/up_conv_cols.spv +0 -0
  192. vidmag-0.2.0/src/vidmag/vulkan/shaders/up_conv_rows.comp +43 -0
  193. vidmag-0.2.0/src/vidmag/vulkan/shaders/up_conv_rows.spv +0 -0
  194. vidmag-0.2.0/tests/__init__.py +39 -0
  195. vidmag-0.2.0/tests/cuda/conftest.py +73 -0
  196. vidmag-0.2.0/tests/cuda/test_api_cuda.py +197 -0
  197. vidmag-0.2.0/tests/cuda/test_batched_kernels.py +393 -0
  198. vidmag-0.2.0/tests/cuda/test_benchmark.py +97 -0
  199. vidmag-0.2.0/tests/cuda/test_color_cvt.py +73 -0
  200. vidmag-0.2.0/tests/cuda/test_device_array.py +164 -0
  201. vidmag-0.2.0/tests/cuda/test_device_buffer.py +48 -0
  202. vidmag-0.2.0/tests/cuda/test_device_resident_bandpass.py +159 -0
  203. vidmag-0.2.0/tests/cuda/test_lpyr.py +66 -0
  204. vidmag-0.2.0/tests/cuda/test_ops.py +199 -0
  205. vidmag-0.2.0/tests/cuda/test_pipelines.py +236 -0
  206. vidmag-0.2.0/tests/cuda/test_spatial.py +58 -0
  207. vidmag-0.2.0/tests/cuda/test_temporal.py +75 -0
  208. vidmag-0.2.0/tests/cuda/test_upsample.py +64 -0
  209. vidmag-0.2.0/tests/fixtures/golden_color_gdown_ideal.npz +0 -0
  210. vidmag-0.2.0/tests/fixtures/golden_input.npz +0 -0
  211. vidmag-0.2.0/tests/fixtures/golden_motion_lpyr_butter.npz +0 -0
  212. vidmag-0.2.0/tests/fixtures/golden_motion_lpyr_ideal.npz +0 -0
  213. vidmag-0.2.0/tests/fixtures/golden_motion_lpyr_iir.npz +0 -0
  214. vidmag-0.2.0/tests/test_against_mit_reference.py +74 -0
  215. vidmag-0.2.0/tests/test_api.py +553 -0
  216. vidmag-0.2.0/tests/test_backend_registry.py +215 -0
  217. vidmag-0.2.0/tests/test_cli.py +278 -0
  218. vidmag-0.2.0/tests/test_documentation.py +139 -0
  219. vidmag-0.2.0/tests/test_filters.py +101 -0
  220. vidmag-0.2.0/tests/test_generic_pipelines.py +140 -0
  221. vidmag-0.2.0/tests/test_golden.py +190 -0
  222. vidmag-0.2.0/tests/test_phase_based.py +226 -0
  223. vidmag-0.2.0/tests/test_pipeline.py +136 -0
  224. vidmag-0.2.0/tests/test_portable_backends.py +417 -0
  225. vidmag-0.2.0/tests/test_public_api.py +157 -0
  226. vidmag-0.2.0/tests/test_pyramids.py +82 -0
  227. vidmag-0.2.0/tests/test_reference_lock.py +123 -0
  228. vidmag-0.2.0/tests/test_streaming.py +155 -0
  229. vidmag-0.2.0/tests/test_video_encode.py +80 -0
@@ -0,0 +1,87 @@
1
+ # Development Practices (binding)
2
+
3
+ These rules govern **how** every step of `docs/dev/PLAN.md` is implemented. They are
4
+ section 3d of that plan, committed here so every session loads them. They are not advice.
5
+
6
+ ## 1. Test-driven — the check comes before the thing it checks
7
+
8
+ - Start each plan step by writing, or pointing at, the failing check named in that phase's
9
+ success criteria. No implementation file is created before that check exists.
10
+ - The check is a real command, not a claim. Examples in this repo:
11
+ `.venv/bin/python -m pytest tests/ -q -p no:randomly` (300 passed, 102 skipped on 2026-08-18),
12
+ `pytest tests/cuda/ -q` on a GPU host, `scripts/dev/verify_install.sh` for packaging work.
13
+ - Non-Python work gets a check too: a `src/vidmag/cuda/CMakeLists.txt` change is tested by a scripted
14
+ fresh-venv install, a docs change by `mkdocs build --strict` plus running its snippets.
15
+ - Phase 0 exists to build this net (golden fixtures, `tests/test_reference_lock.py`, recorded
16
+ baselines in `benches/`). Later red-green cycles run inside it.
17
+
18
+ ## 2. KISS — two sanctioned abstraction layers, no third
19
+
20
+ - The only abstractions this restructure may introduce are `vidmag.backend.Ops` and
21
+ `vidmag.backend.Pipelines` (plan section 3c) plus the backend registry. Base classes, config
22
+ objects, plugin hooks or dispatch layers beyond those need the operator's approval first.
23
+ - Prefer a plain function to a class, a frozen dict to a config system, a copy-pasteable
24
+ example to a helper framework. `vidmag/` is written that way — match it.
25
+
26
+ ## 3. YAGNI — build exactly the current step
27
+
28
+ - No speculative parameters, no "while I'm here" generality, no Phase 4 code smuggled into
29
+ Phase 1. If the plan looks wrong or incomplete, stop and surface the gap to the operator
30
+ instead of quietly building extra.
31
+ - Touch only the files your step names. No reformatting or refactoring of neighbouring code.
32
+
33
+ ## 4. DRY — one source of truth, one named exception
34
+
35
+ - Single-sourced by design: the version string, the preset table, `tests/cuda/conftest.py:TOL`,
36
+ the conformance suite, the backend registry, and the reference constants
37
+ (`DROP_LAST`, `EXAGGERATION_FACTOR`, `BINOM5`, `BINOM5_SUM1`). Import them; never retype a
38
+ literal that can be imported. New duplication needs a stated reason in the commit message.
39
+ - **The named exception:** the FP32/FP16 pipeline bodies in `src/vidmag/cuda/batched.py` and the
40
+ templated kernels may stay duplicated where merging them risks numeric drift — the README's
41
+ accuracy claims (motion FP16 vs FP32 RMSE 0.00140) are load-bearing. Correctness outranks
42
+ DRY. Comment the exception at the site.
43
+
44
+ ## 5. Fail loud
45
+
46
+ - No silent fallbacks and no silently skipped work. Backend selection is always printed; a
47
+ missing backend reports *why* (missing extra, no driver, no device). A ~700x CPU/GPU cliff
48
+ must never be reached by accident.
49
+ - No bare `except`. Every CUDA runtime call is wrapped in `CUDA_CHECK`
50
+ (`src/vidmag/cuda/include/evm_check.cuh`), which throws `std::runtime_error` so pybind11 surfaces it as
51
+ a catchable Python exception — "a silent error can never propagate into a tolerance
52
+ failure". Keep that posture in Python: raise, don't degrade.
53
+ - Report skip counts, never just passes. On this Mac the honest line is
54
+ "300 passed, 102 skipped" — those skips include the whole NVIDIA suite and prove
55
+ nothing about it. No single machine runs every backend.
56
+ - `tests/cuda/conftest.py:TOL` and `tests/test_against_mit_reference.py` are append-only.
57
+ Loosening a tolerance is its own separately reviewed commit carrying the measurement.
58
+
59
+ ## 6. Verify, then claim; land one plan step per commit
60
+
61
+ - A step is done only when its named command has actually run and its output is recorded in
62
+ the report. "Should work" and "looks right" are not results.
63
+ - One plan step = one commit, full suite green, the step named in the message
64
+ (e.g. `Phase 1 step 1.6: make CUDA optional in CMake`), so history maps one-to-one onto
65
+ `docs/dev/PLAN.md`. Unrelated changes in that commit are not permitted.
66
+ - Do not commit unless the operator asked for it; the orchestrating session commits.
67
+
68
+ ## 7. The NVIDIA GPU code leads; the other backends follow it
69
+
70
+ - Performance work starts in the NVIDIA GPU code — `src/vidmag/cuda/kernels/*.cu` and `src/vidmag/cuda/bindings.cpp`.
71
+ That is what this project exists to make fast, and it is where a measurement is worth taking.
72
+ - **A change to the NVIDIA code is not finished when it lands.** Once it is accepted, check
73
+ whether the same change applies to every other backend, and apply it where it does:
74
+
75
+ | Backend | Where its kernels live |
76
+ |---|---|
77
+ | OpenCL | `src/vidmag/opencl/kernels.cl` |
78
+ | Apple Metal | `src/vidmag/metal/kernels.metal` |
79
+ | Vulkan | `src/vidmag/vulkan/shaders/*.comp` |
80
+ | PyTorch | `src/vidmag/torch_backend/ops.py` (tensor operations, no kernels) |
81
+ | Processor (Python) | `src/vidmag/cpu/pyramids.py`, `src/vidmag/cpu/filters.py` |
82
+
83
+ - State which backends the change was carried to and which it does not apply to, with the
84
+ reason. "Does not apply, because that backend has no separate upsample kernel" is a complete
85
+ answer; saying nothing is not.
86
+ - Carry it in its own commit, separate from the NVIDIA change, so a numeric regression on one
87
+ backend cannot be mistaken for a fault in the original.
@@ -0,0 +1,30 @@
1
+ # Editor defaults, matching the formatter this project already runs so that
2
+ # saving a file in any editor produces what `ruff format` would produce.
3
+ # Settings live in pyproject.toml under [tool.ruff]; these mirror them.
4
+ root = true
5
+
6
+ [*]
7
+ charset = utf-8
8
+ end_of_line = lf
9
+ insert_final_newline = true
10
+ trim_trailing_whitespace = true
11
+ indent_style = space
12
+ indent_size = 4
13
+
14
+ [*.{py,pyi}]
15
+ max_line_length = 88
16
+
17
+ # CUDA, C++ and the shader languages follow the same four-space style as the
18
+ # Python, which is what the existing kernel sources use.
19
+ [*.{cu,cuh,cpp,h,hpp,cl,metal,comp,glsl}]
20
+ indent_size = 4
21
+
22
+ [*.{yml,yaml,json,toml}]
23
+ indent_size = 2
24
+
25
+ [*.md]
26
+ # Two trailing spaces are a hard line break in Markdown; do not eat them.
27
+ trim_trailing_whitespace = false
28
+
29
+ [Makefile]
30
+ indent_style = tab
@@ -0,0 +1,35 @@
1
+ # Line endings and binary detection, declared rather than guessed.
2
+ #
3
+ # Git infers "binary" from content, and gets it right most of the time. The
4
+ # cost of the rare miss is high here: a compiled Vulkan shader or a test
5
+ # fixture that had line endings rewritten on checkout is silently corrupt, and
6
+ # the failure surfaces far from the cause. So the file types are stated.
7
+
8
+ # Text files: normalise to LF in the repository, whatever the editor wrote.
9
+ * text=auto eol=lf
10
+
11
+ # Shell scripts and Makefiles break outright with CRLF, so pin them.
12
+ *.sh text eol=lf
13
+ Makefile text eol=lf
14
+
15
+ # GPU kernel sources are text and read by a compiler at build or run time.
16
+ *.cu text eol=lf
17
+ *.cuh text eol=lf
18
+ *.cl text eol=lf
19
+ *.metal text eol=lf
20
+ *.comp text eol=lf
21
+ *.glsl text eol=lf
22
+
23
+ # Binary. Never converted, never diffed as text.
24
+ *.spv binary
25
+ *.npz binary
26
+ *.mp4 binary
27
+ *.gif binary
28
+ *.png binary
29
+ *.jpg binary
30
+ *.ipynb binary linguist-language=Python
31
+
32
+ # Keep the language statistics on the shelf honest: generated, vendored and
33
+ # non-source material should not count as the project's implementation.
34
+ docs/** linguist-documentation
35
+ benches/** linguist-generated
@@ -0,0 +1,33 @@
1
+ ---
2
+ name: Something is wrong
3
+ about: Report incorrect behaviour
4
+ title: ""
5
+ labels: bug
6
+ ---
7
+
8
+ **What happened, and what you expected instead**
9
+
10
+ **How to reproduce it**
11
+
12
+ The smallest script that shows the problem. If it needs a video, say which one
13
+ and where to get it.
14
+
15
+ ```python
16
+ ```
17
+
18
+ **What it printed**
19
+
20
+ Errors in full, please, not just the last line.
21
+
22
+ **Your setup**
23
+
24
+ - Version (`python -c "import vidmag; print(vidmag.__version__)"`):
25
+ - Backend (`python -c "from vidmag import backend; print(backend.select('auto')[0])"`):
26
+ - Operating system and Python version:
27
+
28
+ **Before you file: is the output identical to the input?**
29
+
30
+ That is usually the frequency band rather than a defect — a band narrower than
31
+ your clip can resolve selects nothing. The library warns when this happens.
32
+ [What can go wrong](../../docs/concepts/pitfalls.md) covers it and several
33
+ similar cases.
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: Result from hardware we cannot test
3
+ about: Report running the suite on an AMD, Intel or other device
4
+ title: "Hardware result: "
5
+ labels: hardware-report
6
+ ---
7
+
8
+ This project has an NVIDIA card and an Apple machine to test on, and nothing
9
+ else. Support for other hardware is described as expected rather than verified
10
+ because nobody has run it. A report here changes that.
11
+
12
+ **The device**
13
+
14
+ - Name as reported (`python -c "from vidmag.opencl import runtime; print(runtime.device_name())"`):
15
+ - Operating system and version:
16
+ - Driver version:
17
+
18
+ **Test suite**
19
+
20
+ ```
21
+ python -m pytest tests/ -q
22
+ ```
23
+
24
+ Paste the output, including the skip count.
25
+
26
+ **Anything that failed**
27
+
28
+ Paste the failure in full.
29
+
30
+ **Speed, if you measured it**
31
+
32
+ The steps are in `benches/apple_m2_max_opencl_2026-08-10.md`.
@@ -0,0 +1,32 @@
1
+ ## What this changes, and why
2
+
3
+ <!-- What the change does, and the problem it solves. If it fixes an issue, link it. -->
4
+
5
+ ## How it was verified
6
+
7
+ <!-- The commands you actually ran, with their output. Not "tests pass" — the
8
+ numbers. This project reports skip counts alongside pass counts, because no
9
+ single machine can run every backend: a machine without an NVIDIA card
10
+ skips that whole suite, and a machine with one has no Apple or OpenCL
11
+ driver. Say which machine you ran on. -->
12
+
13
+ - [ ] `python -m pytest tests/ -q -p no:randomly` — passed / skipped:
14
+ - [ ] `ruff check .` and `ruff format --check .`
15
+ - [ ] `mypy`
16
+ - [ ] Ran on (operating system, and graphics hardware if relevant):
17
+
18
+ ## For performance changes
19
+
20
+ <!-- Delete if not applicable. -->
21
+
22
+ - [ ] Measured, with the before and after numbers and how they were taken
23
+ - [ ] Said which backends the change reached, and which it does not apply to
24
+ and why (see rule 7 in `.claude/rules/development-practices.md`)
25
+
26
+ ## For changes that alter numerical output
27
+
28
+ <!-- Delete if not applicable. -->
29
+
30
+ - [ ] Compared against the NumPy baseline, which is the correctness oracle
31
+ - [ ] No tolerance in `tests/cuda/conftest.py` was loosened. Loosening one is a
32
+ separate pull request carrying the measurement that justifies it.
@@ -0,0 +1,10 @@
1
+ # Config for actionlint (https://github.com/rhysd/actionlint).
2
+ #
3
+ # Without this, actionlint rejects `runs-on: [self-hosted, linux, x64, cuda]` in
4
+ # gpu.yml with "label \"cuda\" is unknown" — it has no way to know which custom
5
+ # labels this project's self-hosted runners carry. Declaring the label here is
6
+ # also the one place that says a runner must be registered with it; see
7
+ # docs/dev/gpu-runner.md.
8
+ self-hosted-runner:
9
+ labels:
10
+ - cuda
@@ -0,0 +1,31 @@
1
+ # Keeps the two things this project pins from going stale.
2
+ #
3
+ # The workflow actions are pinned to commit hashes, which is what stops a moved
4
+ # tag from running unreviewed code with this repository's credentials. The cost
5
+ # of that is that nothing updates them; this is what updates them, as a pull
6
+ # request that can be read before it is merged.
7
+ version: 2
8
+ updates:
9
+ - package-ecosystem: github-actions
10
+ directory: "/"
11
+ schedule:
12
+ interval: monthly
13
+ commit-message:
14
+ prefix: "ci"
15
+ labels: ["dependencies"]
16
+
17
+ - package-ecosystem: pip
18
+ directory: "/"
19
+ schedule:
20
+ interval: monthly
21
+ commit-message:
22
+ prefix: "deps"
23
+ labels: ["dependencies"]
24
+ # ruff and mypy are pinned exactly on purpose (see pyproject.toml): a new
25
+ # release adds rules, and a lint gate that turns red without anybody
26
+ # editing code is one people learn to ignore. Let Dependabot propose those
27
+ # bumps, but as their own pull request so the rule churn is reviewed alone.
28
+ groups:
29
+ runtime:
30
+ patterns: ["*"]
31
+ exclude-patterns: ["ruff", "mypy"]