Skip to content

M17: native window ops (conv-pool training) - #34

Merged
ausimian merged 2 commits into
mainfrom
ausimian/m17-window-ops
Apr 18, 2026
Merged

M17: native window ops (conv-pool training)#34
ausimian merged 2 commits into
mainfrom
ausimian/m17-window-ops

Conversation

@ausimian

@ausimian ausimian commented Apr 18, 2026

Copy link
Copy Markdown
Owner

Summary

  • Lift window_sum, window_max, window_min, window_product, window_scatter_max, window_scatter_min off via_binary onto native MLX — composed as pad → as_strided → reduce for the reductions, pad → as_strided → argmax-tie-break → scatter_add for the scatters. Closes M17 in PLAN.md.
  • Grad of window_max in Nx rewrites to window_scatter_max, so every MaxPool2d backward now stays on MLX instead of round-tripping through BinaryBackend — the stated M17 exit criterion ("small-CNN MNIST training converges in :training_full").
  • All six NIFs live in the new c_src/ops/pooling.cpp; the Backend resolves :valid/:same padding and dtype-specific identities (0/1/±∞, plus int-dtype min/max) in Elixir before dispatch.
  • EXLA goldens regenerated (EXLA 0.11.0 CPU) to cover the three new zoo fns end-to-end; Emily's last-occurrence argmax tie-break matches XLA's convention to (1e-6, 1e-5).

Test plan

  • mix precommit — 401 tests + 76 properties green
  • mix test test/emily/backend_window_test.exs — 19 forward-parity cases (shape × stride × padding × dilation × {f32, bf16, s32, u8})
  • mix test test/emily/backend_window_scatter_test.exs — 11 cases incl. overlapping windows, last-occurrence tie-break, non-zero init, 1-D/3-D
  • mix test test/emily/grad/ — grad-equivalence extensions: 3 new zoo fns (grad_window_sum, grad_window_max_pool, grad_window_avg_pool); bf16_grad_equivalence_test auto-picks-up; EXLA oracle covers all 11 zoo entries
  • mix test test/emily/training/cnn_curve_test.exs — handwritten 2-conv + max-pool CNN, 30-step loss trajectory match vs BinaryBackend within rtol 1e-2
  • mix test --only training_full test/emily/training/mnist_cnn_full_test.exs — LeNet-style Axon CNN, 1.60 → 0.12 loss over 5 epochs, ≥ 97% test accuracy

Lift window_sum, window_max, window_min, window_product,
window_scatter_max, window_scatter_min off via_binary onto native MLX.
MLX has no direct primitives — each op is composed as mx::pad →
mx::as_strided (sliding-window view) → reduce, mirroring
nn/layers/pooling.py generalised to N-D so Nx's per-axis
window/stride/padding/dilation all lower cleanly. Scatter variants
add argmax-with-last-occurrence-tie-break (mask * arange(K) trick,
since mx::argmax is first-occurrence) → per-axis absolute-index
computation → mx::scatter_add into a mx::full(init_value) buffer →
slice back. Closes the training primitive set: grad(window_max) in Nx
rewrites to window_scatter_max, so every MaxPool backward pass now
stays on MLX instead of round-tripping through BinaryBackend.

Tests: backend_window_test (19 unit — shape × stride × padding ×
dilation × f32/bf16/s32/u8), backend_window_scatter_test (11 —
overlapping windows, tie-break, non-zero init, 1-D/3-D),
grad-equivalence extensions (3 new zoo fns: window_sum,
window_max_pool, window_avg_pool; bf16 equivalence auto-picks-up;
EXLA oracle skips un-regenerated entries via
ExlaGoldenData.has_golden?/1), cnn_curve_test (handwritten 2-conv +
max-pool CNN, 30 steps), mnist_cnn_full_test (:training_full, LeNet
≥97%, observed 1.60 → 0.12 loss over 5 epochs).
Regenerate test/support/exla_golden_data.ex via bench/exla_golden_gen.exs
with EXLA 0.11.0 CPU. Adds the three M17 window zoo functions
(grad_window_sum, grad_window_max_pool, grad_window_avg_pool) to both
the generator's @zoo and the oracle test's @Tolerance table, restoring
full cross-backend coverage. Emily's last-occurrence argmax tie-break
matches XLA's convention — all three pass the (1e-6, 1e-5) tolerance,
tight enough to catch any numerics drift since the values are
integer/rational (window counts / 1/4 averages / one-hot scatter).

Removes the temporary ExlaGoldenData.has_golden?/1 skip scaffolding —
no longer needed now that every zoo entry has a golden.
@ausimian
ausimian merged commit bf5a110 into main Apr 18, 2026
1 check passed
@ausimian
ausimian deleted the ausimian/m17-window-ops branch April 18, 2026 02:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant