scatter scatterupdate slice sliceupdate whileloop stopgradient shape cast converttotensor converttonumpy cond istensor returns a tensor of shape shape where indices are set to values at a high level this operation does zerosindices updates and returns the output it is equivalent to python zeros keras ops zerosshape output keras ops scatterupdatezeros indices values args indices a tensor or listtuple specifying indices for the values in values values a tensor the values to be set at indices shape shape of the output tensor example indices 0 1 1 1 values np array1 1 keras ops scatterindices values shape2 2 array0 1 0 1 update inputs via updates at scattered sparse indices at a high level this operation does inputsindices updates assume inputs is a tensor of shape d0 d1 dn there are 2 main usages of scatterupdate 1 indices is a 2d tensor of shape numupdates n where numupdates is the number of updates to perform and updates is a 1d tensor of shape numupdates for example if inputs is zeros4 4 4 and we want to update inputs1 2 3 and inputs0 1 3 as 1 then we can use python inputs np zeros4 4 4 indices 1 2 3 0 1 3 updates np array1 1 inputs keras ops scatterupdateinputs indices updates 2 indices is a 2d tensor of shape numupdates k where numupdates is the number of updates to perform and k k n is the size of each index in indices updates is a n kd tensor of shape numupdates inputs shapek for example if inputs np zeros4 4 4 and we want to update inputs1 2 and inputs2 3 as 1 1 1 1 then indices would have shape numupdates 2 k 2 and updates would have shape numupdates 4 inputs shape2 4 see the code below python inputs np zeros4 4 4 indices 1 2 2 3 updates np array1 1 1 1 1 1 1 1 inputs keras ops scatterupdateinputs indices updates args inputs a tensor the tensor to be updated indices a tensor or listtuple of shape n inputs ndim specifying indices to update n is the number of indices to update must be equal to the first dimension of updates updates a tensor the new values to be put to inputs at indices returns a tensor has the same shape and dtype as inputs return a slice of an input tensor at a high level this operation is an explicit replacement for array slicing e g inputsstartindices startindices shape unlike slicing via brackets this operation will accept tensor start indices on all backends which is useful when indices dynamically computed via other tensor operations python inputs np zeros5 5 startindices np array3 3 shape np array2 2 inputs keras ops sliceinputs startindices updates args inputs a tensor the tensor to be updated startindices a listtuple of shape inputs ndim specifying the starting indices for updating shape the full shape of the returned slice returns a tensor has the same shape and dtype as inputs update an input by slicing in a tensor of updated values at a high level this operation does inputsstartindices startindices updates shape updates assume inputs is a tensor of shape d0 d1 dn startindices must be a listtuple of n integers specifying the starting indices updates must have the same rank as inputs and the size of each dim must not exceed di startindicesi for example if we have 2d inputs inputs np zeros5 5 and we want to update the intersection of last 2 rows and last 2 columns as 1 i e inputs3 3 np ones2 2 then we can use the code below python inputs np zeros5 5 startindices 3 3 updates np ones2 2 inputs keras ops sliceupdateinputs startindices updates args inputs a tensor the tensor to be updated startindices a listtuple of shape inputs ndim specifying the starting indices for updating updates a tensor the new values to be put to inputs at indices updates must have the same rank as inputs returns a tensor has the same shape and dtype as inputs while loop implementation args cond a callable that represents the termination condition of the loop must have the same number of args as loopvars and return a bool body a callable that represents the loop body must have the same number of args as loopvars and return a listtuple of the same length shape and dtype as loopvars loopvars a listtuple of tensors the loop variables maximumiterations optional maximum number of iterations of the while loop to run if provided the cond output is anded with an additional condition ensuring the number of iterations executed is no greater than maximumiterations returns a listtuple of tensors has the same shape and dtype as inputs examples i 0 cond lambda i i 10 body lambda i i 1 keras ops whileloopcond body i0 10 stops gradient computation args variable a tensor variable for which the gradient computation is to be disabled returns the variable with gradient computation disabled examples var keras backend converttotensor 1 2 3 dtypefloat32 var keras ops stopgradientvar for loop implementation args lower the initial value of the loop variable upper the upper bound of the loop variable bodyfun a callable that represents the loop body must take two arguments the loop variable and the loop state the loop state should be updated and returned by this function initval the initial value of the loop state returns the final state after the loop example lower 0 upper 10 bodyfun lambda i s i 1 s i initval 0 keras ops forilooplower upper bodyfun initval 45 unpacks the given dimension of a rankr tensor into rankr1 tensors args x the input tensor num the length of the dimension axis automatically inferred if none axis the axis along which to unpack returns a list of tensors unpacked along the given axis example x keras ops array1 2 3 4 keras ops unstackx axis0 array1 2 array3 4 gets the shape of the tensor input note on the tensorflow backend when x is a tf tensor with dynamic shape dimensions which are dynamic in the context of a compiled function will have a tf tensor value instead of a static integer value args x a tensor this function will try to access the shape attribute of the input tensor returns a tuple of integers or none values indicating the shape of the input tensor example x keras zeros8 12 keras ops shapex 8 12 cast a tensor to the desired dtype args x a tensor or variable dtype the target type returns a tensor of the specified dtype example x keras ops arange4 x keras ops castx dtypefloat16 convert a numpy array to a tensor args x a numpy array dtype the target type sparse whether to keep sparse tensors false will cause sparse tensors to be densified the default value of none means that sparse tensors are kept only if the backend supports them returns a tensor of the specified dtype example x np array1 2 3 y keras ops converttotensorx convert a tensor to a numpy array args x a tensor returns a numpy array this will raise a valueerror defined in the kerastensor class we trigger it rather than duplicate it here fallback on symbolic case wrap self call to provide helpful info in case of exception plain flow conditionally applies truefn or falsefn args pred boolean scalar type truefn callable returning the output for the pred true case falsefn callable returning the output for the pred false case returns the output of either truefn or falsefn depending on pred todo also create an op subclass vectorizedmap parallel map of function on axis 0 of tensors elements schematically vectorizedmap implements the following in the case of a single tensor input elements python def vectorizedmapfunction elements outputs for e in elements outputs appendfunctione return stackoutputs in the case of an iterable of tensors elements it implements the following python def vectorizedmapfunction elements batchsize elements0 shape0 outputs for index in rangebatchsize outputs appendfunctioneindex for e in elements return np stackoutputs in this case function is expected to take as input a single list of tensor arguments check whether the given object is a tensor note this checks for backend specific tensors so passing a tensorflow tensor would return false if your backend is pytorch or jax args x a variable returns true if x is a tensor otherwise false returns a tensor of shape shape where indices are set to values at a high level this operation does zeros indices updates and returns the output it is equivalent to python zeros keras ops zeros shape output keras ops scatter_update zeros indices values args indices a tensor or list tuple specifying indices for the values in values values a tensor the values to be set at indices shape shape of the output tensor example indices 0 1 1 1 values np array 1 1 keras ops scatter indices values shape 2 2 array 0 1 0 1 update inputs via updates at scattered sparse indices at a high level this operation does inputs indices updates assume inputs is a tensor of shape d0 d1 dn there are 2 main usages of scatter_update 1 indices is a 2d tensor of shape num_updates n where num_updates is the number of updates to perform and updates is a 1d tensor of shape num_updates for example if inputs is zeros 4 4 4 and we want to update inputs 1 2 3 and inputs 0 1 3 as 1 then we can use python inputs np zeros 4 4 4 indices 1 2 3 0 1 3 updates np array 1 1 inputs keras ops scatter_update inputs indices updates 2 indices is a 2d tensor of shape num_updates k where num_updates is the number of updates to perform and k k n is the size of each index in indices updates is a n k d tensor of shape num_updates inputs shape k for example if inputs np zeros 4 4 4 and we want to update inputs 1 2 and inputs 2 3 as 1 1 1 1 then indices would have shape num_updates 2 k 2 and updates would have shape num_updates 4 inputs shape 2 4 see the code below python inputs np zeros 4 4 4 indices 1 2 2 3 updates np array 1 1 1 1 1 1 1 1 inputs keras ops scatter_update inputs indices updates args inputs a tensor the tensor to be updated indices a tensor or list tuple of shape n inputs ndim specifying indices to update n is the number of indices to update must be equal to the first dimension of updates updates a tensor the new values to be put to inputs at indices returns a tensor has the same shape and dtype as inputs return a slice of an input tensor at a high level this operation is an explicit replacement for array slicing e g inputs start_indices start_indices shape unlike slicing via brackets this operation will accept tensor start indices on all backends which is useful when indices dynamically computed via other tensor operations python inputs np zeros 5 5 start_indices np array 3 3 shape np array 2 2 inputs keras ops slice inputs start_indices updates args inputs a tensor the tensor to be updated start_indices a list tuple of shape inputs ndim specifying the starting indices for updating shape the full shape of the returned slice returns a tensor has the same shape and dtype as inputs update an input by slicing in a tensor of updated values at a high level this operation does inputs start_indices start_indices updates shape updates assume inputs is a tensor of shape d0 d1 dn start_indices must be a list tuple of n integers specifying the starting indices updates must have the same rank as inputs and the size of each dim must not exceed di start_indices i for example if we have 2d inputs inputs np zeros 5 5 and we want to update the intersection of last 2 rows and last 2 columns as 1 i e inputs 3 3 np ones 2 2 then we can use the code below python inputs np zeros 5 5 start_indices 3 3 updates np ones 2 2 inputs keras ops slice_update inputs start_indices updates args inputs a tensor the tensor to be updated start_indices a list tuple of shape inputs ndim specifying the starting indices for updating updates a tensor the new values to be put to inputs at indices updates must have the same rank as inputs returns a tensor has the same shape and dtype as inputs while loop implementation args cond a callable that represents the termination condition of the loop must have the same number of args as loop_vars and return a bool body a callable that represents the loop body must have the same number of args as loop_vars and return a list tuple of the same length shape and dtype as loop_vars loop_vars a list tuple of tensors the loop variables maximum_iterations optional maximum number of iterations of the while loop to run if provided the cond output is and ed with an additional condition ensuring the number of iterations executed is no greater than maximum_iterations returns a list tuple of tensors has the same shape and dtype as inputs examples i 0 cond lambda i i 10 body lambda i i 1 keras ops while_loop cond body i 0 10 stops gradient computation args variable a tensor variable for which the gradient computation is to be disabled returns the variable with gradient computation disabled examples var keras backend convert_to_tensor 1 2 3 dtype float32 var keras ops stop_gradient var for loop implementation args lower the initial value of the loop variable upper the upper bound of the loop variable body_fun a callable that represents the loop body must take two arguments the loop variable and the loop state the loop state should be updated and returned by this function init_val the initial value of the loop state returns the final state after the loop example lower 0 upper 10 body_fun lambda i s i 1 s i init_val 0 keras ops fori_loop lower upper body_fun init_val 45 unpacks the given dimension of a rank r tensor into rank r 1 tensors args x the input tensor num the length of the dimension axis automatically inferred if none axis the axis along which to unpack returns a list of tensors unpacked along the given axis example x keras ops array 1 2 3 4 keras ops unstack x axis 0 array 1 2 array 3 4 gets the shape of the tensor input note on the tensorflow backend when x is a tf tensor with dynamic shape dimensions which are dynamic in the context of a compiled function will have a tf tensor value instead of a static integer value args x a tensor this function will try to access the shape attribute of the input tensor returns a tuple of integers or none values indicating the shape of the input tensor example x keras zeros 8 12 keras ops shape x 8 12 cast a tensor to the desired dtype args x a tensor or variable dtype the target type returns a tensor of the specified dtype example x keras ops arange 4 x keras ops cast x dtype float16 convert a numpy array to a tensor args x a numpy array dtype the target type sparse whether to keep sparse tensors false will cause sparse tensors to be densified the default value of none means that sparse tensors are kept only if the backend supports them returns a tensor of the specified dtype example x np array 1 2 3 y keras ops convert_to_tensor x convert a tensor to a numpy array args x a tensor returns a numpy array this will raise a valueerror defined in the kerastensor class we trigger it rather than duplicate it here fallback on symbolic case wrap self call to provide helpful info in case of exception plain flow conditionally applies true_fn or false_fn args pred boolean scalar type true_fn callable returning the output for the pred true case false_fn callable returning the output for the pred false case returns the output of either true_fn or false_fn depending on pred todo also create an op subclass vectorizedmap parallel map of function on axis 0 of tensor s elements schematically vectorized_map implements the following in the case of a single tensor input elements python def vectorized_map function elements outputs for e in elements outputs append function e return stack outputs in the case of an iterable of tensors elements it implements the following python def vectorized_map function elements batch_size elements 0 shape 0 outputs for index in range batch_size outputs append function e index for e in elements return np stack outputs in this case function is expected to take as input a single list of tensor arguments check whether the given object is a tensor note this checks for backend specific tensors so passing a tensorflow tensor would return false if your backend is pytorch or jax args x a variable returns true if x is a tensor otherwise false
import numpy as np

