From a8ba5ac3e0080732a7fdfe0454f435c55c394019 Mon Sep 17 00:00:00 2001 From: willem adnet <140308890+Vlor999@users.noreply.github.com> Date: Wed, 25 Feb 2026 22:42:40 +0100 Subject: [PATCH] Implement mlx.core.blackman (#3136) --- mlx/ops.cpp | 27 +++++++++++++++++++++++++++ mlx/ops.h | 3 +++ python/src/ops.cpp | 24 ++++++++++++++++++++++++ python/tests/test_ops.py | 12 ++++++++++++ 4 files changed, 66 insertions(+) diff --git a/mlx/ops.cpp b/mlx/ops.cpp index deb1c270..0cafd479 100644 --- a/mlx/ops.cpp +++ b/mlx/ops.cpp @@ -2346,6 +2346,33 @@ array hamming(int M, StreamOrDevice s /* = {} */) { return subtract(left_coef, multiply(right_coef, cos_vals, s), s); } +array blackman(int M, StreamOrDevice s /* = {} */) { + if (M < 1) { + return array({}); + } + if (M == 1) { + return ones({1}, float32, s); + } + + auto n = arange(0, M, float32, s); + + float arg_val = (2.0 * M_PI) / (M - 1); + auto x = multiply(array(arg_val, float32), n, s); + + auto cos_x = cos(x, s); + + auto alpha = array(0.34f, float32); + auto beta = array(0.5f, float32); + auto gamma = array(0.16f, float32); + + auto term1 = multiply(beta, cos_x, s); + + auto cos_sq = square(cos_x, s); + auto term2 = multiply(gamma, cos_sq, s); + + return add(subtract(alpha, term1, s), term2, s); +} + /** Returns a sorted copy of the flattened array. */ array sort(const array& a, StreamOrDevice s /* = {} */) { int size = a.size(); diff --git a/mlx/ops.h b/mlx/ops.h index e40d6aff..1f8331c1 100644 --- a/mlx/ops.h +++ b/mlx/ops.h @@ -672,6 +672,9 @@ MLX_API array hanning(int M, StreamOrDevice s = {}); /** Returns the Hamming window of size M. */ MLX_API array hamming(int M, StreamOrDevice s = {}); +/** Returns the Blackmann window of size M. */ +MLX_API array blackman(int M, StreamOrDevice s = {}); + /** Returns the index of the minimum value in the array. */ MLX_API array argmin(const array& a, bool keepdims, StreamOrDevice s = {}); inline array argmin(const array& a, StreamOrDevice s = {}) { diff --git a/python/src/ops.cpp b/python/src/ops.cpp index dac4b8f7..19231700 100644 --- a/python/src/ops.cpp +++ b/python/src/ops.cpp @@ -1475,6 +1475,30 @@ void init_ops(nb::module_& m) { appears only if the number of samples is odd). )pbdoc"); m.def( + "blackman", + &mlx::core::blackman, + "M"_a, + nb::kw_only(), + "stream"_a = nb::none(), + nb::sig( + "def blackman(M: int, *, stream: Union[None, Stream, Device] = None) -> array"), // <--- J'ai rajouté ça + R"pbdoc( + Return the Blackman window. + + The Blackman window is a taper formed by using the first three terms of a summation of cosines. + + .. math:: + w(n) = 0.42 - 0.5 \cos\left(\frac{2\pi n}{M-1}\right) + 0.08 \cos\left(\frac{4\pi n}{M-1}\right) + \qquad 0 \le n \le M-1 + + Args: + M (int): Number of points in the output window. + + Returns: + array: The window, with the maximum value normalized to one (the value one + appears only if the number of samples is odd). + )pbdoc"); + m.def( "linspace", [](Scalar start, Scalar stop, diff --git a/python/tests/test_ops.py b/python/tests/test_ops.py index bd8f5850..a4981161 100644 --- a/python/tests/test_ops.py +++ b/python/tests/test_ops.py @@ -1474,6 +1474,18 @@ class TestOps(mlx_tests.MLXTestCase): self.assertEqual(a.size, 0) self.assertEqual(a.dtype, mx.float32) + def test_blackman_general(self): + a = mx.blackman(10) + expected = np.blackman(10) + self.assertTrue(np.allclose(a, expected, atol=1e-5)) + + a = mx.blackman(1) + self.assertEqual(a.item(), 1.0) + + a = mx.blackman(0) + self.assertEqual(a.size, 0) + self.assertEqual(a.dtype, mx.float32) + def test_unary_ops(self): def test_ops(npop, mlxop, x, y, atol, rtol): r_np = npop(x)