keras-nightly 3.12.0.dev2025092403__py3-none-any.whl → 3.14.0.dev2026010104__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 (133) 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/layers/__init__.py +21 -0
  7. keras/_tf_keras/keras/ops/__init__.py +13 -0
  8. keras/_tf_keras/keras/ops/image/__init__.py +1 -0
  9. keras/_tf_keras/keras/ops/linalg/__init__.py +1 -0
  10. keras/_tf_keras/keras/ops/nn/__init__.py +3 -0
  11. keras/_tf_keras/keras/ops/numpy/__init__.py +9 -0
  12. keras/_tf_keras/keras/quantizers/__init__.py +12 -0
  13. keras/callbacks/__init__.py +3 -0
  14. keras/distillation/__init__.py +16 -0
  15. keras/distribution/__init__.py +3 -0
  16. keras/layers/__init__.py +21 -0
  17. keras/ops/__init__.py +13 -0
  18. keras/ops/image/__init__.py +1 -0
  19. keras/ops/linalg/__init__.py +1 -0
  20. keras/ops/nn/__init__.py +3 -0
  21. keras/ops/numpy/__init__.py +9 -0
  22. keras/quantizers/__init__.py +12 -0
  23. keras/src/applications/imagenet_utils.py +4 -1
  24. keras/src/backend/common/backend_utils.py +30 -6
  25. keras/src/backend/common/dtypes.py +1 -1
  26. keras/src/backend/common/name_scope.py +2 -1
  27. keras/src/backend/common/variables.py +33 -16
  28. keras/src/backend/jax/core.py +92 -3
  29. keras/src/backend/jax/distribution_lib.py +16 -2
  30. keras/src/backend/jax/linalg.py +4 -0
  31. keras/src/backend/jax/nn.py +485 -20
  32. keras/src/backend/jax/numpy.py +92 -23
  33. keras/src/backend/jax/optimizer.py +3 -2
  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 +313 -2
  37. keras/src/backend/numpy/numpy.py +76 -7
  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 +1030 -185
  43. keras/src/backend/openvino/random.py +7 -14
  44. keras/src/backend/tensorflow/layer.py +43 -9
  45. keras/src/backend/tensorflow/linalg.py +24 -0
  46. keras/src/backend/tensorflow/nn.py +545 -1
  47. keras/src/backend/tensorflow/numpy.py +264 -54
  48. keras/src/backend/torch/core.py +3 -1
  49. keras/src/backend/torch/linalg.py +4 -0
  50. keras/src/backend/torch/nn.py +125 -0
  51. keras/src/backend/torch/numpy.py +84 -8
  52. keras/src/callbacks/__init__.py +1 -0
  53. keras/src/callbacks/callback_list.py +45 -11
  54. keras/src/callbacks/model_checkpoint.py +5 -0
  55. keras/src/callbacks/orbax_checkpoint.py +299 -0
  56. keras/src/callbacks/terminate_on_nan.py +54 -5
  57. keras/src/datasets/cifar10.py +5 -0
  58. keras/src/distillation/__init__.py +1 -0
  59. keras/src/distillation/distillation_loss.py +390 -0
  60. keras/src/distillation/distiller.py +598 -0
  61. keras/src/distribution/distribution_lib.py +14 -0
  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/attention.py +1 -1
  70. keras/src/layers/attention/multi_head_attention.py +4 -1
  71. keras/src/layers/core/dense.py +191 -172
  72. keras/src/layers/core/einsum_dense.py +235 -186
  73. keras/src/layers/core/embedding.py +83 -93
  74. keras/src/layers/core/input_layer.py +1 -0
  75. keras/src/layers/core/reversible_embedding.py +390 -0
  76. keras/src/layers/input_spec.py +17 -17
  77. keras/src/layers/layer.py +40 -15
  78. keras/src/layers/merging/dot.py +4 -1
  79. keras/src/layers/pooling/adaptive_average_pooling1d.py +65 -0
  80. keras/src/layers/pooling/adaptive_average_pooling2d.py +62 -0
  81. keras/src/layers/pooling/adaptive_average_pooling3d.py +63 -0
  82. keras/src/layers/pooling/adaptive_max_pooling1d.py +65 -0
  83. keras/src/layers/pooling/adaptive_max_pooling2d.py +62 -0
  84. keras/src/layers/pooling/adaptive_max_pooling3d.py +63 -0
  85. keras/src/layers/pooling/base_adaptive_pooling.py +63 -0
  86. keras/src/layers/preprocessing/discretization.py +6 -5
  87. keras/src/layers/preprocessing/index_lookup.py +19 -1
  88. keras/src/layers/preprocessing/normalization.py +16 -1
  89. keras/src/layers/regularization/dropout.py +43 -1
  90. keras/src/layers/rnn/gru.py +1 -1
  91. keras/src/layers/rnn/lstm.py +2 -2
  92. keras/src/layers/rnn/rnn.py +19 -0
  93. keras/src/layers/rnn/simple_rnn.py +1 -1
  94. keras/src/losses/loss.py +1 -1
  95. keras/src/metrics/confusion_metrics.py +7 -6
  96. keras/src/models/cloning.py +4 -0
  97. keras/src/models/functional.py +11 -3
  98. keras/src/models/model.py +156 -27
  99. keras/src/ops/image.py +184 -3
  100. keras/src/ops/linalg.py +93 -0
  101. keras/src/ops/nn.py +268 -2
  102. keras/src/ops/numpy.py +541 -43
  103. keras/src/optimizers/adafactor.py +29 -10
  104. keras/src/optimizers/base_optimizer.py +22 -3
  105. keras/src/optimizers/loss_scale_optimizer.py +51 -18
  106. keras/src/optimizers/muon.py +65 -31
  107. keras/src/optimizers/schedules/learning_rate_schedule.py +4 -3
  108. keras/src/quantizers/__init__.py +12 -1
  109. keras/src/quantizers/gptq.py +8 -6
  110. keras/src/quantizers/gptq_config.py +36 -1
  111. keras/src/quantizers/gptq_core.py +150 -78
  112. keras/src/quantizers/quantization_config.py +232 -0
  113. keras/src/quantizers/quantizers.py +114 -38
  114. keras/src/quantizers/utils.py +23 -0
  115. keras/src/random/seed_generator.py +4 -2
  116. keras/src/saving/file_editor.py +81 -6
  117. keras/src/saving/saving_lib.py +1 -1
  118. keras/src/testing/__init__.py +1 -0
  119. keras/src/testing/test_case.py +45 -5
  120. keras/src/trainers/compile_utils.py +14 -5
  121. keras/src/utils/backend_utils.py +31 -4
  122. keras/src/utils/dataset_utils.py +234 -35
  123. keras/src/utils/file_utils.py +49 -11
  124. keras/src/utils/image_utils.py +14 -2
  125. keras/src/utils/jax_layer.py +187 -36
  126. keras/src/utils/module_utils.py +18 -0
  127. keras/src/utils/progbar.py +10 -12
  128. keras/src/utils/rng_utils.py +9 -1
  129. keras/src/version.py +1 -1
  130. {keras_nightly-3.12.0.dev2025092403.dist-info → keras_nightly-3.14.0.dev2026010104.dist-info}/METADATA +16 -6
  131. {keras_nightly-3.12.0.dev2025092403.dist-info → keras_nightly-3.14.0.dev2026010104.dist-info}/RECORD +133 -116
  132. {keras_nightly-3.12.0.dev2025092403.dist-info → keras_nightly-3.14.0.dev2026010104.dist-info}/WHEEL +0 -0
  133. {keras_nightly-3.12.0.dev2025092403.dist-info → keras_nightly-3.14.0.dev2026010104.dist-info}/top_level.txt +0 -0
@@ -76,25 +76,81 @@ def multiply(x1, x2):
76
76
 
77
77
 
78
78
  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))
79
+ x_ov = get_ov_output(x)
80
+ x_type = x_ov.get_element_type()
81
+
82
+ was_axis_none = axis is None
83
+ x_resolved, axis_resolved = _resolve_axis(x_ov, axis)
84
+
85
+ if axis_resolved is None:
86
+ return OpenVINOKerasTensor(x_ov)
87
+
88
+ if x_type.is_integral():
89
+ ov_type = OPENVINO_DTYPES[config.floatx()]
90
+ x_resolved = ov_opset.convert(x_resolved, ov_type).output(0)
91
+
92
+ result = ov_opset.reduce_mean(x_resolved, axis_resolved, keepdims).output(0)
93
+
94
+ if keepdims and was_axis_none:
95
+ rank = x.get_partial_shape().rank.get_length()
96
+ result_shape = [1] * rank
97
+ result = ov_opset.reshape(
98
+ result,
99
+ ov_opset.constant(result_shape, Type.i32).output(0),
100
+ False,
101
+ ).output(0)
102
+
103
+ return OpenVINOKerasTensor(result)
87
104
 
88
105
 
89
106
  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
- )
107
+ return _compute_extrema(x, "max", axis, keepdims, initial)
108
+
109
+
110
+ def _compute_extrema(x, operation, axis=None, keepdims=False, initial=None):
111
+ if operation == "min":
112
+ reduction_op = ov_opset.reduce_min
113
+ elementwise_op = ov_opset.minimum
114
+ elif operation == "max":
115
+ reduction_op = ov_opset.reduce_max
116
+ elementwise_op = ov_opset.maximum
117
+ else:
118
+ raise ValueError(
119
+ f"Operation must be 'min' or 'max', received {operation}"
120
+ )
121
+
93
122
  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
