keras-nightly 3.14.0.dev2025122704__py3-none-any.whl → 3.14.0.dev2026012204__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.
- keras/_tf_keras/keras/dtype_policies/__init__.py +3 -0
- keras/_tf_keras/keras/ops/__init__.py +3 -0
- keras/_tf_keras/keras/ops/numpy/__init__.py +3 -0
- keras/_tf_keras/keras/quantizers/__init__.py +1 -0
- keras/dtype_policies/__init__.py +3 -0
- keras/ops/__init__.py +3 -0
- keras/ops/numpy/__init__.py +3 -0
- keras/quantizers/__init__.py +1 -0
- keras/src/backend/jax/nn.py +26 -9
- keras/src/backend/jax/numpy.py +16 -0
- keras/src/backend/numpy/numpy.py +23 -0
- keras/src/backend/openvino/numpy.py +369 -16
- keras/src/backend/tensorflow/numpy.py +34 -1
- keras/src/backend/tensorflow/rnn.py +17 -7
- keras/src/backend/torch/numpy.py +36 -0
- keras/src/backend/torch/rnn.py +28 -11
- keras/src/callbacks/orbax_checkpoint.py +75 -42
- keras/src/dtype_policies/__init__.py +2 -0
- keras/src/dtype_policies/dtype_policy.py +90 -1
- keras/src/layers/core/dense.py +122 -6
- keras/src/layers/core/einsum_dense.py +151 -7
- keras/src/layers/core/embedding.py +1 -1
- keras/src/layers/core/reversible_embedding.py +10 -1
- keras/src/layers/layer.py +5 -0
- keras/src/layers/preprocessing/feature_space.py +8 -4
- keras/src/layers/preprocessing/image_preprocessing/aug_mix.py +2 -2
- keras/src/layers/preprocessing/image_preprocessing/center_crop.py +13 -15
- keras/src/layers/preprocessing/image_preprocessing/random_contrast.py +3 -3
- keras/src/layers/preprocessing/image_preprocessing/resizing.py +10 -0
- keras/src/losses/losses.py +24 -0
- keras/src/models/model.py +18 -9
- keras/src/ops/image.py +109 -96
- keras/src/ops/numpy.py +181 -0
- keras/src/quantizers/__init__.py +2 -0
- keras/src/quantizers/awq.py +361 -0
- keras/src/quantizers/awq_config.py +140 -0
- keras/src/quantizers/awq_core.py +217 -0
- keras/src/quantizers/gptq.py +1 -2
- keras/src/quantizers/gptq_core.py +1 -1
- keras/src/quantizers/quantization_config.py +14 -0
- keras/src/quantizers/quantizers.py +61 -52
- keras/src/random/seed_generator.py +2 -2
- keras/src/saving/file_editor.py +81 -6
- keras/src/saving/orbax_util.py +50 -0
- keras/src/saving/saving_api.py +37 -14
- keras/src/utils/jax_layer.py +69 -31
- keras/src/utils/module_utils.py +11 -0
- keras/src/utils/tracking.py +5 -5
- keras/src/version.py +1 -1
- {keras_nightly-3.14.0.dev2025122704.dist-info → keras_nightly-3.14.0.dev2026012204.dist-info}/METADATA +1 -1
- {keras_nightly-3.14.0.dev2025122704.dist-info → keras_nightly-3.14.0.dev2026012204.dist-info}/RECORD +53 -49
- {keras_nightly-3.14.0.dev2025122704.dist-info → keras_nightly-3.14.0.dev2026012204.dist-info}/WHEEL +1 -1
- {keras_nightly-3.14.0.dev2025122704.dist-info → keras_nightly-3.14.0.dev2026012204.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
|
|
@@ -705,7 +706,16 @@ def broadcast_to(x, shape):
|
|
|
705
706
|
|
|
706
707
|
|
|
707
708
|
def cbrt(x):
|
|
708
|
-
|
|
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))
|
|
709
719
|
|
|
710
720
|
|
|
711
721
|
def ceil(x):
|
|
@@ -893,9 +903,53 @@ def diag(x, k=0):
|
|
|
893
903
|
|
|
894
904
|
|
|
895
905
|
def diagonal(x, offset=0, axis1=0, axis2=1):
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
)
|
|
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))
|
|
899
953
|
|
|
900
954
|
|
|
901
955
|
def diff(a, n=1, axis=-1):
|
|
@@ -1071,7 +1125,94 @@ def expm1(x):
|
|
|
1071
1125
|
|
|
1072
1126
|
|
|
1073
1127
|
def flip(x, axis=None):
|
|
1074
|
-
|
|
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)
|
|
1075
1216
|
|
|
1076
1217
|
|
|
1077
1218
|
def floor(x):
|
|
@@ -1150,7 +1291,34 @@ def hstack(xs):
|
|
|
1150
1291
|
|
|
1151
1292
|
|
|
1152
1293
|
def hypot(x1, x2):
|
|
1153
|
-
|
|
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))
|
|
1154
1322
|
|
|
1155
1323
|
|
|
1156
1324
|
def identity(n, dtype=None):
|
|
@@ -1287,7 +1455,66 @@ def isreal(x):
|
|
|
1287
1455
|
|
|
1288
1456
|
|
|
1289
1457
|
def kron(x1, x2):
|
|
1290
|
-
|
|
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)
|
|
1291
1518
|
|
|
1292
1519
|
|
|
1293
1520
|
def lcm(x1, x2):
|
|
@@ -1552,9 +1779,42 @@ def logaddexp(x1, x2):
|
|
|
1552
1779
|
|
|
1553
1780
|
|
|
1554
1781
|
def logaddexp2(x1, x2):
|
|
1555
|
-
|
|
1556
|
-
|
|
1782
|
+
element_type = None
|
|
1783
|
+
if isinstance(x1, OpenVINOKerasTensor):
|
|
1784
|
+
element_type = x1.output.get_element_type()
|
|
1785
|
+
if isinstance(x2, OpenVINOKerasTensor):
|
|
1786
|
+
element_type = x2.output.get_element_type()
|
|
1787
|
+
x1 = get_ov_output(x1, element_type)
|
|
1788
|
+
x2 = get_ov_output(x2, element_type)
|
|
1789
|
+
x1, x2 = _align_operand_types(x1, x2, "logaddexp2()")
|
|
1790
|
+
|
|
1791
|
+
if x1.element_type.is_integral() or x2.element_type.is_integral():
|
|
1792
|
+
float_dtype = OPENVINO_DTYPES[config.floatx()]
|
|
1793
|
+
if x1.get_element_type().is_integral():
|
|
1794
|
+
x1 = ov_opset.convert(x1, float_dtype)
|
|
1795
|
+
if x2.get_element_type().is_integral():
|
|
1796
|
+
x2 = ov_opset.convert(x2, float_dtype)
|
|
1797
|
+
|
|
1798
|
+
max_val = ov_opset.maximum(x1, x2)
|
|
1799
|
+
|
|
1800
|
+
sub = ov_opset.subtract(x1, x2)
|
|
1801
|
+
abs_diff = ov_opset.abs(sub)
|
|
1802
|
+
|
|
1803
|
+
neg_abs_diff = ov_opset.negative(abs_diff)
|
|
1804
|
+
|
|
1805
|
+
element_type = neg_abs_diff.get_element_type()
|
|
1806
|
+
|
|
1807
|
+
two = ov_opset.constant(2, dtype=element_type)
|
|
1808
|
+
|
|
1809
|
+
power_of_2 = ov_opset.power(two, neg_abs_diff)
|
|
1810
|
+
|
|
1811
|
+
one_plus_power = ov_opset.add(
|
|
1812
|
+
ov_opset.constant(1, dtype=element_type), power_of_2
|
|
1557
1813
|
)
|
|
1814
|
+
log2_term = ov_opset.divide(ov_opset.log(one_plus_power), ov_opset.log(two))
|
|
1815
|
+
result = ov_opset.add(max_val, log2_term).output(0)
|
|
1816
|
+
|
|
1817
|
+
return OpenVINOKerasTensor(result)
|
|
1558
1818
|
|
|
1559
1819
|
|
|
1560
1820
|
def logical_and(x1, x2):
|
|
@@ -1829,6 +2089,10 @@ def moveaxis(x, source, destination):
|
|
|
1829
2089
|
return OpenVINOKerasTensor(ov_opset.transpose(x, axes_const).output(0))
|
|
1830
2090
|
|
|
1831
2091
|
|
|
2092
|
+
def nansum(x, axis=None, keepdims=False):
|
|
2093
|
+
raise NotImplementedError("`nansum` is not supported with openvino backend")
|
|
2094
|
+
|
|
2095
|
+
|
|
1832
2096
|
def nan_to_num(x, nan=0.0, posinf=None, neginf=None):
|
|
1833
2097
|
x = get_ov_output(x)
|
|
1834
2098
|
dtype = x.get_element_type()
|
|
@@ -1979,6 +2243,10 @@ def prod(x, axis=None, keepdims=False, dtype=None):
|
|
|
1979
2243
|
return OpenVINOKerasTensor(result)
|
|
1980
2244
|
|
|
1981
2245
|
|
|
2246
|
+
def ptp(x, axis=None, keepdims=False):
|
|
2247
|
+
raise NotImplementedError("`ptp` is not supported with openvino backend")
|
|
2248
|
+
|
|
2249
|
+
|
|
1982
2250
|
def quantile(x, q, axis=None, method="linear", keepdims=False):
|
|
1983
2251
|
raise NotImplementedError(
|
|
1984
2252
|
"`quantile` is not supported with openvino backend"
|
|
@@ -2115,7 +2383,14 @@ def sinh(x):
|
|
|
2115
2383
|
|
|
2116
2384
|
|
|
2117
2385
|
def size(x):
|
|
2118
|
-
|
|
2386
|
+
x = get_ov_output(x)
|
|
2387
|
+
shape_tensor = ov_opset.shape_of(x, output_type=Type.i64)
|
|
2388
|
+
final_size = ov_opset.reduce_prod(
|
|
2389
|
+
shape_tensor,
|
|
2390
|
+
ov_opset.constant([0], Type.i64),
|
|
2391
|
+
keep_dims=False,
|
|
2392
|
+
)
|
|
2393
|
+
return OpenVINOKerasTensor(final_size.output(0))
|
|
2119
2394
|
|
|
2120
2395
|
|
|
2121
2396
|
def sort(x, axis=-1):
|
|
@@ -2257,9 +2532,20 @@ def std(x, axis=None, keepdims=False):
|
|
|
2257
2532
|
|
|
2258
2533
|
|
|
2259
2534
|
def swapaxes(x, axis1, axis2):
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2535
|
+
x = get_ov_output(x)
|
|
2536
|
+
x_shape = x.get_partial_shape()
|
|
2537
|
+
if x_shape.rank.is_dynamic:
|
|
2538
|
+
raise ValueError(
|
|
2539
|
+
"`swapaxes` does not support tensors with dynamic rank for the "
|
|
2540
|
+
"OpenVINO backend."
|
|
2541
|
+
)
|
|
2542
|
+
rank = x_shape.rank.get_length()
|
|
2543
|
+
axis1 = canonicalize_axis(axis1, rank)
|
|
2544
|
+
axis2 = canonicalize_axis(axis2, rank)
|
|
2545
|
+
axes = list(range(rank))
|
|
2546
|
+
axes[axis1], axes[axis2] = axes[axis2], axes[axis1]
|
|
2547
|
+
result = ov_opset.transpose(x, ov_opset.constant(axes, Type.i32))
|
|
2548
|
+
return OpenVINOKerasTensor(result.output(0))
|
|
2263
2549
|
|
|
2264
2550
|
|
|
2265
2551
|
def take(x, indices, axis=None):
|
|
@@ -2378,7 +2664,8 @@ def tile(x, repeats):
|
|
|
2378
2664
|
|
|
2379
2665
|
|
|
2380
2666
|
def trace(x, offset=0, axis1=0, axis2=1):
|
|
2381
|
-
|
|
2667
|
+
x = diagonal(x, offset=offset, axis1=axis1, axis2=axis2)
|
|
2668
|
+
return sum(x, axis=-1)
|
|
2382
2669
|
|
|
2383
2670
|
|
|
2384
2671
|
def tri(N, M=None, k=0, dtype=None):
|
|
@@ -2572,6 +2859,12 @@ def negative(x):
|
|
|
2572
2859
|
return OpenVINOKerasTensor(ov_opset.negative(x).output(0))
|
|
2573
2860
|
|
|
2574
2861
|
|
|
2862
|
+
def nextafter(x1, x2):
|
|
2863
|
+
raise NotImplementedError(
|
|
2864
|
+
"`nextafter` is not supported with openvino backend"
|
|
2865
|
+
)
|
|
2866
|
+
|
|
2867
|
+
|
|
2575
2868
|
def square(x):
|
|
2576
2869
|
x = get_ov_output(x)
|
|
2577
2870
|
x_type = x.get_element_type()
|
|
@@ -2905,6 +3198,66 @@ def slogdet(x):
|
|
|
2905
3198
|
|
|
2906
3199
|
|
|
2907
3200
|
def argpartition(x, kth, axis=-1):
|
|
2908
|
-
|
|
2909
|
-
|
|
3201
|
+
x = get_ov_output(x)
|
|
3202
|
+
x_shape = x.get_partial_shape()
|
|
3203
|
+
rank = x_shape.rank.get_length()
|
|
3204
|
+
axis = canonicalize_axis(axis, rank)
|
|
3205
|
+
axes = list(range(rank))
|
|
3206
|
+
axes[axis], axes[-1] = axes[-1], axes[axis]
|
|
3207
|
+
x = ov_opset.transpose(x, ov_opset.constant(axes))
|
|
3208
|
+
x_shape_tensor = ov_opset.shape_of(x)
|
|
3209
|
+
n = ov_opset.gather(
|
|
3210
|
+
x_shape_tensor,
|
|
3211
|
+
ov_opset.constant(-1),
|
|
3212
|
+
ov_opset.constant(0),
|
|
3213
|
+
)
|
|
3214
|
+
if isinstance(kth, int) and kth < 0:
|
|
3215
|
+
kth_tensor = ov_opset.add(
|
|
3216
|
+
n,
|
|
3217
|
+
ov_opset.constant(kth, n.get_element_type()),
|
|
3218
|
+
)
|
|
3219
|
+
else:
|
|
3220
|
+
kth_tensor = ov_opset.constant(kth, n.get_element_type())
|
|
3221
|
+
one = ov_opset.constant(1, kth_tensor.get_element_type())
|
|
3222
|
+
k_val = ov_opset.add(kth_tensor, one)
|
|
3223
|
+
bottom_ind = ov_opset.topk(
|
|
3224
|
+
ov_opset.negative(x),
|
|
3225
|
+
k=k_val,
|
|
3226
|
+
axis=-1,
|
|
3227
|
+
mode="max",
|
|
3228
|
+
sort="value",
|
|
3229
|
+
).output(1)
|
|
3230
|
+
one_hot_mask = ov_opset.one_hot(
|
|
3231
|
+
bottom_ind,
|
|
3232
|
+
n,
|
|
3233
|
+
ov_opset.constant(1),
|
|
3234
|
+
ov_opset.constant(0),
|
|
3235
|
+
axis=-1,
|
|
3236
|
+
)
|
|
3237
|
+
mask = ov_opset.reduce_sum(
|
|
3238
|
+
one_hot_mask,
|
|
3239
|
+
ov_opset.constant([-2]),
|
|
3240
|
+
keep_dims=False,
|
|
2910
3241
|
)
|
|
3242
|
+
ones = ov_opset.broadcast(
|
|
3243
|
+
ov_opset.constant(1),
|
|
3244
|
+
x_shape_tensor,
|
|
3245
|
+
)
|
|
3246
|
+
proxy = ov_opset.subtract(ones, mask)
|
|
3247
|
+
remaining_k = ov_opset.subtract(n, k_val)
|
|
3248
|
+
top_ind = ov_opset.topk(
|
|
3249
|
+
proxy,
|
|
3250
|
+
k=remaining_k,
|
|
3251
|
+
axis=-1,
|
|
3252
|
+
mode="max",
|
|
3253
|
+
sort="value",
|
|
3254
|
+
).output(1)
|
|
3255
|
+
result = ov_opset.concat([bottom_ind, top_ind], axis=-1)
|
|
3256
|
+
inv_axes = [0] * rank
|
|
3257
|
+
for i, a in enumerate(axes):
|
|
3258
|
+
inv_axes[a] = i
|
|
3259
|
+
result = ov_opset.transpose(
|
|
3260
|
+
result,
|
|
3261
|
+
ov_opset.constant(inv_axes),
|
|
3262
|
+
).output(0)
|
|
3263
|
+
return OpenVINOKerasTensor(result)
|
|
@@ -2125,6 +2125,22 @@ def moveaxis(x, source, destination):
|
|
|
2125
2125
|
return tf.transpose(x, perm)
|
|
2126
2126
|
|
|
2127
2127
|
|
|
2128
|
+
def nansum(x, axis=None, keepdims=False):
|
|
2129
|
+
x = convert_to_tensor(x)
|
|
2130
|
+
dtype = standardize_dtype(x.dtype)
|
|
2131
|
+
x_clean = tf.where(
|
|
2132
|
+
tf.math.is_nan(cast(x, config.floatx())), tf.zeros((), dtype=dtype), x
|
|
2133
|
+
)
|
|
2134
|
+
|
|
2135
|
+
if dtype in ("bool", "int8", "int16"):
|
|
2136
|
+
dtype = "int32"
|
|
2137
|
+
elif dtype in ("uint8", "uint16"):
|
|
2138
|
+
dtype = "uint32"
|
|
2139
|
+
x_clean = cast(x_clean, dtype)
|
|
2140
|
+
|
|
2141
|
+
return tf.reduce_sum(x_clean, axis=axis, keepdims=keepdims)
|
|
2142
|
+
|
|
2143
|
+
|
|
2128
2144
|
def nan_to_num(x, nan=0.0, posinf=None, neginf=None):
|
|
2129
2145
|
x = convert_to_tensor(x)
|
|
2130
2146
|
|
|
@@ -2151,7 +2167,7 @@ def nan_to_num(x, nan=0.0, posinf=None, neginf=None):
|
|
|
2151
2167
|
|
|
2152
2168
|
def ndim(x):
|
|
2153
2169
|
x = convert_to_tensor(x)
|
|
2154
|
-
return x.
|
|
2170
|
+
return x.shape.rank
|
|
2155
2171
|
|
|
2156
2172
|
|
|
2157
2173
|
def nonzero(x):
|
|
@@ -2215,6 +2231,13 @@ def prod(x, axis=None, keepdims=False, dtype=None):
|
|
|
2215
2231
|
return tf.reduce_prod(x, axis=axis, keepdims=keepdims)
|
|
2216
2232
|
|
|
2217
2233
|
|
|
2234
|
+
def ptp(x, axis=None, keepdims=False):
|
|
2235
|
+
x = convert_to_tensor(x)
|
|
2236
|
+
return tf.reduce_max(x, axis=axis, keepdims=keepdims) - tf.reduce_min(
|
|
2237
|
+
x, axis=axis, keepdims=keepdims
|
|
2238
|
+
)
|
|
2239
|
+
|
|
2240
|
+
|
|
2218
2241
|
def _quantile(x, q, axis=None, method="linear", keepdims=False):
|
|
2219
2242
|
# ref: tfp.stats.percentile
|
|
2220
2243
|
# float64 is needed here and below, else we get the wrong index if the array
|
|
@@ -3017,6 +3040,16 @@ def negative(x):
|
|
|
3017
3040
|
return tf.negative(x)
|
|
3018
3041
|
|
|
3019
3042
|
|
|
3043
|
+
def nextafter(x1, x2):
|
|
3044
|
+
x1 = convert_to_tensor(x1)
|
|
3045
|
+
x2 = convert_to_tensor(x2)
|
|
3046
|
+
|
|
3047
|
+
dtype = dtypes.result_type(x1.dtype, x2.dtype, float)
|
|
3048
|
+
x1 = tf.cast(x1, tf.float64)
|
|
3049
|
+
x2 = tf.cast(x2, tf.float64)
|
|
3050
|
+
return tf.cast(tf.math.nextafter(x1, x2), dtype)
|
|
3051
|
+
|
|
3052
|
+
|
|
3020
3053
|
@sparse.elementwise_unary
|
|
3021
3054
|
def square(x):
|
|
3022
3055
|
x = convert_to_tensor(x)
|
|
@@ -539,11 +539,21 @@ def _do_lstm_arguments_support_cudnn(
|
|
|
539
539
|
|
|
540
540
|
|
|
541
541
|
def _has_fully_masked_sequence(mask):
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
542
|
+
"""Check if input sequence contains any fully masked data.
|
|
543
|
+
|
|
544
|
+
cuDNN kernel will error out if the input sequence contains any fully masked
|
|
545
|
+
data. We work around this issue by rerouting the computation to the
|
|
546
|
+
standard kernel until the issue on the cuDNN side has been fixed. For a
|
|
547
|
+
fully masked sequence, it will contain all `False` values. To make it easy
|
|
548
|
+
to check, we invert the boolean and check if any of the sequences has all
|
|
549
|
+
`True` values.
|
|
550
|
+
|
|
551
|
+
Args:
|
|
552
|
+
mask: The mask tensor.
|
|
553
|
+
|
|
554
|
+
Returns:
|
|
555
|
+
A boolean tensor, `True` if the mask contains a fully masked sequence.
|
|
556
|
+
"""
|
|
547
557
|
return tf.reduce_any(
|
|
548
558
|
tf.reduce_all(tf.logical_not(tf.cast(mask, dtype="bool")), axis=1)
|
|
549
559
|
)
|
|
@@ -900,8 +910,8 @@ def _cudnn_lstm(
|
|
|
900
910
|
|
|
901
911
|
if tf.sysconfig.get_build_info()["is_rocm_build"]:
|
|
902
912
|
# ROCm MIOpen's weight sequence for LSTM is different from both
|
|
903
|
-
# canonical and
|
|
904
|
-
# MIOpen: [i, f, o, c]
|
|
913
|
+
# canonical and cuDNN format
|
|
914
|
+
# MIOpen: [i, f, o, c] cuDNN/Canonical: [i, f, c, o]
|
|
905
915
|
# i is input gate weights.
|
|
906
916
|
# f is forget gate weights.
|
|
907
917
|
# o is output gate weights.
|
keras/src/backend/torch/numpy.py
CHANGED
|
@@ -1272,6 +1272,20 @@ def moveaxis(x, source, destination):
|
|
|
1272
1272
|
return torch.moveaxis(x, source=source, destination=destination)
|
|
1273
1273
|
|
|
1274
1274
|
|
|
1275
|
+
def nansum(x, axis=None, keepdims=False):
|
|
1276
|
+
if isinstance(x, (list, tuple)):
|
|
1277
|
+
x = stack(x)
|
|
1278
|
+
x = convert_to_tensor(x)
|
|
1279
|
+
dtype = standardize_dtype(x.dtype)
|
|
1280
|
+
|
|
1281
|
+
if dtype in ("bool", "uint8", "int8", "int16"):
|
|
1282
|
+
dtype = "int32"
|
|
1283
|
+
|
|
1284
|
+
if axis == () or axis == []:
|
|
1285
|
+
return cast(torch.nan_to_num(x, nan=0), dtype)
|
|
1286
|
+
return cast(torch.nansum(x, dim=axis, keepdim=keepdims), dtype)
|
|
1287
|
+
|
|
1288
|
+
|
|
1275
1289
|
def nan_to_num(x, nan=0.0, posinf=None, neginf=None):
|
|
1276
1290
|
x = convert_to_tensor(x)
|
|
1277
1291
|
return torch.nan_to_num(x, nan=nan, posinf=posinf, neginf=neginf)
|
|
@@ -1382,6 +1396,18 @@ def prod(x, axis=None, keepdims=False, dtype=None):
|
|
|
1382
1396
|
return x
|
|
1383
1397
|
|
|
1384
1398
|
|
|
1399
|
+
def ptp(x, axis=None, keepdims=False):
|
|
1400
|
+
x = convert_to_tensor(x)
|
|
1401
|
+
if axis is None:
|
|
1402
|
+
return x.max() - x.min()
|
|
1403
|
+
elif axis == ():
|
|
1404
|
+
return torch.zeros_like(x)
|
|
1405
|
+
else:
|
|
1406
|
+
return torch.amax(x, dim=axis, keepdim=keepdims) - torch.amin(
|
|
1407
|
+
x, dim=axis, keepdim=keepdims
|
|
1408
|
+
)
|
|
1409
|
+
|
|
1410
|
+
|
|
1385
1411
|
def quantile(x, q, axis=None, method="linear", keepdims=False):
|
|
1386
1412
|
x = convert_to_tensor(x)
|
|
1387
1413
|
q = convert_to_tensor(q)
|
|
@@ -1793,6 +1819,16 @@ def negative(x):
|
|
|
1793
1819
|
return torch.negative(x)
|
|
1794
1820
|
|
|
1795
1821
|
|
|
1822
|
+
def nextafter(x1, x2):
|
|
1823
|
+
x1 = convert_to_tensor(x1)
|
|
1824
|
+
x2 = convert_to_tensor(x2)
|
|
1825
|
+
|
|
1826
|
+
dtype = dtypes.result_type(x1.dtype, x2.dtype, float)
|
|
1827
|
+
x1 = cast(x1, torch.float64)
|
|
1828
|
+
x2 = cast(x2, torch.float64)
|
|
1829
|
+
return cast(torch.nextafter(x1, x2), dtype)
|
|
1830
|
+
|
|
1831
|
+
|
|
1796
1832
|
def square(x):
|
|
1797
1833
|
x = convert_to_tensor(x)
|
|
1798
1834
|
if standardize_dtype(x.dtype) == "bool":
|
keras/src/backend/torch/rnn.py
CHANGED
|
@@ -413,11 +413,21 @@ def _is_sequence_right_padded(mask):
|
|
|
413
413
|
|
|
414
414
|
|
|
415
415
|
def _has_fully_masked_sequence(mask):
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
416
|
+
"""Check if input sequence contains any fully masked data.
|
|
417
|
+
|
|
418
|
+
cuDNN kernel will error out if the input sequence contains any fully masked
|
|
419
|
+
data. We work around this issue by rerouting the computation to the
|
|
420
|
+
standard kernel until the issue on the cuDNN side has been fixed. For a
|
|
421
|
+
fully masked sequence, it will contain all `False` values. To make it easy
|
|
422
|
+
to check, we invert the boolean and check if any of the sequences has all
|
|
423
|
+
`True` values.
|
|
424
|
+
|
|
425
|
+
Args:
|
|
426
|
+
mask: The mask tensor.
|
|
427
|
+
|
|
428
|
+
Returns:
|
|
429
|
+
A boolean tensor, `True` if the mask contains a fully masked sequence.
|
|
430
|
+
"""
|
|
421
431
|
return torch.any(torch.all(~mask, dim=1))
|
|
422
432
|
|
|
423
433
|
|
|
@@ -447,8 +457,8 @@ def _compute_sequence_length_from_mask(mask, batch_first):
|
|
|
447
457
|
The masking tensor is a 2D boolean tensor with shape [batch, timestep]. For
|
|
448
458
|
any timestep that should be masked, the corresponding field will be False.
|
|
449
459
|
Consider the following example:
|
|
450
|
-
|
|
451
|
-
|
|
460
|
+
a = [[True, True, False, False]
|
|
461
|
+
[True, True, True, False]]
|
|
452
462
|
It is a (2, 4) tensor, and the corresponding sequence length result should
|
|
453
463
|
be 1D tensor with value [2, 3]. Note that the masking tensor must be right
|
|
454
464
|
padded that could be checked by, e.g., `is_sequence_right_padded()`.
|
|
@@ -467,12 +477,19 @@ def _compute_sequence_length_from_mask(mask, batch_first):
|
|
|
467
477
|
|
|
468
478
|
|
|
469
479
|
def prepare_lstm_weights(lstm, kernel, recurrent_kernel, bias, device):
|
|
470
|
-
"""Copies kernel and recurrent kernel weights
|
|
480
|
+
"""Copies kernel and recurrent kernel weights into the PyTorch format.
|
|
481
|
+
|
|
471
482
|
We split the kernel and recurrent kernel weights, create associated
|
|
472
|
-
torch tensors adapted to be in line with the
|
|
473
|
-
After we have copied the weights, we ensure the
|
|
474
|
-
the same device and memory layout is optimized for
|
|
483
|
+
torch tensors adapted to be in line with the cuDNN optimization.
|
|
484
|
+
After we have copied the weights, we ensure the parameters are on
|
|
485
|
+
the same device and memory layout is optimized for cuDNN.
|
|
475
486
|
|
|
487
|
+
Args:
|
|
488
|
+
lstm: The PyTorch LSTM layer to prepare weights for.
|
|
489
|
+
kernel: The kernel weights tensor.
|
|
490
|
+
recurrent_kernel: The recurrent kernel weights tensor.
|
|
491
|
+
bias: The bias tensor.
|
|
492
|
+
device: The device to place the tensors on.
|
|
476
493
|
"""
|
|
477
494
|
|
|
478
495
|
lstm = lstm.to(device)
|