keras-nightly 3.12.0.dev2025100503__py3-none-any.whl → 3.14.0.dev2026011604__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. keras/__init__.py +1 -0
  2. keras/_tf_keras/keras/__init__.py +1 -0
  3. keras/_tf_keras/keras/callbacks/__init__.py +3 -0
  4. keras/_tf_keras/keras/distillation/__init__.py +16 -0
  5. keras/_tf_keras/keras/distribution/__init__.py +3 -0
  6. keras/_tf_keras/keras/dtype_policies/__init__.py +3 -0
  7. keras/_tf_keras/keras/layers/__init__.py +21 -0
  8. keras/_tf_keras/keras/ops/__init__.py +13 -0
  9. keras/_tf_keras/keras/ops/image/__init__.py +1 -0
  10. keras/_tf_keras/keras/ops/linalg/__init__.py +1 -0
  11. keras/_tf_keras/keras/ops/nn/__init__.py +3 -0
  12. keras/_tf_keras/keras/ops/numpy/__init__.py +9 -0
  13. keras/_tf_keras/keras/quantizers/__init__.py +13 -0
  14. keras/callbacks/__init__.py +3 -0
  15. keras/distillation/__init__.py +16 -0
  16. keras/distribution/__init__.py +3 -0
  17. keras/dtype_policies/__init__.py +3 -0
  18. keras/layers/__init__.py +21 -0
  19. keras/ops/__init__.py +13 -0
  20. keras/ops/image/__init__.py +1 -0
  21. keras/ops/linalg/__init__.py +1 -0
  22. keras/ops/nn/__init__.py +3 -0
  23. keras/ops/numpy/__init__.py +9 -0
  24. keras/quantizers/__init__.py +13 -0
  25. keras/src/applications/imagenet_utils.py +4 -1
  26. keras/src/backend/common/backend_utils.py +30 -6
  27. keras/src/backend/common/name_scope.py +2 -1
  28. keras/src/backend/common/variables.py +30 -15
  29. keras/src/backend/jax/core.py +92 -3
  30. keras/src/backend/jax/distribution_lib.py +16 -2
  31. keras/src/backend/jax/linalg.py +4 -0
  32. keras/src/backend/jax/nn.py +509 -29
  33. keras/src/backend/jax/numpy.py +59 -8
  34. keras/src/backend/jax/trainer.py +14 -2
  35. keras/src/backend/numpy/linalg.py +4 -0
  36. keras/src/backend/numpy/nn.py +311 -1
  37. keras/src/backend/numpy/numpy.py +65 -2
  38. keras/src/backend/openvino/__init__.py +1 -0
  39. keras/src/backend/openvino/core.py +2 -23
  40. keras/src/backend/openvino/linalg.py +4 -0
  41. keras/src/backend/openvino/nn.py +271 -20
  42. keras/src/backend/openvino/numpy.py +943 -189
  43. keras/src/backend/tensorflow/layer.py +43 -9
  44. keras/src/backend/tensorflow/linalg.py +24 -0
  45. keras/src/backend/tensorflow/nn.py +545 -1
  46. keras/src/backend/tensorflow/numpy.py +250 -50
  47. keras/src/backend/torch/core.py +3 -1
  48. keras/src/backend/torch/linalg.py +4 -0
  49. keras/src/backend/torch/nn.py +125 -0
  50. keras/src/backend/torch/numpy.py +80 -2
  51. keras/src/callbacks/__init__.py +1 -0
  52. keras/src/callbacks/model_checkpoint.py +5 -0
  53. keras/src/callbacks/orbax_checkpoint.py +332 -0
  54. keras/src/callbacks/terminate_on_nan.py +54 -5
  55. keras/src/datasets/cifar10.py +5 -0
  56. keras/src/distillation/__init__.py +1 -0
  57. keras/src/distillation/distillation_loss.py +390 -0
  58. keras/src/distillation/distiller.py +598 -0
  59. keras/src/distribution/distribution_lib.py +14 -0
  60. keras/src/dtype_policies/__init__.py +2 -0
  61. keras/src/dtype_policies/dtype_policy.py +90 -1
  62. keras/src/export/__init__.py +2 -0
  63. keras/src/export/export_utils.py +39 -2
  64. keras/src/export/litert.py +248 -0
  65. keras/src/export/openvino.py +1 -1
  66. keras/src/export/tf2onnx_lib.py +3 -0
  67. keras/src/layers/__init__.py +13 -0
  68. keras/src/layers/activations/softmax.py +9 -4
  69. keras/src/layers/attention/multi_head_attention.py +4 -1
  70. keras/src/layers/core/dense.py +241 -111
  71. keras/src/layers/core/einsum_dense.py +316 -131
  72. keras/src/layers/core/embedding.py +84 -94
  73. keras/src/layers/core/input_layer.py +1 -0
  74. keras/src/layers/core/reversible_embedding.py +399 -0
  75. keras/src/layers/input_spec.py +17 -17
  76. keras/src/layers/layer.py +45 -15
  77. keras/src/layers/merging/dot.py +4 -1
  78. keras/src/layers/pooling/adaptive_average_pooling1d.py +65 -0
  79. keras/src/layers/pooling/adaptive_average_pooling2d.py +62 -0
  80. keras/src/layers/pooling/adaptive_average_pooling3d.py +63 -0
  81. keras/src/layers/pooling/adaptive_max_pooling1d.py +65 -0
  82. keras/src/layers/pooling/adaptive_max_pooling2d.py +62 -0
  83. keras/src/layers/pooling/adaptive_max_pooling3d.py +63 -0
  84. keras/src/layers/pooling/base_adaptive_pooling.py +63 -0
  85. keras/src/layers/preprocessing/discretization.py +6 -5
  86. keras/src/layers/preprocessing/feature_space.py +8 -4
  87. keras/src/layers/preprocessing/image_preprocessing/aug_mix.py +2 -2
  88. keras/src/layers/preprocessing/image_preprocessing/random_contrast.py +3 -3
  89. keras/src/layers/preprocessing/image_preprocessing/resizing.py +10 -0
  90. keras/src/layers/preprocessing/index_lookup.py +19 -1
  91. keras/src/layers/preprocessing/normalization.py +14 -1
  92. keras/src/layers/regularization/dropout.py +43 -1
  93. keras/src/layers/rnn/rnn.py +19 -0
  94. keras/src/losses/loss.py +1 -1
  95. keras/src/losses/losses.py +24 -0
  96. keras/src/metrics/confusion_metrics.py +7 -6
  97. keras/src/models/cloning.py +4 -0
  98. keras/src/models/functional.py +11 -3
  99. keras/src/models/model.py +172 -34
  100. keras/src/ops/image.py +257 -20
  101. keras/src/ops/linalg.py +93 -0
  102. keras/src/ops/nn.py +258 -0
  103. keras/src/ops/numpy.py +569 -36
  104. keras/src/optimizers/muon.py +65 -31
  105. keras/src/optimizers/schedules/learning_rate_schedule.py +4 -3
  106. keras/src/quantizers/__init__.py +14 -1
  107. keras/src/quantizers/awq.py +361 -0
  108. keras/src/quantizers/awq_config.py +140 -0
  109. keras/src/quantizers/awq_core.py +217 -0
  110. keras/src/quantizers/gptq.py +2 -8
  111. keras/src/quantizers/gptq_config.py +36 -1
  112. keras/src/quantizers/gptq_core.py +65 -79
  113. keras/src/quantizers/quantization_config.py +246 -0
  114. keras/src/quantizers/quantizers.py +127 -61
  115. keras/src/quantizers/utils.py +23 -0
  116. keras/src/random/seed_generator.py +6 -4
  117. keras/src/saving/file_editor.py +81 -6
  118. keras/src/saving/orbax_util.py +26 -0
  119. keras/src/saving/saving_api.py +37 -14
  120. keras/src/saving/saving_lib.py +1 -1
  121. keras/src/testing/__init__.py +1 -0
  122. keras/src/testing/test_case.py +45 -5
  123. keras/src/utils/backend_utils.py +31 -4
  124. keras/src/utils/dataset_utils.py +234 -35
  125. keras/src/utils/file_utils.py +49 -11
  126. keras/src/utils/image_utils.py +14 -2
  127. keras/src/utils/jax_layer.py +244 -55
  128. keras/src/utils/module_utils.py +29 -0
  129. keras/src/utils/progbar.py +10 -2
  130. keras/src/utils/rng_utils.py +9 -1
  131. keras/src/utils/tracking.py +5 -5
  132. keras/src/version.py +1 -1
  133. {keras_nightly-3.12.0.dev2025100503.dist-info → keras_nightly-3.14.0.dev2026011604.dist-info}/METADATA +16 -6
  134. {keras_nightly-3.12.0.dev2025100503.dist-info → keras_nightly-3.14.0.dev2026011604.dist-info}/RECORD +136 -115
  135. {keras_nightly-3.12.0.dev2025100503.dist-info → keras_nightly-3.14.0.dev2026011604.dist-info}/WHEEL +0 -0
  136. {keras_nightly-3.12.0.dev2025100503.dist-info → keras_nightly-3.14.0.dev2026011604.dist-info}/top_level.txt +0 -0
@@ -4,6 +4,7 @@ from openvino import Type
4
4
 
5
5
  from keras.src.backend import config
6
6
  from keras.src.backend.common import dtypes
7
+ from keras.src.backend.common.backend_utils import canonicalize_axis
7
8
  from keras.src.backend.common.variables import standardize_dtype
8
9
  from keras.src.backend.openvino.core import DTYPES_MAX
9
10
  from keras.src.backend.openvino.core import DTYPES_MIN
@@ -76,25 +77,81 @@ def multiply(x1, x2):
76
77
 
77
78
 
78
79
  def mean(x, axis=None, keepdims=False):