- )
123
+ x_type = x.get_element_type()
124
+ x_for_rank = x
125
+
126
+ is_bool = x_type == Type.boolean
127
+ if is_bool:
128
+ x = ov_opset.convert(x, Type.i32).output(0)
129
+ x_type = Type.i32
130
+
131
+ if isinstance(axis, tuple) and len(axis) == 0:
132
+ return OpenVINOKerasTensor(x)
133
+
134
+ was_axis_none = axis is None
135
+ x, axis = _resolve_axis(x, axis)
136
+
137
+ result = reduction_op(x, axis, keepdims).output(0)
138
+
139
+ if initial is not None:
140
+ initial_tensor = ov_opset.constant(initial, x_type).output(0)
141
+ result = elementwise_op(result, initial_tensor).output(0)
142
+
143
+ if keepdims and was_axis_none:
144
+ orig_shape = ov_opset.shape_of(x_for_rank, Type.i32).output(0)
145
+ orig_rank_shape = ov_opset.shape_of(orig_shape, Type.i32).output(0)
146
+ one = ov_opset.constant(1, Type.i32).output(0)
147
+ result_shape = ov_opset.broadcast(one, orig_rank_shape).output(0)
148
+ result = ov_opset.reshape(result, result_shape, False).output(0)
149
+
150
+ if is_bool:
151
+ result = ov_opset.convert(result, Type.boolean).output(0)
152
+
153
+ return OpenVINOKerasTensor(result)
98
154
 
99
155
 
100
156
  def ones(shape, dtype=None):
@@ -125,6 +181,9 @@ def zeros(shape, dtype=None):
125
181
 
126
182
  def absolute(x):
127
183
  x = get_ov_output(x)
184
+ x_type = x.get_element_type()
185
+ if x_type == Type.boolean:
186
+ return OpenVINOKerasTensor(x)
128
187
  return OpenVINOKerasTensor(ov_opset.absolute(x).output(0))
129
188
 
130
189
 
@@ -135,11 +194,10 @@ def abs(x):
135
194
 
136
195
  def all(x, axis=None, keepdims=False):
137
196
  x = get_ov_output(x)
197
+ x, axis = _resolve_axis(x, axis)
138
198
  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)
199
+ return OpenVINOKerasTensor(x)
200
+ x = ov_opset.convert(x, Type.boolean).output(0)
143
201
  return OpenVINOKerasTensor(
144
202
  ov_opset.reduce_logical_and(x, axis, keepdims).output(0)
145
203
  )
@@ -151,28 +209,21 @@ def angle(x):
151
209
 
152
210
  def any(x, axis=None, keepdims=False):
153
211
  x = get_ov_output(x)
212
+ x, axis = _resolve_axis(x, axis)
154
213
  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)
214
+ return OpenVINOKerasTensor(x)
215
+ x = ov_opset.convert(x, Type.boolean).output(0)
159
216
  return OpenVINOKerasTensor(
160
217
  ov_opset.reduce_logical_or(x, axis, keepdims).output(0)
161
218
  )
162
219
 
163
220
 
164
221
  def amax(x, axis=None, keepdims=False):
165
- if axis == () or axis == []:
166
- return x
167
222
  x = get_ov_output(x)
168
223
  x_type = x.get_element_type()
224
+ x, axis = _resolve_axis(x, axis)
169
225
  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)
226
+ return OpenVINOKerasTensor(x)
176
227
  if x_type == Type.boolean:
177
228
  return OpenVINOKerasTensor(
178
229
  ov_opset.reduce_logical_or(x, axis, keepdims).output(0)
@@ -181,10 +232,21 @@ def amax(x, axis=None, keepdims=False):
181
232
 
182
233
 
183
234
  def amin(x, axis=None, keepdims=False):
184
- if axis == () or axis == []:
185
- return x
186
235
  x = get_ov_output(x)
187
236
  x_type = x.get_element_type()
237
+ x, axis = _resolve_axis(x, axis)
238
+ if axis is None:
239
+ return OpenVINOKerasTensor(x)
240
+ if x_type == Type.boolean:
241
+ return OpenVINOKerasTensor(
242
+ ov_opset.reduce_logical_and(x, axis, keepdims).output(0)
243
+ )
244
+ return OpenVINOKerasTensor(ov_opset.reduce_min(x, axis, keepdims).output(0))
245
+
246
+
247
+ def _resolve_axis(x, axis):
248
+ if axis == () or axis == []:
249
+ return x, None
188
250
  if axis is None:
189
251
  flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
190
252
  x = ov_opset.reshape(x, flatten_shape, False).output(0)
@@ -192,11 +254,18 @@ def amin(x, axis=None, keepdims=False):
192
254
  if isinstance(axis, tuple):
193
255
  axis = list(axis)
194
256
  axis = ov_opset.constant(axis, Type.i32).output(0)
257
+ return x, axis
258
+
259
+
260
+ def _upcast_type_if_needed(x):
261
+ x_type = x.get_element_type()
195
262
  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))
263
+ x = ov_opset.convert(x, Type.i32).output(0)
264
+ elif x_type in (Type.i8, Type.i16):
265
+ x = ov_opset.convert(x, Type.i32).output(0)
266
+ elif x_type in (Type.u8, Type.u16):
267
+ x = ov_opset.convert(x, Type.u32).output(0)
268
+ return x
200
269
 
201
270
 
202
271
  def append(x1, x2, axis=None):
@@ -210,7 +279,7 @@ def append(x1, x2, axis=None):
210
279
  return OpenVINOKerasTensor(ov_opset.concat([x1, x2], axis).output(0))
211
280
 
212
281
 
213
- def arange(start, stop=None, step=1, dtype=None):
282
+ def arange(start, stop=None, step=None, dtype=None):
214
283
  if stop is None:
215
284
  start, stop = get_ov_output(0), get_ov_output(start)
216
285
  else:
@@ -439,6 +508,10 @@ def array(x, dtype=None):
439
508
  return np.array(x)
440
509
 
441
510
 
511
+ def view(x, dtype=None):
512
+ raise NotImplementedError("`view` is not supported with openvino backend")
513
+
514
+
442
515
  def average(x, axis=None, weights=None):
443
516
  x = get_ov_output(x)
444
517
  if weights is not None:
@@ -472,22 +545,79 @@ def average(x, axis=None, weights=None):
472
545
 
473
546
 
474
547
  def bartlett(x):