from keras import backend
from keras.api_export import keras_export
from keras.backend import KerasTensor
from keras.backend import any_symbolic_tensors
from keras.ops.operation import Operation
from keras.utils import traceback_utils


class Scatter(Operation):
    def call(self, indices, values, shape):
        return backend.core.scatter(indices, values, shape)

    def compute_output_spec(self, indices, values, shape):
        return KerasTensor(shape, dtype=values.dtype)


@keras_export("keras.ops.scatter")
def scatter(indices, values, shape):
    
    if any_symbolic_tensors((indices, values, shape)):
        return Scatter().symbolic_call(indices, values, shape)
    return backend.core.scatter(indices, values, shape)


class ScatterUpdate(Operation):
    def call(self, inputs, indices, updates):
        return backend.core.scatter_update(inputs, indices, updates)

    def compute_output_spec(self, inputs, indices, updates):
        return KerasTensor(inputs.shape, dtype=inputs.dtype)


@keras_export("keras.ops.scatter_update")
def scatter_update(inputs, indices, updates):
    
    if any_symbolic_tensors((inputs, indices, updates)):
        return ScatterUpdate().symbolic_call(inputs, indices, updates)
    return backend.core.scatter_update(inputs, indices, updates)


class Slice(Operation):
    def call(self, inputs, start_indices, shape):
        return backend.core.slice(inputs, start_indices, shape)

    def compute_output_spec(self, inputs, start_indices, shape):
        return KerasTensor(shape, dtype=inputs.dtype)


