Add some numpy ops (#1)

* Add numpy ops (initial batch) and some config

* Add unit test

* fix call

* Revert "fix call"

This reverts commit 6748ad183029ff4b97317b77ceed8661916bb9a0.

* full unit test coverage

* fix setup.py
This commit is contained in:
Chen Qian 2023-04-12 11:31:58 -07:00 committed by Francois Chollet
parent d7189027c0
commit eabdb87f9f
51 changed files with 1395 additions and 349 deletions

@ -1,9 +1,9 @@
from keras_core import backend from keras_core import backend
from keras_core.layers.layer import Layer
from keras_core.backend import KerasTensor
from keras_core.operations.function import Function
from keras_core import initializers from keras_core import initializers
from keras_core.backend import KerasTensor
from keras_core.layers.layer import Layer
from keras_core.operations import numpy as knp from keras_core.operations import numpy as knp
from keras_core.operations.function import Function
class MiniDense(Layer): class MiniDense(Layer):

@ -1,11 +1,12 @@
from keras_core import operations as ops
from keras_core import backend
from keras_core.optimizers import SGD
from keras_core.layers.layer import Layer
from keras_core import initializers
import jax import jax
from jax import numpy as jnp
import numpy as np import numpy as np
from jax import numpy as jnp
from keras_core import backend
from keras_core import initializers
from keras_core import operations as ops
from keras_core.layers.layer import Layer
from keras_core.optimizers import SGD
class MiniDense(Layer): class MiniDense(Layer):

@ -0,0 +1,2 @@
from keras_core import backend
from keras_core import operations

@ -1,4 +1,5 @@
import types import types
from keras_core.saving import register_keras_core_serializable from keras_core.saving import register_keras_core_serializable
try: try:

@ -1,21 +1,20 @@
import os
import json import json
import os
import sys import sys
from keras_core.utils.io_utils import print_msg
from keras_core.backend.keras_tensor import KerasTensor
from keras_core.backend.keras_tensor import is_keras_tensor
from keras_core.backend.keras_tensor import any_symbolic_tensors
from keras_core.backend.config import floatx
from keras_core.backend.config import epsilon
from keras_core.backend.config import image_data_format
from keras_core.backend.config import set_floatx
from keras_core.backend.config import set_epsilon
from keras_core.backend.config import set_image_data_format
from keras_core.backend.common import standardize_shape
from keras_core.backend.common import standardize_dtype
from keras_core.backend.common import StatelessScope from keras_core.backend.common import StatelessScope
from keras_core.backend.common import standardize_dtype
from keras_core.backend.common import standardize_shape
from keras_core.backend.config import epsilon
from keras_core.backend.config import floatx
from keras_core.backend.config import image_data_format
from keras_core.backend.config import set_epsilon
from keras_core.backend.config import set_floatx
from keras_core.backend.config import set_image_data_format
from keras_core.backend.keras_tensor import KerasTensor
from keras_core.backend.keras_tensor import any_symbolic_tensors
from keras_core.backend.keras_tensor import is_keras_tensor
from keras_core.utils.io_utils import print_msg
# Set Keras base dir path given KERAS_HOME env variable, if applicable. # Set Keras base dir path given KERAS_HOME env variable, if applicable.
# Otherwise either ~/.keras or /tmp. # Otherwise either ~/.keras or /tmp.

@ -1,7 +1,9 @@
from keras_core.backend.config import floatx
from tensorflow import nest
import threading import threading
from tensorflow import nest
from keras_core.backend.config import floatx
class KerasVariable: class KerasVariable:
def __init__(self, value, dtype, trainable=True, name=None): def __init__(self, value, dtype, trainable=True, name=None):

@ -1,14 +1,15 @@
from jax import numpy as jnp import jax
import numpy as np import numpy as np
from jax import numpy as jnp
from tensorflow import nest
from keras_core.backend.common import KerasVariable from keras_core.backend.common import KerasVariable
from keras_core.backend.common import StatelessScope
from keras_core.backend.common import get_stateless_scope
from keras_core.backend.common import in_stateless_scope
from keras_core.backend.common import standardize_dtype from keras_core.backend.common import standardize_dtype
from keras_core.backend.keras_tensor import KerasTensor from keras_core.backend.keras_tensor import KerasTensor
from keras_core.utils.naming import auto_name from keras_core.utils.naming import auto_name
from keras_core.backend.common import in_stateless_scope
from keras_core.backend.common import get_stateless_scope
from keras_core.backend.common import StatelessScope
from tensorflow import nest
import jax
DYNAMIC_SHAPES_OK = False # Dynamic shapes NG DYNAMIC_SHAPES_OK = False # Dynamic shapes NG

@ -1,4 +1,5 @@
import jax import jax
from keras_core.backend import floatx from keras_core.backend import floatx
from keras_core.backend.random import draw_seed from keras_core.backend.random import draw_seed

@ -1,6 +1,7 @@
from keras_core.utils.naming import auto_name
from tensorflow import nest from tensorflow import nest
from keras_core.utils.naming import auto_name
class KerasTensor: class KerasTensor:
def __init__(self, shape, dtype="float32", name=None): def __init__(self, shape, dtype="float32", name=None):

@ -1,5 +1,4 @@
from keras_core.backend import backend from keras_core.backend import backend
from keras_core.backend.random.random_seed_generator import RandomSeedGenerator from keras_core.backend.random.random_seed_generator import RandomSeedGenerator
from keras_core.backend.random.random_seed_generator import draw_seed from keras_core.backend.random.random_seed_generator import draw_seed
from keras_core.backend.random.random_seed_generator import make_default_seed from keras_core.backend.random.random_seed_generator import make_default_seed

@ -1,13 +1,13 @@
import tensorflow as tf import tensorflow as tf
from tensorflow.experimental import numpy as tfnp from tensorflow.experimental import numpy as tfnp
from keras_core.backend.common import KerasVariable from keras_core.backend.common import KerasVariable
from keras_core.backend.common import get_stateless_scope
from keras_core.backend.common import in_stateless_scope
from keras_core.backend.common import standardize_dtype from keras_core.backend.common import standardize_dtype
from keras_core.backend.keras_tensor import KerasTensor from keras_core.backend.keras_tensor import KerasTensor
from keras_core.utils.naming import auto_name
from keras_core.backend.tensorflow.trainer import Trainer from keras_core.backend.tensorflow.trainer import Trainer
from keras_core.backend.common import in_stateless_scope from keras_core.utils.naming import auto_name
from keras_core.backend.common import get_stateless_scope
DYNAMIC_SHAPES_OK = True DYNAMIC_SHAPES_OK = True

@ -1,6 +1,7 @@
from keras_core.backend.random import draw_seed
import tensorflow as tf import tensorflow as tf
from keras_core.backend import floatx from keras_core.backend import floatx
from keras_core.backend.random import draw_seed
def tf_draw_seed(seed): def tf_draw_seed(seed):

@ -1,6 +1,7 @@
from keras_core.trainers import trainer
import tensorflow as tf import tensorflow as tf
from keras_core.trainers import trainer
class Trainer(trainer.Trainer): class Trainer(trainer.Trainer):
def train_step(self, data): def train_step(self, data):

@ -1,3 +1,3 @@
from keras_core.initializers.constant_initializers import *
from keras_core.initializers.initializer import Initializer from keras_core.initializers.initializer import Initializer
from keras_core.initializers.random_initializers import * from keras_core.initializers.random_initializers import *
from keras_core.initializers.constant_initializers import *

@ -1,6 +1,6 @@
from keras_core.backend import standardize_dtype
from keras_core.initializers.initializer import Initializer from keras_core.initializers.initializer import Initializer
from keras_core.operations import numpy as knp from keras_core.operations import numpy as knp
from keras_core.backend import standardize_dtype
class Zeros(Initializer): class Zeros(Initializer):

@ -1,4 +1,5 @@
import warnings import warnings
from keras_core.api_export import keras_core_export from keras_core.api_export import keras_core_export

@ -1,6 +1,7 @@
import math import math
from keras_core.initializers.initializer import Initializer
from keras_core.backend import random from keras_core.backend import random
from keras_core.initializers.initializer import Initializer
class VarianceScaling(Initializer): class VarianceScaling(Initializer):

@ -1,5 +1,6 @@
from keras_core import backend
from tensorflow import nest from tensorflow import nest
from keras_core import backend
from keras_core.api_export import keras_core_export from keras_core.api_export import keras_core_export

@ -14,21 +14,23 @@ And some more magic:
- metric tracking - metric tracking
- RNG seed tracking - RNG seed tracking
""" """
from keras_core.operations.operation import Operation import collections
from keras_core.backend import KerasTensor
from keras_core import backend
from keras_core.utils.tracking import Tracker
from keras_core.metrics.metric import Metric
from keras_core import utils
from keras_core.utils import summary_utils
from keras_core.layers import input_spec
from keras_core.api_export import keras_core_export
from tensorflow import nest
from tensorflow import keras as tf_keras
import numpy as np
import inspect import inspect
import threading import threading
import collections
import numpy as np
from tensorflow import keras as tf_keras
from tensorflow import nest
from keras_core import backend
from keras_core import utils
from keras_core.api_export import keras_core_export
from keras_core.backend import KerasTensor
from keras_core.layers import input_spec
from keras_core.metrics.metric import Metric
from keras_core.operations.operation import Operation
from keras_core.utils import summary_utils
from keras_core.utils.tracking import Tracker
# TODO: cache all call signature processing. See layer_utils.CallFunctionSpec() in Keras. # TODO: cache all call signature processing. See layer_utils.CallFunctionSpec() in Keras.

@ -1,7 +1,8 @@
import numpy as np
from keras_core import testing from keras_core import testing
from keras_core.engine import keras_tensor from keras_core.engine import keras_tensor
from keras_core.layers.layer import Layer from keras_core.layers.layer import Layer
import numpy as np
class FunctionTest(testing.TestCase): class FunctionTest(testing.TestCase):

@ -1,8 +1,9 @@
from keras_core import operations as ops
from keras_core import backend from keras_core import backend
from keras_core.utils.naming import auto_name from keras_core.utils.naming import auto_name
from keras_core.utils import dtype_utils from keras_core.utils import dtype_utils
from keras_core import operations as ops
from keras_core.api_export import keras_core_export from keras_core.api_export import keras_core_export
from keras_core.utils.naming import auto_name
@keras_core_export(["keras_core.Loss", "keras_core.losses.Loss"]) @keras_core_export(["keras_core.Loss", "keras_core.losses.Loss"])

@ -1,7 +1,8 @@
import numpy as np
from keras_core import operations as ops
from keras_core import testing from keras_core import testing
from keras_core.losses.loss import Loss from keras_core.losses.loss import Loss
from keras_core import operations as ops
import numpy as np
class ExampleLoss(Loss): class ExampleLoss(Loss):

@ -1,5 +1,5 @@
from keras_core.losses.loss import Loss
from keras_core import operations as ops from keras_core import operations as ops
from keras_core.losses.loss import Loss
from keras_core.losses.loss import squeeze_to_same_rank from keras_core.losses.loss import squeeze_to_same_rank

@ -1,7 +1,7 @@
from keras_core import backend from keras_core import backend
from keras_core.utils.tracking import Tracker
from keras_core.utils.naming import auto_name
from keras_core.api_export import keras_core_export from keras_core.api_export import keras_core_export
from keras_core.utils.naming import auto_name
from keras_core.utils.tracking import Tracker
@keras_core_export(["keras_core.Metric", "keras_core.metrics.Metric"]) @keras_core_export(["keras_core.Metric", "keras_core.metrics.Metric"])

@ -1,9 +1,10 @@
from keras_core import testing import numpy as np
from keras_core import backend
from keras_core import initializers from keras_core import initializers
from keras_core import operations as ops from keras_core import operations as ops
from keras_core import backend from keras_core import testing
from keras_core.metrics.metric import Metric from keras_core.metrics.metric import Metric
import numpy as np
class ExampleMetric(Metric): class ExampleMetric(Metric):

@ -1,6 +1,6 @@
from keras_core.metrics.metric import Metric
from keras_core import backend from keras_core import backend
from keras_core import initializers from keras_core import initializers
from keras_core.metrics.metric import Metric
class MeanSquareError(Metric): class MeanSquareError(Metric):

@ -1,6 +1,6 @@
from keras_core.operations.function import Function
from keras_core.models.model import Model
from keras_core.layers.layer import Layer from keras_core.layers.layer import Layer
from keras_core.models.model import Model
from keras_core.operations.function import Function
class Functional(Function, Model): class Functional(Function, Model):

@ -1,6 +1,6 @@
from keras_core.api_export import keras_core_export from keras_core.api_export import keras_core_export
from keras_core.layers.layer import Layer
from keras_core.backend import Trainer from keras_core.backend import Trainer
from keras_core.layers.layer import Layer
@keras_core_export(["keras_core.Model", "keras_core.models.Model"]) @keras_core_export(["keras_core.Model", "keras_core.models.Model"])

@ -2,11 +2,11 @@
# from keras_core.operations.numpy import Add, add # from keras_core.operations.numpy import Add, add
# from keras_core.operations.numpy import Multiply, multiply # from keras_core.operations.numpy import Multiply, multiply
from keras_core.operations.numpy import *
from keras_core.operations.nn import *
from keras_core.backend import is_tensor
from keras_core.backend import convert_to_tensor
from keras_core.backend import cast from keras_core.backend import cast
from keras_core.backend import shape
from keras_core.backend import cond from keras_core.backend import cond
from keras_core.backend import convert_to_tensor
from keras_core.backend import is_tensor
from keras_core.backend import name_scope from keras_core.backend import name_scope
from keras_core.backend import shape
from keras_core.operations.nn import *
from keras_core.operations.numpy import *

@ -1,8 +1,10 @@
import collections
from tensorflow import nest
from keras_core.backend import KerasTensor from keras_core.backend import KerasTensor
from keras_core.operations.operation import Operation from keras_core.operations.operation import Operation
from keras_core.utils.naming import auto_name from keras_core.utils.naming import auto_name
from tensorflow import nest
import collections
class Function(Operation): class Function(Operation):

@ -1,10 +1,11 @@
import numpy as np
import pytest
from keras_core import backend
from keras_core import testing
from keras_core.backend import keras_tensor from keras_core.backend import keras_tensor
from keras_core.operations import function from keras_core.operations import function
from keras_core.operations import numpy as knp from keras_core.operations import numpy as knp
from keras_core import testing
from keras_core import backend
import pytest
import numpy as np
class FunctionTest(testing.TestCase): class FunctionTest(testing.TestCase):

@ -1,5 +1,7 @@
import collections import collections
from tensorflow import nest from tensorflow import nest
from keras_core.backend import KerasTensor from keras_core.backend import KerasTensor
from keras_core.operations.symbolic_arguments import SymbolicArguments from keras_core.operations.symbolic_arguments import SymbolicArguments

@ -1,201 +1,217 @@
""" """
MANIFEST: MANIFEST:
matmul abs
add
subtract
multiply
divide
true_divide
power
negative
absolute absolute
add
all
amax
amin
append
arange
arccos
arcsin
arctan
arctanh
argmax
argmin
argsort
array
array_equal
average
broadcast_to
ceil
clip
concatenate
conj
conjugate
copy
cos
count_nonzero
cov
cross
cumprod
cumsum
diag
diag_indices
diagonal
diff
divide
dot
dtype
einsum
empty
equal
exp
expand_dims
expm1
eye
flip
floor
full
full_like
greater
greater_equal
hstack
identity
imag
indices
interp
isclose
isfinite
isin
isinf
isnan
isscalar
issubdtype
issubctype
less
less_equal
linspace
log
log10
log1p
log2
logaddexp
logical_and
logical_not
logical_or
logspace
matmul
max
maximum
mean mean
var median
zeros meshgrid
mgrid
min
minimum
mod
moveaxis
multiply
nan_to_num
ndim
nonzero
not_equal
ones ones
ones_like
outer
pad
percentile
power
prod
ravel
real
reciprocal
repeat
reshape
roll
round
shape
sign
sin
size
sort
split
sqrt
square
squeeze
stack
std
subtract
sum
swapaxes
take
take_along_axis
tan
tensordot
tile
trace
transpose
tri
tril
triu
true_divide
unique
unrival_index
vdot
vectorize
vstack
where
zeros
zeros_like
""" """
import numpy as np
from keras_core import backend
from keras_core.backend import KerasTensor from keras_core.backend import KerasTensor
from keras_core.backend import any_symbolic_tensors from keras_core.backend import any_symbolic_tensors
from keras_core.backend import convert_to_tensor
from keras_core.operations.symbolic_arguments import SymbolicArguments
from keras_core.operations.operation import Operation from keras_core.operations.operation import Operation
from keras_core import backend
from tensorflow import nest
import jax
def broadcast_shapes(shape1, shape2):
# TODO: replace this function with one that can handle # Broadcast input shapes to a unified shape.
# dynamic shapes. # Convert to list for mutability.
def compute_np_output_spec(op_name, *args, **kwargs): shape1 = list(shape1)
op = getattr(jax.numpy, op_name) shape2 = list(shape2)
origin_shape1 = shape1
def convert_keras_tensor_to_jax_array(x): origin_shape2 = shape2
if isinstance(x, KerasTensor):
return jax.numpy.zeros(x.shape, dtype=x.dtype) if len(shape1) > len(shape2):
return x shape2 = [None] * (len(shape1) - len(shape2)) + shape2
if len(shape1) < len(shape2):
args, kwargs = SymbolicArguments(*args, **kwargs).convert( shape1 = [None] * (len(shape2) - len(shape1)) + shape1
convert_keras_tensor_to_jax_array output_shape = list(shape1)
) for i in range(len(shape1)):
jax_out = jax.eval_shape(op, *args, **kwargs) if shape1[i] == 1:
output_shape[i] = shape2[i]
def convert_jax_spec_to_keras_tensor(x): elif shape1[i] == None:
if isinstance(x, jax.ShapeDtypeStruct): output_shape[i] = shape2[i]
return KerasTensor(x.shape, x.dtype) else:
return x if shape2[i] == 1 or shape2[i] == None or shape2[i] == shape1[i]:
output_shape[i] = shape1[i]
return nest.map_structure(convert_jax_spec_to_keras_tensor, jax_out) else:
raise ValueError(
"Cannot broadcast shape, the failure dim has value "
##################### f"{shape1[i]}, which cannot be broadcasted to {shape2[i]}. "
### Two-input ops ### f"Input shapes are: {origin_shape1} and {origin_shape2}."
##################### )
return output_shape
### matmul ###
def reduce_shape(shape, axis=None, keepdims=False):
class Matmul(Operation): shape = list(shape)
def call(self, x1, x2): if axis is None:
return backend.execute("matmul", x1, x2) if keepdims:
output_shape = [1 for _ in range(shape)]
def compute_output_spec(self, x1, x2): else:
return compute_np_output_spec("matmul", x1, x2) output_shape = []
return output_shape
def matmul(x1, x2): if keepdims:
if any_symbolic_tensors((x1, x2)): for ax in axis:
return Matmul().symbolic_call(x1, x2) shape[ax] = 1
x1 = convert_to_tensor(x1, x1.dtype) return shape
x2 = convert_to_tensor(x2, x2.dtype) else:
return backend.execute("matmul", x1, x2) for ax in axis:
shape[ax] = -1
output_shape = list(filter((-1).__ne__, shape))
### add ### return output_shape
class Add(Operation): def shape_equal(shape1, shape2, axis=None):
def call(self, x1, x2): if len(shape1) != len(shape2):
return backend.execute("add", x1, x2) return False
if axis is not None:
def compute_output_spec(self, x1, x2): shape1 = list(shape1)
return compute_np_output_spec("add", x1, x2) shape2 = list(shape2)
for ax in axis:
shape1[ax] = -1
def add(x1, x2): shape2[ax] = -1
if any_symbolic_tensors((x1, x2)): return shape1 == shape2
return Add().symbolic_call(x1, x2)
return backend.execute("add", x1, x2)
### subtract ###
class Subtract(Operation):
def call(self, x1, x2):
return backend.execute("subtract", x1, x2)
def compute_output_spec(self, x1, x2):
return compute_np_output_spec("subtract", x1, x2)
def subtract(x1, x2):
if any_symbolic_tensors((x1, x2)):
return Subtract().symbolic_call(x1, x2)
return backend.execute("subtract", x1, x2)
### multiply ###
class Multiply(Operation):
def call(self, x1, x2):
return backend.execute("multiply", x1, x2)
def compute_output_spec(self, x1, x2):
return compute_np_output_spec("multiply", x1, x2)
def multiply(x1, x2):
if any_symbolic_tensors((x1, x2)):
return Multiply().symbolic_call(x1, x2)
return backend.execute("multiply", x1, x2)
### divide ###
class Divide(Operation):
def call(self, x1, x2):
return backend.execute("divide", x1, x2)
def compute_output_spec(self, x1, x2):
return compute_np_output_spec("divide", x1, x2)
def divide(x1, x2):
if any_symbolic_tensors((x1, x2)):
return Divide().symbolic_call(x1, x2)
return backend.execute("divide", x1, x2)
### true_divide ###
class TrueDivide(Operation):
def call(self, x1, x2):
return backend.execute("true_divide", x1, x2)
def compute_output_spec(self, x1, x2):
return compute_np_output_spec("true_divide", x1, x2)
def true_divide(x1, x2):
if any_symbolic_tensors((x1, x2)):
return TrueDivide().symbolic_call(x1, x2)
return backend.execute("true_divide", x1, x2)
class Power(Operation):
def call(self, x1, x2):
return backend.execute("power", x1, x2)
def compute_output_spec(self, x1, x2):
return KerasTensor(x1.shape, dtype=x1.dtype)
def power(x1, x2):
if any_symbolic_tensors((x1, x2)):
return Power().symbolic_call(x1, x2)
return backend.execute("power", x1, x2)
########################
### Single-input ops ###
########################
### negative ###
class Negative(Operation):
def call(self, x):
return backend.execute("negative", x)
def compute_output_spec(self, x):
return KerasTensor(x.shape, dtype=x.dtype)
def negative(x):
if any_symbolic_tensors((x,)):
return Negative().symbolic_call(x)
return backend.execute("negative", x)
### absolute ###
class Absolute(Operation): class Absolute(Operation):
@ -212,7 +228,297 @@ def absolute(x):
return backend.execute("absolute", x) return backend.execute("absolute", x)
### square ### class Abs(Absolute):
pass
def abs(x):
return absolute(x)
class Add(Operation):
def call(self, x1, x2):
return backend.execute("add", x1, x2)
def compute_output_spec(self, x1, x2):
output_shape = broadcast_shapes(x1.shape, x2.shape)
return KerasTensor(output_shape, dtype=x1.dtype)
def add(x1, x2):
if any_symbolic_tensors((x1, x2)):
return Add().symbolic_call(x1, x2)
return backend.execute("add", x1, x2)
class All(Operation):
def __init__(self, axis=None, keepdims=False):
super().__init__()
if isinstance(axis, int):
self.axis = [axis]
else:
self.axis = axis
self.keepdims = keepdims
def call(self, x):
return backend.execute(
"all",
x,
axis=self.axis,
keepdims=self.keepdims,
)
def compute_output_spec(self, x):
return KerasTensor(
reduce_shape(
x.shape,
axis=self.axis,
keepdims=self.keepdims,
),
dtype=x.dtype,
)
def all(x, axis=None, keepdims=False):
if any_symbolic_tensors((x,)):
return All(axis=axis, keepdims=keepdims).symbolic_call(x)
return backend.execute("all", x, axis=axis, keepdims=keepdims)
class Amax(Operation):
def __init__(self, axis=None, keepdims=False):
super().__init__()
if isinstance(axis, int):
axis = [axis]
self.axis = axis
self.keepdims = keepdims
def call(self, x):
return backend.execute(
"amax",
x,
axis=self.axis,
keepdims=self.keepdims,
)
def compute_output_spec(self, x):
return KerasTensor(
reduce_shape(x.shape, axis=self.axis, keepdims=self.keepdims),
dtype=x.dtype,
)
def amax(x, axis=None, keepdims=False):
if any_symbolic_tensors((x,)):
return All(axis=axis, keepdims=keepdims).symbolic_call(x)
return backend.execute("amax", x, axis=axis, keepdims=keepdims)
class Amin(Operation):
def __init__(self, axis=None, keepdims=False):
super().__init__()
if isinstance(axis, int):
axis = [axis]
self.axis = axis
self.keepdims = keepdims
def call(self, x):
return backend.execute(
"amin", x, axis=self.axis, keepdims=self.keepdims
)
def compute_output_spec(self, x):
return KerasTensor(
reduce_shape(x.shape, axis=self.axis, keepdims=self.keepdims),
dtype=x.dtype,
)
def amin(x, axis=None, keepdims=False):
if any_symbolic_tensors((x,)):
return All(axis=axis, keepdims=keepdims).symbolic_call(x)
return backend.execute("amin", x, axis=axis, keepdims=keepdims)
class Append(Operation):
def __init__(self, axis=None):
super().__init__()
self.axis = axis
def call(self, x1, x2):
return backend.execute("append", x1, x2, axis=self.axis)
def compute_output_spec(self, x1, x2):
x1_shape = x1.shape
x2_shape = x2.shape
if self.axis is None:
if None in x1_shape or None in x2_shape:
output_shape = [None]
else:
output_shape = [int(np.prod(x1_shape) + np.prod(x2_shape))]
return KerasTensor(output_shape, dtype=x1.dtype)
if not shape_equal(x1_shape, x2_shape, [self.axis]):
raise ValueError(
"`append` requires inputs to have the same shape except the "
f"`axis={self.axis}`, but received shape {x1_shape} and "
f"{x2_shape}."
)
output_shape = list(x1_shape)
output_shape[self.axis] = x1_shape[self.axis] + x2_shape[self.axis]
return KerasTensor(output_shape, dtype=x1.dtype)
def append(
x1,
x2,
axis=None,
):
if any_symbolic_tensors((x1, x2)):
return Append(axis=axis).symbolic_call(x1, x2)
return backend.execute("append", x1, x2, axis=axis)
class Matmul(Operation):
def call(self, x1, x2):
return backend.execute("matmul", x1, x2)
def compute_output_spec(self, x1, x2):
x1_shape = x1.shape
x2_shape = x2.shape
if len(x1_shape) == 1:
x1_shape = (1, x1_shape[0])
if len(x2_shape) == 1:
x2_shape = (x2_shape[0], 1)
if (
x1_shape[-1] is not None
and x2_shape[-2] is not None
and x1_shape[-1] != x2_shape[-2]
):
raise ValueError(
"Inner dimensions (`x1.shape[-1]` and `x2.shape[-2]`) must be "
f"equal, but received `x1.shape={x1.shape}` and "
f"`x2.shape={x2.shape}`."
)
leading_shape = broadcast_shapes(x1_shape[:-2], x2_shape[:-2])
last_2_dims_shape = [x1_shape[-2], x2_shape[-1]]
output_shape = leading_shape + last_2_dims_shape
if len(x1.shape) == 1:
del output_shape[-2]
if len(x2.shape) == 1:
del output_shape[-1]
return KerasTensor(output_shape, dtype=x1.dtype)
def matmul(x1, x2):
if any_symbolic_tensors((x1, x2)):
return Matmul().symbolic_call(x1, x2)
return backend.execute("matmul", x1, x2)
class Subtract(Operation):
def call(self, x1, x2):
return backend.execute("subtract", x1, x2)
def compute_output_spec(self, x1, x2):
output_shape = broadcast_shapes(x1.shape, x2.shape)
return KerasTensor(output_shape, dtype=x1.dtype)
def subtract(x1, x2):
if any_symbolic_tensors((x1, x2)):
return Subtract().symbolic_call(x1, x2)
return backend.execute("subtract", x1, x2)
class Multiply(Operation):
def call(self, x1, x2):
return backend.execute("multiply", x1, x2)
def compute_output_spec(self, x1, x2):
output_shape = broadcast_shapes(x1.shape, x2.shape)
return KerasTensor(output_shape, dtype=x1.dtype)
def multiply(x1, x2):
if any_symbolic_tensors((x1, x2)):
return Multiply().symbolic_call(x1, x2)
return backend.execute("multiply", x1, x2)
class Divide(Operation):
def call(self, x1, x2):
return backend.execute("divide", x1, x2)
def compute_output_spec(self, x1, x2):
output_shape = broadcast_shapes(x1.shape, x2.shape)
return KerasTensor(output_shape, dtype=x1.dtype)
def divide(x1, x2):
if any_symbolic_tensors((x1, x2)):
return Divide().symbolic_call(x1, x2)
return backend.execute("divide", x1, x2)
class TrueDivide(Operation):
def call(self, x1, x2):
return backend.execute("true_divide", x1, x2)
def compute_output_spec(self, x1, x2):
output_shape = broadcast_shapes(x1.shape, x2.shape)
return KerasTensor(output_shape, dtype=x1.dtype)
def true_divide(x1, x2):
if any_symbolic_tensors((x1, x2)):
return TrueDivide().symbolic_call(x1, x2)
return backend.execute("true_divide", x1, x2)
class Power(Operation):
def call(self, x1, x2):
return backend.execute("power", x1, x2)
def compute_output_spec(self, x1, x2):
output_shape = broadcast_shapes(x1.shape, x2.shape)
return KerasTensor(output_shape, dtype=x1.dtype)
def power(x1, x2):
if any_symbolic_tensors((x1, x2)):
return Power().symbolic_call(x1, x2)
return backend.execute("power", x1, x2)
class Negative(Operation):
def call(self, x):
return backend.execute("negative", x)
def compute_output_spec(self, x):
return KerasTensor(x.shape, dtype=x.dtype)
def negative(x):
if any_symbolic_tensors((x,)):
return Negative().symbolic_call(x)
return backend.execute("negative", x)
class Absolute(Operation):
def call(self, x):
return backend.execute("absolute", x)
def compute_output_spec(self, x):
return KerasTensor(x.shape, dtype=x.dtype)
def absolute(x):
if any_symbolic_tensors((x,)):
return Absolute().symbolic_call(x)
return backend.execute("absolute", x)
class Square(Operation): class Square(Operation):
@ -229,58 +535,70 @@ def square(x):
return backend.execute("square", x) return backend.execute("square", x)
#####################
### Reshaping ops ###
#####################
### squeeze ###
class Squeeze(Operation): class Squeeze(Operation):
def __init__(self, axis=None): def __init__(self, axis=None):
super().__init__()
self.axis = axis self.axis = axis
def call(self, a): def call(self, x):
return backend.execute("squeeze", a, axis=self.axis) return backend.execute("squeeze", x, axis=self.axis)
def compute_output_spec(self, a): def compute_output_spec(self, x, axis=None):
return compute_np_output_spec("squeeze", a, axis=self.axis) input_shape = list(x.shape)
if axis is None:
output_shape = list(filter((1).__ne__, input_shape))
return KerasTensor(output_shape)
else:
if input_shape[axis] != 1:
raise ValueError(
f"Cannot squeeze axis {axis}, because the dimension is not "
"1."
)
del input_shape[axis]
return KerasTensor(input_shape, dtype=x.dtype)
def squeeze(a, axis=None): def squeeze(x, axis=None):
if any_symbolic_tensors((a,)): if any_symbolic_tensors((x,)):
return Squeeze().symbolic_call(a, axis=axis) return Squeeze().symbolic_call(x, axis=axis)
return backend.execute("squeeze", a, axis=axis) return backend.execute("squeeze", x, axis=axis)
### transpose ###
class Transpose(Operation): class Transpose(Operation):
def __init__(self, axes=None): def __init__(self, axes=None):
super().__init__()
self.axes = axes self.axes = axes
def call(self, a): def call(self, x):
return backend.execute("transpose", a, axes=self.axes) return backend.execute("transpose", x, axes=self.axes)
def compute_output_spec(self, a): def compute_output_spec(self, x):
return compute_np_output_spec("transpose", a, axes=self.axes) x_shape = x.shape
if self.axes is None:
return KerasTensor(x_shape[::-1])
if len(self.axes) != len(x_shape):
raise ValueError(
"axis must be a list of the same length as the input shape, "
f"expected {len(x_shape)}, but received {len(self.axes)}."
)
output_shape = []
for ax in self.axes:
output_shape.append(x_shape[ax])
return KerasTensor(output_shape, dtype=x.dtype)
def transpose(a, axes=None): def transpose(x, axes=None):
if any_symbolic_tensors((a,)): if any_symbolic_tensors((x,)):
return Transpose().symbolic_call(a, axes=axes) return Transpose(axes=axes).symbolic_call(x)
return backend.execute("transpose", a, axes=axes) return backend.execute("transpose", x, axes=axes)
#####################
### Reduction ops ###
#####################
class Mean(Operation): class Mean(Operation):
def __init__(self, axis=None, keepdims=False): def __init__(self, axis=None, keepdims=False):
super().__init__()
if isinstance(axis, int):
axis = [axis]
self.axis = axis self.axis = axis
self.keepdims = keepdims self.keepdims = keepdims
@ -290,8 +608,9 @@ class Mean(Operation):
) )
def compute_output_spec(self, x): def compute_output_spec(self, x):
return compute_np_output_spec( return KerasTensor(
"mean", x, axis=self.axis, keepdims=self.keepdims reduce_shape(x.shape, axis=self.axis, keepdims=self.keepdims),
dtype=x.dtype,
) )
@ -303,6 +622,9 @@ def mean(x, axis=None, keepdims=False):
class Var(Operation): class Var(Operation):
def __init__(self, axis=None, keepdims=False): def __init__(self, axis=None, keepdims=False):
super().__init__()
if isinstance(axis, int):
axis = [axis]
self.axis = axis self.axis = axis
self.keepdims = keepdims self.keepdims = keepdims
@ -310,8 +632,9 @@ class Var(Operation):
return backend.execute("var", x, axis=self.axis, keepdims=self.keepdims) return backend.execute("var", x, axis=self.axis, keepdims=self.keepdims)
def compute_output_spec(self, x): def compute_output_spec(self, x):
return compute_np_output_spec( return KerasTensor(
"var", x, axis=self.axis, keepdims=self.keepdims reduce_shape(x.shape, axis=self.axis, keepdims=self.keepdims),
dtype=x.dtype,
) )
@ -323,6 +646,9 @@ def var(x, axis=None, keepdims=False):
class Sum(Operation): class Sum(Operation):
def __init__(self, axis=None, keepdims=False): def __init__(self, axis=None, keepdims=False):
super().__init__()
if isinstance(axis, int):
axis = [axis]
self.axis = axis self.axis = axis
self.keepdims = keepdims self.keepdims = keepdims
@ -330,8 +656,9 @@ class Sum(Operation):
return backend.execute("sum", x, axis=self.axis, keepdims=self.keepdims) return backend.execute("sum", x, axis=self.axis, keepdims=self.keepdims)
def compute_output_spec(self, x): def compute_output_spec(self, x):
return compute_np_output_spec( return KerasTensor(
"sum", x, axis=self.axis, keepdims=self.keepdims reduce_shape(x.shape, axis=self.axis, keepdims=self.keepdims),
dtype=x.dtype,
) )
@ -341,14 +668,6 @@ def sum(x, axis=None, keepdims=False):
return backend.execute("sum", x, axis=axis, keepdims=keepdims) return backend.execute("sum", x, axis=axis, keepdims=keepdims)
##########################
### Array creation ops ###
##########################
### zeros ###
class Zeros(Operation): class Zeros(Operation):
def call(self, shape, dtype="float32"): def call(self, shape, dtype="float32"):
return backend.execute("zeros", shape, dtype) return backend.execute("zeros", shape, dtype)
@ -361,9 +680,6 @@ def zeros(shape, dtype="float32"):
return backend.execute("zeros", shape, dtype) return backend.execute("zeros", shape, dtype)
### ones ###
class Ones(Operation): class Ones(Operation):
def call(self, shape, dtype="float32"): def call(self, shape, dtype="float32"):
return backend.execute("ones", shape, dtype) return backend.execute("ones", shape, dtype)
@ -376,9 +692,6 @@ def ones(shape, dtype="float32"):
return backend.execute("ones", shape, dtype) return backend.execute("ones", shape, dtype)
### eye ###
class Eye(Operation): class Eye(Operation):
def call(self, N, M=None, k=0, dtype="float32"): def call(self, N, M=None, k=0, dtype="float32"):
return backend.execute("eye", N, M=M, k=k, dtype=dtype) return backend.execute("eye", N, M=M, k=k, dtype=dtype)

