diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index 76a12691d2bf..755c9f70f4ab 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -17,7 +17,6 @@ # pylint: disable=invalid-name, import-self, len-as-condition, unused-argument, too-many-lines # pylint: disable=import-outside-toplevel """Paddle: PArallel Distributed Deep LEarning.""" -import warnings import numpy as np @@ -25,65 +24,171 @@ from tvm.ir import IRModule from .. import analysis +from .. import ty as _ty from .. import expr as _expr from .. import function as _function from .. import ty as _ty from .. import op as _op from .common import ( fold_constant, + get_relay_op, infer_shape, infer_type, infer_value, + try_infer_value, new_var, ) __all__ = ["from_paddle"] +def _get_pad_size(in_size, dilated_kernel_size, stride_size): + """Calculate the paddings size for Conv/Pool in SAME padding mode.""" + + if stride_size == 1 or in_size % stride_size == 0: + pad = max(dilated_kernel_size - stride_size, 0) + else: + pad = max(dilated_kernel_size - (in_size % stride_size), 0) + + pad_before = pad // 2 + pad_after = pad - pad_before + + return [pad_before, pad_after] + + +def _dtype_shape_promotion(inputs): + """Promote data type and shape for list of tensors.""" + + dtype_order = ["bool", "int8", "int16", "int32", "int64", "float32", "float64"] + + ranks = [len(infer_shape(x)) for x in inputs] + if set(ranks) == set([1, 0]): + for i, r in enumerate(ranks): + if r == 0: + inputs[i] = _op.expand_dims(inputs[i], axis=0) + + dtypes = set(dtype_order.index(infer_type(x).checked_type.dtype) for x in inputs) + if len(dtypes) == 1: + return inputs + max_dtype = dtype_order[max(dtypes)] + for i, input_op in enumerate(inputs): + if infer_type(input_op).checked_type.dtype != max_dtype: + inputs[i] = input_op.astype(max_dtype) + return inputs + + def shape_of(x, dtype="int32"): - """Get shape of a tensor""" + """Get shape of a tensor.""" ttype = infer_type(x).checked_type if not _ty.is_dynamic(ttype): shape = list(ttype.shape) - return _expr.const(shape, dtype) + return _expr.const(np.array(shape), dtype) return _op.shape_of(x, dtype) -def _get_pad_size(in_size, dilated_kernel_size, stride_size): - """calculate the paddings size""" +def _convert_dtype_value(val): + """Converts a Paddle type id to a string.""" - if stride_size == 1 or in_size % stride_size == 0: - pad = max(dilated_kernel_size - stride_size, 0) + convert_dtype_map = { + 21: "int8", + 20: "uint8", + 6: "float64", + 5: "float32", + 4: "float16", + 3: "int64", + 2: "int32", + 1: "int16", + 0: "bool", + } + if val not in convert_dtype_map: + msg = "Paddle data type value %d is not handled yet." % (val) + raise NotImplementedError(msg) + return convert_dtype_map[val] + + +def convert_unary_op(g, op, block): + """Operator converter for all the unary operators.""" + + # op_map stores mapping relationship between paddlepaddle and relay + op_map = { + "isinf_v2": _op.isinf, + "isfinite_v2": _op.isfinite, + "isnan_v2": _op.isnan, + } + if op.type in op_map: + unary_func = op_map[op.type] else: - pad = max(dilated_kernel_size - (in_size % stride_size), 0) + # while paddle operator's name is same with relay + unary_func = get_relay_op(op.type) + out = unary_func(g.get_node(op.input("X")[0])) + g.add_node(op.output("Out")[0], out) - pad_before = pad // 2 - pad_after = pad - pad_before - return [pad_before, pad_after] +def convert_binary_logical_op(g, op, block): + """Operator converter for logical op.""" + + ipt0 = g.get_node(op.input("X")[0]) + ipt1 = g.get_node(op.input("Y")[0]) + op_func = get_relay_op(op.type) + out = op_func(ipt0, ipt1) + g.add_node(op.output("Out")[0], out) -def convert_arg_max(g, op, block): - """Operator converter for arg_max.""" +def convert_arg_max_min(g, op, block): + """Operator converter for arg_max and arg_min.""" axis = op.attr("axis") keepdims = op.attr("keepdims") flatten = op.attr("flatten") + dtype = op.attr("dtype") + dtype = _convert_dtype_value(dtype) + func = _op.argmax if op.type == "arg_max" else _op.argmin x = g.get_node(op.input("X")[0]) if axis is None or flatten: x = _op.reshape(x, [-1]) - out = _op.argmax(x, axis=None, keepdims=True) + out = func(x, axis=None, keepdims=True) else: - out = _op.argmax(x, axis=axis, keepdims=keepdims) + out = func(x, axis=axis, keepdims=keepdims) + if dtype != infer_type(out).checked_type.dtype: + out = _op.cast(out, dtype) + g.add_node(op.output("Out")[0], out) + + +def convert_argsort(g, op, block): + """Operator converter for argsort.""" + + x = g.get_node(op.input("X")[0]) + axis = op.attr("axis") + descending = op.attr("descending") + + out_indices = _op.argsort(x, axis, not descending, dtype="int64") + out = _op.gather(x, axis, out_indices) g.add_node(op.output("Out")[0], out) + g.add_node(op.output("Indices")[0], out_indices) def convert_assign(g, op, block): """Operator converter for assign.""" - out = _op.copy(g.get_node(op.input("X")[0])) + out = g.get_node(op.input("X")[0]) + g.add_node(op.output("Out")[0], out) + + +def convert_assign_value(g, op, block): + """Operator converter for assign_value.""" + + keys = ["bool_values", "fp32_values", "int32_values", "int64_values"] + dtypes = ["bool", "float32", "int32", "int64"] + for i, key in enumerate(keys): + dtype = dtypes[i] + value = np.array(op.attr(key)).astype(dtype) + if value is not None and value.size >= 1: + break + shape = op.attr("shape") + value = value.reshape(shape) + out = _op.const(value, dtype=dtype) g.add_node(op.output("Out")[0], out) @@ -110,8 +215,8 @@ def convert_batch_norm(g, op, block): def convert_cast(g, op, block): """Operator converter for cast.""" - dtype = block.var(op.output("Out")[0]).dtype - dtype = str(dtype).strip().split(".")[1] + dtype = op.attr("out_dtype") + dtype = _convert_dtype_value(dtype) x = g.get_node(op.input("X")[0]) out = _op.cast(x, dtype=dtype) g.add_node(op.output("Out")[0], out) @@ -122,6 +227,7 @@ def convert_concat(g, op, block): inputs = [g.get_node(op.input("X")[i]) for i in range(len(op.input("X")))] axis = op.attr("axis") + inputs = _dtype_shape_promotion(inputs) out = _op.concatenate(inputs, axis=axis) g.add_node(op.output("Out")[0], out) @@ -138,12 +244,22 @@ def convert_conv2d(g, op, block): kernel = g.get_node(op.input("Filter")[0]) input_x = g.get_node(op.input("Input")[0]) out_channels, _, k_h, k_w = infer_shape(kernel) - in_h, in_w = infer_shape(input_x)[2:] if padding_algorithm == "VALID": paddings = [0, 0] elif padding_algorithm == "SAME": - pad_h = _get_pad_size(in_h, (k_h - 1) * dilations[0] + 1, strides[0]) - pad_w = _get_pad_size(in_w, (k_w - 1) * dilations[1] + 1, strides[1]) + if strides[0] == 1 and strides[1] == 1: + pad_h = _get_pad_size(0, (k_h - 1) * dilations[0] + 1, strides[0]) + pad_w = _get_pad_size(0, (k_w - 1) * dilations[1] + 1, strides[1]) + else: + input_shape = shape_of(input_x) + h_w = _op.strided_slice(input_shape, [2], [4]) + try: + in_h, in_w = infer_value(h_w, g.get_params()).numpy().tolist() + except Exception as e: + msg = "Dynamic shape is not supported in SAME padding algorithm while stride!=1" + raise tvm.error.OpAttributeInvalid(msg) from e + pad_h = _get_pad_size(in_h, (k_h - 1) * dilations[0] + 1, strides[0]) + pad_w = _get_pad_size(in_w, (k_w - 1) * dilations[1] + 1, strides[1]) paddings = [pad_h[0], pad_w[0], pad_h[1], pad_w[1]] elif padding_algorithm == "EXPLICIT": if len(paddings) == 2: @@ -191,7 +307,18 @@ def convert_dropout(g, op, block): """Operator converter for dropout.""" x = g.get_node(op.input("X")[0]) - out = _op.copy(x) + g.add_node(op.output("Out")[0], x) + + +def convert_dot(g, op, block): + """Operator converter for dot.""" + + # x, y should be 1D or 2D tensor + # when it's 2D tensor, the first dimension means batch dimension + x = g.get_node(op.input("X")[0]) + y = g.get_node(op.input("Y")[0]) + + out = _op.sum(_op.multiply(x, y), axis=[-1], keepdims=True) g.add_node(op.output("Out")[0], out) @@ -199,49 +326,61 @@ def convert_elementwise_op(g, op, block): """Operator converter for all the elementwise operators.""" op_map = { - "elementwise_div": lambda x, y: x / y, - "elementwise_add": lambda x, y: x + y, - "elementwise_mul": lambda x, y: x * y, - "elementwise_sub": lambda x, y: x - y, - "elementwise_mod": lambda x, y: x % y, + "elementwise_div": "divide", + "elementwise_add": "add", + "elementwise_mul": "multiply", + "elementwise_sub": "subtract", + "elementwise_mod": "mod", + "elementwise_max": "maximum", + "elementwise_min": "minimum", + "elementwise_pow": "power", + "elementwise_floordiv": "floor_divide", + "equal": "equal", + "greater_equal": "greater_equal", + "greater_than": "greater", + "less_equal": "less_equal", + "less_than": "less", + "not_equal": "not_equal", } op_func = op_map[op.type] ipt0 = g.get_node(op.input("X")[0]) ipt1 = g.get_node(op.input("Y")[0]) - ipt0_shape = block.var(op.input("X")[0]).shape - ipt1_shape = block.var(op.input("Y")[0]).shape + ipt0_shape = infer_shape(ipt0) + ipt1_shape = infer_shape(ipt1) axis = op.attr("axis") if len(ipt0_shape) != len(ipt1_shape): if axis < 0: axis = axis + len(ipt0_shape) if axis != len(ipt0_shape) - 1: ipt1 = _op.expand_dims(ipt1, axis=axis, num_newaxis=(len(ipt0_shape) - axis - 1)) + op_func = get_relay_op(op_func) out = op_func(ipt0, ipt1) g.add_node(op.output("Out")[0], out) -def convert_equal(g, op, block): - """Operator converter for equal.""" +def convert_expand(g, op, block): + """Operator converter for expand.""" x = g.get_node(op.input("X")[0]) - y = g.get_node(op.input("Y")[0]) - out = _op.equal(x, y) + if op.input("Shape"): + sizes = g.get_node(op.input("Shape")[0]) + sizes = try_infer_value(sizes, g.get_params())[0] + else: + sizes = op.attr("shape") + + if isinstance(sizes, np.ndarray): + sizes = sizes.tolist() + + out = _op.broadcast_to(x, sizes) g.add_node(op.output("Out")[0], out) -def convert_activation(g, op, block): - """Operator converter for all the activation.""" +def convert_expand_as(g, op, block): + """Operator converter for expand_as.""" - op_map = { - "exp": _op.exp, - "relu": _op.nn.relu, - "tanh": _op.tanh, - "sqrt": _op.sqrt, - "erf": _op.erf, - "abs": _op.abs, - } - act_func = op_map[op.type] - out = act_func(g.get_node(op.input("X")[0])) + x = g.get_node(op.input("X")[0]) + target_shape = op.attr("target_shape") + out = _op.broadcast_to(x, target_shape) g.add_node(op.output("Out")[0], out) @@ -250,15 +389,20 @@ def convert_feed(g, op, block): if block is not None: ipt_name = op.output("Out")[0] - ipt_shape = block.var(ipt_name).shape - ipt_dtype = block.var(ipt_name).dtype - ipt_dtype = str(ipt_dtype).strip().split(".")[1] + dtype = op.attr("dtype") + dtype = _convert_dtype_value(dtype) else: ipt_shape = op.shape ipt_dtype = str(op.dtype).strip().split(".")[1] ipt_name = op.name if g.shape_dict is not None: ipt_shape = g.shape_dict[ipt_name] + + if isinstance(ipt_shape, tuple): + ipt_shape = list(ipt_shape) + for i, s in enumerate(ipt_shape): + if s < 0: + ipt_shape[i] = _ty.Any() out = new_var(ipt_name, shape=ipt_shape, dtype=ipt_dtype) g.add_node(ipt_name, out) @@ -266,18 +410,11 @@ def convert_feed(g, op, block): def convert_fill_any_like(g, op, block): """Operator converter for fill_any_like.""" - out_name = op.output("Out")[0] - out_dtype = block.var(out_name).dtype - out_dtype = str(out_dtype).strip().split(".")[1] + dtype = op.attr("dtype") + dtype = _convert_dtype_value(dtype) x = g.get_node(op.input("X")[0]) - ipt_type = infer_type(x).checked_type - value = op.attr("value") - if not _ty.is_dynamic(ipt_type): - shape = infer_shape(x) - const = np.ones(shape) * value - out = _expr.const(const.astype(out_dtype)) - else: - out = _op.transform.full_like(x, value).astype(out_dtype) + value = _expr.const(op.attr("value"), dtype=dtype) + out = _op.transform.full_like(x, value).astype(dtype) g.add_node(op.output("Out")[0], out) @@ -286,16 +423,20 @@ def convert_fill_constant(g, op, block): value = op.attr("value") shape = block.var(op.output("Out")[0]).shape - dtype = block.var(op.output("Out")[0]).dtype - dtype = str(dtype).strip().split(".")[1] - if op.input("ValueTensor"): + dtype = op.attr("dtype") + dtype = _convert_dtype_value(dtype) + value = _expr.const(value).astype(dtype) + if "ValueTensor" in op.input_names and op.input("ValueTensor"): shape = g.get_node(op.input("ValueTensor")[0]) - shape = infer_value(shape, g.get_params()).numpy() - if op.input("ShapeTensor"): + shape = try_infer_value(shape, g.get_params())[0] + if "ShapeTensor" in op.input_names and op.input("ShapeTensor"): shape = g.get_node(op.input("ShapeTensor")[0]) - shape = infer_value(shape, g.get_params()).numpy() - value = np.full(shape, value, dtype) - out = _expr.const(value.astype(dtype)).astype(dtype) + shape = try_infer_value(shape, g.get_params())[0] + + if isinstance(shape, np.ndarray): + shape = shape.tolist() + + out = _op.full(value, shape=shape, dtype=dtype) g.add_node(op.output("Out")[0], out) @@ -722,41 +863,53 @@ def convert_unsqueeze(g, op, block): _convert_map = { - "arg_max": convert_arg_max, + "arg_max": convert_arg_max_min, + "arg_min": convert_arg_max_min, + "argsort": convert_argsort, "assign": convert_assign, + "assign_value": convert_assign_value, "batch_norm": convert_batch_norm, "cast": convert_cast, "concat": convert_concat, "conv2d": convert_conv2d, "cumsum": convert_cumsum, "depthwise_conv2d": convert_conv2d, + "dot": convert_dot, "dropout": convert_dropout, "elementwise_add": convert_elementwise_op, "elementwise_div": convert_elementwise_op, "elementwise_mul": convert_elementwise_op, "elementwise_sub": convert_elementwise_op, - "equal": convert_equal, - "exp": convert_activation, + "equal": convert_elementwise_op, + "exp": convert_unary_op, + "expand_v2": convert_expand, + "expand_as_v2": convert_expand_as, "feed": convert_feed, "fill_any_like": convert_fill_any_like, "fill_constant": convert_fill_constant, "gelu": convert_gelu, "hard_sigmoid": convert_hard_sigmoid, "hard_swish": convert_hard_swish, + "isfinite_v2": convert_unary_op, + "isinf_v2": convert_unary_op, + "isnan_v2": convert_unary_op, "layer_norm": convert_layer_norm, "leaky_relu": convert_leaky_relu, + "logical_and": convert_binary_logical_op, + "logical_or": convert_binary_logical_op, + "logical_xor": convert_binary_logical_op, "lookup_table_v2": convert_lookup_table, "matmul": convert_matmul, "matmul_v2": convert_matmul, "mul": convert_mul, "pool2d": convert_pool2d, - "relu": convert_activation, + "relu": convert_unary_op, "reshape2": convert_reshape, "scale": convert_scale, "shape": convert_shape, "slice": convert_slice, "softmax": convert_softmax, - "tanh": convert_activation, + "tanh": convert_unary_op, "unsqueeze2": convert_unsqueeze, } @@ -781,7 +934,7 @@ def add_node(self, name, node): self.nodes[name] = fold_constant(node) def get_params(self, name=None): - """get params from graph""" + """Get params from graph.""" if name is None: return self.params diff --git a/tests/python/frontend/paddlepaddle/test_forward.py b/tests/python/frontend/paddlepaddle/test_forward.py index db07e07f9d83..1d64f947e68a 100644 --- a/tests/python/frontend/paddlepaddle/test_forward.py +++ b/tests/python/frontend/paddlepaddle/test_forward.py @@ -24,6 +24,7 @@ import tvm.topi.testing from tvm import relay from tvm.contrib import graph_executor +import pytest import paddle import paddle.nn as nn @@ -82,8 +83,9 @@ def verify_model(func, input_data, rtol=1e-5, atol=1e-5): parms_num = min(len(input_names), len(mod["main"].params)) compiled_names = [] for arg in mod["main"].params[:parms_num]: - assert arg.name_hint in input_names - compiled_names.append(arg.name_hint) + assert arg.name_hint in input_names or arg.name_hint in params + if arg.name_hint in input_names: + compiled_names.append(arg.name_hint) with tvm.transform.PassContext(opt_level=3): for target, dev in tvm.testing.enabled_targets(): @@ -125,9 +127,7 @@ def add_subtract3(inputs1, inputs2): @tvm.testing.uses_gpu -def test_forward_argmax(): - input_shape = [1, 3, 10, 10] - +def test_forward_arg_max_min(): class ArgMax(nn.Layer): @paddle.jit.to_static def forward(self, inputs): @@ -148,11 +148,70 @@ class ArgMax3(nn.Layer): def forward(self, inputs): return inputs.argmax(axis=2, keepdim=True) - input_data = paddle.rand(input_shape, dtype="float32") - verify_model(ArgMax(), input_data=input_data) - verify_model(ArgMax1(), input_data=input_data) - verify_model(ArgMax2(), input_data=input_data) - verify_model(ArgMax3(), input_data=input_data) + class ArgMin(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return paddle.argmin(inputs) + + class ArgMin1(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return inputs.argmin(axis=1) + + class ArgMin2(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return inputs.argmax(axis=1, keepdim=False) + + class ArgMin3(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return inputs.argmin(axis=2, keepdim=True) + + input_shapes = [[256], [5, 28], [10, 5, 4], [1, 3, 8, 8]] + for input_shape in input_shapes: + input_data = paddle.rand(input_shape, dtype="float32") + verify_model(ArgMax(), input_data=input_data) + verify_model(ArgMin(), input_data=input_data) + for input_shape in input_shapes[1:]: + input_data = paddle.rand(input_shape, dtype="float32") + verify_model(ArgMax1(), input_data=input_data) + verify_model(ArgMax2(), input_data=input_data) + verify_model(ArgMin1(), input_data=input_data) + verify_model(ArgMin2(), input_data=input_data) + for input_shape in input_shapes[2:]: + input_data = paddle.rand(input_shape, dtype="float32") + verify_model(ArgMax3(), input_data=input_data) + verify_model(ArgMin3(), input_data=input_data) + + +@tvm.testing.uses_gpu +def test_forward_argsort(): + class ArgSort1(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return paddle.argsort(inputs) + + class ArgSort2(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return paddle.argsort(inputs, axis=0, descending=True) + + class ArgSort3(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return paddle.argsort(inputs, axis=-1, descending=True) + + input_shapes = [[256], [10, 20], [10, 5, 3], [1, 3, 5, 5]] + for input_shape in input_shapes: + # Avoid duplicate elements in the array which will bring + # different results with different sort algorithms + np.random.seed(13) + np_data = np.random.choice(range(-5000, 5000), np.prod(input_shape), replace=False) + input_data = paddle.to_tensor(np_data.reshape(input_shape).astype("int64")) + verify_model(ArgSort1(), [input_data]) + verify_model(ArgSort2(), [input_data]) + verify_model(ArgSort3(), [input_data]) @tvm.testing.uses_gpu @@ -161,6 +220,11 @@ def test_forward_assign(): def assign(inputs): return paddle.assign(inputs) + @paddle.jit.to_static + def assign_value(inputs): + x = paddle.to_tensor(np.array([3]).astype("float32")) + return inputs + x + input_shape = [2, 3] input_data = paddle.rand(input_shape, dtype="float32") verify_model( @@ -176,6 +240,7 @@ def assign(inputs): input_data2, ], ) + verify_model(assign_value, [input_data]) @tvm.testing.uses_gpu @@ -241,6 +306,31 @@ def cast2(inputs, dtype="int64"): ) +@tvm.testing.uses_gpu +def test_forward_check_tensor(): + class IsFinite(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return paddle.cast(paddle.isfinite(inputs), "int32") + + class IsNan(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return paddle.cast(paddle.isnan(inputs), "int32") + + class IsInf(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return paddle.cast(paddle.isinf(inputs), "int32") + + input_shapes = [[32], [8, 32], [2, 5, 20], [2, 3, 8, 8], [2, 2, 3, 6, 6]] + for input_shape in input_shapes: + input_data = paddle.rand(input_shape, dtype="float32") + verify_model(IsFinite(), input_data=input_data) + verify_model(IsNan(), input_data=input_data) + verify_model(IsInf(), input_data=input_data) + + @tvm.testing.uses_gpu def test_forward_concat_unsqueeze(): @paddle.jit.to_static @@ -320,6 +410,20 @@ def forward(self, inputs): verify_model(Conv2D2(), input_data=conv2d_input_data) +@tvm.testing.uses_gpu +def test_forward_dot(): + class Dot(nn.Layer): + @paddle.jit.to_static + def forward(self, x, y): + return paddle.dot(x, y) + + input_shapes = [[128], [8, 24]] + for input_shape in input_shapes: + x_data = paddle.rand(input_shape, dtype="float32") + y_data = paddle.rand(input_shape, dtype="float32") + verify_model(Dot(), input_data=[x_data, y_data]) + + @tvm.testing.uses_gpu def test_forward_dropout(): @paddle.jit.to_static @@ -332,6 +436,93 @@ def dropout(inputs): verify_model(dropout, input_data=input_data) +def test_forward_elemwise(): + class ElemwiseAPI(nn.Layer): + def __init__(self, api_name): + super(ElemwiseAPI, self).__init__() + self.api_name_ = api_name + for candidate in (paddle, paddle.nn.functional): + self.func = getattr(candidate, api_name, None) + if self.func: + break + + @paddle.jit.to_static + def forward(self, input1, input2): + y = self.func(input1, input2) + if "equal" in self.api_name_ or "than" in self.api_name_: + # for compare operation, cast boolean result to int32 + y = paddle.cast(y, "int32") + return y + + api_list = [ + "equal", + ] + x_shapes = [[128], [8, 20], [4, 20, 3], [2, 3, 8, 8], [2, 3, 3, 9, 9]] + y_shapes = [[1], [8, 20], [4, 1, 1], [2, 3, 8, 8], [2, 3, 3, 9, 1]] + for x_shape, y_shape in zip(x_shapes, y_shapes): + x_data = paddle.randint(1, 1000, x_shape, dtype="int32") + y_data = paddle.randint(1, 1000, y_shape, dtype="int32") + for api_name in api_list: + verify_model(ElemwiseAPI(api_name), [x_data, y_data]) + + +@tvm.testing.uses_gpu +def test_forward_expand(): + @paddle.jit.to_static + def expand1(inputs): + return paddle.expand(inputs, shape=[2, 128]) + + @paddle.jit.to_static + def expand2(inputs): + return paddle.expand(inputs, shape=[2, 1, 4, 16]) + + @paddle.jit.to_static + def expand3(inputs): + return paddle.expand(inputs, shape=[2, 1, 3, 7, 7]) + + @paddle.jit.to_static + def expand4(inputs): + shape = paddle.to_tensor(np.array([2, 128]).astype("int32")) + return paddle.expand(inputs, shape=shape) + + @paddle.jit.to_static + def expand5(inputs): + shape = paddle.to_tensor(np.array([2, 1, 4, 16]).astype("int32")) + return paddle.expand(inputs, shape=shape) + + @paddle.jit.to_static + def expand6(inputs): + shape = paddle.to_tensor(np.array([2, 1, 3, 7, 7]).astype("int32")) + return paddle.expand(inputs, shape=shape) + + data = paddle.rand([128], dtype="float32") + verify_model(expand1, input_data=[data]) + verify_model(expand4, input_data=[data]) + data = paddle.rand([4, 16], dtype="float32") + verify_model(expand2, input_data=[data]) + verify_model(expand5, input_data=[data]) + data = paddle.rand([1, 3, 7, 7], dtype="float32") + verify_model(expand3, input_data=[data]) + verify_model(expand6, input_data=[data]) + + +@tvm.testing.uses_gpu +def test_forward_expand_as(): + class ExpandAs(nn.Layer): + @paddle.jit.to_static + def forward(self, x, y): + z = paddle.expand_as(x, y) + z += y + return z + + x_shapes = [[1], [8, 128], [8, 1, 1], [2, 3, 229, 229], [2, 3, 3, 224, 1]] + y_shapes = [[128], [8, 128], [8, 200, 300], [2, 3, 229, 229], [2, 3, 3, 224, 224]] + for x_shape, y_shape in zip(x_shapes, y_shapes): + x_data = paddle.rand(x_shape, dtype="float32") + y_data = paddle.rand(y_shape, dtype="float32") + verify_model(ExpandAs(), [x_data, y_data]) + + @tvm.testing.uses_gpu def test_forward_shape_full(): @paddle.jit.to_static @@ -432,6 +623,32 @@ def leaky_relu(inputs): verify_model(leaky_relu, input_data=input_data) +@tvm.testing.uses_gpu +def test_forward_logical_api(): + class LogicalAPI(nn.Layer): + def __init__(self, api_name): + super(LogicalAPI, self).__init__() + for candidate in (paddle, paddle.nn.functional): + self.func = getattr(candidate, api_name, None) + if self.func: + break + + @paddle.jit.to_static + def forward(self, x, y): + out = paddle.to_tensor([True, True, True]) + z = self.func(x, y, out=out) + return paddle.cast(z, "int32") + + x_shapes = [[128], [8, 20], [4, 20, 3], [2, 3, 8, 8], [2, 3, 3, 9, 9]] + y_shapes = [[1], [8, 20], [4, 1, 1], [2, 3, 8, 8], [2, 3, 3, 9, 1]] + for x_shape, y_shape in zip(x_shapes, y_shapes): + x_data = paddle.randint(0, 2, x_shape).astype("bool") + y_data = paddle.randint(0, 2, y_shape).astype("bool") + verify_model(LogicalAPI("logical_and"), [x_data, y_data]) + verify_model(LogicalAPI("logical_or"), [x_data, y_data]) + verify_model(LogicalAPI("logical_xor"), [x_data, y_data]) + + @tvm.testing.uses_gpu def test_forward_look_up(): @paddle.jit.to_static @@ -526,17 +743,6 @@ def pool2d3(inputs): # verify_model(pool2d3, input_data=input_data) -@tvm.testing.uses_gpu -def test_forward_relu(): - @paddle.jit.to_static - def relu(inputs): - return nn.functional.relu(inputs) - - input_shape = [10, 10] - input_data = paddle.rand(input_shape, dtype="float32") - verify_model(relu, input_data=input_data) - - @tvm.testing.uses_gpu def test_forward_reshape(): @paddle.jit.to_static @@ -623,39 +829,30 @@ def slice4(inputs): @tvm.testing.uses_gpu -def test_forward_tanh(): - @paddle.jit.to_static - def tanh(inputs): - return paddle.tanh(inputs) +def test_forward_math_api(): + class MathAPI(nn.Layer): + def __init__(self, api_name): + super(MathAPI, self).__init__() + for candidate in (paddle, paddle.nn.functional): + self.func = getattr(candidate, api_name, None) + if self.func: + break - input_shape = [1, 3, 10, 10] - input_data = paddle.rand(input_shape, dtype="float32") - verify_model(tanh, input_data=input_data) + @paddle.jit.to_static + def forward(self, inputs): + return self.func(inputs) + + api_list = [ + "exp", + "relu", + "tanh", + ] + input_shapes = [[128], [2, 100], [10, 2, 5], [7, 3, 4, 1]] + for input_shape in input_shapes: + input_data = paddle.rand(input_shape, dtype="float32") + for api_name in api_list: + verify_model(MathAPI(api_name), input_data=input_data) if __name__ == "__main__": - test_forward_add_subtract() - test_forward_argmax() - test_forward_assign() - test_forward_batch_norm() - test_forward_cast() - test_forward_concat_unsqueeze() - test_forward_cumsum() - test_forward_conv() - test_forward_dropout() - test_forward_shape_full() - test_forward_ones_like() - test_forward_gelu() - test_forward_hard_sigmoid() - test_forward_hard_swish() - test_forward_layer_norm() - test_forward_leaky_relu() - test_forward_look_up() - test_forward_multiply() - test_forward_matmul() - test_forward_pool2d() - test_forward_relu() - test_forward_reshape() - test_forward_scale() - test_forward_slice() - test_forward_tanh() + pytest.main([__file__])