@keras_export("keras.ops.slice")
def slice(inputs, start_indices, shape):
    
    if any_symbolic_tensors((inputs, start_indices, shape)):
        return Slice().symbolic_call(inputs, start_indices, shape)
    return backend.core.slice(inputs, start_indices, shape)


class SliceUpdate(Operation):
    def call(self, inputs, start_indices, updates):
        return backend.core.slice_update(inputs, start_indices, updates)

    def compute_output_spec(self, inputs, start_indices, updates):
        return KerasTensor(inputs.shape, dtype=inputs.dtype)


@keras_export("keras.ops.slice_update")
def slice_update(inputs, start_indices, updates):
    
    if any_symbolic_tensors((inputs, start_indices, updates)):
        return SliceUpdate().symbolic_call(inputs, start_indices, updates)
    return backend.core.slice_update(inputs, start_indices, updates)


class WhileLoop(Operation):
    def __init__(self, cond, body, maximum_iterations):
        super().__init__()
        self.cond = cond
        self.body = body
        self.maximum_iterations = maximum_iterations

    def call(self, loop_vars):
        return backend.core.while_loop(
            self.cond,
            self.body,
            loop_vars,
            maximum_iterations=self.maximum_iterations,
        )

    def compute_output_spec(self, loop_vars):
        return [KerasTensor(v.shape, dtype=v.dtype) for v in loop_vars]