@ -0,0 +1,594 @@
import numpy as np
from keras_core import backend
from keras_core import testing
from keras_core.backend.keras_tensor import KerasTensor
from keras_core.operations import numpy as knp
from keras_core.operations import operation
class NumpyTwoInputOpsShapeTest(testing.TestCase):
def test_add(self):
x = KerasTensor((2, 3))
y = KerasTensor((2, 3))
self.assertEqual(knp.add(x, y).shape, (2, 3))
x = KerasTensor((None, 3))
y = KerasTensor((2, None))
self.assertEqual(knp.add(x, y).shape, (2, 3))
with self.assertRaises(ValueError):
x = KerasTensor([2, 3])
y = KerasTensor([2, 3, 4])
knp.add(x, y)
def test_subtract(self):
x = KerasTensor([2, 3])
y = KerasTensor([2, 3])
self.assertEqual(knp.subtract(x, y).shape, (2, 3))
x = KerasTensor([None, 3])
y = KerasTensor([2, None])
self.assertEqual(knp.subtract(x, y).shape, (2, 3))
with self.assertRaises(ValueError):
x = KerasTensor([2, 3])
y = KerasTensor([2, 3, 4])
knp.subtract(x, y)
def test_multiply(self):
x = KerasTensor([2, 3])
y = KerasTensor([2, 3])
self.assertEqual(knp.multiply(x, y).shape, (2, 3))
x = KerasTensor([None, 3])
y = KerasTensor([2, None])
self.assertEqual(knp.multiply(x, y).shape, (2, 3))
with self.assertRaises(ValueError):
x = KerasTensor([2, 3])
y = KerasTensor([2, 3, 4])
knp.multiply(x, y)
def test_matmul(self):
x = KerasTensor([2, 3])
y = KerasTensor([3, 2])
self.assertEqual(knp.matmul(x, y).shape, (2, 2))
x = KerasTensor([None, 3, 4])
y = KerasTensor([3, None, 4, 5])
self.assertEqual(knp.matmul(x, y).shape, (3, None, 3, 5))
with self.assertRaises(ValueError):
x = KerasTensor([3, 4])
y = KerasTensor([2, 3, 4])
knp.matmul(x, y)
def test_power(self):
x = KerasTensor([2, 3])
y = KerasTensor([2, 3])
self.assertEqual(knp.power(x, y).shape, (2, 3))
x = KerasTensor([None, 3])
y = KerasTensor([2, None])
self.assertEqual(knp.power(x, y).shape, (2, 3))
with self.assertRaises(ValueError):
x = KerasTensor([2, 3])
y = KerasTensor([2, 3, 4])
knp.power(x, y)
def test_divide(self):
x = KerasTensor([2, 3])
y = KerasTensor([2, 3])
self.assertEqual(knp.divide(x, y).shape, (2, 3))
x = KerasTensor([None, 3])
y = KerasTensor([2, None])
self.assertEqual(knp.divide(x, y).shape, (2, 3))
with self.assertRaises(ValueError):
x = KerasTensor([2, 3])
y = KerasTensor([2, 3, 4])
knp.divide(x, y)
def test_true_divide(self):
x = KerasTensor([2, 3])
y = KerasTensor([2, 3])
self.assertEqual(knp.true_divide(x, y).shape, (2, 3))
x = KerasTensor([None, 3])
y = KerasTensor([2, None])
self.assertEqual(knp.true_divide(x, y).shape, (2, 3))
with self.assertRaises(ValueError):
x = KerasTensor([2, 3])
y = KerasTensor([2, 3, 4])
knp.true_divide(x, y)
def test_append(self):
x = KerasTensor([2, 3])
y = KerasTensor([2, 3])
self.assertEqual(knp.append(x, y).shape, (12,))
x = KerasTensor([2, 3])
y = KerasTensor([2, 3])
self.assertEqual(knp.append(x, y, axis=0).shape, (4, 3))
x = KerasTensor([None, 3])
y = KerasTensor([2, None])
self.assertEqual(knp.append(x, y).shape, (None,))
with self.assertRaises(ValueError):
x = KerasTensor([2, 3])
y = KerasTensor([2, 3, 4])
knp.append(x, y, axis=2)
class NumpyOneInputOpsShapeTest(testing.TestCase):
def test_mean(self):
x = KerasTensor([2, 3])
self.assertEqual(knp.mean(x).shape, ())
x = KerasTensor([None, 3])
self.assertEqual(knp.mean(x).shape, ())
x = KerasTensor([None, 3, 3])
self.assertEqual(knp.mean(x, axis=1).shape, (None, 3))
self.assertEqual(knp.mean(x, axis=1, keepdims=True).shape, (None, 1, 3))
def test_all(self):
x = KerasTensor([2, 3])
self.assertEqual(knp.all(x).shape, ())
x = KerasTensor([None, 3])
self.assertEqual(knp.all(x).shape, ())
x = KerasTensor([None, 3, 3])
self.assertEqual(knp.all(x, axis=1).shape, (None, 3))
self.assertEqual(knp.all(x, axis=1, keepdims=True).shape, (None, 1, 3))
def test_var(self):
x = KerasTensor([2, 3])
self.assertEqual(knp.var(x).shape, ())
x = KerasTensor([None, 3])
self.assertEqual(knp.var(x).shape, ())
x = KerasTensor([None, 3, 3])
self.assertEqual(knp.var(x, axis=1).shape, (None, 3))
self.assertEqual(knp.var(x, axis=1, keepdims=True).shape, (None, 1, 3))
def test_sum(self):
x = KerasTensor([2, 3])
self.assertEqual(knp.sum(x).shape, ())
x = KerasTensor([None, 3])
self.assertEqual(knp.sum(x).shape, ())
x = KerasTensor([None, 3, 3])
self.assertEqual(knp.sum(x, axis=1).shape, (None, 3))
self.assertEqual(knp.sum(x, axis=1, keepdims=True).shape, (None, 1, 3))
def test_amax(self):
x = KerasTensor([2, 3])
self.assertEqual(knp.amax(x).shape, ())
x = KerasTensor([None, 3])
self.assertEqual(knp.amax(x).shape, ())
x = KerasTensor([None, 3, 3])
self.assertEqual(knp.amax(x, axis=1).shape, (None, 3))
self.assertEqual(knp.amax(x, axis=1, keepdims=True).shape, (None, 1, 3))
def test_amin(self):
x = KerasTensor([2, 3])
self.assertEqual(knp.amin(x).shape, ())
x = KerasTensor([None, 3])
self.assertEqual(knp.amin(x).shape, ())
x = KerasTensor([None, 3, 3])
self.assertEqual(knp.amin(x, axis=1).shape, (None, 3))
self.assertEqual(knp.amin(x, axis=1, keepdims=True).shape, (None, 1, 3))
def test_square(self):
x = KerasTensor([2, 3])
self.assertEqual(knp.square(x).shape, (2, 3))
x = KerasTensor([None, 3])
self.assertEqual(knp.square(x).shape, (None, 3))
def test_negative(self):
x = KerasTensor([2, 3])
self.assertEqual(knp.negative(x).shape, (2, 3))
x = KerasTensor([None, 3])
self.assertEqual(knp.negative(x).shape, (None, 3))
def test_abs(self):
x = KerasTensor([2, 3])
self.assertEqual(knp.abs(x).shape, (2, 3))
x = KerasTensor([None, 3])
self.assertEqual(knp.abs(x).shape, (None, 3))
def test_absolute(self):
x = KerasTensor([2, 3])
self.assertEqual(knp.absolute(x).shape, (2, 3))
x = KerasTensor([None, 3])
self.assertEqual(knp.absolute(x).shape, (None, 3))
def test_squeeze(self):
x = KerasTensor([2, 3])
self.assertEqual(knp.squeeze(x).shape, (2, 3))
x = KerasTensor([None, 1])
self.assertEqual(knp.squeeze(x).shape, (None,))
self.assertEqual(knp.squeeze(x, axis=1).shape, (None,))
with self.assertRaises(ValueError):
x = KerasTensor([None, 1])
knp.squeeze(x, axis=0)
def test_transpose(self):
x = KerasTensor([2, 3])
self.assertEqual(knp.transpose(x).shape, (3, 2))
x = KerasTensor([None, 3])
self.assertEqual(knp.transpose(x).shape, (3, None))
x = KerasTensor([None, 3, 3])
self.assertEqual(knp.transpose(x, (2, 0, 1)).shape, (3, None, 3))
class NumpyTwoInputOpsCorretnessTest(testing.TestCase):
def test_add(self):
x = np.array([[1, 2, 3], [3, 2, 1]])
y = np.array([[4, 5, 6], [3, 2, 1]])
z = np.array([[[1, 2, 3], [3, 2, 1]]])
np.testing.assert_array_equal(np.array(knp.add(x, y)), np.add(x, y))
np.testing.assert_array_equal(np.array(knp.add(x, z)), np.add(x, z))
np.testing.assert_array_equal(np.array(knp.Add()(x, y)), np.add(x, y))
np.testing.assert_array_equal(np.array(knp.Add()(x, z)), np.add(x, z))
def test_subtract(self):
x = np.array([[1, 2, 3], [3, 2, 1]])
y = np.array([[4, 5, 6], [3, 2, 1]])
z = np.array([[[1, 2, 3], [3, 2, 1]]])
np.testing.assert_array_equal(
np.array(knp.subtract(x, y)), np.subtract(x, y)
)
np.testing.assert_array_equal(
np.array(knp.subtract(x, z)), np.subtract(x, z)
)
np.testing.assert_array_equal(
np.array(knp.Subtract()(x, y)), np.subtract(x, y)
)
np.testing.assert_array_equal(
np.array(knp.Subtract()(x, z)), np.subtract(x, z)
)
def test_multiply(self):
x = np.array([[1, 2, 3], [3, 2, 1]])
y = np.array([[4, 5, 6], [3, 2, 1]])
z = np.array([[[1, 2, 3], [3, 2, 1]]])
np.testing.assert_array_equal(
np.array(knp.multiply(x, y)), np.multiply(x, y)
)
np.testing.assert_array_equal(
np.array(knp.multiply(x, z)), np.multiply(x, z)
)
np.testing.assert_array_equal(
np.array(knp.Multiply()(x, y)), np.multiply(x, y)
)
np.testing.assert_array_equal(
np.array(knp.Multiply()(x, z)), np.multiply(x, z)
)
def test_matmul(self):
x = np.ones([2, 3, 4, 5])
y = np.ones([2, 3, 5, 6])
z = np.ones([5, 6])
np.testing.assert_array_equal(
np.array(knp.matmul(x, y)), np.matmul(x, y)
)
np.testing.assert_array_equal(
np.array(knp.matmul(x, z)), np.matmul(x, z)
)
np.testing.assert_array_equal(
np.array(knp.Matmul()(x, y)), np.matmul(x, y)
)
np.testing.assert_array_equal(
np.array(knp.Matmul()(x, z)), np.matmul(x, z)
)
def test_power(self):
x = np.array([[1, 2, 3], [3, 2, 1]])
y = np.array([[4, 5, 6], [3, 2, 1]])
z = np.array([[[1, 2, 3], [3, 2, 1]]])
np.testing.assert_array_equal(np.array(knp.power(x, y)), np.power(x, y))
np.testing.assert_array_equal(np.array(knp.power(x, z)), np.power(x, z))
np.testing.assert_array_equal(
np.array(knp.Power()(x, y)), np.power(x, y)
)
np.testing.assert_array_equal(
np.array(knp.Power()(x, z)), np.power(x, z)
)
def test_divide(self):
x = np.array([[1, 2, 3], [3, 2, 1]])
y = np.array([[4, 5, 6], [3, 2, 1]])
z = np.array([[[1, 2, 3], [3, 2, 1]]])
np.testing.assert_array_equal(
np.array(knp.divide(x, y)), np.divide(x, y)
)
np.testing.assert_array_equal(
np.array(knp.divide(x, z)), np.divide(x, z)
)
np.testing.assert_array_equal(
np.array(knp.Divide()(x, y)), np.divide(x, y)
)
np.testing.assert_array_equal(
np.array(knp.Divide()(x, z)), np.divide(x, z)
)
def test_true_divide(self):
x = np.array([[1, 2, 3], [3, 2, 1]])
y = np.array([[4, 5, 6], [3, 2, 1]])
z = np.array([[[1, 2, 3], [3, 2, 1]]])
np.testing.assert_array_equal(
np.array(knp.true_divide(x, y)), np.true_divide(x, y)
)
np.testing.assert_array_equal(
np.array(knp.true_divide(x, z)), np.true_divide(x, z)
)
np.testing.assert_array_equal(
np.array(knp.TrueDivide()(x, y)), np.true_divide(x, y)
)
np.testing.assert_array_equal(
np.array(knp.TrueDivide()(x, z)), np.true_divide(x, z)
)
def test_append(self):
x = np.array([[1, 2, 3], [3, 2, 1]])
y = np.array([[4, 5, 6], [3, 2, 1]])
z = np.array([[[1, 2, 3], [3, 2, 1]], [[4, 5, 6], [3, 2, 1]]])
np.testing.assert_array_equal(
np.array(knp.append(x, y)), np.append(x, y)
)
np.testing.assert_array_equal(
np.array(knp.append(x, y, axis=1)), np.append(x, y, axis=1)
)
np.testing.assert_array_equal(
np.array(knp.append(x, z)), np.append(x, z)
)
np.testing.assert_array_equal(
np.array(knp.Append()(x, y)), np.append(x, y)
)
np.testing.assert_array_equal(
np.array(knp.Append(axis=1)(x, y)), np.append(x, y, axis=1)
)
np.testing.assert_array_equal(
np.array(knp.Append()(x, z)), np.append(x, z)
)
class NumpyOneInputOpsCorrectnessTest(testing.TestCase):
def test_mean(self):
x = np.array([[1, 2, 3], [3, 2, 1]])
np.testing.assert_array_equal(np.array(knp.mean(x)), np.mean(x))
np.testing.assert_array_equal(
np.array(knp.mean(x, axis=1)), np.mean(x, axis=1)
)
np.testing.assert_array_equal(
np.array(knp.mean(x, axis=1, keepdims=True)),
np.mean(x, axis=1, keepdims=True),
)
np.testing.assert_array_equal(np.array(knp.Mean()(x)), np.mean(x))
np.testing.assert_array_equal(
np.array(knp.Mean(axis=1)(x)), np.mean(x, axis=1)
)
np.testing.assert_array_equal(
np.array(knp.Mean(axis=1, keepdims=True)(x)),
np.mean(x, axis=1, keepdims=True),
)
def test_all(self):
x = np.array([[True, False, True], [True, True, True]])
np.testing.assert_array_equal(np.array(knp.all(x)), np.all(x))
np.testing.assert_array_equal(
np.array(knp.all(x, axis=1)), np.all(x, axis=1)
)
np.testing.assert_array_equal(
np.array(knp.all(x, axis=1, keepdims=True)),
np.all(x, axis=1, keepdims=True),
)
np.testing.assert_array_equal(np.array(knp.All()(x)), np.all(x))
np.testing.assert_array_equal(
np.array(knp.All(axis=1)(x)), np.all(x, axis=1)
)
np.testing.assert_array_equal(
np.array(knp.All(axis=1, keepdims=True)(x)),
np.all(x, axis=1, keepdims=True),
)
def test_var(self):
x = np.array([[1, 2, 3], [3, 2, 1]])
np.testing.assert_array_equal(np.array(knp.var(x)), np.var(x))
np.testing.assert_array_equal(
np.array(knp.var(x, axis=1)), np.var(x, axis=1)
)
np.testing.assert_array_equal(
np.array(knp.var(x, axis=1, keepdims=True)),
np.var(x, axis=1, keepdims=True),
)
np.testing.assert_array_equal(np.array(knp.Var()(x)), np.var(x))
np.testing.assert_array_equal(
np.array(knp.Var(axis=1)(x)), np.var(x, axis=1)
)
np.testing.assert_array_equal(
np.array(knp.Var(axis=1, keepdims=True)(x)),
np.var(x, axis=1, keepdims=True),
)
def test_sum(self):
x = np.array([[1, 2, 3], [3, 2, 1]])
np.testing.assert_array_equal(np.array(knp.sum(x)), np.sum(x))
np.testing.assert_array_equal(
np.array(knp.sum(x, axis=1)), np.sum(x, axis=1)
)
np.testing.assert_array_equal(
np.array(knp.sum(x, axis=1, keepdims=True)),
np.sum(x, axis=1, keepdims=True),
)
np.testing.assert_array_equal(np.array(knp.Sum()(x)), np.sum(x))
np.testing.assert_array_equal(
np.array(knp.Sum(axis=1)(x)), np.sum(x, axis=1)
)
np.testing.assert_array_equal(
np.array(knp.Sum(axis=1, keepdims=True)(x)),
np.sum(x, axis=1, keepdims=True),
)
def test_amax(self):
x = np.array([[1, 2, 3], [3, 2, 1]])
np.testing.assert_array_equal(np.array(knp.amax(x)), np.amax(x))
np.testing.assert_array_equal(
np.array(knp.amax(x, axis=1)), np.amax(x, axis=1)
)
np.testing.assert_array_equal(
np.array(knp.amax(x, axis=1, keepdims=True)),
np.amax(x, axis=1, keepdims=True),
)
np.testing.assert_array_equal(np.array(knp.Amax()(x)), np.amax(x))
np.testing.assert_array_equal(
np.array(knp.Amax(axis=1)(x)), np.amax(x, axis=1)
)
np.testing.assert_array_equal(
np.array(knp.Amax(axis=1, keepdims=True)(x)),
np.amax(x, axis=1, keepdims=True),
)
def test_amin(self):
x = np.array([[1, 2, 3], [3, 2, 1]])
np.testing.assert_array_equal(np.array(knp.amin(x)), np.amin(x))
np.testing.assert_array_equal(
np.array(knp.amin(x, axis=1)), np.amin(x, axis=1)
)
np.testing.assert_array_equal(
np.array(knp.amin(x, axis=1, keepdims=True)),
np.amin(x, axis=1, keepdims=True),
)
np.testing.assert_array_equal(np.array(knp.Amin()(x)), np.amin(x))
np.testing.assert_array_equal(
np.array(knp.Amin(axis=1)(x)), np.amin(x, axis=1)
)
np.testing.assert_array_equal(
np.array(knp.Amin(axis=1, keepdims=True)(x)),
np.amin(x, axis=1, keepdims=True),
)
def test_square(self):
x = np.array([[1, 2, 3], [3, 2, 1]])
np.testing.assert_array_equal(np.array(knp.square(x)), np.square(x))
np.testing.assert_array_equal(np.array(knp.Square()(x)), np.square(x))
def test_negative(self):
x = np.array([[1, 2, 3], [3, 2, 1]])
np.testing.assert_array_equal(np.array(knp.negative(x)), np.negative(x))
np.testing.assert_array_equal(
np.array(knp.Negative()(x)), np.negative(x)
)
def test_abs(self):
x = np.array([[1, 2, 3], [3, 2, 1]])
np.testing.assert_array_equal(np.array(knp.abs(x)), np.abs(x))
np.testing.assert_array_equal(np.array(knp.Abs()(x)), np.abs(x))
def test_absolute(self):
x = np.array([[1, 2, 3], [3, 2, 1]])
np.testing.assert_array_equal(np.array(knp.absolute(x)), np.absolute(x))
np.testing.assert_array_equal(
np.array(knp.Absolute()(x)), np.absolute(x)
)
def test_squeeze(self):
x = np.ones([1, 2, 3, 4, 5])
np.testing.assert_array_equal(np.array(knp.squeeze(x)), np.squeeze(x))
np.testing.assert_array_equal(
np.array(knp.squeeze(x, axis=0)), np.squeeze(x, axis=0)
)
np.testing.assert_array_equal(np.array(knp.Squeeze()(x)), np.squeeze(x))
np.testing.assert_array_equal(
np.array(knp.Squeeze(axis=0)(x)), np.squeeze(x, axis=0)
)
def test_transpose(self):
x = np.ones([1, 2, 3, 4, 5])
np.testing.assert_array_equal(
np.array(knp.transpose(x)), np.transpose(x)
)
np.testing.assert_array_equal(
np.array(knp.transpose(x, axes=(1, 0, 3, 2, 4))),
np.transpose(x, axes=(1, 0, 3, 2, 4)),
)
np.testing.assert_array_equal(
np.array(knp.Transpose()(x)), np.transpose(x)
)
np.testing.assert_array_equal(
np.array(knp.Transpose(axes=(1, 0, 3, 2, 4))(x)),
np.transpose(x, axes=(1, 0, 3, 2, 4)),
)
class NumpyArrayCreateOpsCorrectnessTest(testing.TestCase):
def test_ones(self):
np.testing.assert_array_equal(
np.array(knp.ones([2, 3])), np.ones([2, 3])
)
np.testing.assert_array_equal(
np.array(knp.Ones()([2, 3])), np.ones([2, 3])
)
def test_zeros(self):
np.testing.assert_array_equal(
np.array(knp.zeros([2, 3])), np.zeros([2, 3])
)
np.testing.assert_array_equal(
np.array(knp.Zeros()([2, 3])), np.zeros([2, 3])
)
def test_eye(self):
np.testing.assert_array_equal(np.array(knp.eye(3)), np.eye(3))
np.testing.assert_array_equal(np.array(knp.eye(3, 4)), np.eye(3, 4))
np.testing.assert_array_equal(
np.array(knp.eye(3, 4, 1)), np.eye(3, 4, 1)
)
np.testing.assert_array_equal(np.array(knp.Eye()(3)), np.eye(3))
np.testing.assert_array_equal(np.array(knp.Eye()(3, 4)), np.eye(3, 4))
np.testing.assert_array_equal(
np.array(knp.Eye()(3, 4, 1)), np.eye(3, 4, 1)
)

