diff --git a/docs/dev/onnx_operators.rst b/docs/dev/onnx_operators.rst index f2d634e979e..fc621b4f894 100644 --- a/docs/dev/onnx_operators.rst +++ b/docs/dev/onnx_operators.rst @@ -575,11 +575,11 @@ Operator Support Matrix +--------------------------+-----------+-----------------+------------------------------+ | QLinearSigmoid | ✅ | UINT8, INT8 | | +--------------------------+-----------+-----------------+------------------------------+ -| QuantizeLinear | ✅ | FP8, FP16, | ``block_size``, | -| | | FP32, INT32 | ``output_dtype``, | -| | | | ``saturate`` | -| | | | are not | -| | | | supported | +| QuantizeLinear | ✅ | FP8, FP16, | ``saturate`` | +| | | FP32, INT32 | is not supported | +| | | | | +| | | | | +| | | | | +--------------------------+-----------+-----------------+------------------------------+ | RandomNormal | ✅ | FP16, FP32, | | | | | FP64 | | diff --git a/src/onnx/include/migraphx/onnx/quantize_dequantize_linear.hpp b/src/onnx/include/migraphx/onnx/quantize_dequantize_linear.hpp new file mode 100644 index 00000000000..886dcdf045d --- /dev/null +++ b/src/onnx/include/migraphx/onnx/quantize_dequantize_linear.hpp @@ -0,0 +1,45 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015-2024 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MIGRAPHX_GUARD_AMDMIGRAPHX_ONNX_QUANTIZE_DEQUANTIZE_LINEAR_HPP +#define MIGRAPHX_GUARD_AMDMIGRAPHX_ONNX_QUANTIZE_DEQUANTIZE_LINEAR_HPP + +#include +#include + +namespace migraphx { +inline namespace MIGRAPHX_INLINE_NS { +namespace onnx { + +std::vector +transform_quantize_dequantize_linear_inputs(const onnx_parser::node_info& info, + const std::string& op_name, + int block_size, + int axis, + std::vector args); + +} // namespace onnx +} // namespace MIGRAPHX_INLINE_NS +} // namespace migraphx + +#endif diff --git a/src/onnx/parse_dequantizelinear.cpp b/src/onnx/parse_dequantizelinear.cpp index e36498ef7be..bc52491ad20 100644 --- a/src/onnx/parse_dequantizelinear.cpp +++ b/src/onnx/parse_dequantizelinear.cpp @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2015-2022 Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2015-2024 Advanced Micro Devices, Inc. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,6 +26,7 @@ #include #include #include +#include namespace migraphx { inline namespace MIGRAPHX_INLINE_NS { @@ -38,49 +39,42 @@ struct parse_dequantizelinear : op_parser instruction_ref parse(const op_desc& opd, const onnx_parser& /*parser*/, const onnx_parser::node_info& info, - const std::vector& args) const + std::vector args) const { - int axis = 1; - if(contains(info.attributes, "axis")) - axis = info.attributes.at("axis").i(); - - auto input_lens = args[0]->get_shape().lens(); - auto n_dim = input_lens.size(); - - instruction_ref x_scale; - if(args[1]->get_shape().elements() != 1) + if(args.size() < 2 or args.size() > 3) { - auto tuned_axis = tune_axis(n_dim, axis, opd.op_name); - x_scale = info.add_instruction( - make_op("broadcast", {{"axis", tuned_axis}, {"out_lens", input_lens}}), args[1]); - } - else - { - x_scale = info.add_instruction(make_op("multibroadcast", {{"out_lens", input_lens}}), - args[1]); + MIGRAPHX_THROW("DequantizeLinear: must have either 2 or 3 inputs, " + + std::to_string(args.size()) + " inputs provided"); } if(args.size() == 3) { - auto x_zero_point = args[2]; - if(x_zero_point->get_shape().elements() != 1) - { - auto tuned_axis = tune_axis(n_dim, axis, opd.op_name); - x_zero_point = info.add_instruction( - make_op("broadcast", {{"axis", tuned_axis}, {"out_lens", input_lens}}), - x_zero_point); - } - else + if(args[0]->get_shape().type() != args[2]->get_shape().type()) + MIGRAPHX_THROW("DequantizeLinear: x and y_zero_point must be of same type"); + + if(args[1]->get_shape().lens() != args[2]->get_shape().lens()) { - x_zero_point = info.add_instruction( - make_op("multibroadcast", {{"out_lens", input_lens}}), x_zero_point); + MIGRAPHX_THROW("DequantizeLinear: y_scale and y_zero_point shape mismatch. " + "Provided y_scale " + "shape: " + + to_string_range(args[1]->get_shape().lens()) + + ", provided y_zero_point shape: " + + to_string_range(args[2]->get_shape().lens())); } - - return info.add_instruction( - make_op("dequantizelinear"), args[0], x_scale, x_zero_point); } - return info.add_instruction(make_op("dequantizelinear"), args[0], x_scale); + int axis = 1; + if(contains(info.attributes, "axis")) + axis = info.attributes.at("axis").i(); + + int block_size = 0; + if(contains(info.attributes, "block_size")) + block_size = info.attributes.at("block_size").i(); + + args = + transform_quantize_dequantize_linear_inputs(info, opd.op_name, block_size, axis, args); + + return info.add_instruction(make_op("dequantizelinear"), args); } }; diff --git a/src/onnx/parse_quantizelinear.cpp b/src/onnx/parse_quantizelinear.cpp index 9523759e455..6369c718e91 100644 --- a/src/onnx/parse_quantizelinear.cpp +++ b/src/onnx/parse_quantizelinear.cpp @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2015-2023 Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2015-2024 Advanced Micro Devices, Inc. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,6 +27,7 @@ #include #include #include +#include namespace migraphx { inline namespace MIGRAPHX_INLINE_NS { @@ -37,47 +38,75 @@ struct parse_quantizelinear : op_parser std::vector operators() const { return {{"QuantizeLinear"}}; } instruction_ref parse(const op_desc& opd, - const onnx_parser& /*parser*/, + const onnx_parser& parser, const onnx_parser::node_info& info, - const std::vector& args) const + std::vector args) const { + if(args.size() < 2 or args.size() > 3) + { + MIGRAPHX_THROW("QuantizeLinear: must have either 2 or 3 inputs, " + + std::to_string(args.size()) + " input(s) provided"); + } + + // Starting with version 19 ONNX introduced the constraint that x and y_scale types must be + // the same + if(parser.opset_version >= 19 and + args[0]->get_shape().type() != args[1]->get_shape().type()) + { + MIGRAPHX_THROW("QuantizeLinear: x and y_scale must be of same type"); + } + + if(args.size() == 3 and args[1]->get_shape().lens() != args[2]->get_shape().lens()) + { + MIGRAPHX_THROW( + "QuantizeLinear: y_scale and y_zero_point shapes must be equal. Provided y_scale " + "shape: " + + to_string_range(args[1]->get_shape().lens()) + + ", provided y_zero_point shape: " + to_string_range(args[2]->get_shape().lens())); + } + int axis = 1; if(contains(info.attributes, "axis")) axis = info.attributes.at("axis").i(); - auto input_lens = args[0]->get_shape().lens(); - auto n_dim = input_lens.size(); + int block_size = 0; + if(contains(info.attributes, "block_size")) + block_size = info.attributes.at("block_size").i(); - instruction_ref y_scale = args[1]; - if(args[1]->get_shape().elements() != 1) + std::optional output_type; + if(contains(info.attributes, "output_dtype")) { - auto tuned_axis = tune_axis(n_dim, axis, opd.op_name); - y_scale = info.add_instruction( - make_op("broadcast", {{"axis", tuned_axis}, {"out_lens", input_lens}}), args[1]); + output_type = get_type(info.attributes.at("output_dtype").i()); } - auto common_args = add_common_args(*info.mod, {args[0], y_scale}); - - if(args.size() == 3) + if(output_type.has_value() and args.size() == 3 and + *output_type != args[2]->get_shape().type()) { - auto y_zero_point = args[2]; - if(y_zero_point->get_shape().elements() != 1) - { - auto tuned_axis = tune_axis(n_dim, axis, opd.op_name); - y_zero_point = info.add_instruction( - make_op("broadcast", {{"axis", tuned_axis}, {"out_lens", input_lens}}), - y_zero_point); - } - else - { - y_zero_point = info.add_instruction( - make_op("multibroadcast", {{"out_lens", input_lens}}), y_zero_point); - } + MIGRAPHX_THROW( + "QuantizeLinear: output_type and y_zero_point type must match. output_type: " + + to_string(*output_type) + + +", y_zero_point type: " + to_string(args[2]->get_shape().type())); + } + + args = + transform_quantize_dequantize_linear_inputs(info, opd.op_name, block_size, axis, args); - common_args.push_back(y_zero_point); + if(parser.opset_version < 19) + { + auto common_type = common_shape({args[0]->get_shape(), args[1]->get_shape()}).type(); + std::transform(args.begin(), args.begin() + 2, args.begin(), [&](auto ins) { + if(ins->get_shape().type() != common_type) + ins = info.add_instruction(make_op("convert", {{"target_type", common_type}}), + ins); + return ins; + }); } - return info.add_instruction(make_op("quantizelinear"), common_args); + if(output_type.has_value()) + return info.add_instruction(make_op("quantizelinear", {{"out_type", *output_type}}), + args); + else + return info.add_instruction(make_op("quantizelinear"), args); } }; diff --git a/src/onnx/quantize_dequantize_linear.cpp b/src/onnx/quantize_dequantize_linear.cpp new file mode 100644 index 00000000000..db06cac2514 --- /dev/null +++ b/src/onnx/quantize_dequantize_linear.cpp @@ -0,0 +1,142 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015-2024 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include +#include + +namespace migraphx { +inline namespace MIGRAPHX_INLINE_NS { +namespace onnx { + +std::vector +transform_quantize_dequantize_linear_inputs(const onnx_parser::node_info& info, + const std::string& op_name, + int block_size, + int axis, + std::vector args) +{ + const auto x = args.at(0); + const auto x_lens = x->get_shape().lens(); + const auto x_rank = x_lens.size(); + + instruction_ref y_scale = args.at(1); + const auto y_scale_lens = y_scale->get_shape().lens(); + const auto y_scale_rank = y_scale_lens.size(); + + // Per-tensor (per-layer) granularity + if(y_scale->get_shape().elements() == 1) + { + std::transform(args.begin() + 1, args.end(), args.begin() + 1, [&](auto ins) { + return info.add_instruction(make_op("multibroadcast", {{"out_lens", x_lens}}), ins); + }); + } + // Per-axis granularity + else if(y_scale_rank == 1) + { + axis = tune_axis(x_rank, axis, op_name); + if(x_lens[axis] != y_scale_lens[0]) + { + MIGRAPHX_THROW(op_name + ": For per axis granularity the length of y_scale (actual: " + + to_string(y_scale_lens[0]) + ") must be equal to size of x on axis " + + to_string(axis) + "(actual: " + to_string(x_lens[axis]) + ")"); + } + + std::transform(args.begin() + 1, args.end(), args.begin() + 1, [&](auto ins) { + return info.add_instruction( + make_op("broadcast", {{"axis", axis}, {"out_lens", x_lens}}), ins); + }); + } + // Blocked granularity + else + { + axis = tune_axis(x_rank, axis, op_name); + if(block_size == 0) + { + MIGRAPHX_THROW(op_name + ": Invalid blocksize(0)"); + } + + if(x_rank != y_scale_rank) + { + MIGRAPHX_THROW(op_name + ": x(rank: " + to_string(x_rank) + + ") and y_scale(rank: " + to_string(y_scale_rank) + + ") must be of same rank for block granularity"); + } + + for(auto i = 0u; i < x_lens.size(); ++i) + { + if(x_lens[i] != y_scale_lens[i] and i != axis) + { + MIGRAPHX_THROW(op_name + ": x(shape: " + to_string_range(x_lens) + + ") and y_scale(shape: " + to_string_range(y_scale_lens) + + ") shapes may only differ along provided axis(" + to_string(axis) + + ")"); + } + } + + // Given x shape (D0, ..., Di, ..., Dn), y_scale shape (S0, ... Si, ...Sn) and + // axis=i, the accepted range is [ceil(Di/Si), ceil(Di/(Si-1))-1] + float di = x_lens[axis]; + float si = y_scale_lens[axis]; + int block_size_min = std::ceil(di / si); + int block_size_max = std::ceil(di / (si - 1)) - 1; + if(block_size < block_size_min or block_size > block_size_max) + MIGRAPHX_THROW(op_name + ": Block size(actual: " + to_string(block_size) + + ") must be within range [" + to_string(block_size_min) + ", " + + to_string(block_size_max) + "]"); + + std::transform(args.begin() + 1, args.end(), args.begin() + 1, [&](auto ins) { + if(block_size == 1) + return ins; + + ins = info.add_instruction(make_op("unsqueeze", {{"axes", {axis + 1}}}), ins); + + auto bc_lens = ins->get_shape().lens(); + bc_lens[axis + 1] = block_size; + ins = info.add_instruction(make_op("multibroadcast", {{"out_lens", bc_lens}}), ins); + + auto reshape_lens = x_lens; + reshape_lens[axis] = ins->get_shape().lens()[axis] * block_size; + ins = info.add_instruction(make_op("reshape", {{"dims", reshape_lens}}), ins); + + // Detect runt block + if(x_lens[axis] < reshape_lens[axis]) + { + ins = info.add_instruction( + make_op("slice", {{"axes", {axis}}, {"starts", {0}}, {"ends", {x_lens[axis]}}}), + ins); + } + + return ins; + }); + } + + return args; +} + +} // namespace onnx +} // namespace MIGRAPHX_INLINE_NS +} // namespace migraphx diff --git a/test/onnx/dequantizelinear_2d_blocked_with_zp_test.onnx b/test/onnx/dequantizelinear_2d_blocked_with_zp_test.onnx new file mode 100644 index 00000000000..96f11cfccdb Binary files /dev/null and b/test/onnx/dequantizelinear_2d_blocked_with_zp_test.onnx differ diff --git a/test/onnx/dequantizelinear_3d_blocked_with_zp_runt_block_test.onnx b/test/onnx/dequantizelinear_3d_blocked_with_zp_runt_block_test.onnx new file mode 100644 index 00000000000..1236ef3eaa1 --- /dev/null +++ b/test/onnx/dequantizelinear_3d_blocked_with_zp_runt_block_test.onnx @@ -0,0 +1,28 @@ + 3dequantizelinear_3d_blocked_with_zp_runt_block_test:ã +C +x +scale +zpy"DequantizeLinear* +axis * + +block_size 3dequantizelinear_3d_blocked_with_zp_runt_block_testZ +x + + + +Z +scale + + + +Z +zp + + + +b +y + + + +B \ No newline at end of file diff --git a/test/onnx/dequantizelinear_scale_and_zp_shape_mismatch_test.onnx b/test/onnx/dequantizelinear_scale_and_zp_shape_mismatch_test.onnx new file mode 100644 index 00000000000..6dfd0163870 Binary files /dev/null and b/test/onnx/dequantizelinear_scale_and_zp_shape_mismatch_test.onnx differ diff --git a/test/onnx/dequantizelinear_too_few_inputs_test.onnx b/test/onnx/dequantizelinear_too_few_inputs_test.onnx new file mode 100644 index 00000000000..8305968618c Binary files /dev/null and b/test/onnx/dequantizelinear_too_few_inputs_test.onnx differ diff --git a/test/onnx/dequantizelinear_too_many_inputs_test.onnx b/test/onnx/dequantizelinear_too_many_inputs_test.onnx new file mode 100644 index 00000000000..2ffdad747e2 Binary files /dev/null and b/test/onnx/dequantizelinear_too_many_inputs_test.onnx differ diff --git a/test/onnx/dequantizelinear_x_and_zp_type_mismatch_test.onnx b/test/onnx/dequantizelinear_x_and_zp_type_mismatch_test.onnx new file mode 100644 index 00000000000..357876350cf Binary files /dev/null and b/test/onnx/dequantizelinear_x_and_zp_type_mismatch_test.onnx differ diff --git a/test/onnx/gen_onnx.py b/test/onnx/gen_onnx.py index 7b05f961b8c..e945e369ed8 100644 --- a/test/onnx/gen_onnx.py +++ b/test/onnx/gen_onnx.py @@ -2175,6 +2175,293 @@ def dequantizelinear_neg_axis_test(): return make_dequantizelinear_axis_graph(-2) +@onnx_test() +def dequantizelinear_2d_blocked_with_zp_test(): + x = helper.make_tensor_value_info('x', TensorProto.INT8, [2, 2]) + scale = helper.make_tensor_value_info('scale', TensorProto.FLOAT, [2, 2]) + zp = helper.make_tensor_value_info('zp', TensorProto.INT8, [2, 2]) + y = helper.make_tensor_value_info('y', TensorProto.FLOAT, [2, 2]) + + node = onnx.helper.make_node('DequantizeLinear', + inputs=['x', 'scale', 'zp'], + outputs=['y'], + axis=0, + block_size=1) + + return ([node], [x, scale, zp], [y]) + + +@onnx_test() +def dequantizelinear_3d_blocked_with_zp_runt_block_test(): + x = helper.make_tensor_value_info('x', TensorProto.INT8, [2, 5, 2]) + scale = helper.make_tensor_value_info('scale', TensorProto.FLOAT, + [2, 2, 2]) + zp = helper.make_tensor_value_info('zp', TensorProto.INT8, [2, 2, 2]) + y = helper.make_tensor_value_info('y', TensorProto.FLOAT, [2, 5, 2]) + + node = onnx.helper.make_node('DequantizeLinear', + inputs=['x', 'scale', 'zp'], + outputs=['y'], + axis=1, + block_size=3) + + return ([node], [x, scale, zp], [y]) + + +@onnx_test() +def dequantizelinear_too_few_inputs_test(): + x = helper.make_tensor_value_info('x', TensorProto.INT8, [2, 2]) + y = helper.make_tensor_value_info('y', TensorProto.FLOAT, [2, 2]) + + node = onnx.helper.make_node('DequantizeLinear', + inputs=['x'], + outputs=['y'], + axis=0, + block_size=1) + + return ([node], [x], [y]) + + +@onnx_test() +def dequantizelinear_too_many_inputs_test(): + x = helper.make_tensor_value_info('x', TensorProto.INT8, [2, 2]) + scale = helper.make_tensor_value_info('scale', TensorProto.FLOAT, [2, 2]) + zp = helper.make_tensor_value_info('zp', TensorProto.INT8, [2, 2]) + zp2 = helper.make_tensor_value_info('zp2', TensorProto.INT8, [2, 2]) + y = helper.make_tensor_value_info('y', TensorProto.FLOAT, [2, 2]) + + node = onnx.helper.make_node('DequantizeLinear', + inputs=['x', 'scale', 'zp', 'zp2'], + outputs=['y'], + axis=0, + block_size=1) + + return ([node], [x, scale, zp, zp2], [y]) + + +@onnx_test() +def dequantizelinear_x_and_zp_type_mismatch_test(): + x = helper.make_tensor_value_info('x', TensorProto.INT8, [2, 2]) + scale = helper.make_tensor_value_info('scale', TensorProto.FLOAT, [2, 2]) + zp = helper.make_tensor_value_info('zp', TensorProto.UINT8, [2, 2]) + y = helper.make_tensor_value_info('y', TensorProto.FLOAT, [2, 2]) + + node = onnx.helper.make_node('DequantizeLinear', + inputs=['x', 'scale', 'zp'], + outputs=['y'], + axis=0, + block_size=1) + + return ([node], [x, scale, zp], [y]) + + +@onnx_test() +def dequantizelinear_scale_and_zp_shape_mismatch_test(): + x = helper.make_tensor_value_info('x', TensorProto.INT8, [2, 2]) + scale = helper.make_tensor_value_info('scale', TensorProto.FLOAT, [2, 2]) + zp = helper.make_tensor_value_info('zp', TensorProto.INT8, [2, 3]) + y = helper.make_tensor_value_info('y', TensorProto.FLOAT, [2, 2]) + + node = onnx.helper.make_node('DequantizeLinear', + inputs=['x', 'scale', 'zp'], + outputs=['y'], + axis=0, + block_size=1) + + return ([node], [x, scale, zp], [y]) + + +@onnx_test() +def quantizelinear_2d_blocked_with_zp_test(): + x = helper.make_tensor_value_info('x', TensorProto.FLOAT, [6, 2]) + scale = helper.make_tensor_value_info('scale', TensorProto.FLOAT, [2, 2]) + zp = helper.make_tensor_value_info('zp', TensorProto.INT8, [2, 2]) + y = helper.make_tensor_value_info('y', TensorProto.INT8, [6, 2]) + + node = onnx.helper.make_node('QuantizeLinear', + inputs=['x', 'scale', 'zp'], + outputs=['y'], + axis=0, + block_size=3) + + return ([node], [x, scale, zp], [y]) + + +@onnx_test() +def quantizelinear_2d_blocked_runt_block_test(): + x = helper.make_tensor_value_info('x', TensorProto.FLOAT, [3, 5]) + y_scale = helper.make_tensor_value_info('y_scale', TensorProto.FLOAT, + [3, 3]) + y = helper.make_tensor_value_info('y', TensorProto.UINT8, [3, 5]) + + node = onnx.helper.make_node('QuantizeLinear', + inputs=['x', 'y_scale'], + outputs=['y'], + axis=1, + block_size=2) + + return ([node], [x, y_scale], [y]) + + +@onnx_test() +def quantizelinear_3d_blocked_with_zp_runt_block_test(): + x = helper.make_tensor_value_info('x', TensorProto.FLOAT, [2, 5, 2]) + scale = helper.make_tensor_value_info('scale', TensorProto.FLOAT, + [2, 2, 2]) + zp = helper.make_tensor_value_info('zp', TensorProto.INT8, [2, 2, 2]) + y = helper.make_tensor_value_info('y', TensorProto.INT8, [2, 5, 2]) + + node = onnx.helper.make_node('QuantizeLinear', + inputs=['x', 'scale', 'zp'], + outputs=['y'], + axis=1, + block_size=3) + + return ([node], [x, scale, zp], [y]) + + +@onnx_test() +def quantizelinear_too_few_inputs_test(): + x = helper.make_tensor_value_info('x', TensorProto.FLOAT, [6, 2]) + y = helper.make_tensor_value_info('y', TensorProto.INT8, [6, 2]) + + node = onnx.helper.make_node('QuantizeLinear', + inputs=['x'], + outputs=['y'], + axis=0, + block_size=3) + + return ([node], [x], [y]) + + +@onnx_test() +def quantizelinear_too_many_inputs_test(): + x = helper.make_tensor_value_info('x', TensorProto.FLOAT, [6, 2]) + scale = helper.make_tensor_value_info('scale', TensorProto.FLOAT, [2, 2]) + zp = helper.make_tensor_value_info('zp', TensorProto.INT8, [2, 2]) + zp2 = helper.make_tensor_value_info('zp2', TensorProto.INT8, [2, 2]) + y = helper.make_tensor_value_info('y', TensorProto.INT8, [6, 2]) + + node = onnx.helper.make_node('QuantizeLinear', + inputs=['x', 'scale', 'zp', 'zp2'], + outputs=['y'], + axis=0, + block_size=3) + + return ([node], [x, scale, zp, zp2], [y]) + + +@onnx_test() +def quantizelinear_scales_and_zp_shape_mismatch_test(): + x = helper.make_tensor_value_info('x', TensorProto.FLOAT, [6, 2]) + scale = helper.make_tensor_value_info('scale', TensorProto.FLOAT, [2, 2]) + zp = helper.make_tensor_value_info('zp', TensorProto.INT8, [1, 2]) + y = helper.make_tensor_value_info('y', TensorProto.INT8, [6, 2]) + + node = onnx.helper.make_node('QuantizeLinear', + inputs=['x', 'scale', 'zp'], + outputs=['y'], + axis=0, + block_size=3) + + return ([node], [x, scale, zp], [y]) + + +@onnx_test() +def quantizelinear_output_dtype_and_zp_type_mismatch_test(): + x = helper.make_tensor_value_info('x', TensorProto.FLOAT, [6, 2]) + scale = helper.make_tensor_value_info('scale', TensorProto.FLOAT, [2, 2]) + zp = helper.make_tensor_value_info('zp', TensorProto.INT8, [2, 2]) + y = helper.make_tensor_value_info('y', TensorProto.INT8, [6, 2]) + + node = onnx.helper.make_node('QuantizeLinear', + inputs=['x', 'scale', 'zp'], + outputs=['y'], + axis=0, + block_size=3, + output_dtype=2) + + return ([node], [x, scale, zp], [y]) + + +@onnx_test() +def quantizelinear_per_axis_shape_mismatch_test(): + x = helper.make_tensor_value_info('x', TensorProto.FLOAT, [3, 5]) + y_scale = helper.make_tensor_value_info('y_scale', TensorProto.FLOAT, [4]) + y = helper.make_tensor_value_info('y', TensorProto.UINT8, [3, 5]) + + node = onnx.helper.make_node('QuantizeLinear', + inputs=['x', 'y_scale'], + outputs=['y'], + axis=1) + + return ([node], [x, y_scale], [y]) + + +@onnx_test() +def quantizelinear_blocked_zero_block_size_test(): + x = helper.make_tensor_value_info('x', TensorProto.FLOAT, [3, 5]) + y_scale = helper.make_tensor_value_info('y_scale', TensorProto.FLOAT, + [3, 3]) + y = helper.make_tensor_value_info('y', TensorProto.UINT8, [3, 5]) + + node = onnx.helper.make_node('QuantizeLinear', + inputs=['x', 'y_scale'], + outputs=['y'], + axis=1, + block_size=0) + + return ([node], [x, y_scale], [y]) + + +@onnx_test() +def quantizelinear_blocked_x_and_scales_rank_mismatch_test(): + x = helper.make_tensor_value_info('x', TensorProto.FLOAT, [3, 5]) + y_scale = helper.make_tensor_value_info('y_scale', TensorProto.FLOAT, + [3, 3, 3]) + y = helper.make_tensor_value_info('y', TensorProto.UINT8, [3, 5]) + + node = onnx.helper.make_node('QuantizeLinear', + inputs=['x', 'y_scale'], + outputs=['y'], + axis=1, + block_size=2) + + return ([node], [x, y_scale], [y]) + + +@onnx_test() +def quantizelinear_blocked_non_bc_axis_size_mismatch_test(): + x = helper.make_tensor_value_info('x', TensorProto.FLOAT, [3, 5]) + y_scale = helper.make_tensor_value_info('y_scale', TensorProto.FLOAT, + [2, 3]) + y = helper.make_tensor_value_info('y', TensorProto.UINT8, [3, 5]) + + node = onnx.helper.make_node('QuantizeLinear', + inputs=['x', 'y_scale'], + outputs=['y'], + axis=1, + block_size=2) + + return ([node], [x, y_scale], [y]) + + +@onnx_test() +def quantizelinear_blocked_invalid_block_size_test(): + x = helper.make_tensor_value_info('x', TensorProto.FLOAT, [3, 5]) + y_scale = helper.make_tensor_value_info('y_scale', TensorProto.FLOAT, + [3, 3]) + y = helper.make_tensor_value_info('y', TensorProto.UINT8, [3, 5]) + + node = onnx.helper.make_node('QuantizeLinear', + inputs=['x', 'y_scale'], + outputs=['y'], + axis=1, + block_size=3) + + return ([node], [x, y_scale], [y]) + + @onnx_test() def dim_param_test(): x = helper.make_tensor_value_info('0', TensorProto.FLOAT, ["dim0", "dim1"]) diff --git a/test/onnx/parse/dequantizelinear_2d_blocked_with_zp_test.cpp b/test/onnx/parse/dequantizelinear_2d_blocked_with_zp_test.cpp new file mode 100644 index 00000000000..2bb9503d89c --- /dev/null +++ b/test/onnx/parse/dequantizelinear_2d_blocked_with_zp_test.cpp @@ -0,0 +1,41 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015-2024 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "migraphx/make_op.hpp" +#include + +TEST_CASE(dequantizelinear_2d_blocked_with_zp_test) +{ + migraphx::program p; + auto* mm = p.get_main_module(); + + auto x = mm->add_parameter("x", migraphx::shape{migraphx::shape::int8_type, {2, 2}}); + auto scale = mm->add_parameter("scale", migraphx::shape{migraphx::shape::float_type, {2, 2}}); + auto zp = mm->add_parameter("zp", migraphx::shape{migraphx::shape::int8_type, {2, 2}}); + + mm->add_instruction(migraphx::make_op("dequantizelinear"), x, scale, zp); + + auto prog = optimize_onnx("dequantizelinear_2d_blocked_with_zp_test.onnx"); + EXPECT(p == prog); +} diff --git a/test/onnx/parse/dequantizelinear_3d_blocked_with_zp_runt_block_test.cpp b/test/onnx/parse/dequantizelinear_3d_blocked_with_zp_runt_block_test.cpp new file mode 100644 index 00000000000..5fac3d17b4f --- /dev/null +++ b/test/onnx/parse/dequantizelinear_3d_blocked_with_zp_runt_block_test.cpp @@ -0,0 +1,55 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015-2024 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "migraphx/make_op.hpp" +#include + +TEST_CASE(dequantizelinear_3d_blocked_with_zp_runt_block_test) +{ + migraphx::program p; + auto* mm = p.get_main_module(); + + auto x = mm->add_parameter("x", migraphx::shape{migraphx::shape::int8_type, {2, 5, 2}}); + auto scale = + mm->add_parameter("scale", migraphx::shape{migraphx::shape::float_type, {2, 2, 2}}); + auto zp = mm->add_parameter("zp", migraphx::shape{migraphx::shape::int8_type, {2, 2, 2}}); + + scale = mm->add_instruction(migraphx::make_op("unsqueeze", {{"axes", {2}}}), scale); + scale = mm->add_instruction(migraphx::make_op("multibroadcast", {{"out_lens", {2, 2, 3, 2}}}), + scale); + scale = mm->add_instruction(migraphx::make_op("reshape", {{"dims", {2, 6, 2}}}), scale); + scale = mm->add_instruction( + migraphx::make_op("slice", {{"axes", {1}}, {"starts", {0}}, {"ends", {5}}}), scale); + + zp = mm->add_instruction(migraphx::make_op("unsqueeze", {{"axes", {2}}}), zp); + zp = mm->add_instruction(migraphx::make_op("multibroadcast", {{"out_lens", {2, 2, 3, 2}}}), zp); + zp = mm->add_instruction(migraphx::make_op("reshape", {{"dims", {2, 6, 2}}}), zp); + zp = mm->add_instruction( + migraphx::make_op("slice", {{"axes", {1}}, {"starts", {0}}, {"ends", {5}}}), zp); + + mm->add_instruction(migraphx::make_op("dequantizelinear"), x, scale, zp); + + auto prog = optimize_onnx("dequantizelinear_3d_blocked_with_zp_runt_block_test.onnx"); + EXPECT(p == prog); +} diff --git a/test/onnx/parse/dequantizelinear_negative_tests.cpp b/test/onnx/parse/dequantizelinear_negative_tests.cpp new file mode 100644 index 00000000000..51f92036490 --- /dev/null +++ b/test/onnx/parse/dequantizelinear_negative_tests.cpp @@ -0,0 +1,46 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015-2024 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +TEST_CASE(dequantizelinear_too_few_inputs_test) +{ + EXPECT(test::throws([&] { read_onnx("dequantizelinear_too_few_inputs_test.onnx"); })); +} + +TEST_CASE(dequantizelinear_too_many_inputs_test) +{ + EXPECT(test::throws([&] { read_onnx("dequantizelinear_too_many_inputs_test.onnx"); })); +} + +TEST_CASE(dequantizelinear_x_and_zp_type_mismatch_test) +{ + EXPECT(test::throws([&] { read_onnx("dequantizelinear_x_and_zp_type_mismatch_test.onnx"); })); +} + +TEST_CASE(dequantizelinear_scale_and_zp_shape_mismatch_test) +{ + EXPECT( + test::throws([&] { read_onnx("dequantizelinear_scale_and_zp_shape_mismatch_test.onnx"); })); +} diff --git a/test/onnx/parse/quantizelinear_2d_blocked_with_zp_test.cpp b/test/onnx/parse/quantizelinear_2d_blocked_with_zp_test.cpp new file mode 100644 index 00000000000..a6f6ad37f1a --- /dev/null +++ b/test/onnx/parse/quantizelinear_2d_blocked_with_zp_test.cpp @@ -0,0 +1,50 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015-2024 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "migraphx/make_op.hpp" +#include + +TEST_CASE(quantizelinear_2d_blocked_with_zp_test) +{ + migraphx::program p; + auto* mm = p.get_main_module(); + + auto x = mm->add_parameter("x", migraphx::shape{migraphx::shape::float_type, {6, 2}}); + auto scale = mm->add_parameter("scale", migraphx::shape{migraphx::shape::float_type, {2, 2}}); + auto zp = mm->add_parameter("zp", migraphx::shape{migraphx::shape::int8_type, {2, 2}}); + + scale = mm->add_instruction(migraphx::make_op("unsqueeze", {{"axes", {1}}}), scale); + scale = + mm->add_instruction(migraphx::make_op("multibroadcast", {{"out_lens", {2, 3, 2}}}), scale); + scale = mm->add_instruction(migraphx::make_op("reshape", {{"dims", {6, 2}}}), scale); + + zp = mm->add_instruction(migraphx::make_op("unsqueeze", {{"axes", {1}}}), zp); + zp = mm->add_instruction(migraphx::make_op("multibroadcast", {{"out_lens", {2, 3, 2}}}), zp); + zp = mm->add_instruction(migraphx::make_op("reshape", {{"dims", {6, 2}}}), zp); + + mm->add_instruction(migraphx::make_op("quantizelinear"), x, scale, zp); + + auto prog = optimize_onnx("quantizelinear_2d_blocked_with_zp_test.onnx"); + EXPECT(p == prog); +} diff --git a/test/onnx/parse/quantizelinear_3d_blocked_with_zp_runt_block_test.cpp b/test/onnx/parse/quantizelinear_3d_blocked_with_zp_runt_block_test.cpp new file mode 100644 index 00000000000..b4deefef854 --- /dev/null +++ b/test/onnx/parse/quantizelinear_3d_blocked_with_zp_runt_block_test.cpp @@ -0,0 +1,55 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015-2024 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "migraphx/make_op.hpp" +#include + +TEST_CASE(quantizelinear_3d_blocked_with_zp_runt_block_test) +{ + migraphx::program p; + auto* mm = p.get_main_module(); + + auto x = mm->add_parameter("x", migraphx::shape{migraphx::shape::float_type, {2, 5, 2}}); + auto scale = + mm->add_parameter("scale", migraphx::shape{migraphx::shape::float_type, {2, 2, 2}}); + auto zp = mm->add_parameter("zp", migraphx::shape{migraphx::shape::int8_type, {2, 2, 2}}); + + scale = mm->add_instruction(migraphx::make_op("unsqueeze", {{"axes", {2}}}), scale); + scale = mm->add_instruction(migraphx::make_op("multibroadcast", {{"out_lens", {2, 2, 3, 2}}}), + scale); + scale = mm->add_instruction(migraphx::make_op("reshape", {{"dims", {2, 6, 2}}}), scale); + scale = mm->add_instruction( + migraphx::make_op("slice", {{"axes", {1}}, {"starts", {0}}, {"ends", {5}}}), scale); + + zp = mm->add_instruction(migraphx::make_op("unsqueeze", {{"axes", {2}}}), zp); + zp = mm->add_instruction(migraphx::make_op("multibroadcast", {{"out_lens", {2, 2, 3, 2}}}), zp); + zp = mm->add_instruction(migraphx::make_op("reshape", {{"dims", {2, 6, 2}}}), zp); + zp = mm->add_instruction( + migraphx::make_op("slice", {{"axes", {1}}, {"starts", {0}}, {"ends", {5}}}), zp); + + mm->add_instruction(migraphx::make_op("quantizelinear"), x, scale, zp); + + auto prog = optimize_onnx("quantizelinear_3d_blocked_with_zp_runt_block_test.onnx"); + EXPECT(p == prog); +} diff --git a/test/onnx/parse/quantizelinear_negative_tests.cpp b/test/onnx/parse/quantizelinear_negative_tests.cpp new file mode 100644 index 00000000000..9e84346a87a --- /dev/null +++ b/test/onnx/parse/quantizelinear_negative_tests.cpp @@ -0,0 +1,74 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015-2024 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +TEST_CASE(quantizelinear_too_few_inputs_test) +{ + EXPECT(test::throws([&] { read_onnx("quantizelinear_too_few_inputs_test.onnx"); })); +} + +TEST_CASE(quantizelinear_too_many_inputs_test) +{ + EXPECT(test::throws([&] { read_onnx("quantizelinear_too_many_inputs_test.onnx"); })); +} + +TEST_CASE(quantizelinear_scales_and_zp_shape_mismatch_test) +{ + EXPECT( + test::throws([&] { read_onnx("quantizelinear_scales_and_zp_shape_mismatch_test.onnx"); })); +} + +TEST_CASE(quantizelinear_output_dtype_and_zp_type_mismatch_test) +{ + EXPECT(test::throws( + [&] { read_onnx("quantizelinear_output_dtype_and_zp_type_mismatch_test.onnx"); })); +} + +TEST_CASE(quantizelinear_per_axis_shape_mismatch_test) +{ + EXPECT(test::throws([&] { read_onnx("quantizelinear_per_axis_shape_mismatch_test.onnx"); })); +} + +TEST_CASE(quantizelinear_blocked_zero_block_size_test) +{ + EXPECT(test::throws([&] { read_onnx("quantizelinear_blocked_zero_block_size_test.onnx"); })); +} + +TEST_CASE(quantizelinear_blocked_x_and_scales_rank_mismatch_test) +{ + EXPECT(test::throws( + [&] { read_onnx("quantizelinear_blocked_x_and_scales_rank_mismatch_test.onnx"); })); +} + +TEST_CASE(quantizelinear_blocked_non_bc_axis_size_mismatch_test) +{ + EXPECT(test::throws( + [&] { read_onnx("quantizelinear_blocked_non_bc_axis_size_mismatch_test.onnx"); })); +} + +TEST_CASE(quantizelinear_blocked_invalid_block_size_test) +{ + EXPECT(test::throws([&] { read_onnx("quantizelinear_blocked_invalid_block_size_test.onnx"); })); +} diff --git a/test/onnx/quantizelinear_2d_blocked_runt_block_test.onnx b/test/onnx/quantizelinear_2d_blocked_runt_block_test.onnx new file mode 100644 index 00000000000..ce553be3183 --- /dev/null +++ b/test/onnx/quantizelinear_2d_blocked_runt_block_test.onnx @@ -0,0 +1,19 @@ + )quantizelinear_2d_blocked_runt_block_test:± +? +x +y_scaley"QuantizeLinear* +axis * + +block_size )quantizelinear_2d_blocked_runt_block_testZ +x +  + +Z +y_scale +  + +b +y +  + +B \ No newline at end of file diff --git a/test/onnx/quantizelinear_2d_blocked_with_zp_test.onnx b/test/onnx/quantizelinear_2d_blocked_with_zp_test.onnx new file mode 100644 index 00000000000..102484f29ad Binary files /dev/null and b/test/onnx/quantizelinear_2d_blocked_with_zp_test.onnx differ diff --git a/test/onnx/quantizelinear_3d_blocked_with_zp_runt_block_test.onnx b/test/onnx/quantizelinear_3d_blocked_with_zp_runt_block_test.onnx new file mode 100644 index 00000000000..baa7712cccc --- /dev/null +++ b/test/onnx/quantizelinear_3d_blocked_with_zp_runt_block_test.onnx @@ -0,0 +1,28 @@ + 1quantizelinear_3d_blocked_with_zp_runt_block_test:ß +A +x +scale +zpy"QuantizeLinear* +axis * + +block_size 1quantizelinear_3d_blocked_with_zp_runt_block_testZ +x + + + +Z +scale + + + +Z +zp + + + +b +y + + + +B \ No newline at end of file diff --git a/test/onnx/quantizelinear_blocked_invalid_block_size_test.onnx b/test/onnx/quantizelinear_blocked_invalid_block_size_test.onnx new file mode 100644 index 00000000000..fa1366ac163 --- /dev/null +++ b/test/onnx/quantizelinear_blocked_invalid_block_size_test.onnx @@ -0,0 +1,19 @@ + .quantizelinear_blocked_invalid_block_size_test:¶ +? +x +y_scaley"QuantizeLinear* +axis * + +block_size .quantizelinear_blocked_invalid_block_size_testZ +x +  + +Z +y_scale +  + +b +y +  + +B \ No newline at end of file diff --git a/test/onnx/quantizelinear_blocked_non_bc_axis_size_mismatch_test.onnx b/test/onnx/quantizelinear_blocked_non_bc_axis_size_mismatch_test.onnx new file mode 100644 index 00000000000..9e7a30b5c4b --- /dev/null +++ b/test/onnx/quantizelinear_blocked_non_bc_axis_size_mismatch_test.onnx @@ -0,0 +1,19 @@ + 5quantizelinear_blocked_non_bc_axis_size_mismatch_test:½ +? +x +y_scaley"QuantizeLinear* +axis * + +block_size 5quantizelinear_blocked_non_bc_axis_size_mismatch_testZ +x +  + +Z +y_scale +  + +b +y +  + +B \ No newline at end of file diff --git a/test/onnx/quantizelinear_blocked_x_and_scales_rank_mismatch_test.onnx b/test/onnx/quantizelinear_blocked_x_and_scales_rank_mismatch_test.onnx new file mode 100644 index 00000000000..482867b8bce --- /dev/null +++ b/test/onnx/quantizelinear_blocked_x_and_scales_rank_mismatch_test.onnx @@ -0,0 +1,20 @@ + 6quantizelinear_blocked_x_and_scales_rank_mismatch_test: +? +x +y_scaley"QuantizeLinear* +axis * + +block_size 6quantizelinear_blocked_x_and_scales_rank_mismatch_testZ +x +  + +Z +y_scale + + + +b +y +  + +B \ No newline at end of file diff --git a/test/onnx/quantizelinear_blocked_zero_block_size_test.onnx b/test/onnx/quantizelinear_blocked_zero_block_size_test.onnx new file mode 100644 index 00000000000..2cae2f4492a Binary files /dev/null and b/test/onnx/quantizelinear_blocked_zero_block_size_test.onnx differ diff --git a/test/onnx/quantizelinear_output_dtype_and_zp_type_mismatch_test.onnx b/test/onnx/quantizelinear_output_dtype_and_zp_type_mismatch_test.onnx new file mode 100644 index 00000000000..5c2fef32e30 Binary files /dev/null and b/test/onnx/quantizelinear_output_dtype_and_zp_type_mismatch_test.onnx differ diff --git a/test/onnx/quantizelinear_per_axis_shape_mismatch_test.onnx b/test/onnx/quantizelinear_per_axis_shape_mismatch_test.onnx new file mode 100644 index 00000000000..76693e2fa70 --- /dev/null +++ b/test/onnx/quantizelinear_per_axis_shape_mismatch_test.onnx @@ -0,0 +1,17 @@ + +quantizelinear_per_axis_shape_mismatch_test:œ +, +x +y_scaley"QuantizeLinear* +axis +quantizelinear_per_axis_shape_mismatch_testZ +x +  + +Z +y_scale + + +b +y +  + +B \ No newline at end of file diff --git a/test/onnx/quantizelinear_scales_and_zp_shape_mismatch_test.onnx b/test/onnx/quantizelinear_scales_and_zp_shape_mismatch_test.onnx new file mode 100644 index 00000000000..30d185d48ea Binary files /dev/null and b/test/onnx/quantizelinear_scales_and_zp_shape_mismatch_test.onnx differ diff --git a/test/onnx/quantizelinear_too_few_inputs_test.onnx b/test/onnx/quantizelinear_too_few_inputs_test.onnx new file mode 100644 index 00000000000..8843ff3a7cc Binary files /dev/null and b/test/onnx/quantizelinear_too_few_inputs_test.onnx differ diff --git a/test/onnx/quantizelinear_too_many_inputs_test.onnx b/test/onnx/quantizelinear_too_many_inputs_test.onnx new file mode 100644 index 00000000000..b6358104e48 Binary files /dev/null and b/test/onnx/quantizelinear_too_many_inputs_test.onnx differ diff --git a/test/onnx/verify/dequantizelinear_blocked_test.cpp b/test/onnx/verify/dequantizelinear_blocked_test.cpp new file mode 100644 index 00000000000..3c8aed8553a --- /dev/null +++ b/test/onnx/verify/dequantizelinear_blocked_test.cpp @@ -0,0 +1,87 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015-2024 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include + +TEST_CASE(dequantizelinear_2d_blocked_with_zp_test) +{ + migraphx::program p = read_onnx("dequantizelinear_2d_blocked_with_zp_test.onnx"); + p.compile(migraphx::make_target("ref")); + + migraphx::shape x_shape{migraphx::shape::int8_type, {2, 2}}; + std::vector x = {4, 8, 20, 2}; + + migraphx::shape scale_shape{migraphx::shape::float_type, {2, 2}}; + std::vector scale = {1.5f, 2.5f, 3.0f, 4.9f}; + + migraphx::shape zp_shape{migraphx::shape::int8_type, {2, 2}}; + std::vector zp = {0, 1, 2, 3}; + + migraphx::parameter_map pm; + pm["x"] = migraphx::argument{x_shape, x.data()}; + pm["scale"] = migraphx::argument{scale_shape, scale.data()}; + pm["zp"] = migraphx::argument{zp_shape, zp.data()}; + + auto result = p.eval(pm).back(); + EXPECT(result.get_shape() == migraphx::shape{migraphx::shape::float_type, {2, 2}}); + + std::vector result_vector; + result.visit([&](auto output) { result_vector.assign(output.begin(), output.end()); }); + + std::vector gold = {6.0f, 17.5f, 54.0f, -4.9f}; + EXPECT(migraphx::verify::verify_rms_range(result_vector, gold)); +} + +TEST_CASE(dequantizelinear_3d_blocked_with_zp_runt_block_test) +{ + migraphx::program p = read_onnx("dequantizelinear_3d_blocked_with_zp_runt_block_test.onnx"); + p.compile(migraphx::make_target("ref")); + + migraphx::shape x_shape{migraphx::shape::int8_type, {2, 5, 2}}; + std::vector x = {4, 1, 8, 4, 33, 3, 4, 4, 15, 4, 3, 6, 14, 14, 10, 9, 8, 5, 14, 8}; + + migraphx::shape scale_shape{migraphx::shape::float_type, {2, 2, 2}}; + std::vector scale = {1.5f, 2.5f, 3.0f, 4.9f, 1.8f, 3.6f, 2.3f, 4.1f}; + + migraphx::shape zp_shape{migraphx::shape::int8_type, {2, 2, 2}}; + std::vector zp = {0, 1, 2, 3, 3, 2, 1, 0}; + + migraphx::parameter_map pm; + pm["x"] = migraphx::argument{x_shape, x.data()}; + pm["scale"] = migraphx::argument{scale_shape, scale.data()}; + pm["zp"] = migraphx::argument{zp_shape, zp.data()}; + + auto result = p.eval(pm).back(); + EXPECT(result.get_shape() == migraphx::shape{migraphx::shape::float_type, {2, 5, 2}}); + + std::vector result_vector; + result.visit([&](auto output) { result_vector.assign(output.begin(), output.end()); }); + + std::vector gold = {6.0f, 0.0f, 12.0f, 7.5f, 49.5f, 5.0f, 6.0f, 4.9f, 39.0f, 4.9f, + 0.0f, 14.4f, 19.8f, 43.2f, 12.6f, 25.2f, 16.1f, 20.5f, 29.9f, 32.8f}; + EXPECT(migraphx::verify::verify_rms_range(result_vector, gold)); +} diff --git a/test/onnx/verify/quantizelinear_blocked_test.cpp b/test/onnx/verify/quantizelinear_blocked_test.cpp new file mode 100644 index 00000000000..2270f763bdf --- /dev/null +++ b/test/onnx/verify/quantizelinear_blocked_test.cpp @@ -0,0 +1,132 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015-2024 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include + +static migraphx::shape make_shape(migraphx::shape::type_t type, std::vector lens) +{ + return migraphx::shape{type, std::move(lens)}; +} + +TEST_CASE(quantizelinear_2d_blocked_with_zp_test) +{ + migraphx::program p = read_onnx("quantizelinear_2d_blocked_with_zp_test.onnx"); + p.compile(migraphx::make_target("ref")); + + migraphx::shape x_shape{migraphx::shape::float_type, {6, 2}}; + std::vector x = { + 6.0f, 12.0f, 50.0f, 5.0f, 40.0f, 1.0f, 8.0f, 4.0f, 7.0f, 3.0f, 0.0f, 20.0f}; + + migraphx::shape scale_shape{migraphx::shape::float_type, {2, 2}}; + std::vector scale = {1.5f, 2.5f, 3.0f, 4.9f}; + + migraphx::shape zp_shape{migraphx::shape::int8_type, {2, 2}}; + std::vector zp = {0, 1, 2, 3}; + + migraphx::parameter_map pm; + pm["x"] = migraphx::argument{x_shape, x.data()}; + pm["scale"] = migraphx::argument{scale_shape, scale.data()}; + pm["zp"] = migraphx::argument{zp_shape, zp.data()}; + + auto result = p.eval(pm).back(); + EXPECT(result.get_shape() == make_shape(migraphx::shape::int8_type, {6, 2})); + + std::vector result_vector; + result.visit([&](auto output) { result_vector.assign(output.begin(), output.end()); }); + + std::vector gold = {4, 6, 33, 3, 27, 1, 5, 4, 4, 4, 2, 7}; + EXPECT(result_vector == gold); +} + +TEST_CASE(quantizelinear_2d_blocked_runt_block_test) +{ + migraphx::program p = read_onnx("quantizelinear_2d_blocked_runt_block_test.onnx"); + p.compile(migraphx::make_target("ref")); + + migraphx::shape x_shape{migraphx::shape::float_type, {3, 5}}; + std::vector x = {6.0f, + 12.0f, + 50.0f, + 5.0f, + 50.0f, + 1.0f, + 8.0f, + 4.0f, + 5.0f, + 4.0f, + 0.0f, + 20.0f, + 10.0f, + 4.0f, + 10.0f}; + + migraphx::shape scale_shape{migraphx::shape::float_type, {3, 3}}; + std::vector scale = {1.5f, 2.5f, 3.6f, 3.0f, 4.9f, 5.4f, 5.1f, 6.9f, 3.8f}; + + migraphx::parameter_map pm; + pm["x"] = migraphx::argument{x_shape, x.data()}; + pm["y_scale"] = migraphx::argument{scale_shape, scale.data()}; + + auto result = p.eval(pm).back(); + EXPECT(result.get_shape() == make_shape(migraphx::shape::uint8_type, {3, 5})); + + std::vector result_vector; + result.visit([&](auto output) { result_vector.assign(output.begin(), output.end()); }); + + std::vector gold = {4, 8, 20, 2, 14, 0, 3, 1, 1, 1, 0, 4, 1, 1, 3}; + EXPECT(result_vector == gold); +} + +TEST_CASE(quantizelinear_3d_blocked_with_zp_runt_block_test) +{ + migraphx::program p = read_onnx("quantizelinear_3d_blocked_with_zp_runt_block_test.onnx"); + p.compile(migraphx::make_target("ref")); + + migraphx::shape x_shape{migraphx::shape::float_type, {2, 5, 2}}; + std::vector x = {6.0f, 1.0f, 12.0f, 8.0f, 50.0f, 4.0f, 5.0f, 7.0f, 40.0f, 3.0f, + 0.0f, 14.0f, 20.0f, 42.0f, 13.0f, 25.0f, 17.0f, 22.0f, 31.0f, 33.0f}; + + migraphx::shape scale_shape{migraphx::shape::float_type, {2, 2, 2}}; + std::vector scale = {1.5f, 2.5f, 3.0f, 4.9f, 1.8f, 3.6f, 2.3f, 4.1f}; + + migraphx::shape zp_shape{migraphx::shape::int8_type, {2, 2, 2}}; + std::vector zp = {0, 1, 2, 3, 3, 2, 1, 0}; + + migraphx::parameter_map pm; + pm["x"] = migraphx::argument{x_shape, x.data()}; + pm["scale"] = migraphx::argument{scale_shape, scale.data()}; + pm["zp"] = migraphx::argument{zp_shape, zp.data()}; + + auto result = p.eval(pm).back(); + EXPECT(result.get_shape() == make_shape(migraphx::shape::int8_type, {2, 5, 2})); + + std::vector result_vector; + result.visit([&](auto output) { result_vector.assign(output.begin(), output.end()); }); + + std::vector gold = {4, 1, 8, 4, 33, 3, 4, 4, 15, 4, 3, 6, 14, 14, 10, 9, 8, 5, 14, 8}; + EXPECT(result_vector == gold); +} diff --git a/test/py/onnx_backend_test.py b/test/py/onnx_backend_test.py index b8d70db100d..73d199a1de8 100644 --- a/test/py/onnx_backend_test.py +++ b/test/py/onnx_backend_test.py @@ -561,7 +561,6 @@ def disabled_tests_onnx_1_14_0(backend_test): def disabled_tests_onnx_1_16_0(backend_test): - backend_test.exclude(r'test_dequantizelinear_blocked_cpu') backend_test.exclude(r'test_dft_axis_opset19_cpu') backend_test.exclude(r'test_dft_inverse_opset19_cpu') backend_test.exclude(r'test_dft_opset19_cpu') @@ -589,8 +588,6 @@ def disabled_tests_onnx_1_16_0(backend_test): backend_test.exclude(r'test_qlinearmatmul_3D_int8_float32_cpu') backend_test.exclude(r'test_qlinearmatmul_3D_uint8_float16_cpu') backend_test.exclude(r'test_qlinearmatmul_3D_uint8_float32_cpu') - backend_test.exclude(r'test_quantizelinear_blocked_asymmetric_cpu') - backend_test.exclude(r'test_quantizelinear_blocked_symmetric_cpu') backend_test.exclude(r'test_reduce_l1_empty_set_cpu') backend_test.exclude(r'test_reduce_l1_empty_set_expanded_cpu') backend_test.exclude(r'test_reduce_l2_empty_set_cpu')