475
- raise NotImplementedError(
476
- "`bartlett` is not supported with openvino backend"
548
+ x = get_ov_output(x)
549
+ zero_const = ov_opset.constant(0, Type.i64)
550
+ one_const = ov_opset.constant(1, Type.i64)
551
+ two_const = ov_opset.constant(2, Type.i64)
552
+ two_const_f64 = ov_opset.constant(2.0, Type.f64)
553
+ if x.get_element_type() != Type.i64:
554
+ x = ov_opset.convert(x, Type.i64)
555
+ half = ov_opset.convert(
556
+ ov_opset.divide(ov_opset.subtract(x, one_const), two_const), Type.f64
557
+ )
558
+ n = ov_opset.range(zero_const, x, one_const, Type.f64)
559
+ condition = ov_opset.less_equal(n, half)
560
+ first_half = ov_opset.divide(
561
+ ov_opset.multiply(two_const_f64, n),
562
+ ov_opset.convert(ov_opset.subtract(x, one_const), Type.f64),
477
563
  )
564
+ second_half = ov_opset.subtract(two_const_f64, first_half)
565
+ window = ov_opset.select(condition, first_half, second_half)
566
+ window = ov_opset.convert(window, OPENVINO_DTYPES[config.floatx()]).output(
567
+ 0
568
+ )
569
+ return OpenVINOKerasTensor(window)
478
570
 
479
571
 
480
572
  def hamming(x):
481
- raise NotImplementedError(
482
- "`hamming` is not supported with openvino backend"
573
+ m = get_ov_output(x)
574
+
575
+ m_i64 = (
576
+ m if m.get_element_type() == Type.i64 else ov_opset.convert(m, Type.i64)
483
577
  )
484
578
 
579
+ start = ov_opset.constant(0, Type.i64)
580
+ step = ov_opset.constant(1, Type.i64)
581
+ n = ov_opset.range(start, m_i64, step, Type.f64)
485
582
 
486
- def heaviside(x1, x2):
487
- raise NotImplementedError(
488
- "`heaviside` is not supported with openvino backend"
583
+ one_i64 = ov_opset.constant(1, Type.i64)
584
+ denom_i64 = ov_opset.subtract(m_i64, one_i64)
585
+ denom = ov_opset.convert(denom_i64, Type.f64)
586
+
587
+ two_pi = ov_opset.constant(2.0 * np.pi, Type.f64)
588
+ two_pi_over_m_minus_1 = ov_opset.divide(two_pi, denom)
589
+
590
+ x = ov_opset.multiply(two_pi_over_m_minus_1, n)
591
+ c = ov_opset.cos(x)
592
+
593
+ # 0.54 - 0.46 * cos(...)
594
+ a = ov_opset.constant(0.54, Type.f64)
595
+ b = ov_opset.constant(0.46, Type.f64)
596
+ hamming_window = ov_opset.subtract(a, ov_opset.multiply(b, c))
597
+ hamming_window = ov_opset.convert(
598
+ hamming_window, OPENVINO_DTYPES[config.floatx()]
489
599
  )
490
600
 
601
+ return OpenVINOKerasTensor(hamming_window.output(0))
602
+
603
+
604
+ def heaviside(x1, x2):
605
+ x1 = get_ov_output(x1)
606
+ x_type = x1.get_element_type()
607
+ x2 = get_ov_output(x2, x_type)
608
+
609
+ zero_scalar = ov_opset.constant(0, x_type).output(0)
610
+ one_scalar = ov_opset.constant(1, x_type).output(0)
611
+
612
+ neg = ov_opset.less(x1, zero_scalar).output(0)
613
+ pos = ov_opset.greater(x1, zero_scalar).output(0)
614
+ eq = ov_opset.equal(x1, zero_scalar).output(0)
615
+
616
+ x = ov_opset.select(neg, zero_scalar, x1).output(0)
617
+ x = ov_opset.select(pos, one_scalar, x).output(0)
618
+ x = ov_opset.select(eq, x2, x).output(0)
619
+ return OpenVINOKerasTensor(x)
620
+
491
621
 
492
622
  def kaiser(x, beta):
493
623
  raise NotImplementedError("`kaiser` is not supported with openvino backend")
@@ -539,9 +669,30 @@ def bincount(x, weights=None, minlength=0, sparse=False):
539
669
 
540
670
 
541
671
  def blackman(x):
542
- raise NotImplementedError(
543
- "`blackman` is not supported with openvino backend"
672
+ x = get_ov_output(x)
673
+ zero_const = ov_opset.constant(0, Type.i64)
674
+ one_const = ov_opset.constant(1, Type.i64)
675
+ two_pi = ov_opset.constant(2.0 * np.pi, Type.f64)
676
+ term_1 = ov_opset.constant(0.42, Type.f64)
677
+ term_2 = ov_opset.constant(0.5, Type.f64)
678
+ term_3 = ov_opset.constant(0.08, Type.f64)
679
+ if x.get_element_type() != Type.i64:
680
+ x = ov_opset.convert(x, Type.i64)
681
+ n = ov_opset.range(zero_const, x, one_const, Type.f64)
682
+ n_minus_1 = ov_opset.subtract(
683
+ ov_opset.convert(x, Type.f64), ov_opset.constant(1.0, Type.f64)
684
+ ).output(0)
685
+ angle_2pi = ov_opset.divide(ov_opset.multiply(two_pi, n), n_minus_1)
686
+ angle_4pi = ov_opset.multiply(angle_2pi, ov_opset.constant(2.0, Type.f64))
687
+ cos_2pi = ov_opset.cos(angle_2pi)
688
+ cos_4pi = ov_opset.cos(angle_4pi)
689
+ term_2_final = ov_opset.multiply(term_2, cos_2pi)
690
+ term_3_final = ov_opset.multiply(term_3, cos_4pi)
691
+ window = ov_opset.add(ov_opset.subtract(term_1, term_2_final), term_3_final)
692
+ window = ov_opset.convert(window, OPENVINO_DTYPES[config.floatx()]).output(
693
+ 0
544
694
  )
695
+ return OpenVINOKerasTensor(window)
545
696
 
546
697
 
547
698
  def broadcast_to(x, shape):
@@ -559,11 +710,18 @@ def cbrt(x):
559
710
 
560
711
  def ceil(x):
561
712
  x = get_ov_output(x)
562
- return OpenVINOKerasTensor(ov_opset.ceil(x).output(0))
713
+ x_type = x.get_element_type()
714
+ if x_type.is_integral():
715
+ x = ov_opset.convert(x, OPENVINO_DTYPES[config.floatx()]).output(0)
716
+ ceiling = ov_opset.ceil(x).output(0)
717
+ return OpenVINOKerasTensor(ceiling)
563
718
 
564
719
 
565
720
  def clip(x, x_min, x_max):
566
721
  x = get_ov_output(x)
722
+ x_type = x.get_element_type()
723
+ if x_type == Type.boolean:
724
+ x = ov_opset.convert(x, Type.i32).output(0)
567
725
  x_min = get_ov_output(x_min, x.get_element_type())
568
726
  x_max = get_ov_output(x_max, x.get_element_type())
569
727
  clip_by_min = ov_opset.maximum(x, x_min).output(0)
@@ -619,15 +777,9 @@ def count_nonzero(x, axis=None):
619
777
  zero_constant = ov_opset.convert_like(zero_constant, x)
620
778
  x = ov_opset.not_equal(x, zero_constant).output(0)
621
779
  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 == []:
780
+ x, axis = _resolve_axis(x, axis)
781
+ if not axis:
629
782
  return OpenVINOKerasTensor(x)
630
- axis = ov_opset.constant(axis, Type.i32).output(0)
631
783
  return OpenVINOKerasTensor(ov_opset.reduce_sum(x, axis, False).output(0))
632
784
 
633
785
 
@@ -646,11 +798,9 @@ def cumsum(x, axis=None, dtype=None):
646
798
  if dtype is not None:
647
799
  ov_type = OPENVINO_DTYPES[standardize_dtype(dtype)]
648
800
  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)
801
+ x, axis = _resolve_axis(x, axis)
802
+ if x.get_element_type() == Type.boolean:
803
+ x = ov_opset.convert(x, Type.i32).output(0)
654
804
  return OpenVINOKerasTensor(ov_opset.cumsum(x, axis).output(0))
655
805
 
656
806
 
@@ -676,7 +826,70 @@ def deg2rad(x):
676
826
 
677
827
 
678
828
  def diag(x, k=0):
679
- raise NotImplementedError("`diag` is not supported with openvino backend")
829
+ x = get_ov_output(x)
830
+ x_shape = x.get_partial_shape()
831
+ rank = x_shape.rank.get_length()
832
+
833
+ if rank == 1:
834
+ N_dim = x_shape[0]
835
+ if not N_dim.is_static:
836
+ raise ValueError(
837
+ "diag requires input with static shape for 1D input."
838
+ )
839
+ N = N_dim.get_length()
840
+ output_size = N + np.abs(k)
841
+ out_shape = ov_opset.constant(
842
+ [output_size, output_size], dtype=Type.i32
843
+ ).output(0)
844
+ zeros_const = ov_opset.constant(0, x.get_element_type()).output(0)
845
+ diag_matrix = ov_opset.broadcast(zeros_const, out_shape)
846
+
847
+ indices = []
848
+ if k >= 0:
849
+ for i in range(N):
850
+ indices.append([i, i + k])
851
+ else:
852
+ for i in range(N):
853
+ indices.append([i - k, i])
854
+
855
+ indices = np.array(indices, dtype=np.int32)
856
+ indices_const = ov_opset.constant(indices, dtype=Type.i32).output(0)
857
+ updated = ov_opset.scatter_nd_update(diag_matrix, indices_const, x)
858
+ return OpenVINOKerasTensor(updated.output(0))
859
+
860
+ elif rank == 2:
861
+ M_dim = x_shape[0]
862
+ N_dim = x_shape[1]
863
+ if not M_dim.is_static or not N_dim.is_static:
864
+ raise ValueError(
865
+ "diag requires input with static shape for 2D input."
866
+ )
867
+ M = M_dim.get_length()
868
+ N = N_dim.get_length()
869
+
870
+ if k >= 0:
871
+ L = np.minimum(M, N - k) if (N - k) > 0 else 0
872
+ indices = [[i, i + k] for i in range(L)]
873
+ else:
874
+ L = np.minimum(M + k, N) if (M + k) > 0 else 0
875
+ indices = [[i - k, i] for i in range(L)]
876
+
877
+ if L <= 0:
878
+ keras_dtype = ov_to_keras_type(x.get_element_type())
879
+ np_dtype = np.dtype(keras_dtype)
880
+ empty_np = np.empty((0,), dtype=np_dtype)
881
+ empty_const = ov_opset.constant(
882
+ empty_np, x.get_element_type()
883
+ ).output(0)
884
+ return OpenVINOKerasTensor(empty_const)
885
+
886
+ indices = np.array(indices, dtype=np.int32)
887
+ indices_const = ov_opset.constant(indices, dtype=Type.i32).output(0)
888
+ diag_vec = ov_opset.gather_nd(x, indices_const)
889
+ return OpenVINOKerasTensor(diag_vec.output(0))
890
+
891
+ else:
892
+ raise ValueError("diag supports only 1D or 2D tensors")
680
893
 
681
894
 
682
895
  def diagonal(x, offset=0, axis1=0, axis2=1):
@@ -759,9 +972,30 @@ def diff(a, n=1, axis=-1):
759
972
 
760
973
 
761
974
  def digitize(x, bins):
762
- raise NotImplementedError(
763
- "`digitize` is not supported with openvino backend"
764
- )
975
+ x_node = get_ov_output(x)
976
+
977
+ if isinstance(bins, OpenVINOKerasTensor):
978
+ bins_node = get_ov_output(bins)
979
+ else:
980
+ bins_np = np.asarray(bins)
981
+ if bins_np.ndim != 1:
982
+ raise ValueError("`bins` must be 1-D array-like")
983
+ bins_node = ov_opset.constant(bins_np).output(0)
984
+
985
+ x_node, bins_node = _align_operand_types(x_node, bins_node, "digitize()")
986
+
987
+ if x_node.get_element_type() == Type.boolean:
988
+ x_node = ov_opset.convert(x_node, Type.f32).output(0)
989
+ bins_node = ov_opset.convert(bins_node, Type.f32).output(0)
990
+
991
+ result = ov_opset.bucketize(
992
+ x_node,
993
+ bins_node,
994
+ output_type=Type.i32,
995
+ with_right_bound=False,
996
+ ).output(0)
997
+
998
+ return OpenVINOKerasTensor(result)
765
999
 
766
1000
 
767
1001
  def dot(x1, x2):
@@ -791,6 +1025,10 @@ def empty(shape, dtype=None):
791
1025
  return OpenVINOKerasTensor(empty_tensor)
792
1026
 
793
1027
 
1028
+ def empty_like(x, dtype=None):
1029
+ return zeros_like(x, dtype=dtype)
1030
+
1031
+
794
1032
  def equal(x1, x2):
795
1033
  element_type = None
796
1034
  if isinstance(x1, OpenVINOKerasTensor):
@@ -833,11 +1071,40 @@ def expm1(x):
833
1071
 
834
1072
 
835
1073
  def flip(x, axis=None):
836
- raise NotImplementedError("`flip` is not supported with openvino backend")
1074
+ x_node = get_ov_output(x)
1075
+ ndim = x.ndim
1076
+ if ndim is None:
1077
+ raise ValueError(
1078
+ "The `flip` operation does not support tensors with dynamic rank"
1079
+ "for the OpenVINO backend."
1080
+ )
1081
+ if axis is None:
1082
+ axis = list(range(ndim))
1083
+ elif isinstance(axis, int):
1084
+ axis = [axis]
1085
+ axis = [a + ndim if a < 0 else a for a in axis]
1086
+ begin = [0] * ndim
1087
+ end = [0] * ndim
1088
+ strides = [1] * ndim
1089
+ for a in axis:
1090
+ strides[a] = -1
1091
+ all_ones_mask = [1] * ndim
1092
+ result = ov_opset.strided_slice(
1093
+ data=x_node,
1094
+ begin=begin,
1095
+ end=end,
1096
+ strides=strides,
1097
+ begin_mask=all_ones_mask,
1098
+ end_mask=all_ones_mask,
1099
+ )
1100
+ return OpenVINOKerasTensor(result.output(0))
837
1101
 
838
1102
 
839
1103
  def floor(x):
840
1104
  x = get_ov_output(x)
1105
+ x_type = x.get_element_type()
1106
+ if x_type.is_integral():
1107
+ x = ov_opset.convert(x, OPENVINO_DTYPES[config.floatx()])
841
1108
  return OpenVINOKerasTensor(ov_opset.floor(x).output(0))
842
1109
 
843
1110
 
@@ -950,25 +1217,70 @@ def isclose(x1, x2, rtol=1e-5, atol=1e-8, equal_nan=False):
950
1217
 
951
1218
 
952
1219
  def isfinite(x):
953
- x = get_ov_output(x)
954
- return OpenVINOKerasTensor(ov_opset.is_finite(x).output(0))
1220
+ # NOTE: openvino has an is_finite operation but it does not properly
1221
+ # catch np.inf and -np.inf as not finite values. Hence we bootstrap here. If
1222
+ # that ever changes, we could simplify this to just call that operation.
1223
+ inf_values = get_ov_output(isinf(x))
1224
+ nan_values = get_ov_output(isnan(x))
1225
+ all_non_finite_values = ov_opset.logical_or(inf_values, nan_values).output(
1226
+ 0
1227
+ )
1228
+ is_finite = ov_opset.logical_not(all_non_finite_values).output(0)
1229
+ return OpenVINOKerasTensor(is_finite)
955
1230
 
956
1231
 
957
1232
  def isin(x1, x2, assume_unique=False, invert=False):
958
- raise NotImplementedError("`isin` is not supported with openvino backend")
1233
+ x1 = get_ov_output(x1)
1234
+ x2 = get_ov_output(x2)
1235
+ output_shape = ov_opset.shape_of(x1).output(0)
1236
+ x1, x2 = _align_operand_types(x1, x2, "isin()")
1237
+
1238
+ minus_one = ov_opset.constant([-1], dtype=Type.i64)
1239
+ x1 = ov_opset.reshape(x1, minus_one, special_zero=False).output(0)
1240
+ x2 = ov_opset.reshape(x2, minus_one, special_zero=False).output(0)
1241
+ if not assume_unique:
1242
+ x2 = ov_opset.unique(x2).output(0)
1243
+ x1 = ov_opset.unsqueeze(x1, 1).output(0)
1244
+ x2 = ov_opset.unsqueeze(x2, 0).output(0)
1245
+ cmp = ov_opset.equal(x1, x2).output(0)
1246
+ result_flat = ov_opset.reduce_logical_or(cmp, 1).output(0)
1247
+
1248
+ if invert:
1249
+ result_flat = ov_opset.logical_not(result_flat).output(0)
1250
+ result = ov_opset.reshape(result_flat, output_shape, False).output(0)
1251
+ return OpenVINOKerasTensor(result)
959
1252
 
960
1253
 
961
1254
  def isinf(x):
962
- x = get_ov_output(x)
963
- return OpenVINOKerasTensor(ov_opset.is_inf(x).output(0))
1255
+ pos_inf = get_ov_output(isposinf(x))
1256
+ neg_inf = get_ov_output(isneginf(x))
1257
+ inf = ov_opset.logical_or(pos_inf, neg_inf).output(0)
1258
+ return OpenVINOKerasTensor(inf)
964
1259
 
965
1260
 
966
1261
  def isnan(x):
967
1262
  x = get_ov_output(x)
1263
+ x_type = x.get_element_type()
1264
+ if x_type.is_integral():
1265
+ x = ov_opset.convert(x, OPENVINO_DTYPES[config.floatx()])
968
1266
  return OpenVINOKerasTensor(ov_opset.is_nan(x).output(0))
969
1267
 
970
1268
 
971
1269
  def isneginf(x):
1270
+ return _is_inf(x, pos=False)
1271
+
1272
+
1273
+ def isposinf(x):
1274
+ return _is_inf(x)
1275
+
1276
+
1277
+ def _is_inf(x, pos=True):
1278
+ # NOTE: there is an ov_opset.is_inf but it does not catch
1279
+ # numpy infinite values like np.inf and -np.inf, hence why we have this
1280
+ # if this ever changes in OpenVINO, we can do this instead:
1281
+ # ov_opset.is_inf(x, {"detect_positive": pos, "detect_negative": not pos})
1282
+ # for each infinite sign
1283
+ inf_value = np.inf if pos else -np.inf
972
1284
  x = get_ov_output(x)
973
1285
  x_type = x.get_element_type()
974
1286
 
@@ -981,26 +1293,23 @@ def isneginf(x):
981
1293
 
982
1294
  if x_type == Type.bf16:
983
1295
  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)
1296
+ inf = ov_opset.constant(inf_value, Type.f32).output(0)
1297
+ is_inf = ov_opset.equal(x_f32, inf).output(0)
986
1298
  else:
987
1299
  if x_type == Type.f16:
988
- neg_inf = ov_opset.constant(-np.inf, Type.f16).output(0)
1300
+ inf = ov_opset.constant(inf_value, Type.f16).output(0)
989
1301
  elif x_type == Type.f32:
990
- neg_inf = ov_opset.constant(-np.inf, Type.f32).output(0)
1302
+ inf = ov_opset.constant(inf_value, Type.f32).output(0)
991
1303
  elif x_type == Type.f64:
992
- neg_inf = ov_opset.constant(-np.inf, Type.f64).output(0)
1304
+ inf = ov_opset.constant(inf_value, Type.f64).output(0)
993
1305
  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)
996
-
997
- return OpenVINOKerasTensor(is_neg_inf)
1306
+ inf = ov_opset.constant(inf_value, Type.f32).output(0)
1307
+ is_inf = ov_opset.equal(x, inf).output(0)
1308
+ return OpenVINOKerasTensor(is_inf)
998
1309
 
999
1310
 
1000
- def isposinf(x):
1001
- raise NotImplementedError(
1002
- "`isposinf` is not supported with openvino backend"
1003
- )
1311
+ def isreal(x):
1312
+ raise NotImplementedError("`isreal` is not supported with openvino backend")
1004
1313
 
1005
1314
 
1006
1315
  def kron(x1, x2):
@@ -1011,6 +1320,10 @@ def lcm(x1, x2):
1011
1320
  raise NotImplementedError("`lcm` is not supported with openvino backend")
1012
1321
 
1013
1322
 
1323
+ def ldexp(x1, x2):
1324
+ raise NotImplementedError("`ldexp` is not supported with openvino backend")
1325
+
1326
+
1014
1327
  def less(x1, x2):
1015
1328
  element_type = None
1016
1329
  if isinstance(x1, OpenVINOKerasTensor):
@@ -1038,9 +1351,131 @@ def less_equal(x1, x2):
1038
1351
  def linspace(
1039
1352
  start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0
1040
1353
  ):
1041
- raise NotImplementedError(
1042
- "`linspace` is not supported with openvino backend"
1354
+ """Return evenly spaced numbers over a specified interval.
1355
+
1356
+ Supports axis=0 (prepend) and axis=-1 (append). Intermediate axis values are
1357
+ treated as axis=-1.
1358
+
1359
+ If `retstep` is True, also returns the step size between values.
1360
+
1361
+ """
1362
+
1363
+ start = get_ov_output(start)
1364
+ stop = get_ov_output(stop)
1365
+
1366
+ if hasattr(num, "output") or isinstance(num, OpenVINOKerasTensor):
1367
+ num_tensor = get_ov_output(num)
1368
+ try:
1369
+ if num_tensor.get_node().get_type_name() == "Constant":
1370
+ num_value = num_tensor.get_node().get_vector()[0]
1371
+ num = int(num_value)
1372
+ else:
1373
+ raise NotImplementedError(
1374
+ "Dynamic num values not fully supported"
1375
+ )
1376
+ except Exception as e:
1377
+ raise NotImplementedError(
1378
+ "Could not extract num value from tensor"
1379
+ ) from e
1380
+ else:
1381
+ num = int(num)
1382
+
1383
+ if dtype is None:
1384
+ output_type = OPENVINO_DTYPES[config.floatx()]
1385
+ else:
1386
+ output_type = OPENVINO_DTYPES[dtype]
1387
+
1388
+ start = ov_opset.convert(start, output_type).output(0)
1389
+ stop = ov_opset.convert(stop, output_type).output(0)
1390
+
1391
+ if num < 0:
1392
+ raise ValueError("Number of samples, `num`, must be non-negative.")
1393
+
1394
+ if num == 0:
1395
+ empty_shape = ov_opset.constant([0], Type.i32).output(0)
1396
+ result = ov_opset.broadcast(
1397
+ ov_opset.constant(0.0, output_type).output(0), empty_shape
1398
+ ).output(0)
1399
+ if retstep:
1400
+ nan_step = ov_opset.constant(np.nan, output_type).output(0)
1401
+ return OpenVINOKerasTensor(result), OpenVINOKerasTensor(nan_step)
1402
+ return OpenVINOKerasTensor(result)
1403
+
1404
+ if num == 1:
1405
+ result_val = start
1406
+ axis_const = ov_opset.constant([axis], Type.i32).output(0)
1407
+ result = ov_opset.unsqueeze(result_val, axis_const).output(0)
1408
+ if retstep:
1409
+ if endpoint:
1410
+ step = ov_opset.constant(np.nan, output_type).output(0)
1411
+ else:
1412
+ step = ov_opset.subtract(stop, start).output(0)
1413
+ return OpenVINOKerasTensor(result), OpenVINOKerasTensor(step)
1414
+ zero_i32 = ov_opset.constant(0, Type.i32).output(0)
1415
+ one_i32 = ov_opset.constant(1, Type.i32).output(0)
1416
+ one_i32_array = ov_opset.constant([1], Type.i32).output(0)
1417
+
1418
+ num_const = ov_opset.constant(num, output_type).output(0)
1419
+
1420
+ if endpoint:
1421
+ divisor = ov_opset.subtract(
1422
+ num_const, ov_opset.constant(1, output_type).output(0)
1423
+ ).output(0)
1424
+ else:
1425
+ divisor = num_const
1426
+
1427
+ step = ov_opset.divide(
1428
+ ov_opset.subtract(stop, start).output(0), divisor
1429
+ ).output(0)
1430
+
1431
+ indices = ov_opset.range(
1432
+ zero_i32,
1433
+ ov_opset.constant(num, Type.i32).output(0),
1434
+ one_i32,
1435
+ output_type,
1436
+ ).output(0)
1437
+
1438
+ start_shape = ov_opset.convert(
1439
+ ov_opset.shape_of(start).output(0), Type.i32
1440
+ ).output(0)
1441
+ indices_shape = ov_opset.convert(
1442
+ ov_opset.shape_of(indices).output(0), Type.i32
1443
+ ).output(0)
1444
+
1445
+ start_rank = ov_opset.shape_of(start_shape).output(0)
1446
+ ones_for_start = ov_opset.broadcast(one_i32, start_rank).output(0)
1447
+
1448
+ if axis == 0:
1449
+ indices_target_shape = ov_opset.concat(
1450
+ [indices_shape, ones_for_start], 0
1451
+ ).output(0)
1452
+ start_target_shape = ov_opset.concat(
1453
+ [one_i32_array, start_shape], 0
1454
+ ).output(0)
1455
+ else:
1456
+ indices_target_shape = ov_opset.concat(
1457
+ [ones_for_start, indices_shape], 0
1458
+ ).output(0)
1459
+ start_target_shape = ov_opset.concat(
1460
+ [start_shape, one_i32_array], 0
1461
+ ).output(0)
1462
+
1463
+ indices_reshaped = ov_opset.reshape(
1464
+ indices, indices_target_shape, False
1465
+ ).output(0)
1466
+ start_reshaped = ov_opset.reshape(start, start_target_shape, False).output(
1467
+ 0
1468
+ )
1469
+ step_reshaped = ov_opset.reshape(step, start_target_shape, False).output(0)
1470
+
1471
+ scaled_indices = ov_opset.multiply(indices_reshaped, step_reshaped).output(
1472
+ 0
1043
1473
  )
1474
+ result = ov_opset.add(start_reshaped, scaled_indices).output(0)
1475
+
1476
+ if retstep:
1477
+ return OpenVINOKerasTensor(result), OpenVINOKerasTensor(step)
1478
+ return OpenVINOKerasTensor(result)
1044
1479
 
1045
1480
 
1046
1481
  def log(x):
@@ -1142,6 +1577,12 @@ def logaddexp(x1, x2):
1142
1577
  return OpenVINOKerasTensor(result)
1143
1578
 
1144
1579
 
1580
+ def logaddexp2(x1, x2):
1581
+ raise NotImplementedError(
1582
+ "`logaddexp2` is not supported with openvino backend"
1583
+ )
1584
+
1585
+
1145
1586
  def logical_and(x1, x2):
1146
1587
  x1 = get_ov_output(x1)
1147
1588
  x2 = get_ov_output(x2)
@@ -1165,10 +1606,30 @@ def logical_or(x1, x2):
1165
1606
 
1166
1607
 
1167
1608
  def logspace(start, stop, num=50, endpoint=True, base=10, dtype=None, axis=0):
1168
- raise NotImplementedError(
1169
- "`logspace` is not supported with openvino backend"
1609
+ linear_samples = linspace(
1610
+ start=start,
1611
+ stop=stop,
1612
+ num=num,
1613
+ endpoint=endpoint,
1614
+ retstep=False,
1615
+ dtype=dtype,
1616
+ axis=axis,
1170
1617
  )
1171
1618
 
1619
+ if dtype is None:
1620
+ output_type = OPENVINO_DTYPES[config.floatx()]
1621
+ else:
1622
+ output_type = OPENVINO_DTYPES[dtype]
1623
+
1624
+ linear_output = get_ov_output(linear_samples)
1625
+ base_tensor = get_ov_output(base)
1626
+
1627
+ base_tensor = ov_opset.convert(base_tensor, output_type).output(0)
1628
+
1629
+ result = ov_opset.power(base_tensor, linear_output).output(0)
1630
+
1631
+ return OpenVINOKerasTensor(result)
1632
+
1172
1633
 
1173
1634
  def maximum(x1, x2):
1174
1635
  x1 = get_ov_output(x1)
@@ -1178,7 +1639,138 @@ def maximum(x1, x2):
1178
1639
 
1179
1640
 
1180
1641
  def median(x, axis=None, keepdims=False):
1181
- raise NotImplementedError("`median` is not supported with openvino backend")
1642
+ x = get_ov_output(x)
1643
+ x_shape = x.get_partial_shape()
1644
+ rank = x_shape.rank.get_length()
1645
+
1646
+ if rank == 0:
1647
+ return OpenVINOKerasTensor(x)
1648
+
1649
+ # Handle axis=None by flattening the input
1650
+ flattened_all = False
1651
+ if axis is None:
1652
+ x = ov_opset.reshape(x, [-1], False).output(0)
1653
+ axis = 0
1654
+ original_rank = rank
1655
+ rank = 1
1656
+ flattened_all = True
1657
+ else:
1658
+ # Handle tuple axis - for median, we only support single axis
1659
+ if isinstance(axis, (tuple, list)):
1660
+ if len(axis) != 1:
1661
+ raise ValueError("median only supports single axis reduction")
1662
+ axis = axis[0]
1663
+
1664
+ # Handle negative axis
1665
+ if axis < 0:
1666
+ axis = rank + axis
1667
+ original_rank = rank
1668
+
1669
+ # Get the size of the dimension to sort
1670
+ shape_tensor = ov_opset.shape_of(x, output_type=Type.i32).output(0)
1671
+ k = ov_opset.gather(
1672
+ shape_tensor,
1673
+ ov_opset.constant([axis], Type.i32).output(0),
1674
+ ov_opset.constant(0, Type.i32).output(0),
1675
+ ).output(0)
1676
+
1677
+ # Convert k to a scalar value
1678
+ k_scalar = ov_opset.squeeze(k, [0]).output(0)
1679
+
1680
+ # Use topk with k=size_of_axis to get all elements sorted
1681
+ topk_outputs = ov_opset.topk(
1682
+ x, k=k_scalar, axis=axis, mode="min", sort="value", stable=True
1683
+ )
1684
+
1685
+ # Get the sorted values
1686
+ sorted_values = topk_outputs.output(0)
1687
+
1688
+ # Convert to float for median calculation
1689
+ x1_type = ov_to_keras_type(sorted_values.get_element_type())
1690
+ result_type = dtypes.result_type(x1_type, float)
1691
+ result_type = OPENVINO_DTYPES[result_type]
1692
+ sorted_values = ov_opset.convert(sorted_values, result_type).output(0)
1693
+
1694
+ # Calculate median indices
1695
+ # For odd length: median_idx = (k-1) // 2
1696
+ # For even length: we need indices (k//2 - 1) and k//2, then average
1697
+
1698
+ k_minus_1 = ov_opset.subtract(
1699
+ k_scalar, ov_opset.constant(1, Type.i32).output(0)
1700
+ ).output(0)
1701
+ k_div_2 = ov_opset.divide(
1702
+ k_scalar, ov_opset.constant(2, Type.i32).output(0)
1703
+ ).output(0)
1704
+ k_minus_1_div_2 = ov_opset.divide(
1705
+ k_minus_1, ov_opset.constant(2, Type.i32).output(0)
1706
+ ).output(0)
1707
+
1708
+ # Check if k is odd
1709
+ k_mod_2 = ov_opset.mod(
1710
+ k_scalar, ov_opset.constant(2, Type.i32).output(0)
1711
+ ).output(0)
1712
+ is_odd = ov_opset.equal(
1713
+ k_mod_2, ov_opset.constant(1, Type.i32).output(0)
1714
+ ).output(0)
1715
+
1716
+ # For odd case: take the middle element
1717
+ odd_idx = k_minus_1_div_2
1718
+
1719
+ # For even case: take average of two middle elements
1720
+ even_idx1 = ov_opset.subtract(
1721
+ k_div_2, ov_opset.constant(1, Type.i32).output(0)
1722
+ ).output(0)
1723
+ even_idx2 = k_div_2
1724
+
1725
+ # Gather elements for both cases
1726
+ # Create gather indices tensor for the axis
1727
+ gather_indices_odd = ov_opset.unsqueeze(odd_idx, [0]).output(0)
1728
+ gather_indices_even1 = ov_opset.unsqueeze(even_idx1, [0]).output(0)
1729
+ gather_indices_even2 = ov_opset.unsqueeze(even_idx2, [0]).output(0)
1730
+
1731
+ # Gather the median elements
1732
+ odd_result = ov_opset.gather(
1733
+ sorted_values,
1734
+ gather_indices_odd,
1735
+ ov_opset.constant(axis, Type.i32).output(0),
1736
+ ).output(0)
1737
+ even_result1 = ov_opset.gather(
1738
+ sorted_values,
1739
+ gather_indices_even1,
1740
+ ov_opset.constant(axis, Type.i32).output(0),
1741
+ ).output(0)
1742
+ even_result2 = ov_opset.gather(
1743
+ sorted_values,
1744
+ gather_indices_even2,
1745
+ ov_opset.constant(axis, Type.i32).output(0),
1746
+ ).output(0)
1747
+
1748
+ # Average the two middle elements for even case
1749
+ even_sum = ov_opset.add(even_result1, even_result2).output(0)
1750
+ even_result = ov_opset.divide(
1751
+ even_sum, ov_opset.constant(2.0, result_type).output(0)
1752
+ ).output(0)
1753
+
1754
+ # Select between odd and even results
1755
+ median_result = ov_opset.select(is_odd, odd_result, even_result).output(0)
1756
+
1757
+ # Remove the gathered dimension (squeeze)
1758
+ median_result = ov_opset.squeeze(median_result, [axis]).output(0)
1759
+
1760
+ # Handle keepdims
1761
+ if keepdims:
1762
+ if flattened_all:
1763
+ # When axis=None, keepdims should restore all dimensions as 1
1764
+ ones_shape = ov_opset.constant(
1765
+ [1] * original_rank, Type.i32
1766
+ ).output(0)
1767
+ median_result = ov_opset.reshape(
1768
+ median_result, ones_shape, False
1769
+ ).output(0)
1770
+ else:
1771
+ median_result = ov_opset.unsqueeze(median_result, [axis]).output(0)
1772
+
1773
+ return OpenVINOKerasTensor(median_result)
1182
1774
 
1183
1775
 
1184
1776
  def meshgrid(*x, indexing="xy"):
@@ -1226,46 +1818,7 @@ def meshgrid(*x, indexing="xy"):
1226
1818
 
1227
1819
 
1228
1820
  def min(x, axis=None, keepdims=False, initial=None):
1229
- x = get_ov_output(x)
1230
- original_type = x.get_element_type()
1231
- x_type = original_type
1232
- x_shape = x.get_partial_shape().to_shape()
1233
-
1234
- is_bool = x_type == Type.boolean
1235
- if is_bool:
1236
- x = ov_opset.convert(x, Type.i32).output(0)
1237
- x_type = Type.i32
1238
-
1239
- if isinstance(axis, tuple) and len(axis) == 0:
1240
- return OpenVINOKerasTensor(x)
1241
-
1242
- if axis is None:
1243
- flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
1244
- x = ov_opset.reshape(x, flatten_shape, False).output(0)
1245
- axis = 0
1246
-
1247
- if isinstance(axis, tuple):
1248
- axis = list(axis)
1249
-
1250
- axis_const = ov_opset.constant(axis, Type.i32).output(0)
1251
- min_result = ov_opset.reduce_min(x, axis_const, keepdims).output(0)
1252
-
1253
- if initial is not None:
1254
- initial_tensor = ov_opset.constant(initial, x_type).output(0)
1255
- min_result = ov_opset.minimum(min_result, initial_tensor).output(0)
1256
-
1257
- if keepdims:
1258
- result_shape = [1] * len(x_shape)
1259
- min_result = ov_opset.reshape(
1260
- min_result,
1261
- ov_opset.constant(result_shape, Type.i32).output(0),
1262
- False,
1263
- ).output(0)
1264
-
1265
- if is_bool:
1266
- min_result = ov_opset.convert(min_result, Type.boolean).output(0)
1267
-
1268
- return OpenVINOKerasTensor(min_result)
1821
+ return _compute_extrema(x, "min", axis, keepdims, initial)
1269
1822
 
1270
1823
 
1271
1824
  def minimum(x1, x2):
@@ -1416,7 +1969,9 @@ def pad(x, pad_width, mode="constant", constant_values=None):
1416
1969
  "`pad` operation supports only scalar pad value "
1417
1970
  "in constant mode by openvino backend"
1418
1971
  )
1419
- pad_value = constant_values
1972
+ pad_value = ov_opset.constant(
1973
+ constant_values, x.get_element_type()
1974
+ ).output(0)
1420
1975
 
1421
1976
  # split pad_width into two tensors pads_begin and pads_end
1422
1977
  pads_begin = []
@@ -1440,23 +1995,10 @@ def prod(x, axis=None, keepdims=False, dtype=None):
1440
1995
  x = ov_opset.convert(x, ov_dtype).output(0)
1441
1996
  # Otherwise, apply dtype promotion rules before reduction.
1442
1997
  else:
1443
- x_type = x.get_element_type()
1444
- if x_type == Type.boolean:
1445
- x = ov_opset.convert(x, Type.i32).output(0)
1446
- elif x_type in (Type.i8, Type.i16):
1447
- x = ov_opset.convert(x, Type.i32).output(0)
1448
- elif x_type in (Type.u8, Type.u16):
1449
- x = ov_opset.convert(x, Type.u32).output(0)
1450
-
1998
+ x = _upcast_type_if_needed(x)
1999
+ x, axis = _resolve_axis(x, axis)
1451
2000
  if axis is None:
1452
- flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
1453
- x = ov_opset.reshape(x, flatten_shape, False).output(0)
1454
- axis = 0
1455
-
1456
- if isinstance(axis, tuple):
1457
- axis = list(axis)
1458
- axis = ov_opset.constant(axis, Type.i32).output(0)
1459
-
2001
+ return OpenVINOKerasTensor(x)
1460
2002
  # Compute the product
1461
2003
  result = ov_opset.reduce_prod(x, axis, keepdims).output(0)
1462
2004
 
@@ -1556,7 +2098,17 @@ def reshape(x, newshape):
1556
2098
 
1557
2099
 
1558
2100
  def roll(x, shift, axis=None):
1559
- raise NotImplementedError("`roll` is not supported with openvino backend")
2101
+ x = get_ov_output(x)
2102
+ if axis is not None:
2103
+ result = ov_opset.roll(x, shift, axis).output(0)
2104
+ else:
2105
+ output_shape = ov_opset.shape_of(x).output(0)
2106
+ flattened = ov_opset.reshape(
2107
+ x, ov_opset.constant([-1], Type.i32), False
2108
+ ).output(0)
2109
+ result = ov_opset.roll(flattened, shift, 0).output(0)
2110
+ result = ov_opset.reshape(result, output_shape, False).output(0)
2111
+ return OpenVINOKerasTensor(result)
1560
2112
 
1561
2113
 
1562
2114
  def sign(x):
@@ -1593,7 +2145,43 @@ def size(x):
1593
2145
 
1594
2146
 
1595
2147
  def sort(x, axis=-1):
1596
- raise NotImplementedError("`sort` is not supported with openvino backend")
2148
+ x = get_ov_output(x)
2149
+ x_shape = x.get_partial_shape()
2150
+ rank = x_shape.rank.get_length()
2151
+
2152
+ if rank == 0:
2153
+ return OpenVINOKerasTensor(x)
2154
+
2155
+ # Handle axis=None by flattening the input
2156
+ if axis is None:
2157
+ x = ov_opset.reshape(
2158
+ x, ov_opset.constant([-1], Type.i32), False
2159
+ ).output(0)
2160
+ axis = 0
2161
+ # Handle negative axis
2162
+ elif axis < 0:
2163
+ axis = rank + axis
2164
+
2165
+ # Get the size of the dimension to sort
2166
+ shape_tensor = ov_opset.shape_of(x, output_type=Type.i32).output(0)
2167
+ k = ov_opset.gather(
2168
+ shape_tensor,
2169
+ ov_opset.constant([axis], Type.i32).output(0),
2170
+ ov_opset.constant(0, Type.i32).output(0),
2171
+ ).output(0)
2172
+
2173
+ # Convert k to a scalar value
2174
+ k_scalar = ov_opset.squeeze(k, ov_opset.constant([0], Type.i32)).output(0)
2175
+
2176
+ # Use topk with k=size_of_axis to get all elements sorted
2177
+ topk_outputs = ov_opset.topk(
2178
+ x, k=k_scalar, axis=axis, mode="min", sort="value", stable=True
2179
+ )
2180
+
2181
+ # Get the sorted values
2182
+ sorted_values = topk_outputs.output(0)
2183
+
2184
+ return OpenVINOKerasTensor(sorted_values)
1597
2185
 
1598
2186
 
1599
2187
  def split(x, indices_or_sections, axis=0):
@@ -1642,6 +2230,37 @@ def split(x, indices_or_sections, axis=0):
1642
2230
  )
1643
2231
 
1644
2232
 
2233
+ def array_split(x, indices_or_sections, axis=0):
2234
+ original_shape = x.shape
2235
+ x = get_ov_output(x)
2236
+
2237
+ num_splits_val = indices_or_sections
2238
+ total_size = original_shape[axis]
2239
+ if total_size is None:
2240
+ raise ValueError(
2241
+ f"Cannot use array_split with static Python logic on dynamic axis. "
2242
+ f"Axis {axis} has unknown dimension for shape {original_shape}."
2243
+ )
2244
+
2245
+ base_size = total_size // num_splits_val
2246
+ remainder = total_size % num_splits_val
2247
+
2248
+ split_lengths = [base_size + 1] * remainder + [base_size] * (
2249
+ num_splits_val - remainder
2250
+ )
2251
+ split_lengths_tensor = ov_opset.constant(
2252
+ split_lengths, dtype=Type.i64
2253
+ ).output(0)
2254
+
2255
+ axis_tensor = ov_opset.constant(axis, dtype=Type.i32).output(0)
2256
+ splits = ov_opset.variadic_split(x, axis_tensor, split_lengths_tensor)
2257
+
2258
+ result = []
2259
+ for i in range(num_splits_val):
2260
+ result.append(OpenVINOKerasTensor(splits.output(i)))
2261
+ return result
2262
+
2263
+
1645
2264
  def stack(x, axis=0):
1646
2265
  if isinstance(x, tuple):
1647
2266
  x = list(x)
@@ -1658,22 +2277,9 @@ def stack(x, axis=0):
1658
2277
 
1659
2278
 
1660
2279
  def std(x, axis=None, keepdims=False):
1661
- x = get_ov_output(x)
1662
- if axis is None:
1663
- flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
1664
- x = ov_opset.reshape(x, flatten_shape, False).output(0)
1665
- axis = 0
1666
- axis = ov_opset.constant(axis, Type.i32).output(0)
1667
- # The variance is computed using $Var = E[|x|^2] - |E[x]|^2$, It is faster
1668
- # but less numerically stable.
1669
- mean = ov_opset.reduce_mean(x, axis, keepdims).output(0)
1670
- const_two = ov_opset.constant(2, x.get_element_type()).output(0)
1671
- squared_x = ov_opset.power(x, const_two).output(0)
1672
- squared_mean = ov_opset.power(mean, const_two).output(0)
1673
- squared_x_mean = ov_opset.reduce_mean(squared_x, axis, keepdims)
1674
- variance = ov_opset.subtract(squared_x_mean, squared_mean).output(0)
1675
- std_var = OpenVINOKerasTensor(ov_opset.sqrt(variance).output(0))
1676
- return std_var
2280
+ var_x = var(x, axis, keepdims)
2281
+ std_dev = ov_opset.sqrt(var_x).output(0)
2282
+ return OpenVINOKerasTensor(std_dev)
1677
2283
 
1678
2284
 
1679
2285
  def swapaxes(x, axis1, axis2):
@@ -1893,7 +2499,20 @@ def triu(x, k=0):
1893
2499
 
1894
2500
 
1895
2501
  def vdot(x1, x2):
1896
- raise NotImplementedError("`vdot` is not supported with openvino backend")
2502
+ element_type = None
2503
+ if isinstance(x1, OpenVINOKerasTensor):
2504
+ element_type = x1.output.get_element_type()
2505
+ if isinstance(x2, OpenVINOKerasTensor):
2506
+ element_type = x2.output.get_element_type()
2507
+ x1 = get_ov_output(x1, element_type)
2508
+ x2 = get_ov_output(x2, element_type)
2509
+ x1, x2 = _align_operand_types(x1, x2, "vdot()")
2510
+ if x1.get_partial_shape().rank == 0 or x2.get_partial_shape().rank == 0:
2511
+ return OpenVINOKerasTensor(ov_opset.multiply(x1, x2).output(0))
2512
+ flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
2513
+ x1 = ov_opset.reshape(x1, flatten_shape, False).output(0)
2514
+ x2 = ov_opset.reshape(x2, flatten_shape, False).output(0)
2515
+ return OpenVINOKerasTensor(ov_opset.matmul(x1, x2, False, False).output(0))
1897
2516
 
1898
2517
 
1899
2518
  def vstack(xs):
@@ -1979,14 +2598,27 @@ def negative(x):
1979
2598
  return OpenVINOKerasTensor(ov_opset.negative(x).output(0))
1980
2599
 
1981
2600
 
2601
+ def nextafter(x1, x2):
2602
+ raise NotImplementedError(
2603
+ "`nextafter` is not supported with openvino backend"
2604
+ )
2605
+
2606
+
1982
2607
  def square(x):
1983
2608
  x = get_ov_output(x)
2609
+ x_type = x.get_element_type()
2610
+ if x_type == Type.boolean:
2611
+ x = ov_opset.convert(x, Type.i32).output(0)
1984
2612
  const_two = ov_opset.constant(2, x.get_element_type()).output(0)
1985
2613
  return OpenVINOKerasTensor(ov_opset.power(x, const_two).output(0))
1986
2614
 
1987
2615
 
1988
2616
  def sqrt(x):
1989
2617
  x = get_ov_output(x)
2618
+ x_type = x.get_element_type()
2619
+ if x_type.is_integral():
2620
+ ov_type = OPENVINO_DTYPES[config.floatx()]
2621
+ x = ov_opset.convert(x, ov_type).output(0)
1990
2622
  return OpenVINOKerasTensor(ov_opset.sqrt(x).output(0))
1991
2623
 
1992
2624
 
@@ -1997,6 +2629,8 @@ def squeeze(x, axis=None):
1997
2629
  for idx, dim in enumerate(x.get_partial_shape()):
1998
2630
  if dim == 1:
1999
2631
  axis.append(idx)
2632
+ if isinstance(axis, tuple):
2633
+ axis = list(axis)
2000
2634
  axis = ov_opset.constant(axis, Type.i32).output(0)
2001
2635
  return OpenVINOKerasTensor(ov_opset.squeeze(x, axis).output(0))
2002
2636
 
@@ -2021,20 +2655,135 @@ def transpose(x, axes=None):
2021
2655
  return OpenVINOKerasTensor(ov_opset.transpose(x, axes).output(0))
2022
2656
 
2023
2657
 
2658
+ def _helper_trapezoid(y, axis):
2659
+ rank = y.get_partial_shape().rank.get_length()
2660
+ strides = ov_opset.constant([1] * rank, dtype=Type.i64).output(0)
2661
+
2662
+ # y[:-1]
2663
+ begin1 = ov_opset.constant([0] * rank, dtype=Type.i64).output(0)
2664
+ end1_list = [0] * rank
2665
+ end1_list[axis] = -1
2666
+ end1 = ov_opset.constant(end1_list, dtype=Type.i64).output(0)
2667
+ begin_mask1 = [1] * rank
2668
+ begin_mask1[axis] = 0
2669
+ end_mask1 = [1] * rank
2670
+ end_mask1[axis] = 0
2671
+ y1 = ov_opset.strided_slice(
2672
+ y, begin1, end1, strides, begin_mask1, end_mask1
2673
+ ).output(0)
2674
+
2675
+ # y[1:]
2676
+ begin2_list = [0] * rank
2677
+ begin2_list[axis] = 1
2678
+ begin2 = ov_opset.constant(begin2_list, dtype=Type.i64).output(0)
2679
+ end2 = ov_opset.constant([0] * rank, dtype=Type.i64).output(0)
2680
+ begin_mask2 = [1] * rank
2681
+ begin_mask2[axis] = 0
2682
+ end_mask2 = [1] * rank
2683
+ y2 = ov_opset.strided_slice(
2684
+ y, begin2, end2, strides, begin_mask2, end_mask2
2685
+ ).output(0)
2686
+
2687
+ return y1, y2
2688
+
2689
+
2690
+ def trapezoid(y, x=None, dx=1.0, axis=-1):
2691
+ y = get_ov_output(y)
2692
+ y_type = y.get_element_type()
2693
+
2694
+ if y_type.is_integral():
2695
+ y_type = OPENVINO_DTYPES[config.floatx()]
2696
+ y = ov_opset.convert(y, y_type).output(0)
2697
+
2698
+ y1, y2 = _helper_trapezoid(y, axis)
2699
+ y_final = ov_opset.add(y1, y2).output(0)
2700
+ const_two = ov_opset.constant(2, dtype=y_type).output(0)
2701
+ y_final = ov_opset.divide(y_final, const_two).output(0)
2702
+
2703
+ if x is not None:
2704
+ x = get_ov_output(x)
2705
+ x_type = x.get_element_type()
2706
+ if x_type.is_integral():
2707
+ x_type = OPENVINO_DTYPES[config.floatx()]
2708
+ x = ov_opset.convert(x, x_type).output(0)
2709
+
2710
+ x1, x2 = _helper_trapezoid(x, axis)
2711
+ x_final = ov_opset.subtract(x2, x1).output(0)
2712
+
2713
+ else:
2714
+ x_final = ov_opset.constant(dx, dtype=y_type).output(0)
2715
+
2716
+ result = ov_opset.multiply(y_final, x_final).output(0)
2717
+ const_axis = ov_opset.constant([axis], Type.i64).output(0)
2718
+ result = ov_opset.reduce_sum(result, const_axis, False).output(0)
2719
+
2720
+ return OpenVINOKerasTensor(result)
2721
+
2722
+
2723
+ def vander(x, N=None, increasing=False):
2724
+ x = get_ov_output(x)
2725
+ x_type = x.get_element_type()
2726
+
2727
+ shape_x = ov_opset.shape_of(x, Type.i64).output(0)
2728
+
2729
+ const_zero_1D = ov_opset.constant([0], dtype=Type.i64).output(0)
2730
+ const_zero = ov_opset.constant(0, dtype=Type.i64).output(0)
2731
+ const_one = ov_opset.constant(1, dtype=Type.i64).output(0)
2732
+ const_mone = ov_opset.constant(-1, dtype=Type.i64).output(0)
2733
+
2734
+ if N is None:
2735
+ const_N = ov_opset.squeeze(shape_x, const_zero_1D).output(0)
2736
+ const_N_1D = shape_x
2737
+ else:
2738
+ const_N = ov_opset.constant(N, Type.i64).output(0)
2739
+ const_N_1D = ov_opset.constant([N], Type.i64).output(0)
2740
+
2741
+ const_N_minus_one = ov_opset.subtract(const_N, const_one).output(0)
2742
+ if increasing:
2743
+ powers = ov_opset.range(const_zero, const_N, const_one, x_type).output(
2744
+ 0
2745
+ )
2746
+ else:
2747
+ powers = ov_opset.range(
2748
+ const_N_minus_one, const_mone, const_mone, x_type
2749
+ ).output(0)
2750
+
2751
+ target_shape = ov_opset.concat([shape_x, const_N_1D], 0).output(0)
2752
+
2753
+ const_one_1D = ov_opset.constant([1], dtype=Type.i64).output(0)
2754
+
2755
+ powers = ov_opset.unsqueeze(powers, const_zero_1D).output(0)
2756
+ x = ov_opset.unsqueeze(x, const_one_1D).output(0)
2757
+
2758
+ result = ov_opset.broadcast(x, target_shape).output(0)
2759
+
2760
+ result = ov_opset.power(result, powers).output(0)
2761
+
2762
+ return OpenVINOKerasTensor(result)
2763
+
2764
+
2024
2765
  def var(x, axis=None, keepdims=False):
2025
2766
  x = get_ov_output(x)
2767
+ x_type = x.get_element_type()
2768
+ x, axis = _resolve_axis(x, axis)
2769
+
2770
+ work_dtype = Type.f64 if x_type.is_integral() else x.get_element_type()
2771
+ if x_type.is_integral():
2772
+ x = ov_opset.convert(x, work_dtype).output(0)
2026
2773
  if axis is None:
2027
- flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
2028
- x = ov_opset.reshape(x, flatten_shape, False).output(0)
2029
- axis = 0
2030
- axis = ov_opset.constant(axis, Type.i32).output(0)
2774
+ const_zero = ov_opset.constant(0, dtype=work_dtype).output(0)
2775
+ return OpenVINOKerasTensor(
2776
+ ov_opset.broadcast(const_zero, ov_opset.shape_of(x)).output(0)
2777
+ )
2031
2778
  # The variance is computed using $Var = E[|x|^2] - |E[x]|^2$, It is faster
2032
2779
  # but less numerically stable.
2033
2780
  mean = ov_opset.reduce_mean(x, axis, keepdims).output(0)
2034
- const_two = ov_opset.constant(2, x.get_element_type()).output(0)
2781
+ const_two = ov_opset.constant(2, work_dtype).output(0)
2782
+
2035
2783
  squared_x = ov_opset.power(x, const_two).output(0)
2036
2784
  squared_mean = ov_opset.power(mean, const_two).output(0)
2037
- squared_x_mean = ov_opset.reduce_mean(squared_x, axis, keepdims)
2785
+
2786
+ squared_x_mean = ov_opset.reduce_mean(squared_x, axis, keepdims).output(0)
2038
2787
  variance = OpenVINOKerasTensor(
2039
2788
  ov_opset.subtract(squared_x_mean, squared_mean).output(0)
2040
2789
  )
@@ -2043,22 +2792,46 @@ def var(x, axis=None, keepdims=False):
2043
2792
 
2044
2793
  def sum(x, axis=None, keepdims=False):
2045
2794
  x = get_ov_output(x)
2795
+ x, axis = _resolve_axis(x, axis)
2046
2796
  if axis is None:
2047
- flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
2048
- x = ov_opset.reshape(x, flatten_shape, False).output(0)
2049
- axis = 0
2050
- axis = ov_opset.constant(axis, Type.i32).output(0)
2051
- return OpenVINOKerasTensor(ov_opset.reduce_sum(x, axis, keepdims).output(0))
2797
+ return OpenVINOKerasTensor(x)
2798
+ x = _upcast_type_if_needed(x)
2799
+ summed_value = ov_opset.reduce_sum(x, axis, keepdims).output(0)
2800
+ return OpenVINOKerasTensor(summed_value)
2052
2801
 
2053
2802
 
2054
2803
  def eye(N, M=None, k=0, dtype=None):
2055
- raise NotImplementedError("`eye` is not supported with openvino backend")
2804
+ dtype = standardize_dtype(dtype) or config.floatx()
2805
+ ov_type = OPENVINO_DTYPES[dtype]
2806
+ if M is None:
2807
+ M = N
2808
+ return OpenVINOKerasTensor(
2809
+ ov_opset.eye(
2810
+ ov_opset.constant(N, Type.i32),
2811
+ ov_opset.constant(M, Type.i32),
2812
+ ov_opset.constant(k, Type.i32),
2813
+ output_type=ov_type,
2814
+ ).output(0)
2815
+ )
2056
2816
 
2057
2817
 
2058
2818
  def floor_divide(x1, x2):
2059
- raise NotImplementedError(
2060
- "`floor_divide` is not supported with openvino backend"
2061
- )
2819
+ x1_output = get_ov_output(x1)
2820
+ x2_output = get_ov_output(x2)
2821
+ if x1_output.get_element_type() == Type.boolean:
2822
+ x1_output = ov_opset.convert(x1_output, Type.i32).output(0)
2823
+ if isinstance(x2, (int, float)):
2824
+ if x1_output.get_element_type().is_integral() and isinstance(x2, float):
2825
+ ov_type = OPENVINO_DTYPES[config.floatx()]
2826
+ else:
2827
+ ov_type = x1_output.get_element_type()
2828
+ x1 = ov_opset.convert(x1_output, ov_type).output(0)
2829
+ x2 = ov_opset.convert(x2_output, ov_type).output(0)
2830
+ else:
2831
+ x1, x2 = _align_operand_types(x1_output, x2_output, "floor_divide()")
2832
+ div = ov_opset.divide(x1, x2).output(0)
2833
+ floored_div = ov_opset.floor(div).output(0)
2834
+ return OpenVINOKerasTensor(floored_div)
2062
2835
 
2063
2836
 
2064
2837
  def logical_xor(x1, x2):
@@ -2070,16 +2843,88 @@ def logical_xor(x1, x2):
2070
2843
 
2071
2844
 
2072
2845
  def corrcoef(x):
2073
- raise NotImplementedError(
2074
- "`corrcoef` is not supported with openvino backend"
2075
- )
2846
+ x_ov = get_ov_output(x)
2847
+ x_type = x_ov.get_element_type()
2848
+ ov_type = x_type
2849
+
2850
+ if x_type.is_integral():
2851
+ ov_type = OPENVINO_DTYPES[config.floatx()]
2852
+ x_ov = ov_opset.convert(x_ov, ov_type).output(0)
2853
+
2854
+ const_one = ov_opset.constant(1, dtype=Type.i64).output(0)
2855
+ const_two = ov_opset.constant(2, dtype=ov_type).output(0)
2856
+
2857
+ mean = ov_opset.reduce_mean(x_ov, const_one, True).output(0)
2858
+ x_ov = ov_opset.subtract(x_ov, mean).output(0)
2859
+
2860
+ cov = ov_opset.matmul(x_ov, x_ov, False, True).output(0)
2861
+ xsqr = ov_opset.power(x_ov, const_two).output(0)
2862
+ xvar = ov_opset.reduce_sum(xsqr, const_one, True).output(0)
2863
+ xstd = ov_opset.sqrt(xvar).output(0)
2864
+
2865
+ den = ov_opset.matmul(xstd, xstd, False, True).output(0)
2866
+
2867
+ result = ov_opset.divide(cov, den).output(0)
2868
+
2869
+ return OpenVINOKerasTensor(result)
2076
2870
 
2077
2871
 
2078
2872
  def correlate(x1, x2, mode="valid"):
2079
- raise NotImplementedError(
2080
- "`correlate` is not supported with openvino backend"
2873
+ x1 = get_ov_output(x1)
2874
+ x2 = get_ov_output(x2)
2875
+ x1_type = x1.get_element_type()
2876
+ x2_type = x2.get_element_type()
2877
+ x1_type = ov_to_keras_type(x1_type)
2878
+ x2_type = ov_to_keras_type(x2_type)
2879
+ result_type = dtypes.result_type(x1_type, x2_type, float)
2880
+
2881
+ result_type = OPENVINO_DTYPES[result_type]
2882
+ x1 = ov_opset.convert(x1, result_type).output(0)
2883
+ x2 = ov_opset.convert(x2, result_type).output(0)
2884
+
2885
+ shape_filter = ov_opset.shape_of(x2, Type.i64).output(0)
2886
+ const_two = ov_opset.constant(2, Type.f64).output(0)
2887
+ const_one = ov_opset.constant(1, Type.i64).output(0)
2888
+ const_zero = ov_opset.constant(0, result_type).output(0)
2889
+ shape_filter_minus_one = ov_opset.subtract(shape_filter, const_one).output(
2890
+ 0
2081
2891
  )
2082
2892
 
2893
+ # padding x1
2894
+ if mode == "valid":
2895
+ pass
2896
+
2897
+ elif mode == "same":
2898
+ shape_minus_one_float = ov_opset.convert(
2899
+ shape_filter_minus_one, Type.f64
2900
+ ).output(0)
2901
+
2902
+ right = ov_opset.divide(shape_minus_one_float, const_two).output(0)
2903
+ left = ov_opset.ceil(right).output(0)
2904
+ right = ov_opset.floor(right).output(0)
2905
+ left = ov_opset.convert(left, Type.i64).output(0)
2906
+ right = ov_opset.convert(right, Type.i64).output(0)
2907
+ x1 = ov_opset.pad(x1, left, right, "constant", const_zero).output(0)
2908
+
2909
+ elif mode == "full":
2910
+ pad = shape_filter_minus_one
2911
+ x1 = ov_opset.pad(x1, pad, pad, "constant", const_zero).output(0)
2912
+
2913
+ else:
2914
+ raise ValueError(
2915
+ f"mode: {mode} not available chose from valid, same, full."
2916
+ )
2917
+
2918
+ axes = ov_opset.constant([0, 1], dtype=Type.i64).output(0)
2919
+ x2 = ov_opset.unsqueeze(x2, axes).output(0)
2920
+ x1 = ov_opset.unsqueeze(x1, axes).output(0)
2921
+
2922
+ result = ov_opset.convolution(x1, x2, [1], [0], [0], [1]).output(0)
2923
+
2924
+ result = ov_opset.squeeze(result, axes).output(0)
2925
+
2926
+ return OpenVINOKerasTensor(result)
2927
+
2083
2928
 
2084
2929
  def select(condlist, choicelist, default=0):
2085
2930
  raise NotImplementedError("`select` is not supported with openvino backend")