Skip to content

Implement hanning function in keras.ops #21318

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions keras/api/_tf_keras/keras/ops/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@
from keras.src.ops.numpy import greater as greater
from keras.src.ops.numpy import greater_equal as greater_equal
from keras.src.ops.numpy import hamming as hamming
from keras.src.ops.numpy import hanning as hanning
from keras.src.ops.numpy import histogram as histogram
from keras.src.ops.numpy import hstack as hstack
from keras.src.ops.numpy import identity as identity
Expand Down
1 change: 1 addition & 0 deletions keras/api/_tf_keras/keras/ops/numpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
from keras.src.ops.numpy import greater as greater
from keras.src.ops.numpy import greater_equal as greater_equal
from keras.src.ops.numpy import hamming as hamming
from keras.src.ops.numpy import hanning as hanning
from keras.src.ops.numpy import histogram as histogram
from keras.src.ops.numpy import hstack as hstack
from keras.src.ops.numpy import identity as identity
Expand Down
1 change: 1 addition & 0 deletions keras/api/ops/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@
from keras.src.ops.numpy import greater as greater
from keras.src.ops.numpy import greater_equal as greater_equal
from keras.src.ops.numpy import hamming as hamming
from keras.src.ops.numpy import hanning as hanning
from keras.src.ops.numpy import histogram as histogram
from keras.src.ops.numpy import hstack as hstack
from keras.src.ops.numpy import identity as identity
Expand Down
1 change: 1 addition & 0 deletions keras/api/ops/numpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
from keras.src.ops.numpy import greater as greater
from keras.src.ops.numpy import greater_equal as greater_equal
from keras.src.ops.numpy import hamming as hamming
from keras.src.ops.numpy import hanning as hanning
from keras.src.ops.numpy import histogram as histogram
from keras.src.ops.numpy import hstack as hstack
from keras.src.ops.numpy import identity as identity
Expand Down
5 changes: 5 additions & 0 deletions keras/src/backend/jax/numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ def hamming(x):
return jnp.hamming(x)


def hanning(x):
x = convert_to_tensor(x)
return jnp.hanning(x)


def kaiser(x, beta):
x = convert_to_tensor(x)
return jnp.kaiser(x, beta)
Expand Down
5 changes: 5 additions & 0 deletions keras/src/backend/numpy/numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,11 @@ def hamming(x):
return np.hamming(x).astype(config.floatx())


def hanning(x):
x = convert_to_tensor(x)
return np.hanning(x).astype(config.floatx())


def kaiser(x, beta):
x = convert_to_tensor(x)
return np.kaiser(x, beta).astype(config.floatx())
Expand Down
3 changes: 3 additions & 0 deletions keras/src/backend/openvino/excluded_concrete_tests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ NumpyDtypeTest::test_array
NumpyDtypeTest::test_bartlett
NumpyDtypeTest::test_blackman
NumpyDtypeTest::test_hamming
NumpyDtypeTest::test_hanning
NumpyDtypeTest::test_kaiser
NumpyDtypeTest::test_bitwise
NumpyDtypeTest::test_ceil
Expand Down Expand Up @@ -76,6 +77,7 @@ NumpyOneInputOpsCorrectnessTest::test_array
NumpyOneInputOpsCorrectnessTest::test_bartlett
NumpyOneInputOpsCorrectnessTest::test_blackman
NumpyOneInputOpsCorrectnessTest::test_hamming
NumpyOneInputOpsCorrectnessTest::test_hanning
NumpyOneInputOpsCorrectnessTest::test_kaiser
NumpyOneInputOpsCorrectnessTest::test_bitwise_invert
NumpyOneInputOpsCorrectnessTest::test_conj
Expand Down Expand Up @@ -150,5 +152,6 @@ NumpyOneInputOpsDynamicShapeTest::test_angle
NumpyOneInputOpsDynamicShapeTest::test_bartlett
NumpyOneInputOpsDynamicShapeTest::test_blackman
NumpyOneInputOpsDynamicShapeTest::test_hamming
NumpyOneInputOpsDynamicShapeTest::test_hanning
NumpyOneInputOpsDynamicShapeTest::test_kaiser
NumpyOneInputOpsStaticShapeTest::test_angle
5 changes: 5 additions & 0 deletions keras/src/backend/tensorflow/numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ def hamming(x):
return tf.signal.hamming_window(x, periodic=False)