@ -1,7 +1,7 @@
from keras_core import backend
from keras_core.backend.keras_tensor import any_symbolic_tensors from keras_core.backend.keras_tensor import any_symbolic_tensors
from keras_core.operations.node import Node from keras_core.operations.node import Node
from keras_core.utils.naming import auto_name from keras_core.utils.naming import auto_name
from keras_core import backend
class Operation: class Operation:

@ -1,9 +1,10 @@
from keras_core.operations import operation import numpy as np
from keras_core.engine import keras_tensor
from keras_core.operations import numpy as knp
from keras_core import backend from keras_core import backend
from keras_core import testing from keras_core import testing
import numpy as np from keras_core.engine import keras_tensor
from keras_core.operations import numpy as knp
from keras_core.operations import operation
class OpWithMultipleInputs(operation.Operation): class OpWithMultipleInputs(operation.Operation):

@ -8,7 +8,7 @@ truncated_normal
dropout dropout
""" """
from keras_core.backend.random import normal
from keras_core.backend.random import uniform
from keras_core.backend.random import truncated_normal
from keras_core.backend.random import dropout from keras_core.backend.random import dropout
from keras_core.backend.random import normal
from keras_core.backend.random import truncated_normal
from keras_core.backend.random import uniform

@ -1,4 +1,5 @@
from tensorflow import nest from tensorflow import nest
from keras_core.backend import KerasTensor from keras_core.backend import KerasTensor

