blob: 5e86ab8da76dbda8563ed6da3efa4dfa9b038a01 [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
""" Support level3 operator test cases.
"""
import sys
from typing import Callable, Optional
import numpy as np
import pytest
import tvm
import tvm.testing
from tvm import relay, te
from tvm.error import TVMError
from tvm.relay import create_executor, transform
from tvm.relay.testing import check_grad, run_infer_type
from utils import ref_funcs
executor_kind = tvm.testing.parameter("graph", "vm")
class TestZerosOnes:
config = {"zeros": (relay.zeros, np.zeros), "ones": (relay.ones, np.ones)}
op, ref = tvm.testing.parameters(*config.values(), ids=config.keys())
def test_zeros_ones(self, op, ref):
y = op(shape=(124, 50), dtype="float64")
yy = run_infer_type(y)
assert yy.checked_type == relay.TensorType((124, 50), "float64")
intrp_res = create_executor().evaluate(y).numpy()
np.testing.assert_allclose(intrp_res, ref((124, 50), "float64"))
class TestUnaryIdentity:
config = {
"zeros_like": (relay.zeros_like, np.zeros_like),
"ones_like": (relay.ones_like, np.ones_like),
"ceil": (relay.ceil, np.ceil),
"floor": (relay.floor, np.floor),
"trunc": (relay.trunc, np.trunc),
"round": (relay.round, np.round),
"abs": (relay.abs, np.abs),
"copy": (relay.copy, None), # np.copy
"negative": (relay.negative, np.negative),
"sign": (relay.sign, np.sign),
}
op, ref = tvm.testing.parameters(*config.values(), ids=config.keys())
def test_unary_identity(self, op, ref):
shape = (8, 9, 4)
x = relay.var("x", relay.TensorType(shape, "float32"))
y = op(x)
yy = run_infer_type(y)
assert yy.checked_type == relay.TensorType(shape, "float32")
if ref is not None:
data = np.random.rand(*shape).astype("float32")
op_res = create_executor().evaluate(y, {x: relay.const(data)})
ref_res = ref(data)
np.testing.assert_allclose(op_res.numpy(), ref_res, rtol=0.01)
def test_cast():
x = relay.var("x", relay.TensorType((8, 9, 4), "float32"))
y = x.astype("int32")
yy = run_infer_type(y)
assert "dtype=" in yy.astext()
assert yy.checked_type == relay.TensorType((8, 9, 4), "int32")
x = relay.var("x", relay.TensorType((8, 9, 4), "float32"))
y = relay.cast(x, "int32")
yy = run_infer_type(y)
assert "dtype=" in yy.astext()
assert yy.checked_type == relay.TensorType((8, 9, 4), "int32")
def test_sliding_window():
# Slide a window of shape (3, 4, 5) over the x tensor, beginning with
# dimension 1, which slides the window over the two subtensors of shape (3,
# 32, 32).
x = relay.var("x", relay.TensorType((2, 3, 32, 32), "float32"))
y = relay.sliding_window(x, 1, [3, 4, 5], [1, 2, 3])
# The resulting shape still has batch size 2. Each dimension in (1, 15, 10)
# represents the locations where we were able to form a window; that is, we
# were able to place the window in one place along the dimension of length
# 3, 15 places along the dimension of length 32 (when striding by 2), and 10
# places along the second dimension of length 32 (when striding by 3). The
# remaining dimensions (3, 4, 5) represent the formed windows.
yy = run_infer_type(y)
assert yy.checked_type == relay.TensorType((2, 1, 15, 10, 3, 4, 5), "float32")
data = np.random.rand(2, 3, 32, 32).astype("float32")
intrp = create_executor()
result = intrp.evaluate(y, {x: relay.const(data)})
result_np = result.numpy()
assert result_np.shape == (2, 1, 15, 10, 3, 4, 5)
assert np.array_equal(result_np[0, 0, 0, 0, :, :, :], data[0, :, 0:4, 0:5])
assert np.array_equal(result_np[1, 0, 7, 3, :, :, :], data[1, :, 14:18, 9:14])
assert np.array_equal(result_np[1, 0, 14, 9, :, :, :], data[1, :, 28:32, 27:32])
def test_clip():
a = relay.var("a", relay.TensorType((10, 4), "float32"))
y = relay.clip(a, 1.0, 4.0)
yy = run_infer_type(y)
assert yy.checked_type == relay.TensorType((10, 4), "float32")
data = np.random.rand(10, 4).astype("float32")
op_res = create_executor().evaluate(y, {a: relay.const(data)})
ref_res = np.clip(data, 1.0, 4.0)
np.testing.assert_allclose(op_res.numpy(), ref_res, rtol=0.01)
def test_fixed_point_multiply():
# Test 23 * 1/16
# [m,s] = [0.5, -3] = frexp(1/16)
# M = 0.5*2^31 = 1073741824
# so M = 1073741824 and s = -3
a = relay.var("a", relay.TensorType((10, 4), "int32"))
y = relay.fixed_point_multiply(a, 1073741824, -3)
yy = run_infer_type(y)
assert yy.checked_type == relay.TensorType((10, 4), "int32")
data = 23 * np.ones((10, 4)).astype("int32")
op_res = create_executor().evaluate(y, {a: relay.const(data)})
ref_res = np.ones((10, 4)).astype("int32")
np.testing.assert_allclose(op_res.numpy(), ref_res, atol=1)
def test_reinterpret():
a = relay.var("a", relay.TensorType((1000, 4), "float32"))
y = relay.reinterpret(a, "int32")
yy = run_infer_type(y)
assert yy.checked_type == relay.TensorType((1000, 4), "int32")
data = np.random.randn(1000, 4).astype("float32") * 1000
op_res = create_executor().evaluate(y, {a: relay.const(data)})
ref_res = data.view("int32")
np.testing.assert_equal(op_res.numpy(), ref_res)
def test_approximate_transcendental():
def C(x):
return relay.expr.const(x, "float32")
def approx_exp(x):
# An approximation derived from Opus,
# https://github.com/xiph/opus/blob/c1c247/celt/mathops.h#L147-L165
x = relay.minimum(relay.maximum(x, C(-88.0)), C(88.0))
x = C(127.0) + x * C(1.44269504)
xf = relay.floor(x)
i = relay.cast(xf, "int32")
x = x - xf
Y = C(0.99992522) + x * (C(0.69583354) + x * (C(0.22606716) + x * C(0.078024523)))
exponent = relay.left_shift(i, relay.expr.const(23, "int32"))
exponent = relay.reinterpret(exponent, "float32")
return exponent * Y
def approximate_sigmoid(x):
y = approx_exp(x)
return y / (y + C(1.0))
def approximate_tanh(x):
x = x * C(2.0)
y = approx_exp(x)
return (y - C(1.0)) / (y + C(1.0))
a = relay.var("a", relay.TensorType((1000,), "float32"))
y = approximate_sigmoid(a)
yy = run_infer_type(y)
assert yy.checked_type == relay.TensorType((1000,), "float32")
data = np.linspace(-5, 5, 1000).astype("float32")
op_res = create_executor().evaluate(y, {a: relay.const(data)})
def reference_sigmoid(x):
return np.exp(-np.logaddexp(0, -x))
np.testing.assert_allclose(op_res.numpy(), reference_sigmoid(data), atol=2e-5, rtol=1e-9)
y = approximate_tanh(a)
yy = run_infer_type(y)
assert yy.checked_type == relay.TensorType((1000,), "float32")
data = np.linspace(-5, 5, 1000).astype("float32")
op_res = create_executor().evaluate(y, {a: relay.const(data)})
def reference_tanh(x):
return np.tanh(x)
np.testing.assert_allclose(op_res.numpy(), reference_tanh(data), atol=4e-5, rtol=1e-9)
class TestSqueeze:
shape, dtype, axis = tvm.testing.parameters(
((1, 3, 2, 5), "float32", None),
((1, 3, 1), "float32", [0]),
((1, 2, 1, 2, 1), "float32", [0, 2]),
((1, 3, 1), "float32", 2),
((1, 3, 1), "float32", []),
)
def test_squeeze(self, shape, dtype, axis):
x = relay.var("x", relay.TensorType(shape, dtype))
squeeze = relay.squeeze(x, axis=axis)
if isinstance(axis, int):
np_axis = (axis,)
else:
np_axis = tuple(axis) if axis is not None else None
data = np.random.random_sample(shape).astype(dtype)
op_res = create_executor().evaluate(squeeze, {x: relay.const(data)})
ref_res = np.squeeze(data, axis=np_axis)
np.testing.assert_allclose(op_res.numpy(), ref_res, rtol=0.01)
def test_transpose_infer_type():
n, t, d = te.size_var("n"), te.size_var("t"), 100
x = relay.var("x", relay.TensorType((n, t, d), "float32"))
y = relay.transpose(x, axes=(1, 0, 2))
assert "axes=" in y.astext()
yy = run_infer_type(y)
assert yy.checked_type == relay.TensorType((t, n, 100), "float32")
y = relay.transpose(x)
assert "axes=" in y.astext()
yy = run_infer_type(y)
assert yy.checked_type == relay.TensorType((100, t, n), "float32")
def test_transpose(target, dev, executor_kind):
dshape = (2, 3, 4)
axes = (0, 2, 1)
x = relay.var("x", relay.TensorType(dshape, "float32"))
z = relay.transpose(x, axes=axes)
func = relay.Function([x], z)
x_data = np.random.uniform(low=-1, high=1, size=dshape).astype("float32")
ref_res = np.transpose(x_data, axes=axes)
op_res = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)(x_data)
tvm.testing.assert_allclose(op_res.numpy(), ref_res, rtol=1e-5)
def test_squeeze_infer_type():
n, t, d = 1, 4, 1
x = relay.var("x", relay.TensorType((n, t, d), "float32"))
y = relay.squeeze(x, axis=(2,))
assert "axis=" in y.astext()
yy = run_infer_type(y)
assert yy.checked_type == relay.TensorType((1, 4), "float32")
n, t, d = 1, 4, 1
x = relay.var("x", relay.TensorType((n, t, d), "float32"))
y = relay.squeeze(x)
assert "axis=" not in y.astext()
yy = run_infer_type(y)
assert yy.checked_type == relay.TensorType((4,), "float32")
@pytest.mark.xfail(raises=tvm._ffi.base.TVMError)
def test_squeeze_bad_axes_infer_type():
n, t, d = 1, 4, 1
x = relay.var("x", relay.TensorType((n, t, d), "float32"))
y = relay.squeeze(x, axis=(1,))
yy = run_infer_type(y)
def test_reshape_infer_type():
n, t, d1, d2 = 10, 20, 100, 20
x = relay.var("x", relay.TensorType((n, t, d1, d2), "float32"))
y = relay.reshape(x, newshape=(n, t, 2000))
assert "newshape=" in y.astext()
yy = run_infer_type(y)
assert yy.checked_type == relay.TensorType((n, t, 2000), "float32")
class TestReshape:
shape, newshape, oshape = tvm.testing.parameters(
((2, 3, 4), (8, 3), (8, 3)),
((4, 7), (2, 7, 2), (2, 7, 2)),
((2, 3, 4), (4, 0, 2), (4, 3, 2)),
((2, 3, 4), (2, 0, 0), (2, 3, 4)),
((2, 3, 4), (0, -1), (2, 12)),
((2, 3, 4), (-1, 0), (8, 3)),
((2, 3, 4), (2, -2), (2, 3, 4)),
((2, 3, 4), (-2, 1, 1), (2, 3, 4, 1, 1)),
((2, 3, 4), (-3, 4), (6, 4)),
((2, 3, 4, 5), (-3, -3), (6, 20)),
((2, 3, 4), (0, -3), (2, 12)),
((2, 3, 4), (-3, -2), (6, 4)),
((2, 3, 4), (-4, 1, 2, -2), (1, 2, 3, 4)),
((2, 3, 4), (2, -4, -1, 3, -2), (2, 1, 3, 4)),
((1,), (), ()),
)
def test_reshape(self, target, dev, executor_kind, shape, newshape, oshape):
x = relay.var("x", relay.TensorType(shape, "float32"))
z = relay.reshape(x, newshape=newshape)
zz = run_infer_type(z)
assert "newshape=" in z.astext()
assert zz.checked_type == relay.ty.TensorType(oshape, "float32")
func = relay.Function([x], z)
check_grad(func)
x_data = np.random.uniform(low=-1, high=1, size=shape).astype("float32")
ref_res = np.reshape(x_data, oshape)
op_res = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)(
x_data
)
tvm.testing.assert_allclose(op_res.numpy(), ref_res, rtol=1e-5)
def test_reshape_fail():
with pytest.raises(TVMError) as reshape_err:
x = relay.var("x", relay.TensorType([2, 3], "float32"))
z = relay.reshape(x, [7])
zz = run_infer_type(z)
def test_reshape_like_infer_type():
# concrete shape
x = relay.var("x", relay.TensorType((1, 2, 3), "float32"))
y = relay.var("y", relay.TensorType((1, 6), "float32"))
z = relay.reshape_like(x, y)
zz = run_infer_type(z)
assert zz.checked_type == relay.TensorType((1, 6), "float32")
# symbolic shape
n, c, h, w = te.size_var("n"), 2, 3, te.size_var("w")
x = relay.var("x", relay.TensorType((n, c, h, w), "float32"))
y = relay.var("y", relay.TensorType((1, 8, 8), "float32"))
z = relay.reshape_like(x, y)
zz = run_infer_type(z)
assert zz.checked_type == relay.TensorType((1, 8, 8), "float32")
# partial reshaping
x = relay.var("x", relay.TensorType((1, 2, 3, 4), "float32"))
y = relay.var("y", relay.TensorType((1, 6, 5), "float32"))
z = relay.reshape_like(x, y, lhs_begin=1, lhs_end=3, rhs_begin=1, rhs_end=2)
zz = run_infer_type(z)
assert zz.checked_type == relay.TensorType((1, 6, 4), "float32")
x = relay.var("x", relay.TensorType((1, 2, 3, 4), "float32"))
y = relay.var("y", relay.TensorType((2, 3, 4, 1, 6), "float32"))
z = relay.reshape_like(x, y, rhs_end=3)
zz = run_infer_type(z)
assert zz.checked_type == relay.TensorType((2, 3, 4), "float32")
z = relay.reshape_like(x, y, rhs_begin=2)
zz = run_infer_type(z)
assert zz.checked_type == relay.TensorType((4, 1, 6), "float32")
# symbolic partial reshaping
n, c, h, w = te.size_var("n"), 2, 3, te.size_var("w")
x = relay.var("x", relay.TensorType((n, c, h, w), "float32"))
y = relay.var("y", relay.TensorType((5, 6), "float32"))
z = relay.var("z", relay.TensorType((4,), "float32"))
w = relay.reshape_like(x, y, lhs_end=3)
w = relay.reshape_like(w, z, lhs_begin=2)
w = run_infer_type(w)
assert w.checked_type == relay.TensorType((5, 6, 4), "float32")
class TestReshapeLike:
shape, oshape, shape_like, reshape_like_kwargs = tvm.testing.parameters(
((2, 3, 4), (1, 8, 3), None, {}),
((4, 7), (2, 7, 2), None, {}),
((1, 2, 3, 4), (1, 6, 4), (1, 6, 5), dict(lhs_begin=1, lhs_end=3, rhs_begin=1, rhs_end=2)),
)
def test_reshape_like(
self, target, dev, executor_kind, shape, oshape, shape_like=None, reshape_like_kwargs={}
):
if shape_like is None:
shape_like = oshape
x_data = np.random.uniform(low=-1, high=1, size=shape).astype("float32")
y_data = np.random.uniform(low=-1, high=1, size=shape_like).astype("float32")
ref_res = np.reshape(x_data, oshape)
x = relay.var("x", relay.TensorType(shape, "float32"))
y = relay.var("x", relay.TensorType(shape_like, "float32"))
z = relay.reshape_like(x, y, **reshape_like_kwargs)
zz = run_infer_type(z)
assert zz.checked_type == relay.ty.TensorType(ref_res.shape, "float32")
func = relay.Function([x, y], z)
op_res = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)(
x_data, y_data
)
tvm.testing.assert_allclose(op_res.numpy(), ref_res, rtol=1e-5)
class TestTakeInferType:
d1, d2, d3 = te.var("d1"), te.var("d2"), te.var("d3")
d4, d5, d6 = te.var("d4"), te.var("d5"), te.var("d6")
dshape, indices_shape, oshape, axis = tvm.testing.parameters(
((d1,), (1,), (1,), 0),
((4,), (d1, d2), (d1, d2), None),
((3, 3, 3), (1, d2), (1, d2), None),
((d1, d2), (d3, d4, d5), (d3, d4, d5, d2), 0),
((d1, d2), (d3, d4, d5), (d1, d3, d4, d5), 1),
((d1, d2, d3, d4), (d5, d6), (d1, d2, d5, d6, d4), -2),
)
def test_take(self, dshape, indices_shape, oshape, axis):
x = relay.var("x", relay.TensorType(dshape, "float32"))
indices = relay.var("indices", relay.TensorType(indices_shape, "int32"))
y = relay.take(x, indices, axis=axis)
yy = run_infer_type(y)
assert yy.checked_type == relay.TensorType(oshape, "float32")
class TestTake:
src_shape, indices_src, axis, mode, indices_dtype = tvm.testing.parameters(
((4,), [1], None, "clip", "int32"),
((4,), [[0, 1, 2, 3]], None, "clip", "int32"),
((3, 3, 3), [[11, 25]], None, "clip", "int32"),
((4,), [[0, 1], [2, 3]], None, "clip", "int32"),
((4,), [1], 0, "clip", "int32"),
((2, 2), [[[1, 0], [0, 1]]], 0, "clip", "int32"),
((2, 2), [[[1, 0], [0, 1]]], 1, "clip", "int32"),
((4, 3, 5, 6), [[2, 1, 0, 0]], -2, "clip", "int32"),
((3, 4), [-5, 20], None, "clip", "int32"),
((3, 4), [-5, 20], None, "wrap", "int32"),
((3, 4), [-1, 2], 0, "clip", "int32"),
((3, 4), [-1, 2], 0, "wrap", "int32"),
((3, 4), [-1, 2], 1, "clip", "int32"),
((3, 4), [-1, 2], 1, "wrap", "int32"),
((3, 3, 3), [[11, 25]], None, "fast", "int32"),
((3, 4), [0, 2], 0, "fast", "int32"),
((3, 4), [0, 2], 1, "fast", "int32"),
((3, 4), [1, 2], 1, "clip", "uint32"),
((3, 4), [1, 2], 1, "wrap", "uint16"),
((3, 3, 3), [1, 2], None, "fast", "uint16"),
((3, 4), [0, 2], 0, "fast", "uint8"),
)
# Incorrect numeric output in some cases on vulkan
@tvm.testing.known_failing_targets("vulkan")
def test_take(
self, target, dev, executor_kind, src_shape, indices_src, axis, mode, indices_dtype
):
src_dtype = "float32"
indices_src = np.array(indices_src, dtype=indices_dtype)
x = relay.var("x", relay.TensorType(src_shape, src_dtype))
indices = relay.var("indices", relay.TensorType(indices_src.shape, indices_dtype))
z = relay.take(x, indices, axis=axis, mode=mode)
func = relay.Function([x, indices], z)
x_data = np.random.uniform(low=-1, high=1, size=src_shape).astype(src_dtype)
np_mode = "raise" if mode == "fast" else mode
op_res = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)(
x_data, indices_src
)
# Old versions of numpy has take internally cast inside take which may violate
# safety rules. We have such version in i386 CI image.
indices_src = indices_src.astype("int32")
ref_res = np.take(x_data, indices=indices_src, axis=axis, mode=np_mode)
tvm.testing.assert_allclose(op_res.numpy(), ref_res, rtol=1e-5)
class TestSplitInferType:
idxd = tvm.tir.indexdiv
d1, d2, d3, d4 = te.var("d1"), te.var("d2"), te.var("d3"), te.var("d4")
axis = te.var("axis")
dshape, indices_or_sections, ret_type, axis = tvm.testing.parameters(
(
(5, 5, 2, 2),
5,
relay.ty.TupleType(
tvm.runtime.convert(
[
relay.ty.TensorType((5, 1, 2, 2), "float32"),
relay.ty.TensorType((5, 1, 2, 2), "float32"),
relay.ty.TensorType((5, 1, 2, 2), "float32"),
relay.ty.TensorType((5, 1, 2, 2), "float32"),
relay.ty.TensorType((5, 1, 2, 2), "float32"),
]
)
),
1,
),
(
(5, 5, 2, 2),
5,
relay.ty.TupleType(
tvm.runtime.convert(
[
relay.ty.TensorType((1, 5, 2, 2), "float32"),
relay.ty.TensorType((1, 5, 2, 2), "float32"),
relay.ty.TensorType((1, 5, 2, 2), "float32"),
relay.ty.TensorType((1, 5, 2, 2), "float32"),
relay.ty.TensorType((1, 5, 2, 2), "float32"),
]
)
),
0,
),
(
(d1, d2, d3, d4),
4,
relay.ty.TupleType(
tvm.runtime.convert(
[
relay.ty.TensorType((d1, d2, idxd(d3, 4), d4), "float32"),
relay.ty.TensorType((d1, d2, idxd(d3, 4), d4), "float32"),
relay.ty.TensorType((d1, d2, idxd(d3, 4), d4), "float32"),
relay.ty.TensorType((d1, d2, idxd(d3, 4), d4), "float32"),
]
)
),
2,
),
(
(d1, d2, d3, d4),
2,
relay.ty.TupleType(
tvm.runtime.convert(
[
relay.ty.TensorType((idxd(d1, 2), d2, d3, d4), "float32"),
relay.ty.TensorType((idxd(d1, 2), d2, d3, d4), "float32"),
]
)
),
0,
),
(
(d1, d2, d3, d4),
(2, 4, 7),
relay.ty.TupleType(
tvm.runtime.convert(
[
relay.ty.TensorType((d1, 2, d3, d4), "float32"),
relay.ty.TensorType((d1, 2, d3, d4), "float32"),
relay.ty.TensorType((d1, 3, d3, d4), "float32"),
relay.ty.TensorType((d1, (d2 - 7), d3, d4), "float32"),
]
)
),
1,
),
(
(d1, d2, d3, d4),
tuple(np.array([2, 4, 7]).astype(np.int64)),
relay.ty.TupleType(
tvm.runtime.convert(
[
relay.ty.TensorType((d1, 2, d3, d4), "float32"),
relay.ty.TensorType((d1, 2, d3, d4), "float32"),
relay.ty.TensorType((d1, 3, d3, d4), "float32"),
relay.ty.TensorType((d1, (d2 - 7), d3, d4), "float32"),
]
)
),
1,
),
)
def test_split(self, dshape, indices_or_sections, ret_type, axis):
x = relay.var("x", relay.ty.TensorType(dshape, "float32"))
y = relay.split(x, indices_or_sections, axis=axis)
yy = run_infer_type(y.astuple())
assert yy.checked_type == ret_type
def test_full_infer_type():
# default settings: match input dtype
x = relay.var("x", relay.TensorType((), "int8"))
y = relay.full(x, ())
yy = run_infer_type(y)
assert yy.checked_type == relay.TensorType((), "int8")
# change the shape and dtype
x = relay.var("x", relay.TensorType((), "float32"))
y = relay.full(x, (1, 2), "int8")
assert "shape=" in y.astext()
yy = run_infer_type(y)
assert yy.checked_type == relay.TensorType((1, 2), "int8")
class TestFull:
fill_value, arr_shape, dtype = tvm.testing.parameters(
(4, (1, 3, 4, 4), "int32"),
(4, (1, 3, 4, 4), "int64"),
(4.0, (1, 4), "float32"),
)
def test_full(self, target, dev, executor_kind, fill_value, arr_shape, dtype):
x = relay.var("x", relay.scalar_type(dtype))
z = relay.full(x, arr_shape, dtype)
func = relay.Function([x], z)
ref_res = np.full(arr_shape, fill_value, dtype=dtype)
op_res = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)(
np.array(fill_value, dtype)
)
tvm.testing.assert_allclose(op_res.numpy(), ref_res, rtol=1e-5)
def test_full_like(self, target, dev, executor_kind, arr_shape, fill_value, dtype):
x_data = np.random.uniform(low=-1, high=1, size=arr_shape).astype(dtype)
x = relay.var("x", relay.TensorType(arr_shape, dtype))
y = relay.var("y", relay.scalar_type(dtype))
z = relay.full_like(x, y)
func = relay.Function([x, y], z)
ref_res = np.full_like(x_data, fill_value)
op_res = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)(
x_data, np.array(fill_value, dtype)
)
tvm.testing.assert_allclose(op_res.numpy(), ref_res, rtol=1e-5)
def test_full_like_infer_type():
# concrete shape
base = relay.var("base", relay.TensorType((1, 2, 3), "float32"))
fill = relay.var("fill", relay.TensorType((), "float32"))
y = relay.full_like(base, fill)
yy = run_infer_type(y)
assert yy.checked_type == relay.TensorType((1, 2, 3), "float32")
# symbolic shape
n, c, h, w = te.size_var("n"), 2, 3, te.size_var("w")
base = relay.var("base", relay.TensorType((n, c, h, w), "float32"))
fill = relay.var("fill", relay.TensorType((), "float32"))
y = relay.full_like(base, fill)
yy = run_infer_type(y)
assert yy.checked_type == relay.TensorType((n, c, h, w), "float32")
def test_infer_type_leaky_relu(target, dev, executor_kind):
n, c, h, w = te.size_var("n"), te.size_var("c"), te.size_var("h"), te.size_var("w")
x = relay.var("x", relay.TensorType((n, c, h, w), "float32"))
y = relay.nn.leaky_relu(x, alpha=0.1)
"alpha=0.1" in y.astext()
yy = run_infer_type(y)
assert yy.checked_type == relay.TensorType((n, c, h, w), "float32")
shape = (1, 5, 10, 10)
dtype = "float32"
x = relay.var("x", relay.TensorType(shape, dtype))
z = relay.nn.leaky_relu(x, alpha=0.1)
assert "alpha=0.1" in z.astext()
zz = run_infer_type(z)
assert zz.checked_type == relay.TensorType(shape, dtype)
func = relay.Function([x], z)
x_data = np.random.uniform(low=-1, high=1, size=shape).astype(dtype)
ref_res = np.where(x_data > 0, x_data, x_data * 0.1)
op_res = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)(x_data)
tvm.testing.assert_allclose(op_res.numpy(), ref_res, rtol=1e-5)
class TestInferTypePrelu:
dtype = tvm.testing.parameter("float32")
n, c, h, w = te.size_var("n"), te.size_var("c"), te.size_var("h"), te.size_var("w")
data, alpha, axis, output = tvm.testing.parameters(
((n, c, h, w), (c,), 1, (n, c, h, w)),
((n, h, w, c), (c,), 3, (n, h, w, c)),
((n, c, h, w), None, 1, (n, c, h, w)),
((n, h, w, c), None, 3, (n, h, w, c)),
((1, 3, 2, 2), (3,), 1, (1, 3, 2, 2)),
((1, 2, 2, 3), (3,), 3, (1, 2, 2, 3)),
((1, 3, 2, 2), None, 1, (1, 3, 2, 2)),
((1, 2, 2, 3), None, 3, (1, 2, 2, 3)),
)
def test_infer_type_prelu(self, target, dev, executor_kind, data, alpha, axis, output, dtype):
x = relay.var("data", relay.TensorType(data, dtype))
if alpha:
y = relay.var("alpha", relay.TensorType(alpha, dtype))
else:
y = relay.var("alpha", relay.IncompleteType())
z = relay.nn.prelu(x, y, axis=axis)
zz = run_infer_type(z)
if axis != 1:
assert "axis" in z.astext()
assert zz.checked_type == relay.ty.TensorType(output, dtype)
if not alpha:
axis = axis if axis else 1
alpha_shape = (data[axis],)
assert zz.args[1].checked_type == relay.TensorType(alpha_shape, "float32")
if all(isinstance(v, tvm.tir.Var) == 1 for v in data) or not alpha:
return
func = relay.Function([x, y], z)
x_data = np.random.uniform(low=-1, high=1, size=data).astype(dtype)
a_data = np.random.uniform(low=-1, high=1, size=alpha).astype(dtype)
if axis == 1:
ref_res = (x_data < 0) * (x_data * a_data.reshape(3, 1, 1)) + (x_data >= 0) * x_data
else:
ref_res = (x_data < 0) * (x_data * a_data.reshape(1, 1, 3)) + (x_data >= 0) * x_data
op_res = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)(
x_data, a_data
)
tvm.testing.assert_allclose(op_res.numpy(), ref_res, rtol=1e-5)
class TestArange:
dtype = tvm.testing.parameter("float32")
start, stop, step = tvm.testing.parameters(
(None, 20, None),
(None, 20, 2),
(1, 20, None),
(1, 20, 2),
# arange doesnt' support floating point right now, see type relation
# (1, 20, 1.5),
(1, 20.5, None),
(1, 20, 3),
(20, 1, -1),
# arange doesnt' support floating point right now, see type relation
# (20, 1, -1.5),
)
def test_arange(self, target, dev, executor_kind, start, stop, step, dtype):
if start is None and step is None:
x = relay.arange(relay.const(stop, dtype=dtype))
ref_res = np.arange(stop).astype(dtype)
elif start is None:
x = relay.arange(relay.const(stop, dtype=dtype), step=relay.const(step, dtype=dtype))
ref_res = np.arange(stop, step=step).astype(dtype)
elif step is None:
x = relay.arange(relay.const(start, dtype=dtype), relay.const(stop, dtype=dtype))
ref_res = np.arange(start, stop).astype(dtype)
else:
x = relay.arange(
relay.const(start, dtype=dtype),
relay.const(stop, dtype=dtype),
relay.const(step, dtype=dtype),
)
ref_res = np.arange(start, stop, step).astype(dtype)
func = relay.Function([], x)
op_res = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)()
tvm.testing.assert_allclose(op_res.numpy(), ref_res, rtol=1e-5)
class TestMeshgrid:
lengths, indexing = tvm.testing.parameters(
([3, 5], "ij"),
([4, 2], "xy"),
([3, 5, 2], "ij"),
([3, 1, 5], "xy"),
# Length 0 signifies scalar.
([3, 5, 0], "ij"),
)
def test_meshgrid(self, target, dev, executor_kind, lengths, indexing="ij"):
input_vars = []
input_data = []
for i, length in enumerate(lengths):
input_name = "x_{}".format(i)
if length == 0:
# Scalar
input_vars.append(relay.var(input_name, relay.scalar_type("float32")))
input_data.append(np.array(1, "float32"))
else:
input_vars.append(relay.var(input_name, relay.TensorType((length,), "float32")))
input_data.append(np.arange(length).astype("float32"))
z = relay.meshgrid(input_vars, indexing=indexing).astuple()
func = relay.Function(input_vars, z)
# Get ref
ref_res = np.meshgrid(*input_data, indexing=indexing)
op_res = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)(
*input_data
)
assert len(op_res) == len(ref_res)
for i in range(len(op_res)):
tvm.testing.assert_allclose(op_res[i].numpy(), ref_res[i], rtol=1e-5)
class TestTile:
dshape, reps = tvm.testing.parameters(
((2, 3, 4), (3, 2, 1)),
((2, 3, 4), (1, 2)),
((2, 3), (3, 2, 1)),
)
def test_tile(self, target, dev, executor_kind, dshape, reps):
x = relay.var("x", relay.TensorType(dshape, "float32"))
z = relay.tile(x, reps=reps)
func = relay.Function([x], z)
x_data = np.random.uniform(low=-1, high=1, size=dshape).astype("float32")
ref_res = np.tile(x_data, reps=reps)
op_res = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)(
x_data
)
tvm.testing.assert_allclose(op_res.numpy(), ref_res, rtol=1e-5)
class TestRepeat:
dshape, repeats, axis = tvm.testing.parameters(
((3,), 2, 0),
((3, 10), 2, -1),
((3, 2, 4), 3, 1),
)
def test_repeat(self, target, dev, executor_kind, dshape, repeats, axis):
x = relay.Var("x", relay.TensorType(dshape, "float32"))
func = relay.Function([x], relay.repeat(x, repeats, axis))
data = np.random.uniform(size=dshape).astype("float32")
ref_res = np.repeat(data, repeats, axis)
op_res = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)(
data
)
tvm.testing.assert_allclose(op_res.numpy(), ref_res, rtol=1e-5)
class TestStack:
dshapes, axis = tvm.testing.parameters(
([(2,), (2,), (2,)], -1),
([(2,), (2,), (2,)], 0),
([(2, 2, 4), (2, 2, 4), (2, 2, 4)], 1),
([(2, 2, 3, 4), (2, 2, 3, 4), (2, 2, 3, 4), (2, 2, 3, 4)], -1),
([(2, 2, 3, 4), (2, 2, 3, 4), (2, 2, 3, 4), (2, 2, 3, 4)], 4),
)
expr_type = tvm.testing.parameter("tuple", "list", "tuple_expr")
@tvm.testing.fixture
def ref_data(self, dshapes, axis):
np_in = [np.random.normal(size=shape).astype("float32") for shape in dshapes]
np_out = np.stack(np_in, axis=axis)
return np_in, np_out
@tvm.testing.fixture
def input_expr(self, dshapes, axis, expr_type, ref_data):
input_vars = [relay.var("input", relay.TensorType(shape, "float32")) for shape in dshapes]
if expr_type == "tuple":
input_expr = relay.Tuple(input_vars)
elif expr_type == "list":
input_expr = input_vars
elif expr_type == "tuple_expr":
# expression that evaluates to a tuple
# but is not a tuple literal
np_in, np_out = ref_data
x = relay.Var("x")
input_expr = relay.Let(x, relay.Tuple([relay.const(inp) for inp in np_in]), x)
else:
raise ValueError(f"Unknown expr_type '{expr_type}'")
return input_expr
def test_stack(self, target, dev, executor_kind, input_expr, ref_data, axis):
z = relay.stack(input_expr, axis=axis)
inp_vars = relay.analysis.free_vars(z)
func = relay.Function(inp_vars, z)
np_in, np_out = ref_data
relay_args = np_in if inp_vars else []
op_res = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)(
*relay_args
)
tvm.testing.assert_allclose(op_res.numpy(), np_out, rtol=1e-5)
class TestReverse:
dshape, axis = tvm.testing.parameters(
((2, 3, 4), 1),
((4, 7), 0),
((2, 3, 4), -1),
)
def test_reverse(self, target, dev, executor_kind, dshape, axis):
x = relay.var("x", relay.TensorType(dshape, "float32"))
z = relay.reverse(x, axis=axis)
zz = run_infer_type(z)
func = relay.Function([x], z)
x_data = np.random.uniform(low=-1, high=1, size=dshape).astype("float32")
ref_res = np.flip(x_data, axis)
op_res = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)(
x_data
)
tvm.testing.assert_allclose(op_res.numpy(), ref_res, rtol=1e-5)
def test_reverse_sequence(target, dev, executor_kind):
def verify_reverse_sequence(x_data, seq_lengths, batch_axis, seq_axis, ref_res):
seq_lengths_data = np.array(seq_lengths).astype("int32")
x = relay.var("x", relay.TensorType(x_data.shape, str(x_data.dtype)))
z = relay.reverse_sequence(x, relay.const(seq_lengths_data), seq_axis, batch_axis)
zz = run_infer_type(z)
assert zz.checked_type == x.type_annotation
func = relay.Function([x], z)
op_res = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)(
x_data
)
tvm.testing.assert_allclose(op_res.numpy(), ref_res, rtol=1e-5)
indata = np.array(np.arange(0, 16)).reshape([4, 4]).astype("int32")
result = [[0, 5, 10, 15], [4, 1, 6, 11], [8, 9, 2, 7], [12, 13, 14, 3]]
verify_reverse_sequence(indata, [1, 2, 3, 4], 1, 0, np.array(result))
verify_reverse_sequence(indata, [1, 2, 3, 4], -1, 0, np.array(result))
verify_reverse_sequence(
indata.astype("float32"), [1, 2, 3, 4], 1, 0, np.array(result).astype("float32")
)
indata = np.array(np.arange(0, 16)).reshape([4, 4]).astype("int32")
result = [[0, 1, 2, 3], [5, 4, 6, 7], [10, 9, 8, 11], [15, 14, 13, 12]]
verify_reverse_sequence(indata, [1, 2, 3, 4], 0, 1, np.array(result))
verify_reverse_sequence(indata, [1, 2, 3, 4], 0, -1, np.array(result))
verify_reverse_sequence(
indata.astype("float32"), [1, 2, 3, 4], 0, 1, np.array(result).astype("float32")
)
indata = np.array(np.arange(0, 16)).reshape([4, 4]).astype("int32")
result = [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [15, 14, 13, 12]]
verify_reverse_sequence(indata, [-1, 0, 1, 5], 0, 1, np.array(result))
indata = np.array(np.arange(0, 54)).reshape([2, 3, 3, 3]).astype("int32")
result = [
[
[[18, 19, 20], [21, 22, 23], [24, 25, 26]],
[[9, 10, 11], [12, 13, 14], [15, 16, 17]],
[[0, 1, 2], [3, 4, 5], [6, 7, 8]],
],
[
[[45, 46, 47], [48, 49, 50], [51, 52, 53]],
[[36, 37, 38], [39, 40, 41], [42, 43, 44]],
[[27, 28, 29], [30, 31, 32], [33, 34, 35]],
],
]
verify_reverse_sequence(indata, [3, 3], 0, 1, np.array(result))
indata = np.array(np.arange(0, 54)).reshape([2, 3, 3, 3]).astype("int32")
result = [
[
[[9, 10, 11], [21, 22, 23], [15, 16, 17]],
[[0, 1, 2], [12, 13, 14], [6, 7, 8]],
[[18, 19, 20], [3, 4, 5], [24, 25, 26]],
],
[
[[36, 37, 38], [48, 49, 50], [42, 43, 44]],
[[27, 28, 29], [39, 40, 41], [33, 34, 35]],
[[45, 46, 47], [30, 31, 32], [51, 52, 53]],
],
]
verify_reverse_sequence(indata, [2, 3, 2], 2, 1, np.array(result))
indata = np.array(np.arange(0, 16)).reshape([4, 4]).astype("int32")
result = []
with pytest.raises(Exception) as execinfo:
verify_reverse_sequence(indata, [2, 3, 2, 4, 5], 1, 0, np.array(result))
assert (
"For reverse_sequnece seq_lengths size should match with dimension of batch axis,"
" but got dimension of batch_axis = 4, and seq_length size = 5" in execinfo.value.args[0]
)
def ref_scatter(data, indices, updates, axis=0):
idx = np.indices(indices.shape).reshape(indices.ndim, -1)
updated_idx = np.copy(idx)
indices = indices.reshape(-1)
for i in range(len(indices)):
updated_idx[axis, i] = indices[i]
scattered = np.copy(data)
scattered[tuple(updated_idx)] = updates[tuple(idx)]
return scattered
def test_scatter(target, dev, executor_kind):
def verify_scatter(dshape, ishape, axis=0, indices_dtype="int64"):
d = relay.var("d", relay.TensorType(dshape, "float32"))
i = relay.var("i", relay.TensorType(ishape, indices_dtype))
u = relay.var("u", relay.TensorType(ishape, "float32"))
z = relay.op.scatter_elements(d, i, u, axis)
func = relay.Function([d, i, u], z)
data_np = np.random.uniform(size=dshape).astype("float32")
updates_np = np.random.uniform(size=ishape).astype("float32")
indices_np = np.random.randint(0, dshape[axis] - 1, ishape).astype(indices_dtype)
ref_res = ref_scatter(data_np, indices_np, updates_np, axis)
op_res = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)(
data_np, indices_np, updates_np
)
tvm.testing.assert_allclose(op_res.numpy(), ref_res, rtol=1e-5)
verify_scatter((10,), (10,), 0)
verify_scatter((10, 5), (10, 5), -2)
verify_scatter((10, 5), (10, 5), -1)
verify_scatter((10, 5), (3, 5), 0)
verify_scatter((12, 4), (7, 2), 1)
verify_scatter((2, 3, 4), (1, 3, 4), 0)
verify_scatter((2, 3, 4), (2, 1, 4), 1)
verify_scatter((2, 3, 4), (2, 3, 1), 2)
verify_scatter((4, 2, 1), (1, 1, 1), 0)
verify_scatter((2, 3, 4, 5), (1, 3, 4, 5), 0)
verify_scatter((6, 3, 4, 5), (2, 3, 4, 5), 1)
verify_scatter((2, 3, 8, 5), (2, 3, 1, 1), 2)
verify_scatter((16, 16, 4, 5), (16, 16, 4, 5), 3)
verify_scatter((16, 16, 4, 5), (16, 16, 4, 5), 3, indices_dtype="uint32")
class TestDynamicScatter:
dshape, ishape, axis = tvm.testing.parameters(
((10,), (10,), 0),
((10, 5), (10, 5), -2),
((10, 5), (10, 5), -1),
((10, 5), (3, 5), 0),
((12, 4), (7, 2), 1),
((2, 3, 4), (1, 3, 4), 0),
((2, 3, 4), (2, 1, 4), 1),
((2, 3, 4), (2, 3, 1), 2),
((4, 2, 1), (1, 1, 1), 0),
((2, 3, 4, 5), (1, 3, 4, 5), 0),
((6, 3, 4, 5), (2, 3, 4, 5), 1),
((2, 3, 8, 5), (2, 3, 1, 1), 2),
((16, 16, 4, 5), (16, 16, 4, 5), 3),
)
@pytest.mark.parametrize("executor_kind", ["vm"])
def test_dynamic_scatter(self, target, dev, executor_kind, dshape, ishape, axis):
d = relay.var("d", relay.TensorType([relay.Any() for i in range(len(dshape))], "float32"))
i = relay.var("i", relay.TensorType([relay.Any() for i in range(len(ishape))], "int64"))
u = relay.var("u", relay.TensorType([relay.Any() for i in range(len(ishape))], "float32"))
z = relay.op.scatter_elements(d, i, u, axis)
func = relay.Function([d, i, u], z)
data_np = np.random.uniform(size=dshape).astype("float32")
updates_np = np.random.uniform(size=ishape).astype("float32")
indices_np = np.random.randint(-dshape[axis], dshape[axis] - 1, ishape).astype("int64")
ref_res = ref_scatter(data_np, indices_np, updates_np, axis)
mod = tvm.ir.IRModule.from_expr(func)
op_res = relay.create_executor(
executor_kind, mod=mod, device=dev, target=target
).evaluate()(data_np, indices_np, updates_np)
tvm.testing.assert_allclose(op_res.numpy(), ref_res, rtol=1e-5)
class TestScatterAdd:
dshape, ishape, axis, dtype, indice_dtype = tvm.testing.parameters(
((10,), (10,), 0, "int32", "int64"),
((1000,), (1000,), 0, "int32", "int64"),
((10, 5), (10, 5), -2, "float32", "int64"),
((10, 5), (10, 5), -1, "float32", "int64"),
((10, 5), (3, 5), 0, "float32", "int64"),
((12, 4), (7, 2), 1, "float32", "int64"),
((2, 3, 4), (1, 3, 4), 0, "float32", "int64"),
((2, 3, 4), (2, 1, 4), 1, "float32", "int64"),
((2, 3, 4), (2, 3, 1), 2, "float32", "int64"),
((2, 3, 4, 5), (1, 3, 4, 5), 0, "float32", "int64"),
((6, 3, 4, 5), (2, 3, 4, 5), 1, "float32", "int64"),
((2, 3, 8, 5), (2, 3, 1, 1), 2, "float32", "int64"),
((16, 16, 4, 5), (16, 16, 4, 5), 3, "float32", "int64"),
((16, 16, 4, 5), (16, 16, 4, 5), 3, "float32", "uint32"),
)
@tvm.testing.fixture(cache_return_value=True)
def ref_data(self, dshape, ishape, axis, dtype, indice_dtype):
data_np = np.random.uniform(size=dshape).astype(dtype)
updates_np = np.random.uniform(size=ishape).astype(dtype)
indices_np = np.random.randint(0, dshape[axis] - 1, ishape).astype(indice_dtype)
out_np = np.copy(data_np)
for index in np.ndindex(*indices_np.shape):
new_index = list(index)
new_index[axis] = indices_np[index]
out_np[tuple(new_index)] += updates_np[index]
return data_np, updates_np, indices_np, out_np
# Optimization can produce tir.atomic_add, not currently supported
# on vulkan runtime.
@tvm.testing.known_failing_targets("vulkan")
def test_scatter_add(self, target, dev, ref_data, dshape, ishape, axis, dtype, indice_dtype):
d = relay.var("d", relay.TensorType(shape=[relay.Any() for _ in dshape], dtype=dtype))
i = relay.var(
"i", relay.TensorType(shape=[relay.Any() for _ in ishape], dtype=indice_dtype)
)
u = relay.var("u", relay.TensorType(shape=[relay.Any() for _ in ishape], dtype=dtype))
z = relay.op.scatter_elements(d, i, u, axis, "add")
func = relay.Function([d, i, u], z)
data_np, updates_np, indices_np, out_np = ref_data
verify_func(target, dev, func, [data_np, indices_np, updates_np], out_np)
@pytest.mark.parametrize(
"data, axis, indices, ref_res",
[
([[1, 2], [3, 4]], 1, [[0, 0], [1, 0]], [[1, 1], [4, 3]]),
([[1, 2], [3, 4]], -1, [[0, 0], [1, 0]], [[1, 1], [4, 3]]),
(
[[[0, 1, 2], [3, 4, 5]], [[6, 7, 8], [9, 10, 11]]],
0,
[[[1, 0, 1], [1, 1, 0]]],
[[[6, 1, 8], [9, 10, 5]]],
),
(
[[[0, 1, 2], [3, 4, 5]], [[6, 7, 8], [9, 10, 11]]],
-3,
[[[1, 0, 1], [1, 1, 0]]],
[[[6, 1, 8], [9, 10, 5]]],
),
(
[
[
[-0.2321, -0.2024, -1.7624],
[-0.3829, -0.4246, 0.2448],
[0.1822, 0.2360, -0.8965],
[0.4497, -0.2224, 0.6103],
],
[
[0.0408, -0.7667, -0.4303],
[-0.3216, 0.7489, -0.1502],
[0.0144, -0.4699, -0.0064],
[-0.0768, -1.6064, 1.3390],
],
],
1,
[[[2, 2, 0], [1, 0, 3]], [[3, 2, 0], [1, 0, 0]]],
[
[[0.1822, 0.2360, -1.7624], [-0.3829, -0.2024, 0.6103]],
[[-0.0768, -0.4699, -0.4303], [-0.3216, -0.7667, -0.4303]],
],
),
(
[
[
[-0.2321, -0.2024, -1.7624],
[-0.3829, -0.4246, 0.2448],
[0.1822, 0.2360, -0.8965],
[0.4497, -0.2224, 0.6103],
],
[
[0.0408, -0.7667, -0.4303],
[-0.3216, 0.7489, -0.1502],
[0.0144, -0.4699, -0.0064],
[-0.0768, -1.6064, 1.3390],
],
],
-2,
[[[2, 2, 0], [1, 0, 3]], [[3, 2, 0], [1, 0, 0]]],
[
[[0.1822, 0.2360, -1.7624], [-0.3829, -0.2024, 0.6103]],
[[-0.0768, -0.4699, -0.4303], [-0.3216, -0.7667, -0.4303]],
],
),
(
[
[
[-0.2321, -0.2024, -1.7624],
[-0.3829, -0.4246, 0.2448],
[0.1822, 0.2360, -0.8965],
[0.4497, -0.2224, 0.6103],
],
[
[0.0408, -0.7667, -0.4303],
[-0.3216, 0.7489, -0.1502],
[0.0144, -0.4699, -0.0064],
[-0.0768, -1.6064, 1.3390],
],
],
-2,
[[[2, 2, 0], [1, 0, 3]], [[3, 2, 0], [1, 0, 0]]],
[
[[0.1822, 0.2360, -1.7624], [-0.3829, -0.2024, 0.6103]],
[[-0.0768, -0.4699, -0.4303], [-0.3216, -0.7667, -0.4303]],
],
),
(
[
[
[0.3050, 1.6986, 1.1034],
[0.7020, -0.6960, -2.1818],
[0.3116, -0.5773, -0.9912],
[0.0835, -1.3915, -1.0720],
],
[
[0.1694, -0.6091, -0.6539],
[-0.5234, -0.1218, 0.5084],
[0.2374, -1.9537, -2.0078],
[-0.5700, -1.0302, 0.1558],
],
],
2,
[
[[1, 1, 0, 1], [0, 0, 2, 2], [1, 2, 1, 2], [2, 2, 1, 0]],
[[0, 0, 1, 2], [2, 2, 1, 0], [1, 2, 0, 0], [0, 2, 0, 2]],
],
[
[
[1.6986, 1.6986, 0.3050, 1.6986],
[0.7020, 0.7020, -2.1818, -2.1818],
[-0.5773, -0.9912, -0.5773, -0.9912],
[-1.0720, -1.0720, -1.3915, 0.0835],
],
[
[0.1694, 0.1694, -0.6091, -0.6539],
[0.5084, 0.5084, -0.1218, -0.5234],
[-1.9537, -2.0078, 0.2374, 0.2374],
[-0.5700, 0.1558, -0.5700, 0.1558],
],
],
),
(
[
[
[0.3050, 1.6986, 1.1034],
[0.7020, -0.6960, -2.1818],
[0.3116, -0.5773, -0.9912],
[0.0835, -1.3915, -1.0720],
],
[
[0.1694, -0.6091, -0.6539],
[-0.5234, -0.1218, 0.5084],
[0.2374, -1.9537, -2.0078],
[-0.5700, -1.0302, 0.1558],
],
],
-1,
[
[[1, 1, 0, 1], [0, 0, 2, 2], [1, 2, 1, 2], [2, 2, 1, 0]],
[[0, 0, 1, 2], [2, 2, 1, 0], [1, 2, 0, 0], [0, 2, 0, 2]],
],
[
[
[1.6986, 1.6986, 0.3050, 1.6986],
[0.7020, 0.7020, -2.1818, -2.1818],
[-0.5773, -0.9912, -0.5773, -0.9912],
[-1.0720, -1.0720, -1.3915, 0.0835],
],
[
[0.1694, 0.1694, -0.6091, -0.6539],
[0.5084, 0.5084, -0.1218, -0.5234],
[-1.9537, -2.0078, 0.2374, 0.2374],
[-0.5700, 0.1558, -0.5700, 0.1558],
],
],
),
],
)
def test_gather(target, dev, executor_kind, data, axis, indices, ref_res):
def verify_gather(data, axis, indices, ref_res):
data = np.asarray(data, dtype="float32")
indices = np.asarray(indices, dtype="int32")
ref_res = np.asarray(ref_res)
d = relay.var("x", relay.TensorType(data.shape, "float32"))
i = relay.var("y", relay.TensorType(indices.shape, "int32"))
z = relay.gather(d, axis, i)
func = relay.Function([d, i], z)
op_res = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)(
data, indices
)
tvm.testing.assert_allclose(op_res.numpy(), ref_res, rtol=1e-5)
verify_gather(data, axis, indices, ref_res)
def test_gather_nd(target, dev, executor_kind):
def verify_gather_nd(xshape, yshape, y_data, batch_dims=0, indices_dtype="int32"):
x = relay.var("x", relay.TensorType(xshape, "float32"))
y = relay.var("y", relay.TensorType(yshape, indices_dtype))
z = relay.gather_nd(x, y, batch_dims)
func = relay.Function([x, y], z)
x_data = np.random.uniform(size=xshape).astype("float32")
if y_data:
y_data = np.array(y_data, dtype=indices_dtype)
else:
y_data = np.random.randint(low=0, high=2, size=yshape, dtype=indices_dtype)
ref_res = ref_funcs.gather_nd(x_data, y_data, batch_dims)
op_res = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)(
x_data, y_data
)
tvm.testing.assert_allclose(op_res.numpy(), ref_res, rtol=1e-5)
verify_gather_nd((2, 2), (2, 3), [[1, 1, 0], [0, 1, 0]])
verify_gather_nd((2, 2, 2), (2, 2), [[0, 1], [1, 0]])
verify_gather_nd((3, 2, 2), (2, 2), [[0, 1], [1, 0]])
verify_gather_nd((3, 2), (2, 2, 3), [[[0, 1, 2], [2, 0, 1]], [[0, 0, 0], [1, 1, 1]]])
# Examples from tensorflow gather_nd doc
# https://www.tensorflow.org/api_docs/python/tf/gather_nd
verify_gather_nd((2, 2, 2), (1, 2), [[1, 0]], 1)
verify_gather_nd((2, 2, 2), (1, 2, 1), [[[1], [0]]], 1)
verify_gather_nd((2, 2, 2), (2, 2, 1), [[[1], [0]], [[0], [1]]], 1)
# Test cases from tensorflow gather_nd tests kernel_tests/array_ops_test.py
verify_gather_nd((2, 2, 2), (1, 2), None, 1)
verify_gather_nd((2, 2, 2), (2, 2), None, 1)
verify_gather_nd((2, 2, 3, 2), (3, 2), None, 1)
verify_gather_nd((2, 2, 3, 2), (2, 2), None, 1)
verify_gather_nd((2, 2, 3, 2), (1, 2), None, 1)
verify_gather_nd((2, 2, 3, 2), (3, 2, 1), None, 1)
verify_gather_nd((2, 2, 3, 2), (2, 2, 2), None, 1)
verify_gather_nd((2, 2, 3, 2), (1, 2, 3), None, 1)
verify_gather_nd((3, 2, 2, 3, 4), (3, 3, 2), None, 2)
verify_gather_nd((3, 2, 2, 3, 4), (2, 3, 2), None, 2)
verify_gather_nd((3, 2, 2, 3, 4), (1, 3, 2), None, 2)
verify_gather_nd((3, 2, 2, 3, 4), (3, 3, 2, 1), None, 2)
verify_gather_nd((3, 2, 2, 3, 4), (2, 3, 2, 2), None, 2)
verify_gather_nd((3, 2, 2, 3, 4), (1, 3, 2, 3), None, 2)
verify_gather_nd((3, 2, 2, 3, 4), (1, 3, 2, 3), None, 2, indices_dtype="uint8")
verify_gather_nd((2, 2, 2), (2, 2, 1), [[[1], [0]], [[0], [1]]], 1, indices_dtype="uint32")
def _verify_infiniteness_ops(relay_op, ref_op, target="llvm", dev=None):
for dtype in ["float32", "float16", "float16", "int32", "int16"]:
shape = (2, 8, 8)
x = relay.var("x", relay.TensorType(shape, dtype))
y = relay_op(x)
yy = run_infer_type(y)
assert yy.checked_type == relay.TensorType(shape, "bool")
data = np.random.uniform(size=shape).astype(dtype)
if dtype.startswith("float"):
data.ravel()[
np.random.choice(data.size, int(data.size * 0.5), replace=False)
] = np.infty
data.ravel()[np.random.choice(data.size, int(data.size * 0.5), replace=False)] = np.nan
op_res = create_executor(target=target, device=dev).evaluate(y, {x: data})
ref_res = ref_op(data)
np.testing.assert_allclose(op_res.numpy(), ref_res, rtol=0.01)
@tvm.testing.requires_gpu
def test_isfinite():
for target, dev in tvm.testing.enabled_targets():
if target not in ["llvm", "cuda"]:
continue
_verify_infiniteness_ops(relay.isfinite, np.isfinite, target=target, dev=dev)
@tvm.testing.requires_gpu
def test_isinf():
for target, dev in tvm.testing.enabled_targets():
if target not in ["llvm", "cuda"]:
continue
_verify_infiniteness_ops(relay.isinf, np.isinf, target=target, dev=dev)
def test_unravel_index(target, dev, executor_kind):
def verify_unravel_index(indices, shape, dtype):
x_data = np.array(indices).astype(dtype)
y_data = np.array(shape).astype(dtype)
x = relay.var("x", relay.TensorType(x_data.shape, dtype))
y = relay.var("y", relay.TensorType(y_data.shape, dtype))
z = relay.unravel_index(x, y)
zz = run_infer_type(z)
if len(x_data.shape) == 1:
out_shape = [y_data.shape[0], x_data.shape[0]]
else:
out_shape = [y_data.shape[0]]
assert zz.checked_type == relay.ty.TensorType(out_shape, dtype)
func = relay.Function([x, y], z)
ref_res = np.unravel_index(x_data, y_data)
op_res = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)(
x_data, y_data
)
tvm.testing.assert_allclose(op_res.numpy(), ref_res, rtol=1e-5)
for dtype in ["int64", "int32"]:
verify_unravel_index([0, 1, 2, 3], [2, 2], dtype)
verify_unravel_index([144], [5, 5, 5, 2], dtype)
verify_unravel_index(144, [5, 5, 5, 2], dtype)
verify_unravel_index([100, 13, 5], [5, 5, 5, 2], dtype)
# In below example, 5 is out of bound for array of size 4.
# Numpy implementation throws error for it
# TVM implementation does not throw error instead it produces
# output which is inline with Tensorflow
# verify_unravel_index([0, 1, 2, 5], [2, 2], dtype)
def test_sparse_to_dense(target, dev, executor_kind):
def verify_sparse_to_dense(sparse_indices, sparse_values, default_value, output_shape, xpected):
sparse_indices_data = np.array(sparse_indices)
sparse_values_data = np.array(sparse_values)
default_value_data = np.array(default_value)
a = relay.var(
"a", relay.TensorType(sparse_indices_data.shape, str(sparse_indices_data.dtype))
)
b = relay.var(
"b", relay.TensorType(sparse_values_data.shape, str(sparse_values_data.dtype))
)
if default_value is None:
args = [a, b]
d = relay.sparse_to_dense(a, output_shape, b)
else:
c = relay.var(
"c", relay.TensorType(default_value_data.shape, str(default_value_data.dtype))
)
args = [a, b, c]
d = relay.sparse_to_dense(a, output_shape, b, c)
zz = run_infer_type(d)
assert zz.checked_type == relay.ty.TensorType(output_shape, str(sparse_values_data.dtype))
func = relay.Function(args, d)
f = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)
if default_value is None:
op_res = f(sparse_indices_data, sparse_values_data)
else:
op_res = f(sparse_indices_data, sparse_values_data, default_value_data)
tvm.testing.assert_allclose(op_res.numpy(), xpected, rtol=1e-5)
verify_sparse_to_dense(1, 3, 0, [5], [0, 3, 0, 0, 0]) # scalar
verify_sparse_to_dense([0, 1, 4], [3, 3, 3], 0, [5], [3, 3, 0, 0, 3]) # vector
verify_sparse_to_dense(
[[0, 0], [1, 2]], [1, 2], 0, [3, 4], [[1, 0, 0, 0], [0, 0, 2, 0], [0, 0, 0, 0]]
) # nXd
verify_sparse_to_dense(
[[0, 0, 0], [1, 2, 3]],
[1, 2],
4,
[2, 3, 4],
[[[1, 4, 4, 4], [4, 4, 4, 4], [4, 4, 4, 4]], [[4, 4, 4, 4], [4, 4, 4, 4], [4, 4, 4, 2]]],
) # nXd
verify_sparse_to_dense(
[0, 1, 4], [3.1, 3.1, 3.1], 3.5, [5], [3.1, 3.1, 3.5, 3.5, 3.1]
) # floats
# default value not specified
verify_sparse_to_dense(1, 3, None, [5], [0, 3, 0, 0, 0])
# negative test cases
# sparse indices should be ints
# verify_sparse_to_dense([[0.1, 1.1, 4.1], [0,2,4]], [3.1, 3.1, 3.1], 3.5, [5], [3.1, 3.1, 3.5, 3.5, 3.1])
# sparse_values should be 0d or 1d only
# verify_sparse_to_dense([[0, 1, 4], [0, 2, 4]], [[[3.1, 3.1, 3.1]]], 3.5, [5], [3.1, 3.1, 3.5, 3.5, 3.1])
# sparse_indices should not be > 2d tensor
# verify_sparse_to_dense([[[[0, 1, 4], [0, 2, 4]]]], [[[[3.1, 3.1, 3.1]]]], 3.5, [5], [3.1, 3.1, 3.5, 3.5, 3.1])
class TestSparseReshape:
sparse_indices_np, sparse_values_np, prev_shape_np, new_shape_np = tvm.testing.parameters(
(
np.array([[0, 0, 0], [0, 0, 1], [0, 1, 0], [1, 0, 0], [1, 2, 3]], dtype=np.int32),
np.array([7, 5, 6, 3, 9], dtype=np.int32),
np.array([2, 3, 6], dtype=np.int32),
np.array([9, -1], dtype=np.int32),
),
(
np.array(
[[0, 0, 0, 0], [0, 0, 1, 2], [0, 1, 0, 3], [1, 0, 0, 4], [1, 2, 3, 6]],
dtype=np.int64,
),
np.array([7, 5, 6, 3, 9], dtype=np.int64),
np.array([2, 3, 6, 7], dtype=np.int64),
np.array([9, -1, 7], dtype=np.int64),
),
(
np.array(
[
[0, 0, 0, 0, 0],
[0, 0, 1, 2, 3],
[0, 1, 0, 3, 5],
[1, 0, 0, 4, 6],
[1, 2, 3, 6, 8],
],
dtype=np.int64,
),
np.array([7, 5, 6, 3, 9], dtype=np.int64),
np.array([2, 3, 6, 7, 9], dtype=np.int64),
np.array([9, -1, 7], dtype=np.int64),
),
(
np.array([[0, 0], [0, 1], [3, 4], [4, 3], [7, 3]], dtype=np.int32),
np.array([7, 5, 6, 3, 9], dtype=np.int32),
np.array([9, 4], dtype=np.int32),
np.array([2, -1, 6], dtype=np.int32),
),
(
np.array([[0, 0], [0, 1], [3, 4], [4, 3], [7, 3]], dtype=np.int64),
np.array([7, 5, 6, 3, 9], dtype=np.int64),
np.array([9, 4], dtype=np.int64),
np.array([-1], dtype=np.int64),
),
(
np.array([[0], [5], [10], [20], [24]], dtype=np.int32),
np.array([7, 5, 6, 3, 9], dtype=np.int32),
np.array([25], dtype=np.int32),
np.array([5, 5], dtype=np.int32),
),
(
np.array([[0, 100], [200, 100], [300, 400], [50, 20], [400, 50]], dtype=np.int64),
np.array([7, 5, 6, 3, 9], dtype=np.int64),
np.array([500, 20], dtype=np.int64),
np.array([500, 20], dtype=np.int64),
),
(
np.array([[0, 100], [200, 100], [300, 400], [50, 20], [400, 50]], dtype=np.int32),
np.array([7, 5, 6, 3, 9], dtype=np.int32),
np.array([500, 20], dtype=np.int32),
np.array([500, -1], dtype=np.int32),
),
(
np.array([[0, 100], [200, 100], [300, 400], [50, 20], [400, 50]], dtype=np.int64),
np.array([7, 5, 6, 3, 9], dtype=np.int64),
np.array([500, 20], dtype=np.int64),
np.array([250, 40], dtype=np.int64),
),
(
np.ones((0, 1), dtype=np.int32),
np.array([], dtype=np.int32),
np.array([4], dtype=np.int32),
np.array([2, -1], dtype=np.int32),
),
(
np.ones((0, 1), dtype=np.int64),
np.array([], dtype=np.int64),
np.array([4], dtype=np.int64),
np.array([2, 2], dtype=np.int64),
),
(
np.ones((0, 2), dtype=np.int32),
np.array([], dtype=np.int32),
np.array([3, 6], dtype=np.int32),
np.array([-1, 2], dtype=np.int32),
),
)
use_dyn = tvm.testing.parameter(True, False, ids=["dyn", "static"])
@tvm.testing.fixture(cache_return_value=True)
def ref_res(
self,
sparse_indices_np: np.ndarray,
prev_shape_np: np.ndarray,
new_shape_np: np.ndarray,
):
"""
This function calculates the expected output of sparse_reshape operator given the inputs.
"""
new_sparse_indices = np.ones(
(sparse_indices_np.shape[0], new_shape_np.shape[0]), dtype=sparse_indices_np.dtype
)
multipliers = np.ones(prev_shape_np.shape[0])
dividers = np.ones(new_shape_np.shape[0])
total_ele = np.prod(prev_shape_np)
division_total_ele = 1
for i in range(new_shape_np.shape[0]):
if new_shape_np[i] == -1:
continue
division_total_ele *= new_shape_np[i]
for i in range(prev_shape_np.shape[0] - 2, -1, -1):
multipliers[i] = prev_shape_np[i + 1] * multipliers[i + 1]
for i in range(len(new_shape_np)):
if new_shape_np[i] == -1:
new_shape_np[i] = total_ele // division_total_ele
if np.array_equal(prev_shape_np, new_shape_np):
return sparse_indices_np, prev_shape_np
for i in range(new_shape_np.shape[0] - 2, -1, -1):
dividers[i] = new_shape_np[i + 1] * dividers[i + 1]
for row_num, sparse_row in enumerate(sparse_indices_np):
flat_idx = 0
if len(sparse_indices_np.shape) != 1:
for i, ele in enumerate(sparse_row):
flat_idx += sparse_row[i] * multipliers[i]
else:
flat_idx += sparse_row
if len(new_sparse_indices.shape) != 1:
for i in range(new_sparse_indices.shape[1]):
new_sparse_indices[row_num][i] = flat_idx // dividers[i]
flat_idx = flat_idx % dividers[i]
else:
new_sparse_indices[row_num] = flat_idx
return new_sparse_indices, new_shape_np
@tvm.testing.known_failing_targets("vulkan")
def test_sparse_reshape(
self,
target,
dev,
ref_res,
sparse_indices_np,
sparse_values_np,
prev_shape_np,
new_shape_np,
use_dyn,
):
if use_dyn:
sparse_indices = relay.var(
"sparse_indices",
shape=[relay.Any(), relay.Any()],
dtype=str(sparse_indices_np.dtype),
)
prev_shape = relay.var(
"prev_shape",
shape=[relay.Any()],
dtype=str(prev_shape_np.dtype),
)
new_shape = relay.var(
"new_shape",
shape=[relay.Any()],
dtype=str(new_shape_np.dtype),
)
else:
sparse_indices = relay.var(
"sparse_indices",
relay.TensorType(sparse_indices_np.shape, str(sparse_indices_np.dtype)),
)
prev_shape = relay.var(
"prev_shape", relay.TensorType(prev_shape_np.shape, str(prev_shape_np.dtype))
)
new_shape = relay.var(
"new_shape", relay.TensorType(new_shape_np.shape, str(new_shape_np.dtype))
)
z = relay.op.sparse_reshape(sparse_indices, prev_shape, new_shape).astuple()
func = relay.Function([sparse_indices, prev_shape, new_shape], z)
outputs = run_infer_type(z)
new_sparse_indices_infer_type, new_shape_infer_type = (
outputs.checked_type.fields[0].dtype,
outputs.checked_type.fields[1].dtype,
)
assert new_sparse_indices_infer_type == sparse_indices_np.dtype
assert new_shape_infer_type == new_shape_np.dtype
verify_func(
target,
dev,
func,
[sparse_indices_np, prev_shape_np, new_shape_np],
ref_res,
)
class TestSegmentSum:
data_np, segment_ids_np, num_segments = tvm.testing.parameters(
(
np.array([5, 1, 7, 2, 3, 4], dtype=np.float32),
np.array([0, 0, 1, 1, 0, 1], dtype=np.int32),
None,
),
(
np.array([[1, 2, 3, 4], [-1, -2, -3, -4], [5, 6, 7, 8]], dtype=np.float64),
np.array([0, 0, 1], dtype=np.int32),
None,
),
(
np.random.random((6, 4, 5)),
np.array([2, 0, 1, 0, 3, 2], dtype=np.int64),
None,
),
(
np.array([[[1, 7]], [[3, 8]], [[2, 9]]], dtype=np.float32),
np.array([0, 0, 1], dtype=np.int32),
None,
),
(
np.random.random((9, 4, 5, 7)),
np.array([5, 0, 1, 0, 3, 6, 8, 7, 7], dtype=np.int64),
9,
),
(
np.array([[1, 2, 3, 4], [-1, -2, -3, -4], [5, 6, 7, 8]], dtype=np.float64),
np.array([0, 2], dtype=np.int32),
4,
),
(
np.random.random((6, 4, 5)),
np.array([0, 0, 1, 5, 5], dtype=np.int32),
100,
),
)
use_dyn = tvm.testing.parameter(True, False, ids=["dyn", "static"])
@tvm.testing.fixture(cache_return_value=True)
def ref_res(
self,
data_np: np.ndarray,
segment_ids_np: np.ndarray,
num_segments: Optional[int],
):
"""
This function calculates the expected output of segment_sum operator given the inputs.
"""
if not num_segments:
num_segments = np.unique(segment_ids_np).shape[0]
result = np.zeros((num_segments,) + data_np.shape[1:], data_np.dtype)
for i, index in enumerate(segment_ids_np):
result[index] += data_np[i]
return result
# Optimization can produce tir.atomic_add, not currently supported
# on vulkan runtime.
@tvm.testing.known_failing_targets("vulkan")
def test_segment_sum(
self,
target,
dev,
ref_res: np.ndarray,
data_np: np.ndarray,
segment_ids_np: np.ndarray,
num_segments: Optional[int],
use_dyn: bool,
):
"""
This function verifies the relay output of segment_sum with its expected output.
"""
if use_dyn:
data = relay.var(
"data",
shape=[relay.Any() for _ in data_np.shape],
dtype=str(data_np.dtype),
)
segment_ids = relay.var(
"segment_ids",
shape=[relay.Any()],
dtype=str(segment_ids_np.dtype),
)
else:
data = relay.var(
"data",
relay.TensorType(data_np.shape, str(data_np.dtype)),
)
segment_ids = relay.var(
"segment_ids", relay.TensorType(segment_ids_np.shape, str(segment_ids_np.dtype))
)
z = relay.op.segment_sum(data, segment_ids, num_segments)
func = relay.Function([data, segment_ids], z)
segment_sum_result = run_infer_type(z)
assert segment_sum_result.checked_type.dtype == data_np.dtype
verify_func(
target,
dev,
func,
[data_np, segment_ids_np],
ref_res,
)
def verify_func(target, dev, func, data, ref_res, rtol=1e-5, atol=1e-7, kinds=["vm"]):
assert isinstance(data, list)
for kind in kinds:
mod = tvm.ir.IRModule.from_expr(func)
op_res = relay.create_executor(kind, mod=mod, device=dev, target=target).evaluate()(*data)
if isinstance(op_res, tvm.runtime.container.ADT):
assert len(op_res) == len(
ref_res
), "Outputs from TVM and Python implementation must be equal "
for op_result, ref_result in zip(op_res, ref_res):
tvm.testing.assert_allclose(op_result.numpy(), ref_result, rtol=rtol, atol=atol)
else:
tvm.testing.assert_allclose(op_res.numpy(), ref_res, rtol=rtol, atol=atol)
relay.backend.te_compiler.get().clear()
def test_adv_index(target, dev, executor_kind):
def verify_adv_index(data_shape, index_shapes):
dtype = "float32"
inputs = [relay.var("data", relay.TensorType(data_shape, dtype))]
np_data = np.random.uniform(size=data_shape).astype(dtype)
np_indices = []
for i, index_shape in enumerate(index_shapes):
limit = data_shape[i]
np_indices.append(np.random.uniform(0, limit - 1, size=index_shape).astype("int64"))
inputs.append(relay.var("index_{}".format(i), relay.TensorType(index_shape, "int64")))
np_out = np_data[tuple(np_indices)]
np_args = [np_data] + np_indices
out = relay.op.adv_index(inputs)
func = relay.Function(inputs, out)
op_res = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)(
*np_args
)
tvm.testing.assert_allclose(op_res.numpy(), np_out, rtol=1e-5)
verify_adv_index((10, 5), [(3, 4), (3, 1)])
verify_adv_index((10, 5), [(1, 4), (3, 1)])
verify_adv_index(
(10, 5),
[
(2,),
],
)
verify_adv_index((10, 5, 15), [(1, 2, 1), (1, 2, 7)])
# Helper for testing binop functions
scanops_supported = {"cumsum": relay.op.cumsum, "cumprod": relay.op.cumprod}
def run_binop_tests(
target,
dev,
executor_kind,
binop_type: str,
gt_func: Callable[..., np.array],
identity_value: int,
):
def assert_relay_scanop(
data_np: np.array,
np_out: np.array,
axis: int = None,
out_dtype: str = None,
rtol: float = 1e-5,
atol: float = 1e-5,
exclusive: bool = False,
):
inp = relay.var("data", relay.TensorType(data_np.shape, str(data_np.dtype)))
if binop_type not in scanops_supported.keys():
raise ValueError(f"Unknown function {binop_type}. Options: {scanops_supported.keys()}")
out = scanops_supported[binop_type](inp, axis, out_dtype, exclusive=exclusive)
func = relay.Function([inp], out)
op_res = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)(
data_np
)
tvm.testing.assert_allclose(op_res.numpy(), np_out, rtol=rtol, atol=atol)
data = np.array([2, 3, 0])
assert_relay_scanop(data, gt_func(data))
assert_relay_scanop(data, gt_func(data), out_dtype="int64")
data = np.random.randn(10, 10)
assert_relay_scanop(data, gt_func(data))
assert_relay_scanop(data, gt_func(data, axis=0), axis=0)
assert_relay_scanop(data, gt_func(data, axis=1), axis=1)
data = np.random.randn(10, 5, 10).astype("float32")
assert_relay_scanop(data, gt_func(data), rtol=1e-4, atol=1e-4)
assert_relay_scanop(data, gt_func(data, axis=0), axis=0, rtol=1e-4, atol=1e-4)
assert_relay_scanop(data, gt_func(data, axis=1), axis=1, rtol=1e-4, atol=1e-4)
assert_relay_scanop(data, gt_func(data, axis=-1), axis=-1, rtol=1e-4, atol=1e-4)
data = np.random.rand(10) > 0.5
data = data.astype(np.int32)
assert_relay_scanop(data, gt_func(data, dtype=np.int32))
assert_relay_scanop(data, gt_func(data, dtype="int64"), out_dtype="int64")
# Test exclusivity operations
data = np.random.randint(-100, 100, size=(10, 10)).astype("int64")
expected_result = np.roll(gt_func(data), 1)
expected_result[0] = identity_value
assert_relay_scanop(data, expected_result, exclusive=True)
expected_result = np.roll(gt_func(data, axis=0), 1, axis=0)
expected_result[0, :] = identity_value
assert_relay_scanop(data, expected_result, exclusive=True, axis=0)
expected_result = np.roll(gt_func(data, axis=1), 1, axis=1)
expected_result[:, 0] = identity_value
assert_relay_scanop(data, expected_result, exclusive=True, axis=1)
@tvm.testing.parametrize_targets
def test_cumsum(target, dev, executor_kind):
run_binop_tests(
target, dev, executor_kind, binop_type="cumsum", gt_func=np.cumsum, identity_value=0
)
@tvm.testing.parametrize_targets
def test_cumprod(target, dev, executor_kind):
run_binop_tests(
target, dev, executor_kind, binop_type="cumprod", gt_func=np.cumprod, identity_value=1
)
@tvm.testing.parametrize_targets
def test_scatter_nd(target, dev, executor_kind):
def test_scatter_nd_large_shape():
def before():
data = relay.const(np.zeros((1, 900, 300), dtype="float32"), dtype="float32")
indices = relay.const(np.ones((3, 1, 900, 300), dtype="int64"), dtype="int64")
update = relay.const(np.ones((1, 900, 300), dtype="float32"), dtype="float32")
b = relay.op.scatter_nd(data, indices, update)
return relay.Function(relay.analysis.free_vars(b), b)
passes = tvm.transform.Sequential(
[
relay.transform.InferType(),
relay.transform.FoldConstant(),
]
)
before_mod = tvm.IRModule.from_expr(before())
with tvm.transform.PassContext(opt_level=3):
after_mod = passes(before_mod)
test_scatter_nd_large_shape()
def test_scatter_nd_inequal_m_k():
def before():
data = relay.const(np.zeros((1, 1, 10), dtype="float32"), dtype="float32")
indices = relay.const(np.zeros((2, 1, 1, 1), dtype="float32"), dtype="int64")
update = relay.const(np.ones((1, 1, 1, 10), dtype="float32"), dtype="float32")
b = relay.op.scatter_nd(data, indices, update)
return relay.Function(relay.analysis.free_vars(b), b)
passes = tvm.transform.Sequential(
[
relay.transform.InferType(),
relay.transform.FoldConstant(),
]
)
before_mod = tvm.IRModule.from_expr(before())
with tvm.transform.PassContext(opt_level=3):
after_mod = passes(before_mod)
test_scatter_nd_inequal_m_k()
def verify_scatter_nd(
data_np, indices_np, updates_np, ref_res, mode="add", rtol=1e-5, atol=1e-5
):
data = relay.var("data", shape=data_np.shape, dtype=str(data_np.dtype))
indices = relay.var("indices", shape=indices_np.shape, dtype=str(indices_np.dtype))
updates = relay.var("updates", shape=updates_np.shape, dtype=str(updates_np.dtype))
out = relay.op.scatter_nd(data, indices, updates, mode)
func = relay.Function([data, indices, updates], out)
op_res = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)(
data_np, indices_np, updates_np
)
tvm.testing.assert_allclose(op_res.numpy(), ref_res, rtol=rtol, atol=atol)
def verify_scatter_nd_with_stack(
data_np, indices_np, updates_np, ref_res, mode="add", rtol=1e-5, atol=1e-5
):
data = relay.var("data", shape=data_np.shape, dtype=str(data_np.dtype))
indices_vars = [
relay.var("ind%d" % i, shape=v.shape, dtype=str(v.dtype))
for i, v in enumerate(indices_np)
]
updates = relay.var("updates", shape=updates_np.shape, dtype=str(updates_np.dtype))
# test if scatter_nd works in case indices are prepared by another Relay operator
indices = relay.op.stack(indices_vars, axis=0)
out = relay.op.scatter_nd(data, indices, updates, mode)
func = relay.Function(
[data, updates] + indices_vars,
out,
)
fargs = [data_np, updates_np]
for a in indices_np:
fargs.append(a)
op_res = relay.create_executor(executor_kind, device=dev, target=target).evaluate(func)(
*fargs
)
tvm.testing.assert_allclose(op_res.numpy(), ref_res, rtol=rtol, atol=atol)
# TODO(vcchernov): check frameworks' int type requirements. ONNX expects int64 only
for indice_dtype in ["uint8", "uint16", "uint32"]:
data = np.zeros((2, 2)).astype("int64")
indices = np.array([[1, 1, 0], [0, 1, 0]]).astype(indice_dtype)
updates = np.array([2, 3, 0])
out = np.array([[0, 0], [2, 3]])
verify_scatter_nd(data, indices, updates, out)
verify_scatter_nd_with_stack(data, indices, updates, out)
data = np.zeros((2, 2, 2, 2)).astype("int64")
indices = np.array([[0, 1], [1, 1]]).astype(indice_dtype)
updates = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
out = np.array([[[[0, 0], [0, 0]], [[1, 2], [3, 4]]], [[[0, 0], [0, 0]], [[5, 6], [7, 8]]]])
verify_scatter_nd(data, indices, updates, out)
verify_scatter_nd_with_stack(data, indices, updates, out)
indices = np.array([[1, 0, 0]]).astype(indice_dtype)
updates = np.reshape(np.arange(1560 * 3), (3, 1560)).astype("float32")
shape = (2, 1560)
data = np.zeros(shape).astype("float32")
out = data.copy()
out[1, :] += updates[0, :]
out[0, :] += updates[1, :]
out[0, :] += updates[2, :]
verify_scatter_nd(data, indices, updates, out, mode="add")
verify_scatter_nd_with_stack(data, indices, updates, out)
for mode in ["update", "add", "mul", "min", "max"]:
indices = np.stack((np.random.randint(2, size=5), np.random.randint(7, size=5))).astype(
indice_dtype
)
updates = np.ones((5, 3)).astype("float64")
shape = (2, 7, 3)
data = np.random.random(shape).astype("float64")
out = data.copy()
for i in range(indices.shape[1]):
for j in range(updates.shape[1]):
if mode == "update":
out[indices[0, i], indices[1, i], j] = updates[i, j]
elif mode == "add":
out[indices[0, i], indices[1, i], j] += updates[i, j]
elif mode == "mul":
out[indices[0, i], indices[1, i], j] *= updates[i, j]
elif mode == "min":
out[indices[0, i], indices[1, i], j] = min(
out[indices[0, i], indices[1, i], j], updates[i, j]
)
elif mode == "max":
out[indices[0, i], indices[1, i], j] = max(
out[indices[0, i], indices[1, i], j], updates[i, j]
)
verify_scatter_nd(data, indices, updates, out, mode)
verify_scatter_nd_with_stack(data, indices, updates, out, mode)
def test_unique(target, dev):
def calc_numpy_unique(data, is_sorted=False):
uniq, index, inverse, counts = np.unique(
data, return_index=True, return_inverse=True, return_counts=True
)
num_uniq = np.array([len(uniq)]).astype("int32")
if not is_sorted:
order = np.argsort(index)
reverse_order = np.argsort(order)
uniq = uniq[order].astype(data.dtype)
inverse = np.array([reverse_order[i] for i in inverse]).astype("int32")
counts = counts[order].astype("int32")
# In unsorted case, need to sort the index of first occurence
index = np.sort(index)
return [
uniq.astype(data.dtype),
index.astype("int32"),
inverse.astype("int32"),
num_uniq,
counts,
]
def verify_unique(n, dtype, is_dyn=False, is_sorted=False, return_counts=False):
if is_dyn:
x = relay.var("x", relay.TensorType([relay.Any()], dtype))
else:
x = relay.var("x", relay.TensorType([n], dtype))
outs = relay.unique(x, is_sorted, return_counts)
outs = outs.astuple()
func = relay.Function([x], outs)
x_data = np.random.randint(50, size=n).astype(dtype)
if is_dyn:
backend = "vm"
else:
backend = "graph"
mod = tvm.ir.IRModule.from_expr(func)
tvm_res = relay.create_executor(backend, mod=mod, device=dev, target=target).evaluate()(
x_data
) # unique, indices, inverse_indices, num_unique, (counts)
np_res = calc_numpy_unique(
x_data, is_sorted
) # unique, indices, inverse_indices, num_unique, counts
num_unique = np_res[3][0]
# num_unique
assert num_unique == tvm_res[3].numpy()[0]
# unique
tvm.testing.assert_allclose(tvm_res[0].numpy()[:num_unique], np_res[0], rtol=1e-5)
# indices
tvm.testing.assert_allclose(tvm_res[1].numpy()[:num_unique], np_res[1], rtol=1e-5)
# inverse_indices
tvm.testing.assert_allclose(tvm_res[2].numpy(), np_res[2], rtol=1e-5)
# counts
if return_counts:
tvm.testing.assert_allclose(tvm_res[4].numpy()[:num_unique], np_res[4], rtol=1e-5)
for dtype in ["int32", "int64"]:
for i in range(8):
is_dyn, is_sorted, return_counts = bool(i & 1), bool(i & 2), bool(i & 4)
verify_unique(10, dtype, is_dyn, is_sorted, return_counts)
class TestSTFT:
(
data_np,
n_fft,
hop_length,
win_length,
window_np,
normalized,
onesided,
) = tvm.testing.parameters(
(
np.array([[1, 2, 3, 4, 5, 6]], dtype=np.float32),
3,
3,
3,
np.array([4, 3, 2], dtype=np.int32),
False,
True,
),
(
np.array([[1, 2, 3, 4, 5, 6, 7, 8, 9], [2, 5, 7, 8, 5, 6, 7, 3, 2]], dtype=np.float32),
2,
1,
2,
np.array([1, 3], dtype=np.int32),
False,
True,
),
(
np.array([[1, 2, 3, 4, 5, 6, 7, 8, 9], [2, 5, 7, 8, 5, 6, 7, 3, 2]], dtype=np.float32),
2,
1,
2,
np.array([1, 3], dtype=np.int32),
True,
True,
),
(
np.array([[1, 2, 3, 4, 5, 6, 7, 8, 9], [2, 5, 7, 8, 5, 6, 7, 3, 2]], dtype=np.float32),
2,
1,
2,
np.array([1, 3], dtype=np.int32),
False,
False,
),
)
@tvm.testing.fixture(cache_return_value=True)
def ref_res(
self,
data_np: np.ndarray,
n_fft: int,
hop_length: int,
win_length: int,
window_np,
normalized,
onesided,
):
"""
This function calculates the expected output of segment_sum operator given the inputs.
"""
def pad_window(window_np, n_fft):
shape = window_np.shape[-1]
lpad = int((n_fft - shape) // 2)
lengths = [(0, 0)] * len(window_np.shape)
lengths[-1] = (lpad, int(n_fft - shape - lpad))
if lpad < 0:
print("ERROR Padding")
return np.pad(window_np, lengths, mode="constant")
import math
if not onesided:
n_rows = n_fft
else:
n_rows = n_fft // 2 + 1
if window_np is None:
window_np = np.ones(win_length, dtype=np.int32)
window_np = pad_window(window_np, n_fft)
n_cols = (data_np.shape[-1] - n_fft) // hop_length + 1
np_result = np.zeros((data_np.shape[0], n_rows, n_cols, 2))
for batch in range(data_np.shape[0]):
for w in range(n_rows):
for m in range(n_cols):
for k in range(n_fft):
np_result[batch][w][m][0] += (
window_np[k]
* data_np[batch][m * hop_length + k]
* math.cos(2 * math.pi * w * k / n_fft)
)
np_result[batch][w][m][1] -= (
window_np[k]
* data_np[batch][m * hop_length + k]
* math.sin(2 * math.pi * w * k / n_fft)
)
if normalized:
np_result[batch][w][m][0] /= math.sqrt(n_fft)
np_result[batch][w][m][1] /= math.sqrt(n_fft)
return np_result
use_dyn = tvm.testing.parameter(True, False, ids=["dyn", "static"])
@tvm.testing.parametrize_targets("llvm", "cuda")
def test_stft(
self,
target,
dev,
ref_res: np.ndarray,
data_np: np.ndarray,
n_fft: int,
hop_length: int,
win_length: int,
window_np: np.ndarray,
normalized: bool,
onesided: bool,
use_dyn,
):
if use_dyn:
data = relay.var(
"data",
relay.TensorType([relay.Any(), relay.Any()], str(data_np.dtype)),
)
window = relay.var(
"window",
relay.TensorType([relay.Any()], str(window_np.dtype)),
)
backends = ["vm"]
else:
data = relay.var(
"data",
relay.TensorType(data_np.shape, str(data_np.dtype)),
)
window = relay.var(
"window",
relay.TensorType(window_np.shape, str(window_np.dtype)),
)
backends = ["graph", "vm"]
z = relay.op.stft(data, n_fft, hop_length, win_length, window, normalized, onesided)
func = relay.Function([data, window], z)
verify_func(
target, dev, func, [data_np, window_np], ref_res, rtol=1e-3, atol=1e-3, kinds=backends
)
def test_trilu(target="llvm", dev=tvm.cpu()):
def verify_trilu(data_shape, upper=True, k=0):
data = relay.var("data", relay.TensorType(data_shape, "float32"))
y = relay.trilu(data, k, upper)
mod = tvm.ir.IRModule.from_expr(y)
data_np = np.random.normal(size=data_shape).astype("float32")
tvm_res = (
relay.create_executor("graph", mod=mod, device=dev, target=target)
.evaluate()(data_np)
.numpy()
)
if upper:
np_res = np.triu(data_np, k)
else:
np_res = np.tril(data_np, k)
tvm.testing.assert_allclose(tvm_res, np_res)
# Test upper and lower triangle
verify_trilu((3, 3), True, 0)
verify_trilu((3, 3), False, 0)
# Test larger matrices with offset.
verify_trilu((6, 6), True, 1)
verify_trilu((6, 6), False, 2)
verify_trilu((6, 6), False, -2)
# Test batch size
verify_trilu((8, 6, 6), False, -2)
def test_trilu_shape_i64():
data_x = np.ones((2, 1), dtype="int32")
x = relay.var("x", shape=[2, 1], dtype="float32")
v0 = relay.broadcast_to(x, shape=relay.const([2, 1], dtype="int64"))
v2 = relay.add(relay.const([[1.0]]), v0)
v3 = relay.trilu(v0, k=0)
f = relay.Function([x], relay.Tuple([v2, v3]))
tvm_res = relay.create_executor("graph", device=tvm.cpu(), target="llvm").evaluate(f)(data_x)
np_res = (
np.array([[2.0], [2.0]], dtype=np.float32),
np.array([[1.0], [0.0]], dtype=np.float32),
)
tvm.testing.assert_allclose(tvm_res[0].numpy(), np_res[0])
tvm.testing.assert_allclose(tvm_res[1].numpy(), np_res[1])
def test_trilu_reduce():
data_i0 = np.ones((2, 2), dtype="int32")
k = 0
i0 = relay.var("i0", shape=[2, 2], dtype="int32")
i1 = relay.var("i1", shape=(), dtype="int64")
v0 = relay.trilu(i0, i1)
v1 = relay.argmin(v0, axis=[0])
f = relay.Function([i0, i1], v1)
tvm_res = (
relay.create_executor("graph", device=tvm.cpu(), target="llvm")
.evaluate(f)(data_i0, k)
.numpy()
)
np_res = np.triu(data_i0, k).argmin(axis=0)
tvm.testing.assert_allclose(tvm_res, np_res)
if __name__ == "__main__":
tvm.testing.main()