@keras_export("keras.ops.while_loop")
def while_loop(
    cond,
    body,
    loop_vars,
    maximum_iterations=None,
):
    
    return backend.core.while_loop(
        cond,
        body,
        loop_vars,
        maximum_iterations=maximum_iterations,
    )


class StopGradient(Operation):
    def __init__(self):
        super().__init__()

    def call(self, variable):
        return backend.core.stop_gradient(variable)

    def compute_output_spec(self, variable):
        return KerasTensor(variable.shape, dtype=variable.dtype)


@keras_export("keras.ops.stop_gradient")
def stop_gradient(variable):
    
    return backend.core.stop_gradient(variable)


class ForiLoop(Operation):
    def __init__(self, lower, upper, body_fun):
        super().__init__()
        self.lower = lower
        self.upper = upper
        self.body_fun = body_fun

    def call(self, init_val):
        return backend.core.fori_loop(
            self.lower,
            self.upper,
            self.body_fun,
            init_val,
        )

    def compute_output_spec(self, init_val):
        return KerasTensor(init_val.shape, dtype=init_val.dtype)


@keras_export("keras.ops.fori_loop")
def fori_loop(lower, upper, body_fun, init_val):
    
    if any_symbolic_tensors((lower, upper, init_val)):
        return ForiLoop(lower, upper, body_fun).symbolic_call(init_val)
    return backend.core.fori_loop(lower, upper, body_fun, init_val)


class Unstack(Operation):
    def __init__(self, num=None, axis=0):
        super().__init__()
        self.num = num
        self.axis = axis

    def call(self, x):
        return backend.core.unstack(x, self.num, self.axis)

    def compute_output_spec(self, x):
        axis = self.axis
        if axis < 0:
            axis = len(x.shape) + axis
        output_shapes = x.shape[:axis] + x.shape[axis + 1 :]
        num = self.num
        if num is None:
            num = x.shape[axis]
        if num is None:
            raise ValueError(
                "Cannot infer argument `num` from shape "
                f"{x.shape}. Either provide a tensor with a "
                "concrete shape in the `axis` dimension or "
                "explicitly pass the `num` argument."
            )
        output = [
            KerasTensor(shape=output_shapes, dtype=x.dtype) for _ in range(num)
        ]
        return output