@ -1,13 +1,14 @@
from keras_core import backend
from keras_core import operations as ops
from keras_core.utils.tracking import Tracker
from keras_core import initializers
from keras_core.optimizers.schedules import learning_rate_schedule
from keras_core.utils.naming import auto_name
from keras_core.api_export import keras_core_export
import re import re
import warnings import warnings
from keras_core import backend
from keras_core import initializers
from keras_core import operations as ops
from keras_core.api_export import keras_core_export
from keras_core.optimizers.schedules import learning_rate_schedule
from keras_core.utils.naming import auto_name
from keras_core.utils.tracking import Tracker
@keras_core_export(["keras_core.Optimizer", "keras_core.optimizers.Optimizer"]) @keras_core_export(["keras_core.Optimizer", "keras_core.optimizers.Optimizer"])
class Optimizer: class Optimizer:

@ -1,5 +1,5 @@
from keras_core.optimizers import optimizer
from keras_core import operations as ops from keras_core import operations as ops
from keras_core.optimizers import optimizer
class SGD(optimizer.Optimizer): class SGD(optimizer.Optimizer):

@ -1,5 +1,5 @@
from keras_core.regularizers.regularizers import Regularizer
from keras_core.regularizers.regularizers import L1 from keras_core.regularizers.regularizers import L1
from keras_core.regularizers.regularizers import L2
from keras_core.regularizers.regularizers import L1L2 from keras_core.regularizers.regularizers import L1L2
from keras_core.regularizers.regularizers import L2
from keras_core.regularizers.regularizers import OrthogonalRegularizer from keras_core.regularizers.regularizers import OrthogonalRegularizer
from keras_core.regularizers.regularizers import Regularizer

