From 445b6c060655b7e7447e3295a10be3579ccb628a Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Sun, 9 Nov 2025 12:38:49 -0500 Subject: [PATCH 1/6] support filtering functions openvino --- .../openvino/excluded_concrete_tests.txt | 9 --- keras/src/backend/openvino/numpy.py | 81 +++++++++++++++++-- keras/src/ops/numpy_test.py | 24 ++++-- 3 files changed, 92 insertions(+), 22 deletions(-) diff --git a/keras/src/backend/openvino/excluded_concrete_tests.txt b/keras/src/backend/openvino/excluded_concrete_tests.txt index e14d190b8294..d85c1c19b2a1 100644 --- a/keras/src/backend/openvino/excluded_concrete_tests.txt +++ b/keras/src/backend/openvino/excluded_concrete_tests.txt @@ -3,10 +3,7 @@ NumpyDtypeTest::test_add_ NumpyDtypeTest::test_angle NumpyDtypeTest::test_argpartition NumpyDtypeTest::test_array -NumpyDtypeTest::test_bartlett -NumpyDtypeTest::test_blackman NumpyDtypeTest::test_gcd -NumpyDtypeTest::test_hamming NumpyDtypeTest::test_hanning NumpyDtypeTest::test_heaviside NumpyDtypeTest::test_hypot @@ -56,9 +53,6 @@ HistogramTest NumpyOneInputOpsCorrectnessTest::test_angle NumpyOneInputOpsCorrectnessTest::test_argpartition NumpyOneInputOpsCorrectnessTest::test_array -NumpyOneInputOpsCorrectnessTest::test_bartlett -NumpyOneInputOpsCorrectnessTest::test_blackman -NumpyOneInputOpsCorrectnessTest::test_hamming NumpyOneInputOpsCorrectnessTest::test_hanning NumpyOneInputOpsCorrectnessTest::test_kaiser NumpyOneInputOpsCorrectnessTest::test_bitwise_invert @@ -111,11 +105,8 @@ NumpyTwoInputOpsCorrectnessTest::test_quantile NumpyTwoInputOpsCorrectnessTest::test_tensordot NumpyTwoInputOpsCorrectnessTest::test_vdot NumpyOneInputOpsDynamicShapeTest::test_angle -NumpyOneInputOpsDynamicShapeTest::test_bartlett -NumpyOneInputOpsDynamicShapeTest::test_blackman NumpyOneInputOpsDynamicShapeTest::test_cbrt NumpyOneInputOpsDynamicShapeTest::test_corrcoef -NumpyOneInputOpsDynamicShapeTest::test_hamming NumpyOneInputOpsDynamicShapeTest::test_hanning NumpyOneInputOpsDynamicShapeTest::test_isreal NumpyOneInputOpsDynamicShapeTest::test_kaiser diff --git a/keras/src/backend/openvino/numpy.py b/keras/src/backend/openvino/numpy.py index 2ef9dc7bdda1..865f70874a2b 100644 --- a/keras/src/backend/openvino/numpy.py +++ b/keras/src/backend/openvino/numpy.py @@ -545,16 +545,61 @@ def average(x, axis=None, weights=None): def bartlett(x): - raise NotImplementedError( - "`bartlett` is not supported with openvino backend" + x = get_ov_output(x) + zero_const = ov_opset.constant(0, Type.i64) + one_const = ov_opset.constant(1, Type.i64) + two_const = ov_opset.constant(2, Type.i64) + two_const_f64 = ov_opset.constant(2.0, Type.f64) + if x.get_element_type() != Type.i64: + x = ov_opset.convert(x, Type.i64) + half = ov_opset.convert( + ov_opset.divide(ov_opset.subtract(x, one_const), two_const), Type.f64 + ) + n = ov_opset.range(zero_const, x, one_const, Type.f64) + condition = ov_opset.less_equal(n, half) + first_half = ov_opset.divide( + ov_opset.multiply(two_const_f64, n), + ov_opset.convert(ov_opset.subtract(x, one_const), Type.f64), + ) + second_half = ov_opset.subtract(two_const_f64, first_half) + window = ov_opset.select(condition, first_half, second_half) + window = ov_opset.convert(window, OPENVINO_DTYPES[config.floatx()]).output( + 0 ) + return OpenVINOKerasTensor(window) -def hamming(x): - raise NotImplementedError( - "`hamming` is not supported with openvino backend" +def hamming(m): + m = get_ov_output(m) + + m_i64 = ( + m if m.get_element_type() == Type.i64 else ov_opset.convert(m, Type.i64) + ) + + start = ov_opset.constant(0, Type.i64) + step = ov_opset.constant(1, Type.i64) + n = ov_opset.range(start, m_i64, step, Type.f64) + + one_i64 = ov_opset.constant(1, Type.i64) + denom_i64 = ov_opset.subtract(m_i64, one_i64) + denom = ov_opset.convert(denom_i64, Type.f64) + + two_pi = ov_opset.constant(2.0 * np.pi, Type.f64) + two_pi_over_m_minus_1 = ov_opset.divide(two_pi, denom) + + x = ov_opset.multiply(two_pi_over_m_minus_1, n) + c = ov_opset.cos(x) + + # 0.54 - 0.46 * cos(...) + a = ov_opset.constant(0.54, Type.f64) + b = ov_opset.constant(0.46, Type.f64) + hamming_window = ov_opset.subtract(a, ov_opset.multiply(b, c)) + hamming_window = ov_opset.convert( + hamming_window, OPENVINO_DTYPES[config.floatx()] ) + return OpenVINOKerasTensor(hamming_window.output(0)) + def heaviside(x1, x2): x1 = get_ov_output(x1) @@ -624,9 +669,31 @@ def bincount(x, weights=None, minlength=0, sparse=False): def blackman(x): - raise NotImplementedError( - "`blackman` is not supported with openvino backend" + x = get_ov_output(x) + zero_const = ov_opset.constant(0, Type.i64) + one_const = ov_opset.constant(1, Type.i64) + two_pi = ov_opset.constant(2.0 * np.pi, Type.f64) + four_pi = ov_opset.multiply(two_pi, ov_opset.constant(2.0, Type.f64)) + term_1 = ov_opset.constant(0.42, Type.f64) + term_2 = ov_opset.constant(-0.5, Type.f64) + term_3 = ov_opset.constant(0.08, Type.f64) + if x.get_element_type() != Type.i64: + x = ov_opset.convert(x, Type.i64) + n = ov_opset.range(zero_const, x, one_const, Type.f64) + n_minus_1 = ov_opset.subtract( + ov_opset.convert(x, Type.f64), ov_opset.constant(1.0, Type.f64) + ).output(0) + angle_2pi = ov_opset.divide(ov_opset.multiply(two_pi, n), n_minus_1) + angle_4pi = ov_opset.divide(ov_opset.multiply(four_pi, n), n_minus_1) + cos_2pi = ov_opset.cos(angle_2pi) + cos_4pi = ov_opset.cos(angle_4pi) + term_2_final = ov_opset.multiply(term_2, cos_2pi) + term_3_final = ov_opset.multiply(term_3, cos_4pi) + window = ov_opset.add(ov_opset.add(term_1, term_2_final), term_3_final) + window = ov_opset.convert(window, OPENVINO_DTYPES[config.floatx()]).output( + 0 ) + return OpenVINOKerasTensor(window) def broadcast_to(x, shape): diff --git a/keras/src/ops/numpy_test.py b/keras/src/ops/numpy_test.py index d6ff3a9456c5..fc9d46f726c3 100644 --- a/keras/src/ops/numpy_test.py +++ b/keras/src/ops/numpy_test.py @@ -3969,21 +3969,33 @@ def test_average(self): def test_bartlett(self): x = np.random.randint(1, 100 + 1) - self.assertAllClose(knp.bartlett(x), np.bartlett(x)) + if backend.backend() == "openvino": + kwargs = {"atol": 1e-3} + else: + kwargs = {} + self.assertAllClose(knp.bartlett(x), np.bartlett(x), **kwargs) - self.assertAllClose(knp.Bartlett()(x), np.bartlett(x)) + self.assertAllClose(knp.Bartlett()(x), np.bartlett(x), **kwargs) def test_blackman(self): x = np.random.randint(1, 100 + 1) - self.assertAllClose(knp.blackman(x), np.blackman(x)) + if backend.backend() == "openvino": + kwargs = {"atol": 1e-3} + else: + kwargs = {} + self.assertAllClose(knp.blackman(x), np.blackman(x), **kwargs) - self.assertAllClose(knp.Blackman()(x), np.blackman(x)) + self.assertAllClose(knp.Blackman()(x), np.blackman(x), **kwargs) def test_hamming(self): x = np.random.randint(1, 100 + 1) - self.assertAllClose(knp.hamming(x), np.hamming(x)) + if backend.backend() == "openvino": + kwargs = {"atol": 1e-3} + else: + kwargs = {} + self.assertAllClose(knp.hamming(x), np.hamming(x), **kwargs) - self.assertAllClose(knp.Hamming()(x), np.hamming(x)) + self.assertAllClose(knp.Hamming()(x), np.hamming(x), **kwargs) def test_hanning(self): x = np.random.randint(1, 100 + 1) From 162f6dfe368e44d07243736d13486e80c41be49c Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Sun, 9 Nov 2025 12:45:35 -0500 Subject: [PATCH 2/6] override `assertAllClose` to reduce duplication --- keras/src/ops/numpy_test.py | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/keras/src/ops/numpy_test.py b/keras/src/ops/numpy_test.py index fc9d46f726c3..1947f9e7d45f 100644 --- a/keras/src/ops/numpy_test.py +++ b/keras/src/ops/numpy_test.py @@ -3612,6 +3612,15 @@ def test_digitize(self): class NumpyOneInputOpsCorrectnessTest(testing.TestCase): + def assertAllClose(self, x1, x2, atol=1e-6, rtol=1e-6, msg=None): + if backend.backend() == "openvino": + # OpenVINO seems to use lower precision for some operations, + # or employs some different algorithms that wind up with + # slightly different results. To address this, we relax + # the tolerances for OpenVINO backend. + atol = 1e-3 + super().assertAllClose(x1, x2, atol=atol, rtol=rtol, msg=msg) + def test_mean(self): x = np.array([[1, 2, 3], [3, 2, 1]]) self.assertAllClose(knp.mean(x), np.mean(x)) @@ -3969,33 +3978,21 @@ def test_average(self): def test_bartlett(self): x = np.random.randint(1, 100 + 1) - if backend.backend() == "openvino": - kwargs = {"atol": 1e-3} - else: - kwargs = {} - self.assertAllClose(knp.bartlett(x), np.bartlett(x), **kwargs) + self.assertAllClose(knp.bartlett(x), np.bartlett(x)) - self.assertAllClose(knp.Bartlett()(x), np.bartlett(x), **kwargs) + self.assertAllClose(knp.Bartlett()(x), np.bartlett(x)) def test_blackman(self): x = np.random.randint(1, 100 + 1) - if backend.backend() == "openvino": - kwargs = {"atol": 1e-3} - else: - kwargs = {} - self.assertAllClose(knp.blackman(x), np.blackman(x), **kwargs) + self.assertAllClose(knp.blackman(x), np.blackman(x)) - self.assertAllClose(knp.Blackman()(x), np.blackman(x), **kwargs) + self.assertAllClose(knp.Blackman()(x), np.blackman(x)) def test_hamming(self): x = np.random.randint(1, 100 + 1) - if backend.backend() == "openvino": - kwargs = {"atol": 1e-3} - else: - kwargs = {} - self.assertAllClose(knp.hamming(x), np.hamming(x), **kwargs) + self.assertAllClose(knp.hamming(x), np.hamming(x)) - self.assertAllClose(knp.Hamming()(x), np.hamming(x), **kwargs) + self.assertAllClose(knp.Hamming()(x), np.hamming(x)) def test_hanning(self): x = np.random.randint(1, 100 + 1) From ec0b030bde653723b18295741c3f07483f20d021 Mon Sep 17 00:00:00 2001 From: Danny <33044223+danielenricocahall@users.noreply.github.com> Date: Sun, 9 Nov 2025 15:42:34 -0500 Subject: [PATCH 3/6] Update keras/src/backend/openvino/numpy.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- keras/src/backend/openvino/numpy.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/keras/src/backend/openvino/numpy.py b/keras/src/backend/openvino/numpy.py index 865f70874a2b..c3b9d922f88a 100644 --- a/keras/src/backend/openvino/numpy.py +++ b/keras/src/backend/openvino/numpy.py @@ -673,9 +673,8 @@ def blackman(x): zero_const = ov_opset.constant(0, Type.i64) one_const = ov_opset.constant(1, Type.i64) two_pi = ov_opset.constant(2.0 * np.pi, Type.f64) - four_pi = ov_opset.multiply(two_pi, ov_opset.constant(2.0, Type.f64)) term_1 = ov_opset.constant(0.42, Type.f64) - term_2 = ov_opset.constant(-0.5, Type.f64) + term_2 = ov_opset.constant(0.5, Type.f64) term_3 = ov_opset.constant(0.08, Type.f64) if x.get_element_type() != Type.i64: x = ov_opset.convert(x, Type.i64) @@ -684,12 +683,12 @@ def blackman(x): ov_opset.convert(x, Type.f64), ov_opset.constant(1.0, Type.f64) ).output(0) angle_2pi = ov_opset.divide(ov_opset.multiply(two_pi, n), n_minus_1) - angle_4pi = ov_opset.divide(ov_opset.multiply(four_pi, n), n_minus_1) + angle_4pi = ov_opset.multiply(angle_2pi, ov_opset.constant(2.0, Type.f64)) cos_2pi = ov_opset.cos(angle_2pi) cos_4pi = ov_opset.cos(angle_4pi) term_2_final = ov_opset.multiply(term_2, cos_2pi) term_3_final = ov_opset.multiply(term_3, cos_4pi) - window = ov_opset.add(ov_opset.add(term_1, term_2_final), term_3_final) + window = ov_opset.add(ov_opset.subtract(term_1, term_2_final), term_3_final) window = ov_opset.convert(window, OPENVINO_DTYPES[config.floatx()]).output( 0 ) From 958ef5604f9504cc5a8183417e9f1a6aac078770 Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Mon, 10 Nov 2025 20:49:57 -0500 Subject: [PATCH 4/6] fix arg --- keras/src/backend/openvino/numpy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/keras/src/backend/openvino/numpy.py b/keras/src/backend/openvino/numpy.py index c3b9d922f88a..94133f82c908 100644 --- a/keras/src/backend/openvino/numpy.py +++ b/keras/src/backend/openvino/numpy.py @@ -569,8 +569,8 @@ def bartlett(x): return OpenVINOKerasTensor(window) -def hamming(m): - m = get_ov_output(m) +def hamming(x): + m = get_ov_output(x) m_i64 = ( m if m.get_element_type() == Type.i64 else ov_opset.convert(m, Type.i64) From f1e10e5ae087a1014ee1ed43719a8ff3e5453e3d Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Sat, 15 Nov 2025 11:02:25 -0500 Subject: [PATCH 5/6] fix empty_like exclusion --- keras/src/backend/openvino/excluded_concrete_tests.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/keras/src/backend/openvino/excluded_concrete_tests.txt b/keras/src/backend/openvino/excluded_concrete_tests.txt index d85c1c19b2a1..d094c366539d 100644 --- a/keras/src/backend/openvino/excluded_concrete_tests.txt +++ b/keras/src/backend/openvino/excluded_concrete_tests.txt @@ -4,6 +4,7 @@ NumpyDtypeTest::test_angle NumpyDtypeTest::test_argpartition NumpyDtypeTest::test_array NumpyDtypeTest::test_gcd +NumpyDtypeTest::test_empty_like NumpyDtypeTest::test_hanning NumpyDtypeTest::test_heaviside NumpyDtypeTest::test_hypot @@ -105,6 +106,7 @@ NumpyTwoInputOpsCorrectnessTest::test_quantile NumpyTwoInputOpsCorrectnessTest::test_tensordot NumpyTwoInputOpsCorrectnessTest::test_vdot NumpyOneInputOpsDynamicShapeTest::test_angle +NumpyOneInputOpsDynamicShapeTest::test_empty_like NumpyOneInputOpsDynamicShapeTest::test_cbrt NumpyOneInputOpsDynamicShapeTest::test_corrcoef NumpyOneInputOpsDynamicShapeTest::test_hanning From 0b366b6b8edae1a427efe847262ed4904b959e1c Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Sat, 15 Nov 2025 11:03:14 -0500 Subject: [PATCH 6/6] fix empty_like exclusion --- keras/src/backend/openvino/excluded_concrete_tests.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keras/src/backend/openvino/excluded_concrete_tests.txt b/keras/src/backend/openvino/excluded_concrete_tests.txt index d094c366539d..f2a589164b20 100644 --- a/keras/src/backend/openvino/excluded_concrete_tests.txt +++ b/keras/src/backend/openvino/excluded_concrete_tests.txt @@ -3,8 +3,8 @@ NumpyDtypeTest::test_add_ NumpyDtypeTest::test_angle NumpyDtypeTest::test_argpartition NumpyDtypeTest::test_array -NumpyDtypeTest::test_gcd NumpyDtypeTest::test_empty_like +NumpyDtypeTest::test_gcd NumpyDtypeTest::test_hanning NumpyDtypeTest::test_heaviside NumpyDtypeTest::test_hypot