Skip to content

Commit 0621090

Browse files
authored
Merge branch 'keras-team:master' into master
2 parents 127418d + 4f85917 commit 0621090

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2959
-280
lines changed

keras/api/_tf_keras/keras/ops/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@
198198
from keras.src.ops.numpy import full as full
199199
from keras.src.ops.numpy import full_like as full_like
200200
from keras.src.ops.numpy import gcd as gcd
201+
from keras.src.ops.numpy import geomspace as geomspace
201202
from keras.src.ops.numpy import get_item as get_item
202203
from keras.src.ops.numpy import greater as greater
203204
from keras.src.ops.numpy import greater_equal as greater_equal
@@ -250,10 +251,12 @@
250251
from keras.src.ops.numpy import moveaxis as moveaxis
251252
from keras.src.ops.numpy import multiply as multiply
252253
from keras.src.ops.numpy import nan_to_num as nan_to_num
254+
from keras.src.ops.numpy import nancumsum as nancumsum
253255
from keras.src.ops.numpy import nanmax as nanmax
254256
from keras.src.ops.numpy import nanmean as nanmean
255257
from keras.src.ops.numpy import nanmin as nanmin
256258
from keras.src.ops.numpy import nanprod as nanprod
259+
from keras.src.ops.numpy import nanstd as nanstd
257260
from keras.src.ops.numpy import nansum as nansum
258261
from keras.src.ops.numpy import nanvar as nanvar
259262
from keras.src.ops.numpy import ndim as ndim

keras/api/_tf_keras/keras/ops/numpy/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
from keras.src.ops.numpy import full as full
8181
from keras.src.ops.numpy import full_like as full_like
8282
from keras.src.ops.numpy import gcd as gcd
83+
from keras.src.ops.numpy import geomspace as geomspace
8384
from keras.src.ops.numpy import get_item as get_item
8485
from keras.src.ops.numpy import greater as greater
8586
from keras.src.ops.numpy import greater_equal as greater_equal
@@ -132,10 +133,12 @@
132133
from keras.src.ops.numpy import moveaxis as moveaxis
133134
from keras.src.ops.numpy import multiply as multiply
134135
from keras.src.ops.numpy import nan_to_num as nan_to_num
136+
from keras.src.ops.numpy import nancumsum as nancumsum
135137
from keras.src.ops.numpy import nanmax as nanmax
136138
from keras.src.ops.numpy import nanmean as nanmean
137139
from keras.src.ops.numpy import nanmin as nanmin
138140
from keras.src.ops.numpy import nanprod as nanprod
141+
from keras.src.ops.numpy import nanstd as nanstd
139142
from keras.src.ops.numpy import nansum as nansum
140143
from keras.src.ops.numpy import nanvar as nanvar
141144
from keras.src.ops.numpy import ndim as ndim

keras/api/ops/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@
198198
from keras.src.ops.numpy import full as full
199199
from keras.src.ops.numpy import full_like as full_like
200200
from keras.src.ops.numpy import gcd as gcd
201+
from keras.src.ops.numpy import geomspace as geomspace
201202
from keras.src.ops.numpy import get_item as get_item
202203
from keras.src.ops.numpy import greater as greater
203204
from keras.src.ops.numpy import greater_equal as greater_equal
@@ -250,10 +251,12 @@
250251
from keras.src.ops.numpy import moveaxis as moveaxis
251252
from keras.src.ops.numpy import multiply as multiply
252253
from keras.src.ops.numpy import nan_to_num as nan_to_num
254+
from keras.src.ops.numpy import nancumsum as nancumsum
253255
from keras.src.ops.numpy import nanmax as nanmax
254256
from keras.src.ops.numpy import nanmean as nanmean
255257
from keras.src.ops.numpy import nanmin as nanmin
256258
from keras.src.ops.numpy import nanprod as nanprod
259+
from keras.src.ops.numpy import nanstd as nanstd
257260
from keras.src.ops.numpy import nansum as nansum
258261
from keras.src.ops.numpy import nanvar as nanvar
259262
from keras.src.ops.numpy import ndim as ndim