@ -1,4 +1,5 @@
import math import math
from keras_core import operations as ops from keras_core import operations as ops
from keras_core.api_export import keras_core_export from keras_core.api_export import keras_core_export

@ -1,9 +1,10 @@
from keras_core import testing import numpy as np
from keras_core import backend
from keras_core import initializers from keras_core import initializers
from keras_core import operations as ops from keras_core import operations as ops
from keras_core import backend
from keras_core import regularizers from keras_core import regularizers
import numpy as np from keras_core import testing
# TODO: serialization tests # TODO: serialization tests

@ -1,6 +1,7 @@
import numpy as np
import unittest import unittest
import numpy as np
class TestCase(unittest.TestCase): class TestCase(unittest.TestCase):
def assertAllClose(self, x1, x2, atol=1e-7, rtol=1e-7): def assertAllClose(self, x1, x2, atol=1e-7, rtol=1e-7):

@ -1,8 +1,10 @@
from keras_core.api_export import keras_core_export
import threading
import sys import sys
import threading
from absl import logging from absl import logging
from keras_core.api_export import keras_core_export
INTERACTIVE_LOGGING = threading.local() INTERACTIVE_LOGGING = threading.local()
INTERACTIVE_LOGGING.enable = True INTERACTIVE_LOGGING.enable = True

