# 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. import tvm import tvm.script import tvm.testing from tvm import IRModule, relax from tvm.script import relax as R def _check( parsed: relax.Function | IRModule, expect: relax.Function | IRModule | None, ): test = parsed.script(show_meta=True) roundtrip_mod = tvm.script.from_source(test) tvm.ir.assert_structural_equal(parsed, roundtrip_mod) if expect: tvm.ir.assert_structural_equal(parsed, expect) def test_all_class_non_max_suppression(): @R.function def foo( boxes: R.Tensor((10, 5, 4), "float32"), scores: R.Tensor((10, 8, 5), "float32"), max_output_boxes_per_class: R.Tensor((), "int64"), iou_threshold: R.Tensor((), "float32"), score_threshold: R.Tensor((), "float32"), ) -> R.Tuple(R.Tensor((400, 3), "int64"), R.Tensor((1,), "int64")): gv: R.Tuple(R.Tensor((400, 3), "int64"), R.Tensor((1,), "int64")) = ( R.vision.all_class_non_max_suppression( boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold, "onnx", ) ) return gv boxes = relax.Var("boxes", R.Tensor((10, 5, 4), "float32")) scores = relax.Var("scores", R.Tensor((10, 8, 5), "float32")) max_output_boxes_per_class = relax.Var("max_output_boxes_per_class", R.Tensor((), "int64")) iou_threshold = relax.Var("iou_threshold", R.Tensor((), "float32")) score_threshold = relax.Var("score_threshold", R.Tensor((), "float32")) bb = relax.BlockBuilder() with bb.function( "foo", [boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold] ): gv = bb.emit( relax.op.vision.all_class_non_max_suppression( boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold, "onnx" ) ) bb.emit_func_output(gv) _check(foo, bb.get()["foo"]) def test_get_valid_counts(): @R.function def foo( data: R.Tensor((10, 5, 6), "float32"), ) -> R.Tuple( R.Tensor((10,), "int32"), R.Tensor((10, 5, 6), "float32"), R.Tensor((10, 5), "int32"), ): gv: R.Tuple( R.Tensor((10,), "int32"), R.Tensor((10, 5, 6), "float32"), R.Tensor((10, 5), "int32"), ) = R.vision.get_valid_counts(data, score_threshold=0.5, id_index=0, score_index=1) return gv data = relax.Var("data", R.Tensor((10, 5, 6), "float32")) bb = relax.BlockBuilder() with bb.function("foo", [data]): gv = bb.emit( relax.op.vision.get_valid_counts(data, score_threshold=0.5, id_index=0, score_index=1) ) bb.emit_func_output(gv) _check(foo, bb.get()["foo"]) def test_non_max_suppression_return_indices(): @R.function def foo( data: R.Tensor((2, 5, 6), "float32"), valid_count: R.Tensor((2,), "int32"), indices: R.Tensor((2, 5), "int32"), ) -> R.Tuple(R.Tensor((2, 5), "int32"), R.Tensor((2, 1), "int32")): gv: R.Tuple(R.Tensor((2, 5), "int32"), R.Tensor((2, 1), "int32")) = ( R.vision.non_max_suppression( data, valid_count, indices, max_output_size=-1, iou_threshold=0.5, force_suppress=False, top_k=3, coord_start=2, score_index=1, id_index=0, return_indices=True, invalid_to_bottom=False, soft_nms_sigma=0.0, score_threshold=0.0, ) ) return gv data = relax.Var("data", R.Tensor((2, 5, 6), "float32")) valid_count = relax.Var("valid_count", R.Tensor((2,), "int32")) indices = relax.Var("indices", R.Tensor((2, 5), "int32")) bb = relax.BlockBuilder() with bb.function("foo", [data, valid_count, indices]): gv = bb.emit( relax.op.vision.non_max_suppression( data, valid_count, indices, max_output_size=-1, iou_threshold=0.5, force_suppress=False, top_k=3, coord_start=2, score_index=1, id_index=0, return_indices=True, invalid_to_bottom=False, soft_nms_sigma=0.0, score_threshold=0.0, ) ) bb.emit_func_output(gv) _check(foo, bb.get()["foo"]) def test_non_max_suppression_return_indices_soft_nms(): @R.function def foo( data: R.Tensor((2, 5, 6), "float32"), valid_count: R.Tensor((2,), "int32"), indices: R.Tensor((2, 5), "int32"), ) -> R.Tuple( R.Tensor((2, 5, 6), "float32"), R.Tensor((2, 5), "int32"), R.Tensor((2, 1), "int32"), ): gv: R.Tuple( R.Tensor((2, 5, 6), "float32"), R.Tensor((2, 5), "int32"), R.Tensor((2, 1), "int32"), ) = R.vision.non_max_suppression( data, valid_count, indices, max_output_size=-1, iou_threshold=0.5, force_suppress=False, top_k=3, coord_start=2, score_index=1, id_index=0, return_indices=True, invalid_to_bottom=False, soft_nms_sigma=0.5, score_threshold=0.0, ) return gv data = relax.Var("data", R.Tensor((2, 5, 6), "float32")) valid_count = relax.Var("valid_count", R.Tensor((2,), "int32")) indices = relax.Var("indices", R.Tensor((2, 5), "int32")) bb = relax.BlockBuilder() with bb.function("foo", [data, valid_count, indices]): gv = bb.emit( relax.op.vision.non_max_suppression( data, valid_count, indices, max_output_size=-1, iou_threshold=0.5, force_suppress=False, top_k=3, coord_start=2, score_index=1, id_index=0, return_indices=True, invalid_to_bottom=False, soft_nms_sigma=0.5, score_threshold=0.0, ) ) bb.emit_func_output(gv) _check(foo, bb.get()["foo"]) def test_non_max_suppression_return_data(): @R.function def foo( data: R.Tensor((2, 5, 6), "float32"), valid_count: R.Tensor((2,), "int32"), indices: R.Tensor((2, 5), "int32"), ) -> R.Tensor((2, 5, 6), "float32"): gv: R.Tensor((2, 5, 6), "float32") = R.vision.non_max_suppression( data, valid_count, indices, max_output_size=-1, iou_threshold=0.5, force_suppress=False, top_k=-1, coord_start=2, score_index=1, id_index=0, return_indices=False, invalid_to_bottom=True, soft_nms_sigma=0.0, score_threshold=0.0, ) return gv data = relax.Var("data", R.Tensor((2, 5, 6), "float32")) valid_count = relax.Var("valid_count", R.Tensor((2,), "int32")) indices = relax.Var("indices", R.Tensor((2, 5), "int32")) bb = relax.BlockBuilder() with bb.function("foo", [data, valid_count, indices]): gv = bb.emit( relax.op.vision.non_max_suppression( data, valid_count, indices, max_output_size=-1, iou_threshold=0.5, force_suppress=False, top_k=-1, coord_start=2, score_index=1, id_index=0, return_indices=False, invalid_to_bottom=True, soft_nms_sigma=0.0, score_threshold=0.0, ) ) bb.emit_func_output(gv) _check(foo, bb.get()["foo"]) def test_multibox_transform_loc(): @R.function def foo( cls: R.Tensor((1, 3, 5), "float32"), loc: R.Tensor((1, 20), "float32"), anc: R.Tensor((1, 5, 4), "float32"), ) -> R.Tuple(R.Tensor((1, 5, 4), "float32"), R.Tensor((1, 3, 5), "float32")): gv: R.Tuple(R.Tensor((1, 5, 4), "float32"), R.Tensor((1, 3, 5), "float32")) = ( R.vision.multibox_transform_loc( cls, loc, anc, clip=False, threshold=0.0, variances=(1.0, 1.0, 1.0, 1.0), keep_background=True, ) ) return gv cls = relax.Var("cls", R.Tensor((1, 3, 5), "float32")) loc = relax.Var("loc", R.Tensor((1, 20), "float32")) anc = relax.Var("anc", R.Tensor((1, 5, 4), "float32")) bb = relax.BlockBuilder() with bb.function("foo", [cls, loc, anc]): gv = bb.emit( relax.op.vision.multibox_transform_loc( cls, loc, anc, clip=False, threshold=0.0, variances=(1.0, 1.0, 1.0, 1.0), keep_background=True, ) ) bb.emit_func_output(gv) _check(foo, bb.get()["foo"]) def test_roi_align(): @R.function def foo( x: R.Tensor((1, 2, 8, 8), "float32"), rois: R.Tensor((2, 5), "float32"), ) -> R.Tensor((2, 2, 3, 3), "float32"): gv: R.Tensor((2, 2, 3, 3), "float32") = R.vision.roi_align( x, rois, pooled_size=(3, 3), spatial_scale=1.0, sample_ratio=2, layout="NCHW", mode="avg", ) return gv x = relax.Var("x", R.Tensor((1, 2, 8, 8), "float32")) rois = relax.Var("rois", R.Tensor((2, 5), "float32")) bb = relax.BlockBuilder() with bb.function("foo", [x, rois]): gv = bb.emit( relax.op.vision.roi_align( x, rois, (3, 3), 1.0, sample_ratio=2, layout="NCHW", mode="avg" ) ) bb.emit_func_output(gv) _check(foo, bb.get()["foo"]) if __name__ == "__main__": tvm.testing.main()