Skip to content

Commit 86bfab4

Browse files
More OpenVINO Numpy Operations (#21925)
* support isin and floor_divide * support empty_like, including support for jax arrays in get_ov_output * support roll * vdot support * vdot support * fix roll for axis = None * simplify floor_divide a bit * further simplifying floor_divide * simplify type conversion in `isin` via `_align_operand_types` * revert change to core, minor change to test * support multi_hot * support psnr
1 parent f48f480 commit 86bfab4

4 files changed

Lines changed: 95 additions & 30 deletions

File tree

‎keras/src/backend/openvino/excluded_concrete_tests.txt‎

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ NumpyDtypeTest::test_add_
33
NumpyDtypeTest::test_angle
44
NumpyDtypeTest::test_argpartition
55
NumpyDtypeTest::test_array
6-
NumpyDtypeTest::test_empty_like
76
NumpyDtypeTest::test_gcd
87
NumpyDtypeTest::test_hanning
98
NumpyDtypeTest::test_heaviside
@@ -21,9 +20,7 @@ NumpyDtypeTest::test_diagonal
2120
NumpyDtypeTest::test_einsum
2221
NumpyDtypeTest::test_exp2
2322
NumpyDtypeTest::test_flip
24-
NumpyDtypeTest::test_floor_divide
2523
NumpyDtypeTest::test_inner
26-
NumpyDtypeTest::test_isin
2724
NumpyDtypeTest::test_isreal
2825
NumpyDtypeTest::test_kron
2926
NumpyDtypeTest::test_lcm
@@ -35,7 +32,6 @@ NumpyDtypeTest::test_minimum_python_types
3532
NumpyDtypeTest::test_multiply
3633
NumpyDtypeTest::test_power
3734
NumpyDtypeTest::test_quantile
38-
NumpyDtypeTest::test_roll
3935
NumpyDtypeTest::test_round
4036
NumpyDtypeTest::test_searchsorted
4137
NumpyDtypeTest::test_signbit
@@ -50,7 +46,6 @@ NumpyDtypeTest::test_trunc
5046
NumpyDtypeTest::test_unravel
5147
NumpyDtypeTest::test_vander
5248
NumpyDtypeTest::test_var
53-
NumpyDtypeTest::test_vdot
5449
NumpyDtypeTest::test_view
5550
NumpyDtypeTest::test_vstack
5651
HistogramTest
@@ -69,13 +64,11 @@ NumpyOneInputOpsCorrectnessTest::test_diagflat
6964
NumpyOneInputOpsCorrectnessTest::test_diagonal
7065
NumpyOneInputOpsCorrectnessTest::test_exp2
7166
NumpyOneInputOpsCorrectnessTest::test_flip
72-
NumpyOneInputOpsCorrectnessTest::test_floor_divide
7367
NumpyOneInputOpsCorrectnessTest::test_imag
7468
NumpyOneInputOpsCorrectnessTest::test_isreal
7569
NumpyOneInputOpsCorrectnessTest::test_logaddexp2
7670
NumpyOneInputOpsCorrectnessTest::test_real
7771
NumpyOneInputOpsCorrectnessTest::test_reshape
78-
NumpyOneInputOpsCorrectnessTest::test_roll
7972
NumpyOneInputOpsCorrectnessTest::test_round
8073
NumpyOneInputOpsCorrectnessTest::test_searchsorted
8174
NumpyOneInputOpsCorrectnessTest::test_select
@@ -103,15 +96,12 @@ NumpyTwoInputOpsCorrectnessTest::test_einsum
10396
NumpyTwoInputOpsCorrectnessTest::test_gcd
10497
NumpyTwoInputOpsCorrectnessTest::test_hypot
10598
NumpyTwoInputOpsCorrectnessTest::test_inner
106-
NumpyTwoInputOpsCorrectnessTest::test_isin
10799
NumpyTwoInputOpsCorrectnessTest::test_kron
108100
NumpyTwoInputOpsCorrectnessTest::test_lcm
109101
NumpyTwoInputOpsCorrectnessTest::test_ldexp
110102
NumpyTwoInputOpsCorrectnessTest::test_quantile
111103
NumpyTwoInputOpsCorrectnessTest::test_tensordot
112-
NumpyTwoInputOpsCorrectnessTest::test_vdot
113104
NumpyOneInputOpsDynamicShapeTest::test_angle
114-
NumpyOneInputOpsDynamicShapeTest::test_empty_like
115105
NumpyOneInputOpsDynamicShapeTest::test_cbrt
116106
NumpyOneInputOpsDynamicShapeTest::test_corrcoef
117107
NumpyOneInputOpsDynamicShapeTest::test_hanning
@@ -124,13 +114,11 @@ NumpyOneInputOpsStaticShapeTest::test_isreal
124114
NumpyOneInputOpsStaticShapeTest::test_view
125115
NumpyTwoInputOpsDynamicShapeTest::test_gcd
126116
NumpyTwoInputOpsDynamicShapeTest::test_hypot
127-
NumpyTwoInputOpsDynamicShapeTest::test_isin
128117
NumpyTwoInputOpsDynamicShapeTest::test_kron
129118
NumpyTwoInputOpsDynamicShapeTest::test_lcm
130119
NumpyTwoInputOpsDynamicShapeTest::test_ldexp
131120
NumpyTwoInputOpsStaticShapeTest::test_gcd
132121
NumpyTwoInputOpsStaticShapeTest::test_hypot
133-
NumpyTwoInputOpsStaticShapeTest::test_isin
134122
NumpyTwoInputOpsStaticShapeTest::test_kron
135123
NumpyTwoInputOpsStaticShapeTest::test_lcm
136124
NumpyTwoInputOpsStaticShapeTest::test_ldexp
@@ -261,10 +249,8 @@ LinalgOpsCorrectnessTest::test_cholesky
261249
LinalgOpsCorrectnessTest::test_cholesky_inverse
262250
NNOpsDynamicShapeTest::test_binary_crossentropy
263251
NNOpsDynamicShapeTest::test_categorical_crossentropy
264-
NNOpsDynamicShapeTest::test_multi_hot_dtype_
265252
NNOpsCorrectnessTest::test_conv_transpose_
266253
NNOpsCorrectnessTest::test_ctc_decode
267-
NNOpsCorrectnessTest::test_multi_hot_
268254
NNOpsCorrectnessTest::test_binary_crossentropy
269255
NNOpsCorrectnessTest::test_categorical_crossentropy
270256
NNOpsCorrectnessTest::test_log_softmax_correctness_with_axis_tuple
@@ -274,7 +260,6 @@ NNOpsCorrectnessTest::test_glu
274260
NNOpsCorrectnessTest::test_moments
275261
NNOpsCorrectnessTest::test_normalize
276262
NNOpsCorrectnessTest::test_polar_corectness
277-
NNOpsCorrectnessTest::test_psnr
278263
NNOpsCorrectnessTest::test_sparse_categorical_crossentropy
279264
NNOpsCorrectnessTest::test_sparsemax
280265
NNOpsCorrectnessTest::test_rms_normalization_10.0

‎keras/src/backend/openvino/nn.py‎

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -559,9 +559,14 @@ def one_hot(x, num_classes, axis=-1, dtype=None, sparse=False):
559559

560560

561561
def multi_hot(x, num_classes, axis=-1, dtype=None, sparse=False):
562-
raise NotImplementedError(
563-
"`multi_hot` is not supported with openvino backend"
564-
)
562+
reduction_axis = 1 if len(x.shape) > 1 else 0
563+
if backend.standardize_dtype(dtype) == "bool":
564+
outputs = one_hot(x, num_classes, axis=axis, dtype=dtype, sparse=sparse)
565+
result = ov_opset.reduce_logical_or(outputs, reduction_axis)
566+
else:
567+
outputs = one_hot(x, num_classes, axis=axis, dtype=dtype)
568+
result = ov_opset.reduce_max(outputs, reduction_axis)
569+
return OpenVINOKerasTensor(result.output(0))
565570

566571

567572
def categorical_crossentropy(target, output, from_logits=False, axis=-1):
@@ -670,7 +675,27 @@ def ctc_decode(
670675

671676

672677
def psnr(x1, x2, max_val):
673-
raise NotImplementedError("`psnr` is not supported with openvino backend")
678+
from keras.src.backend.openvino.numpy import log10
679+
680+
x1 = get_ov_output(x1)
681+
x2 = get_ov_output(x2)
682+
max_val = get_ov_output(max_val, x1.get_element_type())
683+
diff = ov_opset.subtract(x1, x2)
684+
squared_diff = ov_opset.multiply(diff, diff)
685+
reduction_axes = list(range(0, x1.get_partial_shape().rank.get_length()))
686+
mse = ov_opset.reduce_mean(squared_diff, reduction_axes).output(0)
687+
log_max_val = get_ov_output(log10(OpenVINOKerasTensor(max_val)))
688+
log_mse = get_ov_output(log10(OpenVINOKerasTensor(mse)))
689+
690+
psnr = ov_opset.subtract(
691+
ov_opset.multiply(
692+
ov_opset.constant(20, log_max_val.get_element_type()), log_max_val
693+
),
694+
ov_opset.multiply(
695+
ov_opset.constant(10, log_mse.get_element_type()), log_mse
696+
),
697+
).output(0)
698+
return OpenVINOKerasTensor(psnr)
674699

675700

676701
def dot_product_attention(

‎keras/src/backend/openvino/numpy.py‎

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,9 +1026,7 @@ def empty(shape, dtype=None):
10261026

10271027

10281028
def empty_like(x, dtype=None):
1029-
raise NotImplementedError(
1030-
"`empty_like` is not supported with openvino backend"
1031-
)
1029+
return zeros_like(x, dtype=dtype)
10321030

10331031

10341032
def equal(x1, x2):
@@ -1206,7 +1204,25 @@ def isfinite(x):
12061204

12071205

12081206
def isin(x1, x2, assume_unique=False, invert=False):
1209-
raise NotImplementedError("`isin` is not supported with openvino backend")
1207+
x1 = get_ov_output(x1)
1208+
x2 = get_ov_output(x2)
1209+
output_shape = ov_opset.shape_of(x1).output(0)
1210+
x1, x2 = _align_operand_types(x1, x2, "isin()")
1211+
1212+
minus_one = ov_opset.constant([-1], dtype=Type.i64)
1213+
x1 = ov_opset.reshape(x1, minus_one, special_zero=False).output(0)
1214+
x2 = ov_opset.reshape(x2, minus_one, special_zero=False).output(0)
1215+
if not assume_unique:
1216+
x2 = ov_opset.unique(x2).output(0)
1217+
x1 = ov_opset.unsqueeze(x1, 1).output(0)
1218+
x2 = ov_opset.unsqueeze(x2, 0).output(0)
1219+
cmp = ov_opset.equal(x1, x2).output(0)
1220+
result_flat = ov_opset.reduce_logical_or(cmp, 1).output(0)
1221+
1222+
if invert:
1223+
result_flat = ov_opset.logical_not(result_flat).output(0)
1224+
result = ov_opset.reshape(result_flat, output_shape, False).output(0)
1225+
return OpenVINOKerasTensor(result)
12101226

12111227

12121228
def isinf(x):
@@ -2056,7 +2072,17 @@ def reshape(x, newshape):
20562072

20572073

20582074
def roll(x, shift, axis=None):
2059-
raise NotImplementedError("`roll` is not supported with openvino backend")
2075+
x = get_ov_output(x)
2076+
if axis is not None:
2077+
result = ov_opset.roll(x, shift, axis).output(0)
2078+
else:
2079+
output_shape = ov_opset.shape_of(x).output(0)
2080+
flattened = ov_opset.reshape(
2081+
x, ov_opset.constant([-1], Type.i32), False
2082+
).output(0)
2083+
result = ov_opset.roll(flattened, shift, 0).output(0)
2084+
result = ov_opset.reshape(result, output_shape, False).output(0)
2085+
return OpenVINOKerasTensor(result)
20602086

20612087

20622088
def sign(x):
@@ -2447,7 +2473,20 @@ def triu(x, k=0):
24472473

24482474

24492475
def vdot(x1, x2):
2450-
raise NotImplementedError("`vdot` is not supported with openvino backend")
2476+
element_type = None
2477+
if isinstance(x1, OpenVINOKerasTensor):
2478+
element_type = x1.output.get_element_type()
2479+
if isinstance(x2, OpenVINOKerasTensor):
2480+
element_type = x2.output.get_element_type()
2481+
x1 = get_ov_output(x1, element_type)
2482+
x2 = get_ov_output(x2, element_type)
2483+
x1, x2 = _align_operand_types(x1, x2, "vdot()")
2484+
if x1.get_partial_shape().rank == 0 or x2.get_partial_shape().rank == 0:
2485+
return OpenVINOKerasTensor(ov_opset.multiply(x1, x2).output(0))
2486+
flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
2487+
x1 = ov_opset.reshape(x1, flatten_shape, False).output(0)
2488+
x2 = ov_opset.reshape(x2, flatten_shape, False).output(0)
2489+
return OpenVINOKerasTensor(ov_opset.matmul(x1, x2, False, False).output(0))
24512490

24522491

24532492
def vstack(xs):
@@ -2648,9 +2687,22 @@ def eye(N, M=None, k=0, dtype=None):
26482687

26492688

26502689
def floor_divide(x1, x2):
2651-
raise NotImplementedError(
2652-
"`floor_divide` is not supported with openvino backend"
2653-
)
2690+
x1_output = get_ov_output(x1)
2691+
x2_output = get_ov_output(x2)
2692+
if x1_output.get_element_type() == Type.boolean:
2693+
x1_output = ov_opset.convert(x1_output, Type.i32).output(0)
2694+
if isinstance(x2, (int, float)):
2695+
if x1_output.get_element_type().is_integral() and isinstance(x2, float):
2696+
ov_type = OPENVINO_DTYPES[config.floatx()]
2697+
else:
2698+
ov_type = x1_output.get_element_type()
2699+
x1 = ov_opset.convert(x1_output, ov_type).output(0)
2700+
x2 = ov_opset.convert(x2_output, ov_type).output(0)
2701+
else:
2702+
x1, x2 = _align_operand_types(x1_output, x2_output, "floor_divide()")
2703+
div = ov_opset.divide(x1, x2).output(0)
2704+
floored_div = ov_opset.floor(div).output(0)
2705+
return OpenVINOKerasTensor(floored_div)
26542706

26552707

26562708
def logical_xor(x1, x2):

‎keras/src/ops/numpy_test.py‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7414,8 +7414,11 @@ def test_empty(self, dtype):
74147414
def test_empty_like(self, dtype):
74157415
import jax.numpy as jnp
74167416

7417-
x = jnp.empty([2, 3, 4], dtype=dtype)
7418-
expected_dtype = standardize_dtype(jnp.empty_like(x, dtype=dtype).dtype)
7417+
x_jax = jnp.empty([2, 3, 4], dtype=dtype)
7418+
x = knp.ones([2, 3, 4], dtype=dtype)
7419+
expected_dtype = standardize_dtype(
7420+
jnp.empty_like(x_jax, dtype=dtype).dtype
7421+
)
74197422

74207423
self.assertEqual(
74217424
standardize_dtype(knp.empty_like(x, dtype=dtype).dtype),

0 commit comments

Comments
 (0)