def hanning(x):
x = convert_to_tensor(x, dtype=tf.int32)
return tf.signal.hann_window(x, periodic=False)


def kaiser(x, beta):
x = convert_to_tensor(x, dtype=tf.int32)
return tf.signal.kaiser_window(x, beta=beta)
Expand Down
5 changes: 5 additions & 0 deletions keras/src/backend/torch/numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,11 @@ def hamming(x):
return torch.signal.windows.hamming(x)


def hanning(x):
x = convert_to_tensor(x)
return torch.signal.windows.hann(x)


def kaiser(x, beta):
x = convert_to_tensor(x)
return torch.signal.windows.kaiser(x, beta=beta)
Expand Down
31 changes: 31 additions & 0 deletions keras/src/ops/numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -1270,6 +1270,37 @@ def hamming(x):
return backend.numpy.hamming(x)


class Hanning(Operation):
def call(self, x):
return backend.numpy.hanning(x)

def compute_output_spec(self, x):
return KerasTensor(x.shape, dtype=backend.floatx())


@keras_export(["keras.ops.hanning", "keras.ops.numpy.hanning"])
def hanning(x):
"""Hanning window function.

The Hanning window is defined as:
`w[n] = 0.5 - 0.5 * cos(2 * pi * n / (N - 1))` for `0 <= n <= N - 1`.

Args:
x: Scalar or 1D Tensor. The window length.

Returns:
A 1D tensor containing the Hanning window values.

Example:
>>> x = keras.ops.convert_to_tensor(5)
>>> keras.ops.hanning(x)
array([0. , 0.5, 1. , 0.5, 0. ], dtype=float32)
"""
if any_symbolic_tensors((x,)):
return Hanning().symbolic_call(x)
return backend.numpy.hanning(x)


class Kaiser(Operation):
def __init__(self, beta):
super().__init__()
Expand Down
26 changes: 26 additions & 0 deletions keras/src/ops/numpy_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,10 @@ def test_hamming(self):
x = np.random.randint(1, 100 + 1)
self.assertEqual(knp.hamming(x).shape[0], x)

def test_hanning(self):
x = np.random.randint(1, 100 + 1)
self.assertEqual(knp.hanning(x).shape[0], x)

def test_kaiser(self):
x = np.random.randint(1, 100 + 1)
beta = float(np.random.randint(10, 20 + 1))
Expand Down Expand Up @@ -3625,6 +3629,12 @@ def test_hamming(self):

self.assertAllClose(knp.Hamming()(x), np.hamming(x))

def test_hanning(self):
x = np.random.randint(1, 100 + 1)
self.assertAllClose(knp.hanning(x), np.hanning(x))

self.assertAllClose(knp.Hanning()(x), np.hanning(x))

def test_kaiser(self):
x = np.random.randint(1, 100 + 1)
beta = float(np.random.randint(10, 20 + 1))
Expand Down Expand Up @@ -5639,6 +5649,22 @@ def test_hamming(self, dtype):
expected_dtype,
)

@parameterized.named_parameters(named_product(dtype=ALL_DTYPES))
def test_hanning(self, dtype):
import jax.numpy as jnp

x = knp.ones((), dtype=dtype)
x_jax = jnp.ones((), dtype=dtype)
expected_dtype = standardize_dtype(jnp.hanning(x_jax).dtype)

self.assertEqual(
standardize_dtype(knp.hanning(x).dtype), expected_dtype
)
self.assertEqual(
standardize_dtype(knp.Hanning().symbolic_call(x).dtype),
expected_dtype,
)

@parameterized.named_parameters(named_product(dtype=ALL_DTYPES))
def test_kaiser(self, dtype):
import jax.numpy as jnp
Expand Down