@@ -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
523661def 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
767905def 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
773917def 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
779943def slice (inputs , start_indices , shape ):
0 commit comments