blob: f5a495bc71c7cde9d759cff48e40eea18297cd1c [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.
# pylint: disable=invalid-name, missing-docstring, no-else-return
"""Unit tests for the Relay VM serialization and deserialization."""
import pytest
import numpy as np
import tvm
from tvm.runtime import vm as _vm
from tvm.relay import vm as rly_vm
from tvm import relay
from tvm.relay.scope_builder import ScopeBuilder
from tvm.relay import transform
from tvm.relay.prelude import Prelude
from tvm.contrib import utils
from tvm.relay import testing
def create_exec(f, target="llvm", params=None):
if isinstance(f, relay.Expr):
mod = tvm.IRModule()
mod["main"] = f
executable = rly_vm.compile(mod, target=target, params=params)
return executable
else:
assert isinstance(f, tvm.IRModule), "expected mod as tvm.IRModule"
executable = rly_vm.compile(f, target=target, params=params)
return executable
def get_serialized_output(mod, *data, params=None, target="llvm", device=tvm.cpu()):
exe = create_exec(mod, target, params=params)
code, lib = exe.save()
des_exec = _vm.Executable.load_exec(code, lib)
des_vm = _vm.VirtualMachine(des_exec, device)
result = des_vm.run(*data)
return result
def run_network(mod, params, dtype="float32"):
def get_vm_output(mod, data, params, target, device, dtype="float32"):
result = relay.create_executor("vm", mod=mod, device=device).evaluate()(data, **params)
return result.numpy().astype(dtype)
data_shape = [int(x) for x in mod["main"].checked_type.arg_types[0].shape]
data = np.random.uniform(size=data_shape).astype(dtype)
target = "llvm"
dev = tvm.cpu(0)
tvm_out = get_vm_output(mod, tvm.nd.array(data.astype(dtype)), params, target, dev, dtype)
vm_out = get_serialized_output(
mod, tvm.nd.array(data.astype(dtype)), params=params, target=target, device=dev
)
tvm.testing.assert_allclose(vm_out.numpy().astype(dtype), tvm_out, rtol=1e-5, atol=1e-5)
def test_serializer():
mod = tvm.IRModule({})
a = relay.const(1.0, "float32")
x = relay.var("x", shape=(10, 10), dtype="float32")
f1 = relay.Function([x], x + a)
glb_f1 = relay.GlobalVar("f1")
mod[glb_f1] = f1
# TODO(@jroesch): look into optimizing away the need to do this
mod = transform.InferType()(mod)
b = relay.const(2.0, "float32")
y = relay.var("y", shape=(10, 10), dtype="float32")
f2 = relay.Function([y], y - b)
glb_f2 = relay.GlobalVar("f2")
mod[glb_f2] = f2
# TODO(@jroesch): look into optimizing away the need to do this
mod = transform.InferType()(mod)
x1 = relay.var("x1", shape=(10, 10), dtype="float32")
y1 = relay.var("y1", shape=(10, 10), dtype="float32")
main = relay.Function([x1, y1], glb_f1(x1) * glb_f2(y1))
mod["main"] = main
exe = create_exec(mod)
glbs = exe.globals
assert len(glbs) == 3
assert "f1" in glbs
assert "f2" in glbs
assert "main" in glbs
prim_ops = exe.primitive_ops
assert any(item.startswith("vm_mod_fused_add") for item in prim_ops)
assert any(item.startswith("vm_mod_fused_subtract") for item in prim_ops)
assert any(item.startswith("vm_mod_fused_multiply") for item in prim_ops)
code = exe.bytecode
assert "main(x1, y1)" in code
assert "f1(x)" in code
assert "f2(y)" in code
code, lib = exe.save()
assert isinstance(code, bytearray)
assert isinstance(lib, tvm.runtime.Module)
def test_save_load():
x = relay.var("x", shape=(10, 10))
f = relay.Function([x], x + x)
x_data = np.random.rand(10, 10).astype("float32")
# serialize.
vm = create_exec(f)
code, lib = vm.save()
assert isinstance(code, bytearray)
# save and load the code and lib file.
tmp = utils.tempdir()
path_lib = tmp.relpath("lib.so")
lib.export_library(path_lib)
with open(tmp.relpath("code.ro"), "wb") as fo:
fo.write(code)
loaded_lib = tvm.runtime.load_module(path_lib)
loaded_code = bytearray(open(tmp.relpath("code.ro"), "rb").read())
# deserialize.
des_exec = _vm.Executable.load_exec(loaded_code, loaded_lib)
des_vm = _vm.VirtualMachine(des_exec, tvm.cpu())
res = des_vm.run(x_data)
tvm.testing.assert_allclose(res.numpy(), x_data + x_data)
def test_const():
c = relay.const(1.0, "float32")
x = relay.var("x", shape=(10, 10), dtype="float32")
f = relay.Function([x], x + c)
x_data = np.random.rand(10, 10).astype("float32")
res = get_serialized_output(f, x_data)
tvm.testing.assert_allclose(res.numpy(), x_data + 1)
def test_if():
x = relay.var("x", shape=(10, 10))
y = relay.var("y", shape=(10, 10))
equal = relay.op.equal(x, y)
equal = relay.op.nn.batch_flatten(equal)
f = relay.Function([x, y], relay.If(relay.op.min(equal, axis=[0, 1]), x, y))
x_data = np.random.rand(10, 10).astype("float32")
y_data = np.random.rand(10, 10).astype("float32")
# same
res = get_serialized_output(f, x_data, x_data)
tvm.testing.assert_allclose(res.numpy(), x_data)
# diff
res = get_serialized_output(f, x_data, y_data)
tvm.testing.assert_allclose(res.numpy(), y_data)
def test_loop():
mod = tvm.IRModule({})
sum_up = relay.GlobalVar("sum_up")
i = relay.var("i", shape=[], dtype="int32")
accum = relay.var("accum", shape=[], dtype="int32")
sb = ScopeBuilder()
with sb.if_scope(relay.equal(i, relay.const(0, "int32"))):
sb.ret(accum)
with sb.else_scope():
one_less = relay.subtract(i, relay.const(1, "int32"))
new_accum = relay.add(accum, i)
sb.ret(relay.Call(sum_up, [one_less, new_accum]))
func = relay.Function([i, accum], sb.get())
mod[sum_up] = func
mod = transform.InferType()(mod)
loop_bound = 0
i_data = np.array(loop_bound, dtype="int32")
accum_data = np.array(0, dtype="int32")
iarg = relay.var("i", shape=[], dtype="int32")
aarg = relay.var("accum", shape=[], dtype="int32")
mod["main"] = relay.Function([iarg, aarg], sum_up(iarg, aarg))
result = get_serialized_output(mod, i_data, accum_data)
tvm.testing.assert_allclose(result.numpy(), sum(range(1, loop_bound + 1)))
def test_tuple():
ttype = relay.TupleType([relay.TensorType((1,)), relay.TensorType((10,))])
tup = relay.var("tup", type_annotation=ttype)
f = relay.Function([tup], relay.TupleGetItem(tup, 1))
i_data = np.random.rand(41).astype("float32")
j_data = np.random.rand(10).astype("float32")
result = get_serialized_output(f, (i_data, j_data))
tvm.testing.assert_allclose(result.numpy(), j_data)
def test_adt_list():
mod = tvm.IRModule()
p = Prelude(mod)
_, cons, nil = mod.get_type("List")
l1 = cons(relay.const(1), nil())
l21 = cons(relay.const(2), l1)
l321 = cons(relay.const(3), l21)
f = relay.Function([], l321)
mod["main"] = f
result = get_serialized_output(mod)
assert len(result) == 2
assert len(result[1]) == 2
assert len(result[1][1]) == 2
res = []
res.append(result[0].numpy().tolist())
res.append(result[1][0].numpy().tolist())
res.append(result[1][1][0].numpy().tolist())
tvm.testing.assert_allclose(res, np.array([3, 2, 1]))
def test_adt_compose():
mod = tvm.IRModule()
p = Prelude(mod)
compose = mod.get_global_var("compose")
# add_one = fun x -> x + 1
sb = relay.ScopeBuilder()
x = relay.var("x", "float32")
x1 = sb.let("x1", x)
xplusone = x1 + relay.const(1.0, "float32")
sb.ret(xplusone)
body = sb.get()
add_one = relay.GlobalVar("add_one")
add_one_func = relay.Function([x], body)
# add_two = compose(add_one, add_one)
sb = relay.ScopeBuilder()
y = relay.var("y", "float32")
add_two_func = sb.let("add_two", compose(add_one_func, add_one_func))
add_two_res = add_two_func(y)
sb.ret(add_two_res)
add_two_body = sb.get()
mod[add_one] = add_one_func
f = relay.Function([y], add_two_body)
mod["main"] = f
x_data = np.array(np.random.rand()).astype("float32")
result = get_serialized_output(mod, x_data)
tvm.testing.assert_allclose(result.numpy(), x_data + 2.0)
def test_closure():
x = relay.var("x", shape=())
y = relay.var("y", shape=())
f = relay.Function([x], x + y)
ff = relay.Function([y], f)
clo = ff(relay.const(1.0))
main = clo(relay.const(2.0))
res = get_serialized_output(main)
tvm.testing.assert_allclose(res.numpy(), 3.0)
def test_synthetic():
mod, params = testing.synthetic.get_workload()
run_network(mod, params)
def test_mobilenet():
mod, params = testing.mobilenet.get_workload(batch_size=1)
run_network(mod, params)
def test_vm_shape_of():
x = relay.var("x", shape=(relay.Any(), relay.Any(), relay.Any()), dtype="float32")
relu_x = relay.nn.relu(x)
data = np.random.uniform(size=(2, 3, 4)).astype("float32")
args = [data]
newshape_var = relay.var("newshape", shape=(2,), dtype="int64")
args.append(np.array((1, -1), dtype="int64"))
main = relay.Function([x, newshape_var], relay.reshape(relu_x, newshape=newshape_var))
res = get_serialized_output(main, *args).numpy()
tvm.testing.assert_allclose(res.flatten(), data.flatten())
def test_dynamic_bcast():
dtype = "float32"
x = relay.var("x", shape=(relay.Any(), 2), dtype=dtype)
y = relay.var("y", shape=(3, 2), dtype=dtype)
mod = tvm.IRModule()
mod["main"] = relay.Function([x, y], relay.add(x, y))
x_data = np.random.uniform(size=(1, 2)).astype(dtype)
y_data = np.random.uniform(size=(3, 2)).astype(dtype)
res_np = np.add(x_data, y_data)
for target, dev in testing.enabled_targets():
res = get_serialized_output(mod, *(x_data, y_data), target=target, device=dev)
tvm.testing.assert_allclose(res.numpy(), res_np)
if __name__ == "__main__":
tvm.testing.main()