@keras_export("keras.ops.unstack")
def unstack(x, num=None, axis=0):
    
    if any_symbolic_tensors((x,)):
        return Unstack(num, axis).symbolic_call(x)
    return backend.core.unstack(x, num=num, axis=axis)


@keras_export("keras.ops.shape")
def shape(x):
    
    if any_symbolic_tensors((x,)):
        return x.shape
    return backend.core.shape(x)


class Cast(Operation):
    def __init__(self, dtype):
        super().__init__()
        self.dtype = backend.standardize_dtype(dtype)

    def call(self, x):
        return backend.core.cast(x, self.dtype)

    def compute_output_spec(self, x):
        return backend.KerasTensor(shape=x.shape, dtype=self.dtype)


@keras_export("keras.ops.cast")
def cast(x, dtype):
    
    dtype = backend.standardize_dtype(dtype)

    if any_symbolic_tensors((x,)):
        return Cast(dtype=dtype)(x)
    return backend.core.cast(x, dtype)


@keras_export("keras.ops.convert_to_tensor")
def convert_to_tensor(x, dtype=None, sparse=None):
    
    return backend.convert_to_tensor(x, dtype=dtype, sparse=sparse)


@keras_export("keras.ops.convert_to_numpy")
def convert_to_numpy(x):
    
    if any_symbolic_tensors((x,)):
        
        
        return np.array(x)
    return backend.convert_to_numpy(x)


class Cond(Operation):
    @traceback_utils.filter_traceback
    def __call__(self, *args, **kwargs):
        def call_fn(*args, **kwargs):
            if not any_symbolic_tensors(args, kwargs):
                try:
                    return self.call(*args, **kwargs)
                except (TypeError, ValueError):
                    
                    pass
            return self.symbolic_call(*args, **kwargs)

        if traceback_utils.is_traceback_filtering_enabled():
            
            call_fn = traceback_utils.inject_argument_info_in_traceback(
                call_fn,
                object_name=(f"{self.__class__.__name__}.call()"),
            )
            return call_fn(*args, **kwargs)

        
        return call_fn(*args, **kwargs)

    def call(self, pred, true_fn, false_fn):
        return backend.core.cond(pred, true_fn, false_fn)

    def compute_output_spec(self, pred, true_fn, false_fn):
        def call_fn(fn):
            return fn()

        true_fn_spec = backend.compute_output_spec(call_fn, true_fn)
        false_fn_spec = backend.compute_output_spec(call_fn, false_fn)
        if not self._check_output_spec(true_fn_spec, false_fn_spec):
            raise ValueError(
                "`true_fn` and `false_fn` should return outputs "
                "of the same kind (struct, dtype and shape). "
                f"Got {true_fn_spec} and {false_fn_spec} instead."
            )
        return true_fn_spec

    def _check_output_spec(self, true_fn_spec, false_fn_spec):
        if true_fn_spec is None:
            if false_fn_spec is not None:
                return False
        elif isinstance(true_fn_spec, dict):
            if not isinstance(false_fn_spec, dict):
                return False
            if true_fn_spec.keys() != false_fn_spec.keys():
                return False
            if any(
                (not self._check_output_spec(true_fn_spec[k], false_fn_spec[k]))
                for k in true_fn_spec.keys()
            ):
                return False
        elif isinstance(true_fn_spec, list):
            if not isinstance(false_fn_spec, list):
                return False
            if len(true_fn_spec) != len(false_fn_spec):
                return False
            if any(
                (not self._check_output_spec(ti, fi))
                for ti, fi in zip(true_fn_spec, false_fn_spec)
            ):
                return False
        elif isinstance(true_fn_spec, tuple):
            if not isinstance(false_fn_spec, tuple):
                return False
            if len(true_fn_spec) != len(false_fn_spec):
                return False
            if any(
                (not self._check_output_spec(ti, fi))
                for ti, fi in zip(true_fn_spec, false_fn_spec)
            ):
                return False
        else:
            if true_fn_spec.dtype != false_fn_spec.dtype:
                return False
            if true_fn_spec.shape != false_fn_spec.shape:
                return False

        return True


@keras_export("keras.ops.cond")
def cond(pred, true_fn, false_fn):
    
    return Cond()(pred, true_fn, false_fn)



@keras_export("keras.ops.vectorized_map")
def vectorized_map(function, elements):
    
    return backend.core.vectorized_map(function, elements)


@keras_export("keras.ops.is_tensor")
def is_tensor(x):
    
    return backend.core.is_tensor(x)