keras/api/ops/numpy/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
from keras.src.ops.numpy import full as full
8181
from keras.src.ops.numpy import full_like as full_like
8282
from keras.src.ops.numpy import gcd as gcd
83+
from keras.src.ops.numpy import geomspace as geomspace
8384
from keras.src.ops.numpy import get_item as get_item
8485
from keras.src.ops.numpy import greater as greater
8586
from keras.src.ops.numpy import greater_equal as greater_equal
@@ -132,10 +133,12 @@
132133
from keras.src.ops.numpy import moveaxis as moveaxis
133134
from keras.src.ops.numpy import multiply as multiply
134135
from keras.src.ops.numpy import nan_to_num as nan_to_num
136+
from keras.src.ops.numpy import nancumsum as nancumsum
135137
from keras.src.ops.numpy import nanmax as nanmax
136138
from keras.src.ops.numpy import nanmean as nanmean
137139
from keras.src.ops.numpy import nanmin as nanmin
138140
from keras.src.ops.numpy import nanprod as nanprod
141+
from keras.src.ops.numpy import nanstd as nanstd
139142
from keras.src.ops.numpy import nansum as nansum
140143
from keras.src.ops.numpy import nanvar as nanvar
141144
from keras.src.ops.numpy import ndim as ndim

keras/src/backend/jax/numpy.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,12 @@ def gcd(x1, x2):
774774
return jnp.gcd(x1, x2)
775775

776776

777+
def geomspace(start, stop, num=50, endpoint=True, dtype=None, axis=0):
778+
return jnp.geomspace(
779+
start, stop, num=num, endpoint=endpoint, dtype=dtype, axis=axis
780+
)
781+
782+
777783
def greater(x1, x2):
778784
x1 = convert_to_tensor(x1)
779785
x2 = convert_to_tensor(x2)
@@ -1037,6 +1043,11 @@ def moveaxis(x, source, destination):
10371043
return jnp.moveaxis(x, source=source, destination=destination)
10381044

10391045

1046+
def nancumsum(x, axis=None, dtype=None):
1047+
x = convert_to_tensor(x)
1048+
return jnp.nancumsum(x, axis=axis, dtype=dtype)
1049+
1050+
10401051
def nanmax(x, axis=None, keepdims=False):
10411052
x = convert_to_tensor(x)
10421053
return jnp.nanmax(x, axis=axis, keepdims=keepdims)
@@ -1057,6 +1068,11 @@ def nanprod(x, axis=None, keepdims=False):
10571068
return jnp.nanprod(x, axis=axis, keepdims=keepdims)
10581069

10591070

1071+
def nanstd(x, axis=None, keepdims=False):
1072+
x = convert_to_tensor(x)
1073+
return jnp.nanstd(x, axis=axis, keepdims=keepdims)
1074+
1075+
10601076
def nansum(x, axis=None, keepdims=False):
10611077
x = convert_to_tensor(x)
10621078
return jnp.nansum(x, axis=axis, keepdims=keepdims)

keras/src/backend/numpy/numpy.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,13 @@ def gcd(x1, x2):
696696
return np.gcd(x1, x2).astype(dtype)
697697

698698

699+
def geomspace(start, stop, num=50, endpoint=True, dtype=None, axis=0):
700+
dtype = dtype or config.floatx()
701+
return np.geomspace(
702+
start, stop, num=num, endpoint=endpoint, dtype=dtype, axis=axis
703+
)
704+
705+
699706
def greater(x1, x2):
700707
return np.greater(x1, x2)
701708

@@ -979,6 +986,14 @@ def moveaxis(x, source, destination):
979986
return np.moveaxis(x, source=source, destination=destination)
980987

981988

989+
def nancumsum(x, axis=None, dtype=None):
990+
axis = standardize_axis_for_numpy(axis)
991+
dtype = dtypes.result_type(dtype or x.dtype)
992+
if dtype == "bool":
993+
dtype = "int32"
994+
return np.nancumsum(x, axis=axis, dtype=dtype)
995+
996+
982997
def nanmax(x, axis=None, keepdims=False):
983998
return np.nanmax(x, axis=axis, keepdims=keepdims)
984999

@@ -1005,6 +1020,16 @@ def nanprod(x, axis=None, keepdims=False):
10051020
return np.nanprod(x, axis=axis, keepdims=keepdims, dtype=dtype)
10061021

10071022