79
- x = get_ov_output(x)
80
- if axis is None:
81
- flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
82
- x = ov_opset.reshape(x, flatten_shape, False).output(0)
83
- axis = 0
84
- axis_const = ov_opset.constant(axis, dtype=Type.i32).output(0)
85
- mean_ops = ov_opset.reduce_mean(x, axis_const, keepdims)
86
- return OpenVINOKerasTensor(mean_ops.output(0))
80
+ x_ov = get_ov_output(x)
81
+ x_type = x_ov.get_element_type()
82
+
83
+ was_axis_none = axis is None
84
+ x_resolved, axis_resolved = _resolve_axis(x_ov, axis)
85
+
86
+ if axis_resolved is None:
87
+ return OpenVINOKerasTensor(x_ov)
88
+
89
+ if x_type.is_integral():
90
+ ov_type = OPENVINO_DTYPES[config.floatx()]
91
+ x_resolved = ov_opset.convert(x_resolved, ov_type).output(0)
92
+
93
+ result = ov_opset.reduce_mean(x_resolved, axis_resolved, keepdims).output(0)
94
+
95
+ if keepdims and was_axis_none:
96
+ rank = x.get_partial_shape().rank.get_length()
97
+ result_shape = [1] * rank
98
+ result = ov_opset.reshape(
99
+ result,
100
+ ov_opset.constant(result_shape, Type.i32).output(0),
101
+ False,
102
+ ).output(0)
103
+
104
+ return OpenVINOKerasTensor(result)
87
105
 
88
106
 
89
107
  def max(x, axis=None, keepdims=False, initial=None):
90
- assert initial is None, (
91
- "`max` with not None initial is not supported by openvino backend"
92
- )
108
+ return _compute_extrema(x, "max", axis, keepdims, initial)
109
+
110
+
111
+ def _compute_extrema(x, operation, axis=None, keepdims=False, initial=None):
112
+ if operation == "min":
113
+ reduction_op = ov_opset.reduce_min
114
+ elementwise_op = ov_opset.minimum
115
+ elif operation == "max":
116
+ reduction_op = ov_opset.reduce_max
117
+ elementwise_op = ov_opset.maximum
118
+ else:
119
+ raise ValueError(
120
+ f"Operation must be 'min' or 'max', received {operation}"
121
+ )
122
+
93
123
  x = get_ov_output(x)
94
- reduce_axis = ov_opset.constant(axis, Type.i32).output(0)
95
- return OpenVINOKerasTensor(
96
- ov_opset.reduce_max(x, reduce_axis, keepdims).output(0)
97
- )
124
+ x_type = x.get_element_type()
125
+ x_for_rank = x
126
+
127
+ is_bool = x_type == Type.boolean
128
+ if is_bool:
129
+ x = ov_opset.convert(x, Type.i32).output(0)
130
+ x_type = Type.i32
131
+
132
+ if isinstance(axis, tuple) and len(axis) == 0:
133
+ return OpenVINOKerasTensor(x)
134
+
135
+ was_axis_none = axis is None
136
+ x, axis = _resolve_axis(x, axis)
137
+
138
+ result = reduction_op(x, axis, keepdims).output(0)
139
+
140
+ if initial is not None:
141
+ initial_tensor = ov_opset.constant(initial, x_type).output(0)
142
+ result = elementwise_op(result, initial_tensor).output(0)
143
+
144
+ if keepdims and was_axis_none:
145
+ orig_shape = ov_opset.shape_of(x_for_rank, Type.i32).output(0)
146
+ orig_rank_shape = ov_opset.shape_of(orig_shape, Type.i32).output(0)
147
+ one = ov_opset.constant(1, Type.i32).output(0)
148
+ result_shape = ov_opset.broadcast(one, orig_rank_shape).output(0)
149
+ result = ov_opset.reshape(result, result_shape, False).output(0)
150
+
151
+ if is_bool:
152
+ result = ov_opset.convert(result, Type.boolean).output(0)
153
+
154
+ return OpenVINOKerasTensor(result)
98
155
 
99
156
 
100
157
  def ones(shape, dtype=None):
@@ -125,6 +182,9 @@ def zeros(shape, dtype=None):
125
182
 
126
183
  def absolute(x):
127
184
  x = get_ov_output(x)
185
+ x_type = x.get_element_type()
186
+ if x_type == Type.boolean:
187
+ return OpenVINOKerasTensor(x)
128
188
  return OpenVINOKerasTensor(ov_opset.absolute(x).output(0))
129
189
 
130
190
 
@@ -135,11 +195,10 @@ def abs(x):
135
195
 
136
196
  def all(x, axis=None, keepdims=False):
137
197
  x = get_ov_output(x)
198
+ x, axis = _resolve_axis(x, axis)
138
199
  if axis is None:
139
- flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
140
- x = ov_opset.reshape(x, flatten_shape, False).output(0)
141
- axis = 0
142
- axis = ov_opset.constant(axis, Type.i32).output(0)
200
+ return OpenVINOKerasTensor(x)
201
+ x = ov_opset.convert(x, Type.boolean).output(0)
143
202
  return OpenVINOKerasTensor(
144
203
  ov_opset.reduce_logical_and(x, axis, keepdims).output(0)
145
204
  )
@@ -151,28 +210,21 @@ def angle(x):
151
210
 
152
211
  def any(x, axis=None, keepdims=False):
153
212
  x = get_ov_output(x)
213
+ x, axis = _resolve_axis(x, axis)
154
214
  if axis is None:
155
- flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
156
- x = ov_opset.reshape(x, flatten_shape, False).output(0)
157
- axis = 0
158
- axis = ov_opset.constant(axis, Type.i32).output(0)
215
+ return OpenVINOKerasTensor(x)
216
+ x = ov_opset.convert(x, Type.boolean).output(0)
159
217
  return OpenVINOKerasTensor(
160
218
  ov_opset.reduce_logical_or(x, axis, keepdims).output(0)
161
219
  )
162
220
 
163
221
 
164
222
  def amax(x, axis=None, keepdims=False):
165
- if axis == () or axis == []:
166
- return x
167
223
  x = get_ov_output(x)
168
224
  x_type = x.get_element_type()
225
+ x, axis = _resolve_axis(x, axis)
169
226
  if axis is None:
170
- flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
171
- x = ov_opset.reshape(x, flatten_shape, False).output(0)
172
- axis = 0
173
- if isinstance(axis, tuple):
174
- axis = list(axis)
175
- axis = ov_opset.constant(axis, Type.i32).output(0)
227
+ return OpenVINOKerasTensor(x)
176
228
  if x_type == Type.boolean:
