# 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. # ruff: noqa: F401, F811, RUF005 from collections.abc import Callable from typing import Union import numpy as np import pytest import tvm_ffi import tvm import tvm.testing from tvm import relax from tvm.ir import Call from tvm.ir.op import Op from tvm.relax.transform import LegalizeOps from tvm.relax.type import TensorType, TupleType from tvm.testing.utils import check_numerical_grads def relax_check_gradients( op_func: Callable, inputs_numpy: list[np.array], target: str | tvm.target.Target, dev: tvm.runtime.Device, tuple_input: bool = False, ignore_grads: list[int] = [], **kwargs, # attr for operators ): """Generate the forward and the gradient module. Then run them and check numeric gradients. Parameters ---------- op_func : Callable The forward operator function. Should be a function in package relax.op. inputs_numpy : List[np.array] The np array inputs for op_func. inputs_numpy will be transformed into TVM Tensor inside this function. If op_func takes a tuple of tensors as input, you can set tuple_input as True, and pass the tuple input (or list) as inputs_numpy. See test_concat(). target : Union[str, tvm.target.Target] The building target. dev : tvm.runtime.Device The device to deploy the module. tuple_input : bool Whether the operator accepts a tuple as input. If true, operator will accept exactly one tuple of tensors as input; otherwise, operator accept one or more tensors as input. See test_concat(). Default: False. ignore_grads: List[int] Specifies which input we do not need to find gradient. Sometimes the input is not differentiable, such as shape, boolean values, positions, etc. We can specify the index of these inputs to check the gradient of them is no_grad, and prevent computing numeric gradient. kwargs : Any The keyword arguments for the op_func. Will be passed to op_func directly. """ func_name = "main" # Helper functions def _numpy_to_ty(data): if isinstance(data, list): return relax.TupleType([_numpy_to_ty(d) for d in data]) return relax.TensorType(data.shape, str(data.dtype)) def _numpy_to_tvm(data): if isinstance(data, list): return [_numpy_to_tvm(d) for d in data] return tvm.runtime.tensor(data) def _tvm_to_numpy(data, ignore_idx=[]): if isinstance(data, tvm_ffi.Array): return [_tvm_to_numpy(d) for i, d in enumerate(data) if i not in ignore_idx] if isinstance(data, tvm.runtime.Tensor): return data.numpy() return data def _gen_weights(out_ty): if isinstance(out_ty, TupleType): return [_gen_weights(ty) for ty in out_ty.fields] else: assert isinstance(out_ty, TensorType) return np.random.uniform(size=[int(i) for i in out_ty.shape]).astype(out_ty.dtype) def _is_call_no_grad(expr): return isinstance(expr, Call) and expr.op == Op.get("relax.grad.no_grad") # Generate parameter relax Vars param_vars = [ relax.Var("x_" + str(i), _numpy_to_ty(data)) for i, data in enumerate(inputs_numpy) ] # Generate the forward call if tuple_input: t = relax.Tuple(param_vars) call = op_func(t, **kwargs) else: call = op_func(*param_vars, **kwargs) # Forward mod forward_bb = relax.BlockBuilder() with forward_bb.function(func_name, param_vars): with forward_bb.dataflow(): out = forward_bb.emit_output(call) forward_bb.emit_func_output(out) forward_mod = forward_bb.get() forward_ex = tvm.compile(forward_mod, target) forward_vm = relax.VirtualMachine(forward_ex, dev) # Generate weights # In forward process, weights represent the weight of every element of the result of the # forward call. The weighted result will be sum(weight * result). # If the result is a tuple, weights will be a list, and the weighted result will be # sum(i * j for i, j in zip(weights, result)) # In the gradient process, weights is the output gradient, i.e. the gradient w.r.t. the result. out_ty = forward_mod[func_name].body.body.ty weights = _gen_weights(out_ty) # The inputs of the forward function are inputs_filtered below. def forward(*inputs): inputs_iter = iter(inputs) inputs_tvm = [ _numpy_to_tvm(next(inputs_iter)) if i not in ignore_grads else _numpy_to_tvm(inputs_numpy[i]) for i in range(len(inputs_numpy)) ] result = forward_vm[func_name](*inputs_tvm) result_numpy = _tvm_to_numpy(result) if isinstance(result_numpy, list): assert isinstance(weights, list) assert len(weights) == len(result_numpy) ret = 0 for i, weight in enumerate(weights): ret += np.sum(weight * result_numpy[i]) return ret return np.sum(weights * result_numpy) # The gradient function assert isinstance(call.op, Op) op_grad_func = call.op.get_attr("FPrimalGradient") # The parameter Var for gradient grad_var = relax.Var("grad", _numpy_to_ty(weights)) # Gradient mod grad_bb = relax.BlockBuilder() with grad_bb.function(func_name, param_vars + [grad_var]): with grad_bb.dataflow(): orig = grad_bb.emit(call) # op_grad_func returns a list of Exprs representing the gradients grad_call = op_grad_func(orig, call, grad_var, grad_bb) # Check ignore_grads for i, grad in enumerate(grad_call): if i in ignore_grads: assert _is_call_no_grad(grad), f"The {i}-th gradient should be no_grad" else: assert not _is_call_no_grad(grad), f"The {i}-th gradient should not be no_grad" if tuple_input: # If the input is a tuple, the gradient is also a tuple. # The gradient tuple is the first (the only) element of grad_call. out = grad_bb.emit_output(grad_call[0]) else: # We need to wrap the list into a relax.Tuple so as to emit it out = grad_bb.emit_output(relax.Tuple(grad_call)) grad_bb.emit_func_output(out) grad_mod = grad_bb.get() grad_ex = tvm.compile(grad_mod, target) grad_vm = relax.VirtualMachine(grad_ex, dev) # tvm.runtime.Tensor inputs inputs_tvm = [_numpy_to_tvm(i) for i in inputs_numpy] weights_tvm = _numpy_to_tvm(weights) result_filtered = _tvm_to_numpy(grad_vm[func_name](*inputs_tvm, weights_tvm), ignore_grads) # Inputs contained in ignore_grads are removed inputs_filtered = [inputs_numpy[i] for i in range(len(inputs_numpy)) if i not in ignore_grads] check_numerical_grads(forward, inputs_filtered, result_filtered) ##################### Unary ##################### @pytest.mark.parametrize( "unary_op_func,can_be_neg", [ (relax.op.abs, True), (relax.op.cos, True), (relax.op.exp, True), (relax.op.log, False), (relax.op.negative, True), (relax.op.sigmoid, True), (relax.op.sin, True), (relax.op.sqrt, False), (relax.op.tanh, True), ], ) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_unary(unary_op_func, can_be_neg): target = "llvm" dev = tvm.device(target) (low, high) = (-1, 1) if can_be_neg else (0.1, 1) data_numpy = np.random.uniform(low, high, (3, 3)).astype(np.float32) relax_check_gradients(unary_op_func, [data_numpy], target, dev) ##################### Binary ##################### @pytest.mark.parametrize( "binary_arith_op_func", [ relax.op.add, relax.op.subtract, relax.op.multiply, relax.op.divide, relax.op.power, ], ) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_binary_arith(binary_arith_op_func): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(1, 2, (3, 3)).astype(np.float32) data2_numpy = np.random.uniform(1, 2, (3, 3)).astype(np.float32) relax_check_gradients(binary_arith_op_func, [data1_numpy, data2_numpy], target, dev) @pytest.mark.parametrize("binary_minmax_op_func", [relax.op.maximum, relax.op.minimum]) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_binary_minmax(binary_minmax_op_func): target = "llvm" dev = tvm.device(target) # Checking numerical gradient of min and max requires data1_numpy[i] != data2_numpy[i] # for all possible i. # If data1_numpy[i] == data2_numpy[i], the operator is not differentiable w.r.t. place i data1_numpy = np.random.uniform(1, 1.1, (3, 3)).astype(np.float32) delta = np.random.uniform(1, 1.1, (3, 3)).astype(np.float32) sign = np.random.randint(0, 2, (3, 3)).astype(np.float32) * 2 - 1 data2_numpy = data1_numpy + delta * sign relax_check_gradients(binary_minmax_op_func, [data1_numpy, data2_numpy], target, dev) @pytest.mark.parametrize( "binary_cmp_op_func", [ relax.op.equal, relax.op.greater, relax.op.greater_equal, relax.op.less, relax.op.less_equal, relax.op.not_equal, ], ) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_binary_cmp(binary_cmp_op_func): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(1, 2, (3, 3)).astype(np.float32) data2_numpy = np.random.uniform(1, 2, (3, 3)).astype(np.float32) relax_check_gradients( binary_cmp_op_func, [data1_numpy, data2_numpy], target, dev, ignore_grads=[0, 1] ) ##################### Create ##################### @pytest.mark.parametrize("like_op_func", [relax.op.zeros_like, relax.op.ones_like]) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_ones_zeros_like(like_op_func): target = "llvm" dev = tvm.device(target) data_numpy = np.random.uniform(-1, 1, (3, 3)).astype(np.float32) relax_check_gradients(like_op_func, [data_numpy], target, dev, ignore_grads=[0]) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_full_like(): target = "llvm" dev = tvm.device(target) data_numpy = np.random.uniform(-1, 1, (3, 3)).astype(np.float32) fill_value = np.random.uniform(-1, 1, ()).astype(np.float32) relax_check_gradients( relax.op.full_like, [data_numpy, fill_value], target, dev, ignore_grads=[0, 1] ) @pytest.mark.parametrize("create_op_func", [relax.op.zeros, relax.op.ones]) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_ones_zeros(create_op_func): target = "llvm" dev = tvm.device(target) relax_check_gradients( create_op_func, [], target, dev, ignore_grads=[0], shape=(3, 3), dtype="float32" ) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_triu(): target = "llvm" dev = tvm.device(target) data_numpy = np.random.uniform(-1, 1, (3, 3)).astype(np.float32) relax_check_gradients(relax.op.triu, [data_numpy], target, dev, k=0) ##################### Statistical ##################### @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_sum(): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(0, 16, (3, 3)).astype(np.float32) relax_check_gradients(relax.op.sum, [data1_numpy], target, dev) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_sum_with_axis(): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(0, 16, (2, 3, 4, 5)).astype(np.float32) relax_check_gradients(relax.op.sum, [data1_numpy], target, dev, axis=[1, 3]) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_sum_keepdims(): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(0, 16, (3, 3)).astype(np.float32) relax_check_gradients(relax.op.sum, [data1_numpy], target, dev, keepdims=True, axis=1) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_mean(): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(0, 16, (3, 3)).astype(np.float32) relax_check_gradients(relax.op.mean, [data1_numpy], target, dev) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_mean_with_axis(): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(0, 16, (2, 3, 4, 5)).astype(np.float32) relax_check_gradients(relax.op.mean, [data1_numpy], target, dev, axis=[1, 3]) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_mean_keepdims(): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(0, 16, (3, 3)).astype(np.float32) relax_check_gradients(relax.op.mean, [data1_numpy], target, dev, keepdims=True, axis=1) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_variance(): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(0, 16, (3, 3)).astype(np.float32) relax_check_gradients(relax.op.variance, [data1_numpy], target, dev) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_variance_with_axis(): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(0, 16, (2, 3, 4, 5)).astype(np.float32) relax_check_gradients(relax.op.variance, [data1_numpy], target, dev, axis=[1, 3]) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_variance_keepdims(): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(0, 16, (3, 3)).astype(np.float32) relax_check_gradients(relax.op.variance, [data1_numpy], target, dev, keepdims=True, axis=1) ##################### Manipulate ##################### @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_reshape(): target = "llvm" dev = tvm.device(target) data_numpy = np.random.uniform(0, 16, (2, 3, 5)).astype(np.float32) relax_check_gradients( relax.op.reshape, [data_numpy], target, dev, ignore_grads=[1], shape=(5, 6) ) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_reshape_infer_dim(): target = "llvm" dev = tvm.device(target) data_numpy = np.random.uniform(0, 16, (2, 3, 5)).astype(np.float32) relax_check_gradients( relax.op.reshape, [data_numpy], target, dev, ignore_grads=[1], shape=(5, 2, 1, -1) ) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_permute_dims(): target = "llvm" dev = tvm.device(target) data_numpy = np.random.uniform(0, 16, (2, 3, 4, 5)).astype(np.float32) relax_check_gradients(relax.op.permute_dims, [data_numpy], target, dev) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_permute_dims_with_axes(): target = "llvm" dev = tvm.device(target) data_numpy = np.random.uniform(0, 16, (2, 3, 4, 5)).astype(np.float32) relax_check_gradients( relax.op.permute_dims, [data_numpy], target, dev, axes=(0, 3, 1, 2), ) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_concat(): target = "llvm" dev = tvm.device(target) data_numpy1 = np.random.uniform(1, 16, (3, 3)).astype(np.float32) data_numpy2 = np.random.uniform(1, 16, (3, 4)).astype(np.float32) data_numpy3 = np.random.uniform(1, 16, (3, 5)).astype(np.float32) relax_check_gradients( relax.op.concat, [data_numpy1, data_numpy2, data_numpy3], target, dev, tuple_input=True, axis=1, ) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_split_indices(): target = "llvm" dev = tvm.device(target) data_numpy = np.random.uniform(1, 16, (3, 12)).astype(np.float32) relax_check_gradients( relax.op.split, [data_numpy], target, dev, indices_or_sections=[3, 7], axis=1, ) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_split_section(): target = "llvm" dev = tvm.device(target) data_numpy = np.random.uniform(1, 16, (3, 12)).astype(np.float32) relax_check_gradients( relax.op.split, [data_numpy], target, dev, indices_or_sections=3, axis=1, ) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_reshape(): target = "llvm" dev = tvm.device(target) data_numpy = np.random.uniform(1, 16, (3, 4)).astype(np.float32) relax_check_gradients( relax.op.reshape, [data_numpy], target, dev, shape=(3, 2, 2), ignore_grads=[1], ) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_cumsum(): target = "llvm" dev = tvm.device(target) data_numpy1 = np.random.uniform(1, 16, (3, 3)).astype(np.float32) relax_check_gradients( relax.op.cumsum, [data_numpy1], target, dev, axis=1, ) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_cumsum_no_axis(): target = "llvm" dev = tvm.device(target) data_numpy1 = np.random.uniform(1, 16, (3, 3)).astype(np.float32) relax_check_gradients( relax.op.cumsum, [data_numpy1], target, dev, ) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_expand_dims(): target = "llvm" dev = tvm.device(target) data_numpy = np.random.uniform(1, 16, (3, 12)).astype(np.float32) relax_check_gradients(relax.op.expand_dims, [data_numpy], target, dev, axis=1) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_expand_dims_list(): target = "llvm" dev = tvm.device(target) data_numpy = np.random.uniform(1, 16, (3, 12)).astype(np.float32) relax_check_gradients(relax.op.expand_dims, [data_numpy], target, dev, axis=(0, 2, 3)) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_broadcast_to(): target = "llvm" dev = tvm.device(target) data_numpy = np.random.uniform(1, 16, (3, 4)).astype(np.float32) relax_check_gradients( relax.op.broadcast_to, [data_numpy], target, dev, shape=(2, 3, 4), ignore_grads=[1], ) ##################### Index ##################### @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_take(): target = "llvm" dev = tvm.device(target) data_numpy = np.random.uniform(0, 16, size=(2, 3, 4)).astype(np.float32) indices = np.array([0, 1]) relax_check_gradients( relax.op.take, [data_numpy, indices], target, dev, axis=1, ignore_grads=[1], ) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_take_no_axis(): target = "llvm" dev = tvm.device(target) data_numpy = np.random.uniform(0, 16, size=(5,)).astype(np.float32) indices = np.array([1, 3]) relax_check_gradients( relax.op.take, [data_numpy, indices], target, dev, ignore_grads=[1], ) ##################### Search ##################### @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_where(): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(0, 1, size=(3, 3)) > 0.5 data2_numpy = np.random.uniform(0, 16, size=(3, 3)).astype(np.float32) data3_numpy = np.random.uniform(0, 16, size=(3, 3)).astype(np.float32) relax_check_gradients( relax.op.where, [data1_numpy, data2_numpy, data3_numpy], target, dev, ignore_grads=[0], ) ##################### Linear Algebra ##################### @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_matmul_2_2(): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(0, 16, (2, 3)).astype(np.float32) data2_numpy = np.random.uniform(0, 16, (3, 4)).astype(np.float32) relax_check_gradients(relax.op.matmul, [data1_numpy, data2_numpy], target, dev) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_matmul_1_1(): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(0, 16, (4,)).astype(np.float32) data2_numpy = np.random.uniform(0, 16, (4,)).astype(np.float32) relax_check_gradients(relax.op.matmul, [data1_numpy, data2_numpy], target, dev) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_matmul_1_4(): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(0, 16, (4,)).astype(np.float32) data2_numpy = np.random.uniform(0, 16, (2, 3, 4, 5)).astype(np.float32) relax_check_gradients(relax.op.matmul, [data1_numpy, data2_numpy], target, dev) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_matmul_4_1(): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(0, 16, (2, 3, 4, 5)).astype(np.float32) data2_numpy = np.random.uniform(0, 16, (5,)).astype(np.float32) relax_check_gradients(relax.op.matmul, [data1_numpy, data2_numpy], target, dev) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_matmul_5_4(): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(0, 16, (2, 3, 1, 4, 5)).astype(np.float32) data2_numpy = np.random.uniform(0, 16, (3, 2, 5, 4)).astype(np.float32) relax_check_gradients( relax.op.matmul, [data1_numpy, data2_numpy], target, dev, ) ##################### Datatype ##################### @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_astype(): target = "llvm" dev = tvm.device(target) data_numpy = np.random.uniform(0, 16, size=(3, 3)).astype(np.float64) relax_check_gradients(relax.op.astype, [data_numpy], target, dev, dtype="float32") ##################### Neural network ##################### @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_relu(): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(0.2, 1, (3, 3)).astype(np.float32) sign = np.random.randint(0, 2, (3, 3)).astype(np.float32) * 2 - 1 data1_numpy *= sign relax_check_gradients(relax.op.nn.relu, [data1_numpy], target, dev) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_silu(): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(0, 16, (3, 3)).astype(np.float32) relax_check_gradients(relax.op.nn.silu, [data1_numpy], target, dev) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_softmax(): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(0, 16, (3, 3)).astype(np.float32) relax_check_gradients(relax.op.nn.softmax, [data1_numpy], target, dev) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_softmax_with_axis(): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(0, 16, (3, 3)).astype(np.float32) relax_check_gradients(relax.op.nn.softmax, [data1_numpy], target, dev, axis=1) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_log_softmax(): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(0, 16, (3, 3)).astype(np.float32) relax_check_gradients(relax.op.nn.log_softmax, [data1_numpy], target, dev) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_log_softmax_with_axis(): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(0, 16, (3, 3)).astype(np.float32) relax_check_gradients(relax.op.nn.log_softmax, [data1_numpy], target, dev, axis=1) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_cross_entropy_with_logits(): target = "llvm" dev = tvm.device(target) data_numpy1 = np.random.uniform(1, 16, (3,)).astype(np.float32) data_numpy2 = np.random.uniform(1, 16, (3,)).astype(np.float32) relax_check_gradients( relax.op.nn.cross_entropy_with_logits, [data_numpy1, data_numpy2], target, dev, ) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_cross_entropy_with_logits_batch(): target = "llvm" dev = tvm.device(target) data_numpy1 = np.random.uniform(1, 16, (2, 3)).astype(np.float32) data_numpy2 = np.random.uniform(1, 16, (2, 3)).astype(np.float32) relax_check_gradients( relax.op.nn.cross_entropy_with_logits, [data_numpy1, data_numpy2], target, dev, ) @pytest.mark.parametrize( "nll_reduction,nll_weighted,nll_ignore_index", [ ("mean", True, -1), ("sum", True, -1), ("none", True, -1), ("mean", True, 1), ("mean", True, 1), ("mean", False, 1), ], ) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_nll_loss(nll_reduction, nll_weighted, nll_ignore_index): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(0, 16, (2, 3, 4)).astype(np.float32) data2_numpy = np.random.randint(0, 3, (2, 4)).astype(np.int64) # force a position in targets it not ignore_index, to avoid zero total weight data2_numpy[0][0] = 0 # weight > 0 data3_numpy = np.random.uniform(1, 16, (3,)).astype(np.float32) input = [data1_numpy, data2_numpy] + ([data3_numpy] if nll_weighted else []) ignore_grads = [1] + ([2] if nll_weighted else []) relax_check_gradients( relax.op.nn.nll_loss, input, target, dev, ignore_grads=ignore_grads, reduction=nll_reduction, ignore_index=nll_ignore_index, ) @pytest.mark.parametrize( "nll_reduction1,nll_weighted1,nll_ignore_index1", [ ("mean", True, -1), ("sum", True, -1), ("none", True, -1), ], ) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_nll_loss_no_batch(nll_reduction1, nll_weighted1, nll_ignore_index1): target = "llvm" dev = tvm.device(target) data1_numpy = np.random.uniform(0, 16, (3,)).astype(np.float32) data2_numpy = np.random.randint(0, 3, ()).astype(np.int64) # weight > 0 data3_numpy = np.random.uniform(1, 16, (3,)).astype(np.float32) input = [data1_numpy, data2_numpy] + ([data3_numpy] if nll_weighted1 else []) ignore_grads = [1] + ([2] if nll_weighted1 else []) relax_check_gradients( relax.op.nn.nll_loss, input, target, dev, ignore_grads=ignore_grads, reduction=nll_reduction1, ignore_index=nll_ignore_index1, ) @pytest.mark.parametrize( "c2d_shape1,c2d_shape2,c2d_kwargs", [ ( (3, 2, 10, 10), (3, 2, 3, 3), {}, ), ( (3, 2, 10, 10), (3, 2, 1, 2), {}, ), ( (3, 2, 10, 10), (3, 2, 3, 3), {"strides": (2, 2), "padding": (3, 2), "dilation": (1, 1)}, ), ( (3, 2, 10, 10), (3, 2, 3, 3), {"strides": (2, 1), "padding": (2, 2), "dilation": (1, 1)}, ), ( (3, 6, 10, 10), (4, 3, 3, 3), {"groups": 2}, ), ( (3, 2, 10, 10), (4, 1, 3, 3), {"groups": 2, "strides": (2, 2), "padding": (2, 2), "dilation": (1, 1)}, ), ], ) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_conv2d(c2d_shape1, c2d_shape2, c2d_kwargs): target = "llvm" dev = tvm.device(target) import pytest # Use smaller range to reduce numerical errors in gradient check data1_numpy = np.random.uniform(0, 2, c2d_shape1).astype(np.float32) data2_numpy = np.random.uniform(0, 2, c2d_shape2).astype(np.float32) relax_check_gradients( relax.op.nn.conv2d, [data1_numpy, data2_numpy], target, dev, **c2d_kwargs, ) pool_params = [ ( (3, 3), {}, ), ( (3, 3), {"strides": (2, 2), "padding": (1, 2), "dilation": (1, 1), "count_include_pad": True}, ), ( (5, 5), { "strides": (2, 2), "padding": (2, 1), "dilation": (1, 1), "ceil_mode": True, "count_include_pad": True, }, ), ] @pytest.mark.parametrize("pool_size,pool_kwargs", pool_params) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_max_pool2d(pool_size, pool_kwargs): target = "llvm" dev = tvm.device(target) data_numpy = np.random.uniform(0, 3, size=(3, 2, 10, 10)).astype(np.float32) relax_check_gradients( relax.op.nn.max_pool2d, [data_numpy], target, dev, pool_size=pool_size, **pool_kwargs, ) @pytest.mark.parametrize("pool_size,pool_kwargs", pool_params) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_avg_pool2d(pool_size, pool_kwargs): target = "llvm" dev = tvm.device(target) data_numpy = np.random.uniform(0, 3, size=(3, 2, 10, 10)).astype(np.float32) relax_check_gradients( relax.op.nn.avg_pool2d, [data_numpy], target, dev, pool_size=pool_size, **pool_kwargs, ) if __name__ == "__main__": tvm.testing.main()