@ -79,7 +79,8 @@ def print_summary(
matches `layer_range[1]`. By default (`None`) all matches `layer_range[1]`. By default (`None`) all
layers in the model are included in the summary. layers in the model are included in the summary.
""" """
from keras_core.models import Sequential, Functional from keras_core.models import Functional
from keras_core.models import Sequential
if print_fn is None: if print_fn is None:
print_fn = io_utils.print_msg print_fn = io_utils.print_msg

@ -1,4 +1,7 @@
absl
tensorflow tensorflow
jax jax[cpu]
namex namex
black>=22
flake8
isort
pytest

53
setup.cfg Normal file

@ -0,0 +1,53 @@
[tool:pytest]
filterwarnings =
error
ignore::DeprecationWarning
ignore::ImportWarning
ignore::RuntimeWarning
ignore::PendingDeprecationWarning
ignore::FutureWarning
ignore::UserWarning
# Ignore a spurious warning on tf-nightly related to save model changes.
ignore:Custom mask layers require a config
addopts=-vv
# Do not run tests in the `build` folders
norecursedirs = build
[isort]
known_first_party = keras_core,tests
default_section = THIRDPARTY
line_length = 80
profile = black
[coverage:report]
exclude_lines =
pragma: no cover
@abstract
raise NotImplementedError
omit = *_test.py
[flake8]
ignore =
# Conflicts with black
E203
# defaults flake8 ignores
E121,E123,E126,E226,E24,E704,W503,W504
# Function name should be lowercase
N802
# lowercase ... imported as non lowercase
# Useful to ignore for "import keras.backend as K"
N812
# do not use bare 'except'
E722
exclude =
*_pb2.py
*_pb2_grpc.py
#imported but unused in __init__.py, that's ok.
per-file-ignores = **/__init__.py:F401
max-line-length = 80

42
setup.py Normal file

@ -0,0 +1,42 @@
"""Setup script."""
import pathlib
from setuptools import find_packages
from setuptools import setup
HERE = pathlib.Path(__file__).parent
setup(
name="keras-core",
description="Multi-backend Keras.",
long_description_content_type="text/markdown",
version="0.1.0",
url="https://github.com/keras-team/keras-core",
author="Keras team",
author_email="keras@google.com",
license="Apache License 2.0",
install_requires=[
"absl-py",
"numpy",
"packaging",
],
# Supported Python versions
python_requires=">=3.8",
classifiers=[
"Development Status :: 3 - Alpha",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3 :: Only",
"Operating System :: Unix",
"Operating System :: Microsoft :: Windows",
"Operating System :: MacOS",
"Intended Audience :: Science/Research",
"Topic :: Scientific/Engineering",
"Topic :: Software Development",
],
packages=find_packages(exclude=("*_test.py",)),
)

9
shell/format.sh Normal file

@ -0,0 +1,9 @@
#!/bin/bash -e
base_dir=$(dirname $(dirname $0))
targets="${base_dir}/*.py ${base_dir}/keras_core/"
isort --sp "${base_dir}/setup.cfg" --sl ${targets}
black --line-length 80 ${targets}
flake8 --config "${base_dir}/setup.cfg" --max-line-length=200 ${targets}

@ -1,9 +1,9 @@
from keras_core import backend from keras_core import backend
from keras_core.layers.layer import Layer
from keras_core.backend import KerasTensor
from keras_core.operations.function import Function
from keras_core import initializers from keras_core import initializers
from keras_core.backend import KerasTensor
from keras_core.layers.layer import Layer
from keras_core.operations import numpy as knp from keras_core.operations import numpy as knp
from keras_core.operations.function import Function
class MiniDense(Layer): class MiniDense(Layer):