177
229
  return OpenVINOKerasTensor(
178
230
  ov_opset.reduce_logical_or(x, axis, keepdims).output(0)
@@ -181,10 +233,21 @@ def amax(x, axis=None, keepdims=False):
181
233
 
182
234
 
183
235
  def amin(x, axis=None, keepdims=False):
184
- if axis == () or axis == []:
185
- return x
186
236
  x = get_ov_output(x)
187
237
  x_type = x.get_element_type()
238
+ x, axis = _resolve_axis(x, axis)
239
+ if axis is None:
240
+ return OpenVINOKerasTensor(x)
241
+ if x_type == Type.boolean:
242
+ return OpenVINOKerasTensor(
243
+ ov_opset.reduce_logical_and(x, axis, keepdims).output(0)
244
+ )
245
+ return OpenVINOKerasTensor(ov_opset.reduce_min(x, axis, keepdims).output(0))
246
+
247
+
248
+ def _resolve_axis(x, axis):
249
+ if axis == () or axis == []:
250
+ return x, None
188
251
  if axis is None:
189
252
  flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
190
253
  x = ov_opset.reshape(x, flatten_shape, False).output(0)
@@ -192,11 +255,18 @@ def amin(x, axis=None, keepdims=False):
192
255
  if isinstance(axis, tuple):
193
256
  axis = list(axis)
194
257
  axis = ov_opset.constant(axis, Type.i32).output(0)
258
+ return x, axis
259
+
260
+
261
+ def _upcast_type_if_needed(x):
262
+ x_type = x.get_element_type()
195
263
  if x_type == Type.boolean:
196
- return OpenVINOKerasTensor(
197
- ov_opset.reduce_logical_and(x, axis, keepdims).output(0)
198
- )
199
- return OpenVINOKerasTensor(ov_opset.reduce_min(x, axis, keepdims).output(0))
264
+ x = ov_opset.convert(x, Type.i32).output(0)
265
+ elif x_type in (Type.i8, Type.i16):
266
+ x = ov_opset.convert(x, Type.i32).output(0)
267
+ elif x_type in (Type.u8, Type.u16):
268
+ x = ov_opset.convert(x, Type.u32).output(0)
269
+ return x
200
270
 
201
271
 
202
272
  def append(x1, x2, axis=None):
@@ -439,6 +509,10 @@ def array(x, dtype=None):
439
509
  return np.array(x)
440
510
 
441
511
 
512
+ def view(x, dtype=None):
513
+ raise NotImplementedError("`view` is not supported with openvino backend")
514
+
515
+
442
516
  def average(x, axis=None, weights=None):
443
517
  x = get_ov_output(x)
444
518
  if weights is not None:
@@ -472,22 +546,79 @@ def average(x, axis=None, weights=None):
472
546
 
473
547
 
474
548
  def bartlett(x):
475
- raise NotImplementedError(
476
- "`bartlett` is not supported with openvino backend"
549
+ x = get_ov_output(x)
550
+ zero_const = ov_opset.constant(0, Type.i64)
551
+ one_const = ov_opset.constant(1, Type.i64)
552
+ two_const = ov_opset.constant(2, Type.i64)
553
+ two_const_f64 = ov_opset.constant(2.0, Type.f64)
554
+ if x.get_element_type() != Type.i64:
555
+ x = ov_opset.convert(x, Type.i64)
556
+ half = ov_opset.convert(
557
+ ov_opset.divide(ov_opset.subtract(x, one_const), two_const), Type.f64
477
558
  )
559
+ n = ov_opset.range(zero_const, x, one_const, Type.f64)
560
+ condition = ov_opset.less_equal(n, half)
561
+ first_half = ov_opset.divide(
562
+ ov_opset.multiply(two_const_f64, n),
563
+ ov_opset.convert(ov_opset.subtract(x, one_const), Type.f64),
564
+ )
565
+ second_half = ov_opset.subtract(two_const_f64, first_half)
566
+ window = ov_opset.select(condition, first_half, second_half)
567
+ window = ov_opset.convert(window, OPENVINO_DTYPES[config.floatx()]).output(
568
+ 0
569
+ )
570
+ return OpenVINOKerasTensor(window)
478
571
 
479
572
 
480
573
  def hamming(x):
481
- raise NotImplementedError(
482
- "`hamming` is not supported with openvino backend"
574
+ m = get_ov_output(x)
575
+
576
+ m_i64 = (
577
+ m if m.get_element_type() == Type.i64 else ov_opset.convert(m, Type.i64)
483
578
  )
484
579
 
580
+ start = ov_opset.constant(0, Type.i64)
581
+ step = ov_opset.constant(1, Type.i64)
582
+ n = ov_opset.range(start, m_i64, step, Type.f64)
485
583
 
486
- def heaviside(x1, x2):
487
- raise NotImplementedError(
488
- "`heaviside` is not supported with openvino backend"
584
+ one_i64 = ov_opset.constant(1, Type.i64)
585
+ denom_i64 = ov_opset.subtract(m_i64, one_i64)
586
+ denom = ov_opset.convert(denom_i64, Type.f64)
587
+
588
+ two_pi = ov_opset.constant(2.0 * np.pi, Type.f64)
589
+ two_pi_over_m_minus_1 = ov_opset.divide(two_pi, denom)
590
+
591
+ x = ov_opset.multiply(two_pi_over_m_minus_1, n)
592
+ c = ov_opset.cos(x)
593
+
594
+ # 0.54 - 0.46 * cos(...)
595
+ a = ov_opset.constant(0.54, Type.f64)
596
+ b = ov_opset.constant(0.46, Type.f64)
597
+ hamming_window = ov_opset.subtract(a, ov_opset.multiply(b, c))
598
+ hamming_window = ov_opset.convert(
599
+ hamming_window, OPENVINO_DTYPES[config.floatx()]
489
600
  )
490
601
 
602
+ return OpenVINOKerasTensor(hamming_window.output(0))
603
+
604
+
605
+ def heaviside(x1, x2):
606
+ x1 = get_ov_output(x1)
607
+ x_type = x1.get_element_type()
608
+ x2 = get_ov_output(x2, x_type)
609
+
610
+ zero_scalar = ov_opset.constant(0, x_type).output(0)
611
+ one_scalar = ov_opset.constant(1, x_type).output(0)
612
+
613
+ neg = ov_opset.less(x1, zero_scalar).output(0)
614
+ pos = ov_opset.greater(x1, zero_scalar).output(0)
615
+ eq = ov_opset.equal(x1, zero_scalar).output(0)
616
+
617
+ x = ov_opset.select(neg, zero_scalar, x1).output(0)
618
+ x = ov_opset.select(pos, one_scalar, x).output(0)
619
+ x = ov_opset.select(eq, x2, x).output(0)
620
+ return OpenVINOKerasTensor(x)
621
+
491
622
 
492
623
  def kaiser(x, beta):
493
624
  raise NotImplementedError("`kaiser` is not supported with openvino backend")
@@ -539,9 +670,30 @@ def bincount(x, weights=None, minlength=0, sparse=False):
539
670
 
540
671
 
541
672
  def blackman(x):
542
- raise NotImplementedError(
543
- "`blackman` is not supported with openvino backend"
673
+ x = get_ov_output(x)
674
+ zero_const = ov_opset.constant(0, Type.i64)
675
+ one_const = ov_opset.constant(1, Type.i64)
676
+ two_pi = ov_opset.constant(2.0 * np.pi, Type.f64)
677
+ term_1 = ov_opset.constant(0.42, Type.f64)
678
+ term_2 = ov_opset.constant(0.5, Type.f64)
679
+ term_3 = ov_opset.constant(0.08, Type.f64)
680
+ if x.get_element_type() != Type.i64:
681
+ x = ov_opset.convert(x, Type.i64)
682
+ n = ov_opset.range(zero_const, x, one_const, Type.f64)
683
+ n_minus_1 = ov_opset.subtract(
684
+ ov_opset.convert(x, Type.f64), ov_opset.constant(1.0, Type.f64)
685
+ ).output(0)
686
+ angle_2pi = ov_opset.divide(ov_opset.multiply(two_pi, n), n_minus_1)
687
+ angle_4pi = ov_opset.multiply(angle_2pi, ov_opset.constant(2.0, Type.f64))
688
+ cos_2pi = ov_opset.cos(angle_2pi)
689
+ cos_4pi = ov_opset.cos(angle_4pi)
690
+ term_2_final = ov_opset.multiply(term_2, cos_2pi)
691
+ term_3_final = ov_opset.multiply(term_3, cos_4pi)
692
+ window = ov_opset.add(ov_opset.subtract(term_1, term_2_final), term_3_final)
693
+ window = ov_opset.convert(window, OPENVINO_DTYPES[config.floatx()]).output(
694
+ 0
544
695
  )
696
+ return OpenVINOKerasTensor(window)
545
697
 
546
698
 
547
699
  def broadcast_to(x, shape):
@@ -554,16 +706,32 @@ def broadcast_to(x, shape):
554
706
 
555
707
 
556
708
  def cbrt(x):
557
- raise NotImplementedError("`cbrt` is not supported with openvino backend")
709
+ x = get_ov_output(x)
710
+ x_type = x.get_element_type()
711
+ if x_type.is_integral() or x_type == Type.boolean:
712
+ x = ov_opset.convert(x, OPENVINO_DTYPES[config.floatx()]).output(0)
713
+ sign_x = ov_opset.sign(x)
714
+ abs_x = ov_opset.absolute(x)
715
+ one_third = ov_opset.constant(1.0 / 3.0, x.get_element_type())
716
+ root_abs = ov_opset.power(abs_x, one_third)
717
+ res = ov_opset.multiply(sign_x, root_abs)
718
+ return OpenVINOKerasTensor(res.output(0))
558
719
 
559
720
 
560
721
  def ceil(x):
561
722
  x = get_ov_output(x)
562
- return OpenVINOKerasTensor(ov_opset.ceil(x).output(0))
723
+ x_type = x.get_element_type()
724
+ if x_type.is_integral():
725
+ x = ov_opset.convert(x, OPENVINO_DTYPES[config.floatx()]).output(0)
726
+ ceiling = ov_opset.ceil(x).output(0)
727
+ return OpenVINOKerasTensor(ceiling)
563
728
 
564
729
 
565
730
  def clip(x, x_min, x_max):
566
731
  x = get_ov_output(x)
732
+ x_type = x.get_element_type()
733
+ if x_type == Type.boolean:
734
+ x = ov_opset.convert(x, Type.i32).output(0)
567
735
  x_min = get_ov_output(x_min, x.get_element_type())
568
736
  x_max = get_ov_output(x_max, x.get_element_type())
569
737
  clip_by_min = ov_opset.maximum(x, x_min).output(0)
@@ -619,15 +787,9 @@ def count_nonzero(x, axis=None):
619
787
  zero_constant = ov_opset.convert_like(zero_constant, x)
620
788
  x = ov_opset.not_equal(x, zero_constant).output(0)
621
789
  x = ov_opset.convert(x, Type.i32).output(0)
622
- if axis is None:
623
- flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
624
- x = ov_opset.reshape(x, flatten_shape, False).output(0)
625
- axis = 0
626
- if isinstance(axis, tuple):
627
- axis = list(axis)
628
- if axis == []:
790
+ x, axis = _resolve_axis(x, axis)
791
+ if not axis:
629
792
  return OpenVINOKerasTensor(x)
630
- axis = ov_opset.constant(axis, Type.i32).output(0)
631
793
  return OpenVINOKerasTensor(ov_opset.reduce_sum(x, axis, False).output(0))
632
794
 
633
795
 
@@ -646,11 +808,9 @@ def cumsum(x, axis=None, dtype=None):
646
808
  if dtype is not None:
647
809
  ov_type = OPENVINO_DTYPES[standardize_dtype(dtype)]
648
810
  x = ov_opset.convert(x, ov_type).output(0)
649
- if axis is None:
650
- flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
651
- x = ov_opset.reshape(x, flatten_shape, False).output(0)
652
- axis = 0
653
- axis = ov_opset.constant(axis, Type.i32).output(0)
811
+ x, axis = _resolve_axis(x, axis)
812
+ if x.get_element_type() == Type.boolean:
813
+ x = ov_opset.convert(x, Type.i32).output(0)
654
814
  return OpenVINOKerasTensor(ov_opset.cumsum(x, axis).output(0))
655
815
 
656
816
 
@@ -676,13 +836,120 @@ def deg2rad(x):
676
836
 
677
837
 
678
838
  def diag(x, k=0):
679
- raise NotImplementedError("`diag` is not supported with openvino backend")
839
+ x = get_ov_output(x)
840
+ x_shape = x.get_partial_shape()
841
+ rank = x_shape.rank.get_length()
842
+
843
+ if rank == 1:
844
+ N_dim = x_shape[0]
845
+ if not N_dim.is_static:
846
+ raise ValueError(
847
+ "diag requires input with static shape for 1D input."
848
+ )
849
+ N = N_dim.get_length()
850
+ output_size = N + np.abs(k)
851
+ out_shape = ov_opset.constant(
852
+ [output_size, output_size], dtype=Type.i32
853
+ ).output(0)
854
+ zeros_const = ov_opset.constant(0, x.get_element_type()).output(0)
855
+ diag_matrix = ov_opset.broadcast(zeros_const, out_shape)
856
+
857
+ indices = []
858
+ if k >= 0:
859
+ for i in range(N):
860
+ indices.append([i, i + k])
861
+ else:
862
+ for i in range(N):
863
+ indices.append([i - k, i])
864
+
865
+ indices = np.array(indices, dtype=np.int32)
866
+ indices_const = ov_opset.constant(indices, dtype=Type.i32).output(0)
867
+ updated = ov_opset.scatter_nd_update(diag_matrix, indices_const, x)
868
+ return OpenVINOKerasTensor(updated.output(0))
869
+
870
+ elif rank == 2:
871
+ M_dim = x_shape[0]
872
+ N_dim = x_shape[1]
873
+ if not M_dim.is_static or not N_dim.is_static:
874
+ raise ValueError(
875
+ "diag requires input with static shape for 2D input."
876
+ )
877
+ M = M_dim.get_length()
878
+ N = N_dim.get_length()
879
+
880
+ if k >= 0:
881
+ L = np.minimum(M, N - k) if (N - k) > 0 else 0
882
+ indices = [[i, i + k] for i in range(L)]
883
+ else:
884
+ L = np.minimum(M + k, N) if (M + k) > 0 else 0
885
+ indices = [[i - k, i] for i in range(L)]
886
+
887
+ if L <= 0:
888
+ keras_dtype = ov_to_keras_type(x.get_element_type())
889
+ np_dtype = np.dtype(keras_dtype)
890
+ empty_np = np.empty((0,), dtype=np_dtype)
891
+ empty_const = ov_opset.constant(
892
+ empty_np, x.get_element_type()
893
+ ).output(0)
894
+ return OpenVINOKerasTensor(empty_const)
895
+
896
+ indices = np.array(indices, dtype=np.int32)
897
+ indices_const = ov_opset.constant(indices, dtype=Type.i32).output(0)
898
+ diag_vec = ov_opset.gather_nd(x, indices_const)
899
+ return OpenVINOKerasTensor(diag_vec.output(0))
900
+
901
+ else:
902
+ raise ValueError("diag supports only 1D or 2D tensors")
680
903
 
681
904
 
682
905
  def diagonal(x, offset=0, axis1=0, axis2=1):
683
- raise NotImplementedError(
684
- "`diagonal` is not supported with openvino backend"
685
- )
906
+ x = get_ov_output(x)
907
+ shape = x.get_partial_shape()
908
+ rank = x.get_partial_shape().rank.get_length()
909
+ if rank is None:
910
+ raise ValueError("`diagonal` requires input tensor with static rank.")
911
+ if rank < 2:
912
+ raise ValueError(
913
+ f"diagonal requires input tensor with rank >= 2.Given rank: {rank}"
914
+ )
915
+ axis1 = canonicalize_axis(axis1, rank)
916
+ axis2 = canonicalize_axis(axis2, rank)
917
+ if axis1 == axis2:
918
+ raise ValueError("`axis1` and `axis2` cannot be the same.")
919
+
920
+ perm_order = [axis1, axis2] + [
921
+ i for i in range(rank) if i != axis1 and i != axis2
922
+ ]
923
+ perm_const = ov_opset.constant(perm_order, dtype=Type.i32).output(0)
924
+ x_transposed = ov_opset.transpose(x, perm_const)
925
+
926
+ N_dim = shape[axis1]
927
+ M_dim = shape[axis2]
928
+ if not N_dim.is_static or not M_dim.is_static:
929
+ raise ValueError(
930
+ "`diagonal` requires input tensor with static shape for axes "
931
+ f"`axis1` ({axis1}) and `axis2` ({axis2})."
932
+ )
933
+ N = N_dim.get_length()
934
+ M = M_dim.get_length()
935
+ if offset >= 0:
936
+ L = np.minimum(N, M - offset) if (M - offset) > 0 else 0
937
+ indices = [[i, i + offset] for i in range(L)]
938
+ else:
939
+ L = np.minimum(N + offset, M) if (N + offset) > 0 else 0
940
+ indices = [[i - offset, i] for i in range(L)]
941
+
942
+ indices = np.array(indices, dtype=np.int32).reshape(L, 2)
943
+ indices_const = ov_opset.constant(indices, dtype=Type.i32).output(0)
944
+
945
+ diag_gathered = ov_opset.gather_nd(x_transposed, indices_const)
946
+
947
+ out_rank = rank - 1
948
+ out_perm_order = list(range(1, out_rank)) + [0]
949
+ out_perm_const = ov_opset.constant(out_perm_order, dtype=Type.i32).output(0)
950
+
951
+ final_output = ov_opset.transpose(diag_gathered, out_perm_const)
952
+ return OpenVINOKerasTensor(final_output.output(0))
686
953
 
687
954
 
688
955
  def diff(a, n=1, axis=-1):
@@ -759,9 +1026,30 @@ def diff(a, n=1, axis=-1):
759
1026
 
760
1027
 
761
1028
  def digitize(x, bins):
762
- raise NotImplementedError(
763
- "`digitize` is not supported with openvino backend"
764
- )
1029
+ x_node = get_ov_output(x)
1030
+
1031
+ if isinstance(bins, OpenVINOKerasTensor):
1032
+ bins_node = get_ov_output(bins)
1033
+ else:
1034
+ bins_np = np.asarray(bins)
1035
+ if bins_np.ndim != 1:
1036
+ raise ValueError("`bins` must be 1-D array-like")
1037
+ bins_node = ov_opset.constant(bins_np).output(0)
1038
+
1039
+ x_node, bins_node = _align_operand_types(x_node, bins_node, "digitize()")
1040
+
1041
+ if x_node.get_element_type() == Type.boolean:
1042
+ x_node = ov_opset.convert(x_node, Type.f32).output(0)
1043
+ bins_node = ov_opset.convert(bins_node, Type.f32).output(0)
1044
+
1045
+ result = ov_opset.bucketize(
1046
+ x_node,
1047
+ bins_node,
1048
+ output_type=Type.i32,
1049
+ with_right_bound=False,
1050
+ ).output(0)
1051
+
1052
+ return OpenVINOKerasTensor(result)
765
1053
 
766
1054
 
767
1055
  def dot(x1, x2):
@@ -791,6 +1079,10 @@ def empty(shape, dtype=None):
791
1079
  return OpenVINOKerasTensor(empty_tensor)
792
1080
 
793
1081
 
1082
+ def empty_like(x, dtype=None):
1083
+ return zeros_like(x, dtype=dtype)
1084
+
1085
+
794
1086
  def equal(x1, x2):
795
1087
  element_type = None
796
1088
  if isinstance(x1, OpenVINOKerasTensor):
@@ -833,11 +1125,101 @@ def expm1(x):
833
1125
 
834
1126
 
835
1127
  def flip(x, axis=None):
836
- raise NotImplementedError("`flip` is not supported with openvino backend")
1128
+ x_node = get_ov_output(x)
1129
+
1130
+ # Using OpenVINO tensor shape
1131
+ ndim = len(x_node.get_partial_shape())
1132
+ if ndim is None:
1133
+ raise ValueError(
1134
+ "The `flip` operation does not support tensors with dynamic rank "
1135
+ "for the OpenVINO backend."
1136
+ )
1137
+
1138
+ if axis is None:
1139
+ axis = list(range(ndim))
1140
+ elif isinstance(axis, int):
1141
+ axis = [axis]
1142
+
1143
+ axis = [a + ndim if a < 0 else a for a in axis]
1144
+
1145
+ begin = [0] * ndim
1146
+ end = [0] * ndim
1147
+ strides = [1] * ndim
1148
+ for a in axis:
1149
+ strides[a] = -1
1150
+
1151
+ all_ones_mask = [1] * ndim
1152
+ result = ov_opset.strided_slice(
1153
+ data=x_node,
1154
+ begin=begin,
1155
+ end=end,
1156
+ strides=strides,
1157
+ begin_mask=all_ones_mask,
1158
+ end_mask=all_ones_mask,
1159
+ )
1160
+ return OpenVINOKerasTensor(result.output(0))
1161
+
1162
+
1163
+ def rot90(array, k=1, axes=(0, 1)):
1164
+ """Rotate an array by 90 degrees in the plane specified by axes."""
1165
+ array = get_ov_output(array)
1166
+
1167
+ if not isinstance(axes, (tuple, list)) or len(axes) != 2:
1168
+ raise ValueError("axes must be a tuple of length 2")
1169
+
1170
+ shape = array.get_partial_shape()
1171
+ ndim = shape.rank.get_length()
1172
+ if ndim is None:
1173
+ raise ValueError(
1174
+ "`rot90` does not support tensors with dynamic rank "
1175
+ "for the OpenVINO backend."
1176
+ )
1177
+
1178
+ axis1 = canonicalize_axis(axes[0], ndim)
1179
+ axis2 = canonicalize_axis(axes[1], ndim)
1180
+
1181
+ if axis1 == axis2:
1182
+ raise ValueError("axes must be different")
1183
+
1184
+ k = k % 4
1185
+ if k == 0:
1186
+ return OpenVINOKerasTensor(array)
1187
+
1188
+ result = array
1189
+
1190
+ for _ in range(k):
1191
+ # 1️ Transpose axis1 <-> axis2
1192
+ perm = list(range(ndim))
1193
+ perm[axis1], perm[axis2] = perm[axis2], perm[axis1]
1194
+ perm_const = ov_opset.constant(perm, Type.i32).output(0)
1195
+ result = ov_opset.transpose(result, perm_const).output(0)
1196
+
1197
+ # 2️ Reverse along axis1 using StridedSlice
1198
+ begin = [0] * ndim
1199
+ end = [0] * ndim
1200
+ strides = [1] * ndim
1201
+ strides[axis1] = -1
1202
+
1203
+ begin_mask = [1] * ndim
1204
+ end_mask = [1] * ndim
1205
+
1206
+ result = ov_opset.strided_slice(
1207
+ data=result,
1208
+ begin=begin,
1209
+ end=end,
1210
+ strides=strides,
1211
+ begin_mask=begin_mask,
1212
+ end_mask=end_mask,
1213
+ ).output(0)
1214
+
1215
+ return OpenVINOKerasTensor(result)
837
1216
 
838
1217
 
839
1218
  def floor(x):
840
1219
  x = get_ov_output(x)
1220
+ x_type = x.get_element_type()
1221
+ if x_type.is_integral():
1222
+ x = ov_opset.convert(x, OPENVINO_DTYPES[config.floatx()])
841
1223
  return OpenVINOKerasTensor(ov_opset.floor(x).output(0))
842
1224
 
843
1225
 
@@ -909,7 +1291,34 @@ def hstack(xs):
909
1291
 
910
1292
 
911
1293
  def hypot(x1, x2):
912
- raise NotImplementedError("`hypot` is not supported with openvino backend")
1294
+ element_type = None
1295
+ if isinstance(x1, OpenVINOKerasTensor):
1296
+ element_type = x1.output.get_element_type()
1297
+ if isinstance(x2, OpenVINOKerasTensor):
1298
+ element_type = x2.output.get_element_type()
1299
+ x1 = get_ov_output(x1, element_type)
1300
+ x2 = get_ov_output(x2, element_type)
1301
+ x1, x2 = _align_operand_types(x1, x2, "hypot()")
1302
+ x_type = x1.get_element_type()
1303
+ if x_type.is_integral() or x_type == Type.boolean:
1304
+ ov_type = OPENVINO_DTYPES[config.floatx()]
1305
+ x1 = ov_opset.convert(x1, ov_type)
1306
+ x2 = ov_opset.convert(x2, ov_type)
1307
+ x1_abs = ov_opset.absolute(x1)
1308
+ x2_abs = ov_opset.absolute(x2)
1309
+ max_val = ov_opset.maximum(x1_abs, x2_abs)
1310
+ min_val = ov_opset.minimum(x1_abs, x2_abs)
1311
+ one = ov_opset.constant(1, max_val.get_element_type())
1312
+ is_zero_mask = ov_opset.equal(
1313
+ max_val, ov_opset.constant(0, max_val.get_element_type())
1314
+ )
1315
+ safe_divisor = ov_opset.select(is_zero_mask, one, max_val)
1316
+ ratio = ov_opset.divide(min_val, safe_divisor)
1317
+ result = ov_opset.multiply(
1318
+ max_val,
1319
+ ov_opset.sqrt(ov_opset.add(one, ov_opset.multiply(ratio, ratio))),
1320
+ )
1321
+ return OpenVINOKerasTensor(result.output(0))
913
1322
 
914
1323
 
915
1324
  def identity(n, dtype=None):
@@ -950,25 +1359,70 @@ def isclose(x1, x2, rtol=1e-5, atol=1e-8, equal_nan=False):
950
1359
 
951
1360
 
952
1361
  def isfinite(x):
953
- x = get_ov_output(x)
954
- return OpenVINOKerasTensor(ov_opset.is_finite(x).output(0))
1362
+ # NOTE: openvino has an is_finite operation but it does not properly
1363
+ # catch np.inf and -np.inf as not finite values. Hence we bootstrap here. If
1364
+ # that ever changes, we could simplify this to just call that operation.
1365
+ inf_values = get_ov_output(isinf(x))
1366
+ nan_values = get_ov_output(isnan(x))
1367
+ all_non_finite_values = ov_opset.logical_or(inf_values, nan_values).output(
1368
+ 0
1369
+ )
1370
+ is_finite = ov_opset.logical_not(all_non_finite_values).output(0)
1371
+ return OpenVINOKerasTensor(is_finite)
955
1372
 
956
1373
 
957
1374
  def isin(x1, x2, assume_unique=False, invert=False):
958
- raise NotImplementedError("`isin` is not supported with openvino backend")
1375
+ x1 = get_ov_output(x1)
1376
+ x2 = get_ov_output(x2)
1377
+ output_shape = ov_opset.shape_of(x1).output(0)
1378
+ x1, x2 = _align_operand_types(x1, x2, "isin()")
1379
+
1380
+ minus_one = ov_opset.constant([-1], dtype=Type.i64)
1381
+ x1 = ov_opset.reshape(x1, minus_one, special_zero=False).output(0)
1382
+ x2 = ov_opset.reshape(x2, minus_one, special_zero=False).output(0)
1383
+ if not assume_unique:
1384
+ x2 = ov_opset.unique(x2).output(0)
1385
+ x1 = ov_opset.unsqueeze(x1, 1).output(0)
1386
+ x2 = ov_opset.unsqueeze(x2, 0).output(0)
1387
+ cmp = ov_opset.equal(x1, x2).output(0)
1388
+ result_flat = ov_opset.reduce_logical_or(cmp, 1).output(0)
1389
+
1390
+ if invert:
1391
+ result_flat = ov_opset.logical_not(result_flat).output(0)
1392
+ result = ov_opset.reshape(result_flat, output_shape, False).output(0)
1393
+ return OpenVINOKerasTensor(result)
959
1394
 
960
1395
 
961
1396
  def isinf(x):
962
- x = get_ov_output(x)
963
- return OpenVINOKerasTensor(ov_opset.is_inf(x).output(0))
1397
+ pos_inf = get_ov_output(isposinf(x))
1398
+ neg_inf = get_ov_output(isneginf(x))
1399
+ inf = ov_opset.logical_or(pos_inf, neg_inf).output(0)
1400
+ return OpenVINOKerasTensor(inf)
964
1401
 
965
1402
 
966
1403
  def isnan(x):
967
1404
  x = get_ov_output(x)
1405
+ x_type = x.get_element_type()
1406
+ if x_type.is_integral():
1407
+ x = ov_opset.convert(x, OPENVINO_DTYPES[config.floatx()])
968
1408
  return OpenVINOKerasTensor(ov_opset.is_nan(x).output(0))
969
1409
 
970
1410
 
971
1411
  def isneginf(x):
1412
+ return _is_inf(x, pos=False)
1413
+
1414
+
1415
+ def isposinf(x):
1416
+ return _is_inf(x)
1417
+
1418
+
1419
+ def _is_inf(x, pos=True):
1420
+ # NOTE: there is an ov_opset.is_inf but it does not catch
1421
+ # numpy infinite values like np.inf and -np.inf, hence why we have this
1422
+ # if this ever changes in OpenVINO, we can do this instead:
1423
+ # ov_opset.is_inf(x, {"detect_positive": pos, "detect_negative": not pos})
1424
+ # for each infinite sign
1425
+ inf_value = np.inf if pos else -np.inf
972
1426
  x = get_ov_output(x)
973
1427
  x_type = x.get_element_type()
974
1428
 
@@ -981,36 +1435,96 @@ def isneginf(x):
981
1435
 
982
1436
  if x_type == Type.bf16:
983
1437
  x_f32 = ov_opset.convert(x, Type.f32).output(0)
984
- neg_inf = ov_opset.constant(-np.inf, Type.f32).output(0)
985
- is_neg_inf = ov_opset.equal(x_f32, neg_inf).output(0)
1438
+ inf = ov_opset.constant(inf_value, Type.f32).output(0)
1439
+ is_inf = ov_opset.equal(x_f32, inf).output(0)
986
1440
  else:
987
1441
  if x_type == Type.f16:
988
- neg_inf = ov_opset.constant(-np.inf, Type.f16).output(0)
1442
+ inf = ov_opset.constant(inf_value, Type.f16).output(0)
989
1443
  elif x_type == Type.f32:
990
- neg_inf = ov_opset.constant(-np.inf, Type.f32).output(0)
1444
+ inf = ov_opset.constant(inf_value, Type.f32).output(0)
991
1445
  elif x_type == Type.f64:
992
- neg_inf = ov_opset.constant(-np.inf, Type.f64).output(0)
1446
+ inf = ov_opset.constant(inf_value, Type.f64).output(0)
993
1447
  else:
994
- neg_inf = ov_opset.constant(-np.inf, Type.f32).output(0)
995
- is_neg_inf = ov_opset.equal(x, neg_inf).output(0)
1448
+ inf = ov_opset.constant(inf_value, Type.f32).output(0)
1449
+ is_inf = ov_opset.equal(x, inf).output(0)
1450
+ return OpenVINOKerasTensor(is_inf)
996
1451
 
997
- return OpenVINOKerasTensor(is_neg_inf)
998
1452
 
999
-
1000
- def isposinf(x):
1001
- raise NotImplementedError(
1002
- "`isposinf` is not supported with openvino backend"
1003
- )
1453
+ def isreal(x):
1454
+ raise NotImplementedError("`isreal` is not supported with openvino backend")
1004
1455
 
1005
1456
 
1006
1457
  def kron(x1, x2):
1007
- raise NotImplementedError("`kron` is not supported with openvino backend")
1458
+ x1 = get_ov_output(x1)
1459
+ x2 = get_ov_output(x2)
1460
+ x1, x2 = _align_operand_types(x1, x2, "kron()")
1461
+ x1_shape = x1.get_partial_shape()
1462
+ x2_shape = x2.get_partial_shape()
1463
+ if x1_shape.rank.is_dynamic or x2_shape.rank.is_dynamic:
1464
+ raise ValueError(
1465
+ "`kron` does not support tensors with dynamic rank for "
1466
+ "the OpenVINO backend."
1467
+ )
1468
+ ndim1 = x1_shape.rank.get_length()
1469
+ ndim2 = x2_shape.rank.get_length()
1470
+ if ndim1 < ndim2:
1471
+ axes = ov_opset.range(
1472
+ ov_opset.constant(0, Type.i32),
1473
+ ov_opset.constant(ndim2 - ndim1, Type.i32),
1474
+ ov_opset.constant(1, Type.i32),
1475
+ )
1476
+ x1 = ov_opset.unsqueeze(x1, axes)
1477
+ ndim1 = ndim2
1478
+ elif ndim2 < ndim1:
1479
+ axes = ov_opset.range(
1480
+ ov_opset.constant(0, Type.i32),
1481
+ ov_opset.constant(ndim1 - ndim2, Type.i32),
1482
+ ov_opset.constant(1, Type.i32),
1483
+ )
1484
+ x2 = ov_opset.unsqueeze(x2, axes)
1485
+ ndim2 = ndim1
1486
+ shape1 = ov_opset.shape_of(x1, Type.i32)
1487
+ shape2 = ov_opset.shape_of(x2, Type.i32)
1488
+ ones = ov_opset.broadcast(
1489
+ ov_opset.constant(1, Type.i32), ov_opset.constant([ndim1], Type.i32)
1490
+ )
1491
+ axis = ov_opset.constant(1, Type.i32)
1492
+ flatten = ov_opset.constant([-1], Type.i32)
1493
+ unsqueezed_ones = ov_opset.unsqueeze(ones, axis)
1494
+ x1_new_shape = ov_opset.reshape(
1495
+ ov_opset.concat(
1496
+ [ov_opset.unsqueeze(shape1, axis), unsqueezed_ones],
1497
+ axis=1,
1498
+ ),
1499
+ flatten,
1500
+ False,
1501
+ )
1502
+ x2_new_shape = ov_opset.reshape(
1503
+ ov_opset.concat(
1504
+ [unsqueezed_ones, ov_opset.unsqueeze(shape2, axis)],
1505
+ axis=1,
1506
+ ),
1507
+ flatten,
1508
+ False,
1509
+ )
1510
+ result = ov_opset.multiply(
1511
+ ov_opset.reshape(x1, x1_new_shape, False),
1512
+ ov_opset.reshape(x2, x2_new_shape, False),
1513
+ )
1514
+ result = ov_opset.reshape(
1515
+ result, ov_opset.multiply(shape1, shape2), False
1516
+ ).output(0)
1517
+ return OpenVINOKerasTensor(result)
1008
1518
 
1009
1519
 
1010
1520
  def lcm(x1, x2):
1011
1521
  raise NotImplementedError("`lcm` is not supported with openvino backend")
1012
1522
 
1013
1523
 
1524
+ def ldexp(x1, x2):
1525
+ raise NotImplementedError("`ldexp` is not supported with openvino backend")
1526
+
1527
+
1014
1528
  def less(x1, x2):
1015
1529
  element_type = None
1016
1530
  if isinstance(x1, OpenVINOKerasTensor):
@@ -1505,46 +2019,7 @@ def meshgrid(*x, indexing="xy"):
1505
2019
 
1506
2020
 
1507
2021
  def min(x, axis=None, keepdims=False, initial=None):
1508
- x = get_ov_output(x)
1509
- original_type = x.get_element_type()
1510
- x_type = original_type
1511
- x_shape = x.get_partial_shape().to_shape()
1512
-
1513
- is_bool = x_type == Type.boolean
1514
- if is_bool:
1515
- x = ov_opset.convert(x, Type.i32).output(0)
1516
- x_type = Type.i32
1517
-
1518
- if isinstance(axis, tuple) and len(axis) == 0:
1519
- return OpenVINOKerasTensor(x)
1520
-
1521
- if axis is None:
1522
- flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
1523
- x = ov_opset.reshape(x, flatten_shape, False).output(0)
1524
- axis = 0
1525
-
1526
- if isinstance(axis, tuple):
1527
- axis = list(axis)
1528
-
1529
- axis_const = ov_opset.constant(axis, Type.i32).output(0)
1530
- min_result = ov_opset.reduce_min(x, axis_const, keepdims).output(0)
1531
-
1532
- if initial is not None:
1533
- initial_tensor = ov_opset.constant(initial, x_type).output(0)
1534
- min_result = ov_opset.minimum(min_result, initial_tensor).output(0)
1535
-
1536
- if keepdims:
1537
- result_shape = [1] * len(x_shape)
1538
- min_result = ov_opset.reshape(
1539
- min_result,
1540
- ov_opset.constant(result_shape, Type.i32).output(0),
1541
- False,
1542
- ).output(0)
1543
-
1544
- if is_bool:
1545
- min_result = ov_opset.convert(min_result, Type.boolean).output(0)
1546
-
1547
- return OpenVINOKerasTensor(min_result)
2022
+ return _compute_extrema(x, "min", axis, keepdims, initial)
1548
2023
 
1549
2024
 
1550
2025
  def minimum(x1, x2):
@@ -1695,7 +2170,9 @@ def pad(x, pad_width, mode="constant", constant_values=None):
1695
2170
  "`pad` operation supports only scalar pad value "
1696
2171
  "in constant mode by openvino backend"
1697
2172
  )
1698
- pad_value = constant_values
2173
+ pad_value = ov_opset.constant(
2174
+ constant_values, x.get_element_type()
2175
+ ).output(0)
1699
2176
 
1700
2177
  # split pad_width into two tensors pads_begin and pads_end
1701
2178
  pads_begin = []
@@ -1719,29 +2196,20 @@ def prod(x, axis=None, keepdims=False, dtype=None):
1719
2196
  x = ov_opset.convert(x, ov_dtype).output(0)
1720
2197
  # Otherwise, apply dtype promotion rules before reduction.
1721
2198
  else:
1722
- x_type = x.get_element_type()
1723
- if x_type == Type.boolean:
1724
- x = ov_opset.convert(x, Type.i32).output(0)
1725
- elif x_type in (Type.i8, Type.i16):
1726
- x = ov_opset.convert(x, Type.i32).output(0)
1727
- elif x_type in (Type.u8, Type.u16):
1728
- x = ov_opset.convert(x, Type.u32).output(0)
1729
-
2199
+ x = _upcast_type_if_needed(x)
2200
+ x, axis = _resolve_axis(x, axis)
1730
2201
  if axis is None:
1731
- flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
1732
- x = ov_opset.reshape(x, flatten_shape, False).output(0)
1733
- axis = 0
1734
-
1735
- if isinstance(axis, tuple):
1736
- axis = list(axis)
1737
- axis = ov_opset.constant(axis, Type.i32).output(0)
1738
-
2202
+ return OpenVINOKerasTensor(x)
1739
2203
  # Compute the product
1740
2204
  result = ov_opset.reduce_prod(x, axis, keepdims).output(0)
1741
2205
 
1742
2206
  return OpenVINOKerasTensor(result)
1743
2207
 
1744
2208
 
2209
+ def ptp(x, axis=None, keepdims=False):
2210
+ raise NotImplementedError("`ptp` is not supported with openvino backend")
2211
+
2212
+
1745
2213
  def quantile(x, q, axis=None, method="linear", keepdims=False):
1746
2214
  raise NotImplementedError(
1747
2215
  "`quantile` is not supported with openvino backend"
@@ -1835,7 +2303,17 @@ def reshape(x, newshape):
1835
2303
 
1836
2304
 
1837
2305
  def roll(x, shift, axis=None):
1838
- raise NotImplementedError("`roll` is not supported with openvino backend")
2306
+ x = get_ov_output(x)
2307
+ if axis is not None:
2308
+ result = ov_opset.roll(x, shift, axis).output(0)
2309
+ else:
2310
+ output_shape = ov_opset.shape_of(x).output(0)
2311
+ flattened = ov_opset.reshape(
2312
+ x, ov_opset.constant([-1], Type.i32), False
2313
+ ).output(0)
2314
+ result = ov_opset.roll(flattened, shift, 0).output(0)
2315
+ result = ov_opset.reshape(result, output_shape, False).output(0)
2316
+ return OpenVINOKerasTensor(result)
1839
2317
 
1840
2318
 
1841
2319
  def sign(x):
@@ -1868,7 +2346,14 @@ def sinh(x):
1868
2346
 
1869
2347
 
1870
2348
  def size(x):
1871
- raise NotImplementedError("`size` is not supported with openvino backend")
2349
+ x = get_ov_output(x)
2350
+ shape_tensor = ov_opset.shape_of(x, output_type=Type.i64)
2351
+ final_size = ov_opset.reduce_prod(
2352
+ shape_tensor,
2353
+ ov_opset.constant([0], Type.i64),
2354
+ keep_dims=False,
2355
+ )
2356
+ return OpenVINOKerasTensor(final_size.output(0))
1872
2357
 
1873
2358
 
1874
2359
  def sort(x, axis=-1):
@@ -1957,6 +2442,37 @@ def split(x, indices_or_sections, axis=0):
1957
2442
  )
1958
2443
 
1959
2444
 
2445
+ def array_split(x, indices_or_sections, axis=0):
2446
+ original_shape = x.shape
2447
+ x = get_ov_output(x)
2448
+
2449
+ num_splits_val = indices_or_sections
2450
+ total_size = original_shape[axis]
2451
+ if total_size is None:
2452
+ raise ValueError(
2453
+ f"Cannot use array_split with static Python logic on dynamic axis. "
2454
+ f"Axis {axis} has unknown dimension for shape {original_shape}."
2455
+ )
2456
+
2457
+ base_size = total_size // num_splits_val
2458
+ remainder = total_size % num_splits_val
2459
+
2460
+ split_lengths = [base_size + 1] * remainder + [base_size] * (
2461
+ num_splits_val - remainder
2462
+ )
2463
+ split_lengths_tensor = ov_opset.constant(
2464
+ split_lengths, dtype=Type.i64
2465
+ ).output(0)
2466
+
2467
+ axis_tensor = ov_opset.constant(axis, dtype=Type.i32).output(0)
2468
+ splits = ov_opset.variadic_split(x, axis_tensor, split_lengths_tensor)
2469
+
2470
+ result = []
2471
+ for i in range(num_splits_val):
2472
+ result.append(OpenVINOKerasTensor(splits.output(i)))
2473
+ return result
2474
+
2475
+
1960
2476
  def stack(x, axis=0):
1961
2477
  if isinstance(x, tuple):
1962
2478
  x = list(x)
@@ -1973,28 +2489,26 @@ def stack(x, axis=0):
1973
2489
 
1974
2490
 
1975
2491
  def std(x, axis=None, keepdims=False):
1976
- x = get_ov_output(x)
1977
- if axis is None:
1978
- flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
1979
- x = ov_opset.reshape(x, flatten_shape, False).output(0)
1980
- axis = 0
1981
- axis = ov_opset.constant(axis, Type.i32).output(0)
1982
- # The variance is computed using $Var = E[|x|^2] - |E[x]|^2$, It is faster
1983
- # but less numerically stable.
1984
- mean = ov_opset.reduce_mean(x, axis, keepdims).output(0)
1985
- const_two = ov_opset.constant(2, x.get_element_type()).output(0)
1986
- squared_x = ov_opset.power(x, const_two).output(0)
1987
- squared_mean = ov_opset.power(mean, const_two).output(0)
1988
- squared_x_mean = ov_opset.reduce_mean(squared_x, axis, keepdims)
1989
- variance = ov_opset.subtract(squared_x_mean, squared_mean).output(0)
1990
- std_var = OpenVINOKerasTensor(ov_opset.sqrt(variance).output(0))
1991
- return std_var
2492
+ var_x = var(x, axis, keepdims)
2493
+ std_dev = ov_opset.sqrt(var_x).output(0)
2494
+ return OpenVINOKerasTensor(std_dev)
1992
2495
 
1993
2496
 
1994
2497
  def swapaxes(x, axis1, axis2):
1995
- raise NotImplementedError(
1996
- "`swapaxes` is not supported with openvino backend"
1997
- )
2498
+ x = get_ov_output(x)
2499
+ x_shape = x.get_partial_shape()
2500
+ if x_shape.rank.is_dynamic:
2501
+ raise ValueError(
2502
+ "`swapaxes` does not support tensors with dynamic rank for the "
2503
+ "OpenVINO backend."
2504
+ )
2505
+ rank = x_shape.rank.get_length()
2506
+ axis1 = canonicalize_axis(axis1, rank)
2507
+ axis2 = canonicalize_axis(axis2, rank)
2508
+ axes = list(range(rank))
2509
+ axes[axis1], axes[axis2] = axes[axis2], axes[axis1]
2510
+ result = ov_opset.transpose(x, ov_opset.constant(axes, Type.i32))
2511
+ return OpenVINOKerasTensor(result.output(0))
1998
2512
 
1999
2513
 
2000
2514
  def take(x, indices, axis=None):
@@ -2113,7 +2627,8 @@ def tile(x, repeats):
2113
2627
 
2114
2628
 
2115
2629
  def trace(x, offset=0, axis1=0, axis2=1):
2116
- raise NotImplementedError("`trace` is not supported with openvino backend")
2630
+ x = diagonal(x, offset=offset, axis1=axis1, axis2=axis2)
2631
+ return sum(x, axis=-1)
2117
2632
 
2118
2633
 
2119
2634
  def tri(N, M=None, k=0, dtype=None):
@@ -2208,7 +2723,20 @@ def triu(x, k=0):
2208
2723
 
2209
2724
 
2210
2725
  def vdot(x1, x2):
2211
- raise NotImplementedError("`vdot` is not supported with openvino backend")
2726
+ element_type = None
2727
+ if isinstance(x1, OpenVINOKerasTensor):
2728
+ element_type = x1.output.get_element_type()
2729
+ if isinstance(x2, OpenVINOKerasTensor):
2730
+ element_type = x2.output.get_element_type()
2731
+ x1 = get_ov_output(x1, element_type)
2732
+ x2 = get_ov_output(x2, element_type)
2733
+ x1, x2 = _align_operand_types(x1, x2, "vdot()")
2734
+ if x1.get_partial_shape().rank == 0 or x2.get_partial_shape().rank == 0:
2735
+ return OpenVINOKerasTensor(ov_opset.multiply(x1, x2).output(0))
2736
+ flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
2737
+ x1 = ov_opset.reshape(x1, flatten_shape, False).output(0)
2738
+ x2 = ov_opset.reshape(x2, flatten_shape, False).output(0)
2739
+ return OpenVINOKerasTensor(ov_opset.matmul(x1, x2, False, False).output(0))
2212
2740
 
2213
2741
 
2214
2742
  def vstack(xs):
@@ -2294,14 +2822,27 @@ def negative(x):
2294
2822
  return OpenVINOKerasTensor(ov_opset.negative(x).output(0))
2295
2823
 
2296
2824
 
2825
+ def nextafter(x1, x2):
2826
+ raise NotImplementedError(
2827
+ "`nextafter` is not supported with openvino backend"
2828
+ )
2829
+
2830
+
2297
2831
  def square(x):
2298
2832
  x = get_ov_output(x)
2833
+ x_type = x.get_element_type()
2834
+ if x_type == Type.boolean:
2835
+ x = ov_opset.convert(x, Type.i32).output(0)
2299
2836
  const_two = ov_opset.constant(2, x.get_element_type()).output(0)
2300
2837
  return OpenVINOKerasTensor(ov_opset.power(x, const_two).output(0))
2301
2838
 
2302
2839
 
2303
2840
  def sqrt(x):
2304
2841
  x = get_ov_output(x)
2842
+ x_type = x.get_element_type()
2843
+ if x_type.is_integral():
2844
+ ov_type = OPENVINO_DTYPES[config.floatx()]
2845
+ x = ov_opset.convert(x, ov_type).output(0)
2305
2846
  return OpenVINOKerasTensor(ov_opset.sqrt(x).output(0))
2306
2847
 
2307
2848
 
@@ -2312,6 +2853,8 @@ def squeeze(x, axis=None):
2312
2853
  for idx, dim in enumerate(x.get_partial_shape()):
2313
2854
  if dim == 1:
2314
2855
  axis.append(idx)
2856
+ if isinstance(axis, tuple):
2857
+ axis = list(axis)
2315
2858
  axis = ov_opset.constant(axis, Type.i32).output(0)
2316
2859
  return OpenVINOKerasTensor(ov_opset.squeeze(x, axis).output(0))
2317
2860
 
@@ -2336,20 +2879,135 @@ def transpose(x, axes=None):
2336
2879
  return OpenVINOKerasTensor(ov_opset.transpose(x, axes).output(0))
2337
2880
 
2338
2881
 
2882
+ def _helper_trapezoid(y, axis):
2883
+ rank = y.get_partial_shape().rank.get_length()
2884
+ strides = ov_opset.constant([1] * rank, dtype=Type.i64).output(0)
2885
+
2886
+ # y[:-1]
2887
+ begin1 = ov_opset.constant([0] * rank, dtype=Type.i64).output(0)
2888
+ end1_list = [0] * rank
2889
+ end1_list[axis] = -1
2890
+ end1 = ov_opset.constant(end1_list, dtype=Type.i64).output(0)
2891
+ begin_mask1 = [1] * rank
2892
+ begin_mask1[axis] = 0
2893
+ end_mask1 = [1] * rank
2894
+ end_mask1[axis] = 0
2895
+ y1 = ov_opset.strided_slice(
2896
+ y, begin1, end1, strides, begin_mask1, end_mask1
2897
+ ).output(0)
2898
+
2899
+ # y[1:]
2900
+ begin2_list = [0] * rank
2901
+ begin2_list[axis] = 1
2902
+ begin2 = ov_opset.constant(begin2_list, dtype=Type.i64).output(0)
2903
+ end2 = ov_opset.constant([0] * rank, dtype=Type.i64).output(0)
2904
+ begin_mask2 = [1] * rank
2905
+ begin_mask2[axis] = 0
2906
+ end_mask2 = [1] * rank
2907
+ y2 = ov_opset.strided_slice(
2908
+ y, begin2, end2, strides, begin_mask2, end_mask2
2909
+ ).output(0)
2910
+
2911
+ return y1, y2
2912
+
2913
+
2914
+ def trapezoid(y, x=None, dx=1.0, axis=-1):
2915
+ y = get_ov_output(y)
2916
+ y_type = y.get_element_type()
2917
+
2918
+ if y_type.is_integral():
2919
+ y_type = OPENVINO_DTYPES[config.floatx()]
2920
+ y = ov_opset.convert(y, y_type).output(0)
2921
+
2922
+ y1, y2 = _helper_trapezoid(y, axis)
2923
+ y_final = ov_opset.add(y1, y2).output(0)
2924
+ const_two = ov_opset.constant(2, dtype=y_type).output(0)
2925
+ y_final = ov_opset.divide(y_final, const_two).output(0)
2926
+
2927
+ if x is not None:
2928
+ x = get_ov_output(x)
2929
+ x_type = x.get_element_type()
2930
+ if x_type.is_integral():
2931
+ x_type = OPENVINO_DTYPES[config.floatx()]
2932
+ x = ov_opset.convert(x, x_type).output(0)
2933
+
2934
+ x1, x2 = _helper_trapezoid(x, axis)
2935
+ x_final = ov_opset.subtract(x2, x1).output(0)
2936
+
2937
+ else:
2938
+ x_final = ov_opset.constant(dx, dtype=y_type).output(0)
2939
+
2940
+ result = ov_opset.multiply(y_final, x_final).output(0)
2941
+ const_axis = ov_opset.constant([axis], Type.i64).output(0)
2942
+ result = ov_opset.reduce_sum(result, const_axis, False).output(0)
2943
+
2944
+ return OpenVINOKerasTensor(result)
2945
+
2946
+
2947
+ def vander(x, N=None, increasing=False):
2948
+ x = get_ov_output(x)
2949
+ x_type = x.get_element_type()
2950
+
2951
+ shape_x = ov_opset.shape_of(x, Type.i64).output(0)
2952
+
2953
+ const_zero_1D = ov_opset.constant([0], dtype=Type.i64).output(0)
2954
+ const_zero = ov_opset.constant(0, dtype=Type.i64).output(0)
2955
+ const_one = ov_opset.constant(1, dtype=Type.i64).output(0)
2956
+ const_mone = ov_opset.constant(-1, dtype=Type.i64).output(0)
2957
+
2958
+ if N is None:
2959
+ const_N = ov_opset.squeeze(shape_x, const_zero_1D).output(0)
2960
+ const_N_1D = shape_x
2961
+ else:
2962
+ const_N = ov_opset.constant(N, Type.i64).output(0)
2963
+ const_N_1D = ov_opset.constant([N], Type.i64).output(0)
2964
+
2965
+ const_N_minus_one = ov_opset.subtract(const_N, const_one).output(0)
2966
+ if increasing:
2967
+ powers = ov_opset.range(const_zero, const_N, const_one, x_type).output(
2968
+ 0
2969
+ )
2970
+ else:
2971
+ powers = ov_opset.range(
2972
+ const_N_minus_one, const_mone, const_mone, x_type
2973
+ ).output(0)
2974
+
2975
+ target_shape = ov_opset.concat([shape_x, const_N_1D], 0).output(0)
2976
+
2977
+ const_one_1D = ov_opset.constant([1], dtype=Type.i64).output(0)
2978
+
2979
+ powers = ov_opset.unsqueeze(powers, const_zero_1D).output(0)
2980
+ x = ov_opset.unsqueeze(x, const_one_1D).output(0)
2981
+
2982
+ result = ov_opset.broadcast(x, target_shape).output(0)
2983
+
2984
+ result = ov_opset.power(result, powers).output(0)
2985
+
2986
+ return OpenVINOKerasTensor(result)
2987
+
2988
+
2339
2989
  def var(x, axis=None, keepdims=False):
2340
2990
  x = get_ov_output(x)
2991
+ x_type = x.get_element_type()
2992
+ x, axis = _resolve_axis(x, axis)
2993
+
2994
+ work_dtype = Type.f64 if x_type.is_integral() else x.get_element_type()
2995
+ if x_type.is_integral():
2996
+ x = ov_opset.convert(x, work_dtype).output(0)
2341
2997
  if axis is None:
2342
- flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
2343
- x = ov_opset.reshape(x, flatten_shape, False).output(0)
2344
- axis = 0
2345
- axis = ov_opset.constant(axis, Type.i32).output(0)
2998
+ const_zero = ov_opset.constant(0, dtype=work_dtype).output(0)
2999
+ return OpenVINOKerasTensor(
3000
+ ov_opset.broadcast(const_zero, ov_opset.shape_of(x)).output(0)
3001
+ )
2346
3002
  # The variance is computed using $Var = E[|x|^2] - |E[x]|^2$, It is faster
2347
3003
  # but less numerically stable.
2348
3004
  mean = ov_opset.reduce_mean(x, axis, keepdims).output(0)
2349
- const_two = ov_opset.constant(2, x.get_element_type()).output(0)
3005
+ const_two = ov_opset.constant(2, work_dtype).output(0)
3006
+
2350
3007
  squared_x = ov_opset.power(x, const_two).output(0)
2351
3008
  squared_mean = ov_opset.power(mean, const_two).output(0)
2352
- squared_x_mean = ov_opset.reduce_mean(squared_x, axis, keepdims)
3009
+
3010
+ squared_x_mean = ov_opset.reduce_mean(squared_x, axis, keepdims).output(0)
2353
3011
  variance = OpenVINOKerasTensor(
2354
3012
  ov_opset.subtract(squared_x_mean, squared_mean).output(0)
2355
3013
  )
@@ -2358,22 +3016,46 @@ def var(x, axis=None, keepdims=False):
2358
3016
 
2359
3017
  def sum(x, axis=None, keepdims=False):
2360
3018
  x = get_ov_output(x)
3019
+ x, axis = _resolve_axis(x, axis)
2361
3020
  if axis is None:
2362
- flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
2363
- x = ov_opset.reshape(x, flatten_shape, False).output(0)
2364
- axis = 0
2365
- axis = ov_opset.constant(axis, Type.i32).output(0)
2366
- return OpenVINOKerasTensor(ov_opset.reduce_sum(x, axis, keepdims).output(0))
3021
+ return OpenVINOKerasTensor(x)
3022
+ x = _upcast_type_if_needed(x)
3023
+ summed_value = ov_opset.reduce_sum(x, axis, keepdims).output(0)
3024
+ return OpenVINOKerasTensor(summed_value)
2367
3025
 
2368
3026
 
2369
3027
  def eye(N, M=None, k=0, dtype=None):
2370
- raise NotImplementedError("`eye` is not supported with openvino backend")
3028
+ dtype = standardize_dtype(dtype) or config.floatx()
3029
+ ov_type = OPENVINO_DTYPES[dtype]
3030
+ if M is None:
3031
+ M = N
3032
+ return OpenVINOKerasTensor(
3033
+ ov_opset.eye(
3034
+ ov_opset.constant(N, Type.i32),
3035
+ ov_opset.constant(M, Type.i32),
3036
+ ov_opset.constant(k, Type.i32),
3037
+ output_type=ov_type,
3038
+ ).output(0)
3039
+ )
2371
3040
 
2372
3041
 
2373
3042
  def floor_divide(x1, x2):
2374
- raise NotImplementedError(
2375
- "`floor_divide` is not supported with openvino backend"
2376
- )
3043
+ x1_output = get_ov_output(x1)
3044
+ x2_output = get_ov_output(x2)
3045
+ if x1_output.get_element_type() == Type.boolean:
3046
+ x1_output = ov_opset.convert(x1_output, Type.i32).output(0)
3047
+ if isinstance(x2, (int, float)):
3048
+ if x1_output.get_element_type().is_integral() and isinstance(x2, float):
3049
+ ov_type = OPENVINO_DTYPES[config.floatx()]
3050
+ else:
3051
+ ov_type = x1_output.get_element_type()
3052
+ x1 = ov_opset.convert(x1_output, ov_type).output(0)
3053
+ x2 = ov_opset.convert(x2_output, ov_type).output(0)
3054
+ else:
3055
+ x1, x2 = _align_operand_types(x1_output, x2_output, "floor_divide()")
3056
+ div = ov_opset.divide(x1, x2).output(0)
3057
+ floored_div = ov_opset.floor(div).output(0)
3058
+ return OpenVINOKerasTensor(floored_div)
2377
3059
 
2378
3060
 
2379
3061
  def logical_xor(x1, x2):
@@ -2385,16 +3067,88 @@ def logical_xor(x1, x2):
2385
3067
 
2386
3068
 
2387
3069
  def corrcoef(x):
2388
- raise NotImplementedError(
2389
- "`corrcoef` is not supported with openvino backend"
2390
- )
3070
+ x_ov = get_ov_output(x)
3071
+ x_type = x_ov.get_element_type()
3072
+ ov_type = x_type
3073
+
3074
+ if x_type.is_integral():
3075
+ ov_type = OPENVINO_DTYPES[config.floatx()]
3076
+ x_ov = ov_opset.convert(x_ov, ov_type).output(0)
3077
+
3078
+ const_one = ov_opset.constant(1, dtype=Type.i64).output(0)
3079
+ const_two = ov_opset.constant(2, dtype=ov_type).output(0)
3080
+
3081
+ mean = ov_opset.reduce_mean(x_ov, const_one, True).output(0)
3082
+ x_ov = ov_opset.subtract(x_ov, mean).output(0)
3083
+
3084
+ cov = ov_opset.matmul(x_ov, x_ov, False, True).output(0)
3085
+ xsqr = ov_opset.power(x_ov, const_two).output(0)
3086
+ xvar = ov_opset.reduce_sum(xsqr, const_one, True).output(0)
3087
+ xstd = ov_opset.sqrt(xvar).output(0)
3088
+
3089
+ den = ov_opset.matmul(xstd, xstd, False, True).output(0)
3090
+
3091
+ result = ov_opset.divide(cov, den).output(0)
3092
+
3093
+ return OpenVINOKerasTensor(result)
2391
3094
 
2392
3095
 
2393
3096
  def correlate(x1, x2, mode="valid"):
2394
- raise NotImplementedError(
2395
- "`correlate` is not supported with openvino backend"
3097
+ x1 = get_ov_output(x1)
3098
+ x2 = get_ov_output(x2)
3099
+ x1_type = x1.get_element_type()
3100
+ x2_type = x2.get_element_type()
3101
+ x1_type = ov_to_keras_type(x1_type)
3102
+ x2_type = ov_to_keras_type(x2_type)
3103
+ result_type = dtypes.result_type(x1_type, x2_type, float)
3104
+
3105
+ result_type = OPENVINO_DTYPES[result_type]
3106
+ x1 = ov_opset.convert(x1, result_type).output(0)
3107
+ x2 = ov_opset.convert(x2, result_type).output(0)
3108
+
3109
+ shape_filter = ov_opset.shape_of(x2, Type.i64).output(0)
3110
+ const_two = ov_opset.constant(2, Type.f64).output(0)
3111
+ const_one = ov_opset.constant(1, Type.i64).output(0)
3112
+ const_zero = ov_opset.constant(0, result_type).output(0)
3113
+ shape_filter_minus_one = ov_opset.subtract(shape_filter, const_one).output(
3114
+ 0
2396
3115
  )
2397
3116
 
3117
+ # padding x1
3118
+ if mode == "valid":
3119
+ pass
3120
+
3121
+ elif mode == "same":
3122
+ shape_minus_one_float = ov_opset.convert(
3123
+ shape_filter_minus_one, Type.f64
3124
+ ).output(0)
3125
+
3126
+ right = ov_opset.divide(shape_minus_one_float, const_two).output(0)
3127
+ left = ov_opset.ceil(right).output(0)
3128
+ right = ov_opset.floor(right).output(0)
3129
+ left = ov_opset.convert(left, Type.i64).output(0)
3130
+ right = ov_opset.convert(right, Type.i64).output(0)
3131
+ x1 = ov_opset.pad(x1, left, right, "constant", const_zero).output(0)
3132
+
3133
+ elif mode == "full":
3134
+ pad = shape_filter_minus_one
3135
+ x1 = ov_opset.pad(x1, pad, pad, "constant", const_zero).output(0)
3136
+
3137
+ else:
3138
+ raise ValueError(
3139
+ f"mode: {mode} not available chose from valid, same, full."
3140
+ )
3141
+
3142
+ axes = ov_opset.constant([0, 1], dtype=Type.i64).output(0)
3143
+ x2 = ov_opset.unsqueeze(x2, axes).output(0)
3144
+ x1 = ov_opset.unsqueeze(x1, axes).output(0)
3145
+
3146
+ result = ov_opset.convolution(x1, x2, [1], [0], [0], [1]).output(0)
3147
+
3148
+ result = ov_opset.squeeze(result, axes).output(0)
3149
+
3150
+ return OpenVINOKerasTensor(result)
3151
+
2398
3152
 
2399
3153
  def select(condlist, choicelist, default=0):
2400
3154
  raise NotImplementedError("`select` is not supported with openvino backend")