1023+
def nanstd(x, axis=None, keepdims=False):
1024+
axis = standardize_axis_for_numpy(axis)
1025+
x = convert_to_tensor(x)
1026+
compute_dtype = dtypes.result_type(x.dtype, "float32")
1027+
result_dtype = dtypes.result_type(x.dtype, float)
1028+
return np.nanstd(
1029+
x, axis=axis, keepdims=keepdims, dtype=compute_dtype
1030+
).astype(result_dtype)
1031+
1032+
10081033
def nansum(x, axis=None, keepdims=False):
10091034
axis = standardize_axis_for_numpy(axis)
10101035
dtype = standardize_dtype(x.dtype)

keras/src/backend/openvino/core.py

Lines changed: 171 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,10 @@ def __init__(self, x, data=None):
151151
x_type = x.get_element_type()
152152
x_keras_type = ov_to_keras_type(x_type)
153153
self.output = x
154-
self.shape = tuple(x_keras_shape)
154+
if x_keras_shape is not None:
155+
self.shape = tuple(x_keras_shape)
156+
else:
157+
self.shape = None
155158
self.dtype = x_keras_type
156159
self.ndim = None
157160
self.data = data
@@ -496,6 +499,9 @@ def __len__(self):
496499
assert ov_shape[0].is_static, "the first dimension must be static"
497500
return ov_shape[0].get_length()
498501

502+
def __bool__(self):
503+
return bool(self.numpy())
504+
499505
def __mod__(self, other):
500506
first = self.output
501507
other = get_ov_output(other)
@@ -519,6 +525,138 @@ def __array__(self, dtype=None):
519525
def numpy(self):
520526
return self.__array__()
521527

528+
def __rmod__(self, other):
529+
other = get_ov_output(other)
530+
first = self.output
531+
other, first = align_operand_types(
532+
other, first, "OpenVINOKerasTensor::__rmod__"
533+
)
534+
return OpenVINOKerasTensor(ov_opset.mod(other, first).output(0))
535+
536+
def __matmul__(self, other):
537+
first = self.output
538+
other = get_ov_output(other)
539+
first, other = align_operand_types(
540+
first, other, "OpenVINOKerasTensor::__matmul__"
541+
)
542+
return OpenVINOKerasTensor(
543+
ov_opset.matmul(first, other, False, False).output(0)
544+
)
545+
546+
def __rmatmul__(self, other):
547+
other = get_ov_output(other)
548+
first = self.output
549+
other, first = align_operand_types(
550+
other, first, "OpenVINOKerasTensor::__rmatmul__"
551+
)
552+
return OpenVINOKerasTensor(
553+
ov_opset.matmul(other, first, False, False).output(0)
554+
)
555+
556+
def __div__(self, other):
557+
return self.__truediv__(other)
558+
559+
def __rdiv__(self, other):
560+
return self.__rtruediv__(other)
561+
562+
def __and__(self, other):
563+
first = self.output
564+
other = get_ov_output(other)
565+
first, other = align_operand_types(
566+
first, other, "OpenVINOKerasTensor::__and__"
567+
)
568+
return OpenVINOKerasTensor(ov_opset.logical_and(first, other).output(0))
569+
570+
def __rand__(self, other):
571+
other = get_ov_output(other)
572+
first = self.output
573+
other, first = align_operand_types(
574+
other, first, "OpenVINOKerasTensor::__rand__"
575+
)
576+
return OpenVINOKerasTensor(ov_opset.logical_and(other, first).output(0))
577+
578+
def __or__(self, other):
579+
first = self.output
580+
other = get_ov_output(other)
581+
first, other = align_operand_types(
582+
first, other, "OpenVINOKerasTensor::__or__"
583+
)
584+
return OpenVINOKerasTensor(ov_opset.logical_or(first, other).output(0))
585+
586+
def __ror__(self, other):
587+
other = get_ov_output(other)
588+
first = self.output
589+
other, first = align_operand_types(
590+
other, first, "OpenVINOKerasTensor::__ror__"
591+
)
592+
return OpenVINOKerasTensor(ov_opset.logical_or(other, first).output(0))
593+
594+
def __xor__(self, other):
595+
first = self.output
596+
other = get_ov_output(other)
597+
first, other = align_operand_types(
598+
first, other, "OpenVINOKerasTensor::__xor__"
599+
)
600+
return OpenVINOKerasTensor(ov_opset.logical_xor(first, other).output(0))
601+
602+
def __rxor__(self, other):
603+
other = get_ov_output(other)
604+
first = self.output
605+
other, first = align_operand_types(
606+
other, first, "OpenVINOKerasTensor::__rxor__"
607+
)
608+
return OpenVINOKerasTensor(ov_opset.logical_xor(other, first).output(0))
609+
610+
def __int__(self):
611+
arr = self.output.get_node().data
612+
if arr.ndim > 0:
613+
raise TypeError(
614+
"Only scalar arrays can be converted to Python scalars. "
615+
f"Got: shape={arr.shape}"
616+
)
617+
return int(arr)
618+
619+
def __float__(self):
620+
arr = self.output.get_node().data
621+
if arr.ndim > 0:
622+
raise TypeError(
623+
"Only scalar arrays can be converted to Python scalars. "
624+
f"Got: shape={arr.shape}"
625+
)
626+
return float(arr)
627+
628+
def __repr__(self):
629+
return f"<OpenVINOKerasTensor shape={self.shape}, dtype={self.dtype}>"
630+
631+
def __round__(self, ndigits=None):
632+
first = self.output
633+
decimals = ndigits or 0
634+
if decimals == 0:
635+
result = ov_opset.round(first, "half_to_even")
636+
else:
637+
factor = ov_opset.constant(10.0**decimals, first.get_element_type())
638+
scaled = ov_opset.multiply(first, factor)
639+
rounded = ov_opset.round(scaled, "half_to_even")
640+
result = ov_opset.divide(rounded, factor)
641+
return OpenVINOKerasTensor(result.output(0))
642+
643+
def reshape(self, new_shape):
644+
first = self.output
645+
shape_const = get_ov_output(new_shape)
646+
return OpenVINOKerasTensor(
647+
ov_opset.reshape(first, shape_const, False).output(0)
648+
)
649+
650+
def squeeze(self, axis=None):
651+
first = self.output
652+
if axis is not None:
653+
axes = get_ov_output([axis] if isinstance(axis, int) else axis)
654+
else:
655+
axes = get_ov_output(
656+
[i for i, d in enumerate(self.shape) if d == 1]
657+
)
658+
return OpenVINOKerasTensor(ov_opset.squeeze(first, axes).output(0))
659+
522660

523661
def ov_to_keras_type(ov_type):
524662
for _keras_type, _ov_type in OPENVINO_DTYPES.items():
@@ -765,15 +903,41 @@ def scan(f, init, xs=None, length=None, reverse=False, unroll=1):
765903

766904

767905
def scatter(indices, values, shape):
768-
raise NotImplementedError(
769-
"`scatter` is not supported with openvino backend"
770-
)
906+
indices = get_ov_output(indices)
907+
values = get_ov_output(values)
908+
909+
# Create a zeros tensor of the target shape.
910+
shape = get_ov_output(shape)
911+
zero_const = ov_opset.constant(0, values.get_element_type())
912+
zeros = ov_opset.broadcast(zero_const, shape).output(0)
913+
914+
return scatter_update(zeros, indices, values, "add")
771915

772916

773917
def scatter_update(inputs, indices, updates, reduction=None):
774-
raise NotImplementedError(
775-
"`scatter_update` is not supported with openvino backend"
776-
)
918+
inputs = get_ov_output(inputs)
919+
indices = get_ov_output(indices)
920+
updates = get_ov_output(updates)
921+
922+
inputs, updates = align_operand_types(inputs, updates, "scatter_update")
923+
924+
# Map Keras reduction to OpenVINO ScatterNDUpdate reduction.
925+
# OpenVINO Opset 15 supports: "none", "sum", "sub", "prod", "min", "max".
926+
if reduction is None:
927+
ov_reduction = "none"
928+
elif reduction == "add":
929+
ov_reduction = "sum"
930+
elif reduction == "mul":
931+
ov_reduction = "prod"
932+
elif reduction in ("max", "min"):
933+
ov_reduction = reduction
934+
else:
935+
raise ValueError(f"Unsupported reduction: {reduction}")
936+
937+
result = ov_opset.scatter_nd_update(
938+
inputs, indices, updates, reduction=ov_reduction
939+
).output(0)
940+
return OpenVINOKerasTensor(result)
777941

778942

779943
def slice(inputs, start_indices, shape):

0 commit comments

Comments
 (0)