mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-07-22 19:23:30 +00:00
[NNAPI EP] Fix error for QLinearAdd with an initializer as input (#7093)
* Fix the issue where input to qlinearadd is an initializer * Add UT * Adress CR comments
This commit is contained in:
parent
540eac253e
commit
1c04eec2b1
5 changed files with 175 additions and 103 deletions
|
|
@ -3,8 +3,10 @@
|
|||
|
||||
#include <core/common/logging/logging.h>
|
||||
#include <core/common/safeint.h>
|
||||
#include <core/framework/tensorprotoutils.h>
|
||||
|
||||
#include "core/providers/common.h"
|
||||
#include "core/providers/shared/utils/utils.h"
|
||||
#include "core/providers/nnapi/nnapi_builtin/nnapi_lib/nnapi_implementation.h"
|
||||
#include "helper.h"
|
||||
#include "model_builder.h"
|
||||
|
|
@ -46,10 +48,13 @@ void ModelBuilder::AddInitializerToSkip(const std::string& tensor_name) {
|
|||
skipped_initializers_.insert(tensor_name);
|
||||
}
|
||||
|
||||
static std::unordered_map<std::string, vector<const Node*>> GetAllQuantizedOpInputs(const GraphViewer& graph_viewer);
|
||||
|
||||
Status ModelBuilder::Prepare() {
|
||||
nnapi_model_ = std::unique_ptr<Model>(new Model());
|
||||
RETURN_STATUS_ON_ERROR(nnapi_->ANeuralNetworksModel_create(&nnapi_model_->model_));
|
||||
ORT_RETURN_IF_ERROR(GetTargetDevices());
|
||||
all_quantized_op_inputs_ = GetAllQuantizedOpInputs(graph_viewer_);
|
||||
PreprocessInitializers();
|
||||
PreprocessActivations();
|
||||
ORT_RETURN_IF_ERROR(RegisterInitializers());
|
||||
|
|
@ -143,7 +148,7 @@ void ModelBuilder::PreprocessActivations() {
|
|||
}
|
||||
|
||||
// Help to get all quantized operators' input and the node(s) using the input
|
||||
std::unordered_map<std::string, vector<const Node*>> GetAllQuantizedOpInputs(const GraphViewer& graph_viewer) {
|
||||
static std::unordered_map<std::string, vector<const Node*>> GetAllQuantizedOpInputs(const GraphViewer& graph_viewer) {
|
||||
std::unordered_map<std::string, vector<const Node*>> all_quantized_op_inputs;
|
||||
const auto& node_indices = graph_viewer.GetNodesInTopologicalOrder();
|
||||
for (const auto& node_idx : node_indices) {
|
||||
|
|
@ -175,6 +180,50 @@ std::unordered_map<std::string, vector<const Node*>> GetAllQuantizedOpInputs(con
|
|||
return all_quantized_op_inputs;
|
||||
}
|
||||
|
||||
static Status GetInputDataType(
|
||||
const InitializedTensorSet& initializers,
|
||||
const std::unordered_map<std::string, std::vector<const Node*>>& all_quantized_op_inputs,
|
||||
const std::string& name, int32_t data_type, const Shape& shape,
|
||||
OperandType& operand_type) {
|
||||
Type type = Type::TENSOR_FLOAT32;
|
||||
float scale = 0.0f;
|
||||
int32_t zero_point = 0;
|
||||
switch (data_type) {
|
||||
case ONNX_NAMESPACE::TensorProto_DataType_FLOAT:
|
||||
type = Type::TENSOR_FLOAT32;
|
||||
break;
|
||||
case ONNX_NAMESPACE::TensorProto_DataType_UINT8:
|
||||
// For ONNX the quantized input/initializer does not carry scale and zero point info
|
||||
// So we will need to search the operator using this input
|
||||
// And dig out the scale and zero point as the input initializers to the operator
|
||||
type = Type::TENSOR_QUANT8_ASYMM;
|
||||
if (!Contains(all_quantized_op_inputs, name)) {
|
||||
// We current do not support uint8 input if it is not a quantized input
|
||||
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
|
||||
"The input/initializer of graph has unsupported quantized type, name: ", name,
|
||||
" type: ", data_type);
|
||||
}
|
||||
|
||||
// TODO, verify the scale and zero point match if there are multiple op using same input
|
||||
ORT_RETURN_IF_ERROR(GetQuantizedInputScaleAndZeroPoint(
|
||||
initializers, *all_quantized_op_inputs.at(name)[0], name, scale, zero_point));
|
||||
break;
|
||||
// case ONNX_NAMESPACE::TensorProto_DataType_INT8:
|
||||
// We also do not consider ONNX_NAMESPACE::TensorProto_DataType_INT8 case here, since that can only
|
||||
// be input 2 of Qlinear[Conv/MatMul], which has to be an initializer tensor and will be added
|
||||
// separately by OpBuilder, so we do not treat it as an input/initializers here
|
||||
default:
|
||||
// TODO: support other type
|
||||
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
|
||||
"The input/initializer of graph doesn't have valid type, name: ",
|
||||
name, " type: ", data_type);
|
||||
break;
|
||||
}
|
||||
|
||||
operand_type = OperandType(type, shape, scale, zero_point);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status ModelBuilder::RegisterInitializers() {
|
||||
// First pass to get all the stats of the initializers
|
||||
const auto& initializer_tensors(GetInitializerTensors());
|
||||
|
|
@ -201,20 +250,10 @@ Status ModelBuilder::RegisterInitializers() {
|
|||
shape.push_back(1);
|
||||
}
|
||||
|
||||
Type type = Type::TENSOR_FLOAT32;
|
||||
switch (tensor.data_type()) {
|
||||
case ONNX_NAMESPACE::TensorProto_DataType_FLOAT:
|
||||
type = Type::TENSOR_FLOAT32;
|
||||
break;
|
||||
default:
|
||||
// TODO: support other type
|
||||
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
|
||||
"The initializer of graph doesn't have valid type, name: ",
|
||||
name, " type: ", tensor.data_type());
|
||||
break;
|
||||
}
|
||||
|
||||
OperandType operand_type(type, shape);
|
||||
OperandType operand_type(Type::TENSOR_FLOAT32, shape);
|
||||
ORT_RETURN_IF_ERROR(
|
||||
GetInputDataType(GetInitializerTensors(), all_quantized_op_inputs_,
|
||||
name, tensor.data_type(), shape, operand_type));
|
||||
shaper_.AddShape(name, operand_type.dimensions);
|
||||
|
||||
uint32_t index = 0;
|
||||
|
|
@ -240,12 +279,26 @@ Status ModelBuilder::RegisterInitializers() {
|
|||
uint32_t index;
|
||||
size_t size, padded_size;
|
||||
std::tie(index, size, padded_size) = initializers[i++];
|
||||
const char* src = nullptr;
|
||||
if (tensor.data_type() == ONNX_NAMESPACE::TensorProto_DataType_FLOAT) {
|
||||
src = tensor.float_data().empty()
|
||||
? tensor.raw_data().data()
|
||||
: reinterpret_cast<const char*>(tensor.float_data().data());
|
||||
} // We should not get anything else here since we already checked in the 1st pass
|
||||
const uint8_t* src = nullptr;
|
||||
// uint8_t data need unpack, need a holder for free memory after copy
|
||||
std::unique_ptr<uint8_t[]> unpacked_tensor;
|
||||
switch (tensor.data_type()) {
|
||||
case ONNX_NAMESPACE::TensorProto_DataType_FLOAT:
|
||||
src = reinterpret_cast<const uint8_t*>(GetTensorFloatData(tensor));
|
||||
break;
|
||||
case ONNX_NAMESPACE::TensorProto_DataType_UINT8:
|
||||
size_t tensor_byte_size;
|
||||
ORT_RETURN_IF_ERROR(
|
||||
onnxruntime::utils::UnpackInitializerData(tensor, graph_viewer_.ModelPath(),
|
||||
unpacked_tensor, tensor_byte_size));
|
||||
ORT_RETURN_IF_NOT(size == tensor_byte_size,
|
||||
"initializer tensor: ", tensor.name(), "'s size: ", tensor_byte_size,
|
||||
" should match the calculated size: ", size);
|
||||
src = unpacked_tensor.get();
|
||||
break;
|
||||
// default:
|
||||
// We should not get anything else here since we already checked in the 1st pass
|
||||
}
|
||||
|
||||
uint8_t* dest = nnapi_model_->mem_initializers_->GetDataPtr() + offset;
|
||||
memcpy(dest, src, size);
|
||||
|
|
@ -257,7 +310,6 @@ Status ModelBuilder::RegisterInitializers() {
|
|||
}
|
||||
|
||||
Status ModelBuilder::RegisterModelInputs() {
|
||||
const auto all_quantized_op_inputs = GetAllQuantizedOpInputs(graph_viewer_);
|
||||
for (const auto* node_arg : graph_viewer_.GetInputs()) {
|
||||
const auto& input_name = node_arg->Name();
|
||||
|
||||
|
|
@ -285,49 +337,17 @@ Status ModelBuilder::RegisterModelInputs() {
|
|||
shape.push_back(1);
|
||||
}
|
||||
|
||||
Type type = Type::TENSOR_FLOAT32;
|
||||
float scale = 0.0f;
|
||||
int32_t zero_point = 0;
|
||||
OperandType operand_type(Type::TENSOR_FLOAT32, shape);
|
||||
const auto* type_proto = node_arg->TypeAsProto();
|
||||
if (!type_proto || !type_proto->tensor_type().has_elem_type()) {
|
||||
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
|
||||
"The input of graph doesn't have elem_type: ", input_name);
|
||||
} else {
|
||||
switch (type_proto->tensor_type().elem_type()) {
|
||||
case ONNX_NAMESPACE::TensorProto_DataType_FLOAT:
|
||||
type = Type::TENSOR_FLOAT32;
|
||||
break;
|
||||
case ONNX_NAMESPACE::TensorProto_DataType_UINT8: {
|
||||
// For ONNX the quantized input does not carry scale and zero point info
|
||||
// So we will need to search the operator using this input
|
||||
// And dig out the scale and zero point as the input initializers to the operator
|
||||
type = Type::TENSOR_QUANT8_ASYMM;
|
||||
if (!Contains(all_quantized_op_inputs, input_name)) {
|
||||
// We current do not support uint8 input if it is not a quantized input
|
||||
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
|
||||
"The input of graph has unsupported quantized type, name: ", input_name,
|
||||
" type: ", type_proto->tensor_type().elem_type());
|
||||
}
|
||||
|
||||
// TODO, verify the scale and zero point match if there are multiple op using same input
|
||||
ORT_RETURN_IF_ERROR(GetQuantizedInputScaleAndZeroPoint(
|
||||
*this, *all_quantized_op_inputs.at(input_name)[0], input_name, scale, zero_point));
|
||||
break;
|
||||
}
|
||||
// case NNX_NAMESPACE::TensorProto_DataType_INT8:
|
||||
// We also do not consider ONNX_NAMESPACE::TensorProto_DataType_INT8 case here, since that can only
|
||||
// be input 2 of Qlinear[Conv/MatMul], which has to be an initializer tensor, so we do not treat
|
||||
// it as an input here
|
||||
default: {
|
||||
// TODO: support other type
|
||||
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
|
||||
"The input of graph has unsupported type, name: ", input_name,
|
||||
" type: ", type_proto->tensor_type().elem_type());
|
||||
}
|
||||
}
|
||||
ORT_RETURN_IF_ERROR(
|
||||
GetInputDataType(GetInitializerTensors(), all_quantized_op_inputs_,
|
||||
input_name, type_proto->tensor_type().elem_type(), shape, operand_type));
|
||||
}
|
||||
|
||||
OperandType operand_type(type, shape, scale, zero_point);
|
||||
shaper_.AddShape(input_name, operand_type.dimensions);
|
||||
|
||||
uint32_t index = 0;
|
||||
|
|
|
|||
|
|
@ -149,6 +149,10 @@ class ModelBuilder {
|
|||
std::vector<uint32_t> input_index_vec_;
|
||||
std::vector<uint32_t> output_index_vec_;
|
||||
|
||||
// Contains all quantized operators' input and the node(s) using the input
|
||||
// In the form of {input_name, [node(s) using the input]}
|
||||
std::unordered_map<std::string, std::vector<const Node*>> all_quantized_op_inputs_;
|
||||
|
||||
std::unordered_set<std::string> unique_names_;
|
||||
|
||||
TargetDeviceOption target_device_option_{TargetDeviceOption::ALL_DEVICES};
|
||||
|
|
|
|||
|
|
@ -668,12 +668,7 @@ static void AddBinaryOpQuantizationScaleAndZeroPointToSkip(ModelBuilder& model_b
|
|||
model_builder.AddInitializerToSkip(input_defs[7]->Name()); // y_zero_point
|
||||
}
|
||||
|
||||
Status GetQuantizedInputScaleAndZeroPoint(const ModelBuilder& model_builder,
|
||||
const Node& node,
|
||||
const std::string& input_name,
|
||||
float& scale,
|
||||
int32_t& zero_point) ORT_MUST_USE_RESULT;
|
||||
Status GetQuantizedInputScaleAndZeroPoint(const ModelBuilder& model_builder,
|
||||
Status GetQuantizedInputScaleAndZeroPoint(const InitializedTensorSet& initializers,
|
||||
const Node& node,
|
||||
const std::string& input_name,
|
||||
float& scale,
|
||||
|
|
@ -705,10 +700,10 @@ Status GetQuantizedInputScaleAndZeroPoint(const ModelBuilder& model_builder,
|
|||
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "Unsupported op: ", op_type);
|
||||
}
|
||||
|
||||
scale = GetQuantizationScale(model_builder.GetInitializerTensors(), node, scale_idx);
|
||||
scale = GetQuantizationScale(initializers, node, scale_idx);
|
||||
zero_point = 0;
|
||||
if (node.InputDefs().size() > 2) {
|
||||
ORT_RETURN_IF_ERROR(GetQuantizationZeroPoint(model_builder.GetInitializerTensors(), node, zero_point_idx, zero_point));
|
||||
if (node.InputDefs().size() > zero_point_idx) {
|
||||
ORT_RETURN_IF_ERROR(GetQuantizationZeroPoint(initializers, node, zero_point_idx, zero_point));
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ Status TransposeNHWCToNCHW(ModelBuilder& model_builder, const std::string& input
|
|||
ORT_MUST_USE_RESULT;
|
||||
|
||||
// Get the quantized input's scale and zero point for the given input
|
||||
Status GetQuantizedInputScaleAndZeroPoint(const ModelBuilder& model_builder,
|
||||
Status GetQuantizedInputScaleAndZeroPoint(const InitializedTensorSet& initializers,
|
||||
const Node& node, const std::string& input_name,
|
||||
float& scale, int32_t& zero_point) ORT_MUST_USE_RESULT;
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ void RunQLinearMathTestFromFloat(
|
|||
const std::vector<float>& a, const std::vector<int64_t>& a_shape_origin, float A_scale, T A_zero_point,
|
||||
const std::vector<float>& b, const std::vector<int64_t>& b_shape_origin, float B_scale, T B_zero_point,
|
||||
float C_scale, T C_zero_point,
|
||||
bool input_b_is_initializer = false,
|
||||
bool all_initializer_scale_zero_point = false) {
|
||||
size_t number_dims = std::max(a_shape_origin.size(), b_shape_origin.size());
|
||||
std::vector<int64_t> a_shape = PrefixingDims(a_shape_origin, number_dims);
|
||||
|
|
@ -77,7 +78,7 @@ void RunQLinearMathTestFromFloat(
|
|||
for (size_t i = 0, sz = b.size(); i < sz; ++i) {
|
||||
b_quantized[i] = clampi<T>(static_cast<int>(std::nearbyintf(b[i] / B_scale)) + B_zero_point, qmin, qmax);
|
||||
}
|
||||
test.template AddInput<T>("B", b_shape_origin, b_quantized);
|
||||
test.template AddInput<T>("B", b_shape_origin, b_quantized, input_b_is_initializer);
|
||||
test.AddInput<float>("B_scale", {}, {B_scale}, all_initializer_scale_zero_point);
|
||||
test.template AddInput<T>("B_zero_point", {}, {B_zero_point}, all_initializer_scale_zero_point);
|
||||
|
||||
|
|
@ -148,11 +149,20 @@ TEST(QLinearBinaryOpTest, AddU8VectorVectorFull) {
|
|||
C_scale, C_zero_point);
|
||||
|
||||
// NNAPI will require all the scales and zero points be initializers
|
||||
// We also want to test the case input B is an initializer
|
||||
RunQLinearMathTestFromFloat("QLinearAdd", add_function,
|
||||
A, {63}, A_scale, A_zero_point,
|
||||
B, {63}, B_scale, B_zero_point,
|
||||
C_scale, C_zero_point,
|
||||
true);
|
||||
false /* input_b_is_initializer */,
|
||||
true /* all_initializer_scale_zero_point */);
|
||||
|
||||
RunQLinearMathTestFromFloat("QLinearAdd", add_function,
|
||||
A, {63}, A_scale, A_zero_point,
|
||||
B, {63}, B_scale, B_zero_point,
|
||||
C_scale, C_zero_point,
|
||||
true /* input_b_is_initializer */,
|
||||
true /* all_initializer_scale_zero_point */);
|
||||
}
|
||||
|
||||
TEST(QLinearBinaryOpTest, AddU8VectorVectorBroadcast) {
|
||||
|
|
@ -174,11 +184,20 @@ TEST(QLinearBinaryOpTest, AddU8VectorVectorBroadcast) {
|
|||
C_scale, C_zero_point);
|
||||
|
||||
// NNAPI will require all the scales and zero points be initializers
|
||||
// We also want to test the case input B is an initializer
|
||||
RunQLinearMathTestFromFloat("QLinearAdd", add_function,
|
||||
A, {3, 3, 7}, A_scale, A_zero_point,
|
||||
B, {3, 1, 7}, B_scale, B_zero_point,
|
||||
C_scale, C_zero_point,
|
||||
true);
|
||||
false /* input_b_is_initializer */,
|
||||
true /* all_initializer_scale_zero_point */);
|
||||
|
||||
RunQLinearMathTestFromFloat("QLinearAdd", add_function,
|
||||
A, {3, 3, 7}, A_scale, A_zero_point,
|
||||
B, {3, 1, 7}, B_scale, B_zero_point,
|
||||
C_scale, C_zero_point,
|
||||
true /* input_b_is_initializer */,
|
||||
true /* all_initializer_scale_zero_point */);
|
||||
}
|
||||
|
||||
TEST(QLinearBinaryOpTest, AddU8ScalarVectorFull) {
|
||||
|
|
@ -197,11 +216,20 @@ TEST(QLinearBinaryOpTest, AddU8ScalarVectorFull) {
|
|||
C_scale, C_zero_point);
|
||||
|
||||
// NNAPI will require all the scales and zero points be initializers
|
||||
// We also want to test the case input B is an initializer
|
||||
RunQLinearMathTestFromFloat("QLinearAdd", add_function,
|
||||
B, {1}, B_scale, B_zero_point,
|
||||
A, {63}, A_scale, A_zero_point,
|
||||
C_scale, C_zero_point,
|
||||
true);
|
||||
false /* input_b_is_initializer */,
|
||||
true /* all_initializer_scale_zero_point */);
|
||||
|
||||
RunQLinearMathTestFromFloat("QLinearAdd", add_function,
|
||||
B, {1}, B_scale, B_zero_point,
|
||||
A, {63}, A_scale, A_zero_point,
|
||||
C_scale, C_zero_point,
|
||||
true /* input_b_is_initializer */,
|
||||
true /* all_initializer_scale_zero_point */);
|
||||
}
|
||||
|
||||
TEST(QLinearBinaryOpTest, AddU8ScalarVectorBroadcast) {
|
||||
|
|
@ -220,11 +248,20 @@ TEST(QLinearBinaryOpTest, AddU8ScalarVectorBroadcast) {
|
|||
C_scale, C_zero_point);
|
||||
|
||||
// NNAPI will require all the scales and zero points be initializers
|
||||
// We also want to test the case input B is an initializer
|
||||
RunQLinearMathTestFromFloat("QLinearAdd", add_function,
|
||||
B, {3, 1, 1}, B_scale, B_zero_point,
|
||||
A, {3, 7, 3}, A_scale, A_zero_point,
|
||||
C_scale, C_zero_point,
|
||||
true);
|
||||
false /* input_b_is_initializer */,
|
||||
true /* all_initializer_scale_zero_point */);
|
||||
|
||||
RunQLinearMathTestFromFloat("QLinearAdd", add_function,
|
||||
B, {3, 1, 1}, B_scale, B_zero_point,
|
||||
A, {3, 7, 3}, A_scale, A_zero_point,
|
||||
C_scale, C_zero_point,
|
||||
true /* input_b_is_initializer */,
|
||||
true /* all_initializer_scale_zero_point */);
|
||||
}
|
||||
|
||||
TEST(QLinearBinaryOpTest, AddU8VectorScalarFull) {
|
||||
|
|
@ -243,11 +280,20 @@ TEST(QLinearBinaryOpTest, AddU8VectorScalarFull) {
|
|||
C_scale, C_zero_point);
|
||||
|
||||
// NNAPI will require all the scales and zero points be initializers
|
||||
// We also want to test the case input B is an initializer
|
||||
RunQLinearMathTestFromFloat("QLinearAdd", add_function,
|
||||
A, {63}, A_scale, A_zero_point,
|
||||
B, {1}, B_scale, B_zero_point,
|
||||
C_scale, C_zero_point,
|
||||
true);
|
||||
false /* input_b_is_initializer */,
|
||||
true /* all_initializer_scale_zero_point */);
|
||||
|
||||
RunQLinearMathTestFromFloat("QLinearAdd", add_function,
|
||||
A, {63}, A_scale, A_zero_point,
|
||||
B, {1}, B_scale, B_zero_point,
|
||||
C_scale, C_zero_point,
|
||||
true /* input_b_is_initializer */,
|
||||
true /* all_initializer_scale_zero_point */);
|
||||
}
|
||||
|
||||
TEST(QLinearBinaryOpTest, AddU8VectorScalarBroadcast) {
|
||||
|
|
@ -266,11 +312,20 @@ TEST(QLinearBinaryOpTest, AddU8VectorScalarBroadcast) {
|
|||
C_scale, C_zero_point);
|
||||
|
||||
// NNAPI will require all the scales and zero points be initializers
|
||||
// We also want to test the case input B is an initializer
|
||||
RunQLinearMathTestFromFloat("QLinearAdd", add_function,
|
||||
A, {3, 7, 3}, A_scale, A_zero_point,
|
||||
B, {1, 1, 3}, B_scale, B_zero_point,
|
||||
C_scale, C_zero_point,
|
||||
true);
|
||||
false /* input_b_is_initializer */,
|
||||
true /* all_initializer_scale_zero_point */);
|
||||
|
||||
RunQLinearMathTestFromFloat("QLinearAdd", add_function,
|
||||
A, {3, 7, 3}, A_scale, A_zero_point,
|
||||
B, {1, 1, 3}, B_scale, B_zero_point,
|
||||
C_scale, C_zero_point,
|
||||
true /* input_b_is_initializer */,
|
||||
true /* all_initializer_scale_zero_point */);
|
||||
}
|
||||
|
||||
TEST(QLinearBinaryOpTest, AddS8VectorVectorFull) {
|
||||
|
|
@ -386,7 +441,7 @@ TEST(QLinearBinaryOpTest, MulU8VectorVectorFull) {
|
|||
uint8_t C_zero_point = 128;
|
||||
|
||||
RunQLinearMathTestFromFloat("QLinearMul", mul_function,
|
||||
A, {63}, A_scale, A_zero_point, B, {63}, B_scale, B_zero_point, C_scale, C_zero_point);
|
||||
A, {63}, A_scale, A_zero_point, B, {63}, B_scale, B_zero_point, C_scale, C_zero_point);
|
||||
}
|
||||
|
||||
TEST(QLinearBinaryOpTest, MulU8VectorVectorBroadcast) {
|
||||
|
|
@ -394,73 +449,72 @@ TEST(QLinearBinaryOpTest, MulU8VectorVectorBroadcast) {
|
|||
float A_scale = 8.0f / 256.0f;
|
||||
uint8_t A_zero_point = 128;
|
||||
std::vector<float> B = {
|
||||
4.00f, 0.25f, 0.00f, -0.25f, 0.50f, -0.25f, -0.00f, 0.25f,
|
||||
-1.50f, -2.25f, 2.50f, 3.75f, -3.75f, -4.00f, 5.00f, 5.50f,
|
||||
-0.50f, -1.25f, 0.75f, 0.00f, 2.25f
|
||||
};
|
||||
4.00f, 0.25f, 0.00f, -0.25f, 0.50f, -0.25f, -0.00f, 0.25f,
|
||||
-1.50f, -2.25f, 2.50f, 3.75f, -3.75f, -4.00f, 5.00f, 5.50f,
|
||||
-0.50f, -1.25f, 0.75f, 0.00f, 2.25f};
|
||||
float B_scale = 8.0f / 256.0f;
|
||||
uint8_t B_zero_point = 128;
|
||||
float C_scale = 64.0f / 256.0f;
|
||||
uint8_t C_zero_point = 128;
|
||||
|
||||
RunQLinearMathTestFromFloat("QLinearMul", mul_function,
|
||||
A, {3, 3, 7}, A_scale, A_zero_point, B, {3, 1, 7}, B_scale, B_zero_point, C_scale, C_zero_point);
|
||||
A, {3, 3, 7}, A_scale, A_zero_point, B, {3, 1, 7}, B_scale, B_zero_point, C_scale, C_zero_point);
|
||||
}
|
||||
|
||||
TEST(QLinearBinaryOpTest, MulU8ScalarVectorFull) {
|
||||
const std::vector<float>& A(A4Add);
|
||||
float A_scale = 8.0f / 256.0f;
|
||||
uint8_t A_zero_point = 128;
|
||||
std::vector<float> B = { 0.25f };
|
||||
std::vector<float> B = {0.25f};
|
||||
float B_scale = 8.0f / 256.0f;
|
||||
uint8_t B_zero_point = 96;
|
||||
float C_scale = 8.0f / 256.0f;
|
||||
uint8_t C_zero_point = 100;
|
||||
|
||||
RunQLinearMathTestFromFloat("QLinearMul", mul_function,
|
||||
B, {1}, B_scale, B_zero_point, A, {63}, A_scale, A_zero_point, C_scale, C_zero_point);
|
||||
B, {1}, B_scale, B_zero_point, A, {63}, A_scale, A_zero_point, C_scale, C_zero_point);
|
||||
}
|
||||
|
||||
TEST(QLinearBinaryOpTest, MulU8ScalarVectorBroadcast) {
|
||||
const std::vector<float>& A(A4Add);
|
||||
float A_scale = 8.0f / 256.0f;
|
||||
uint8_t A_zero_point = 128;
|
||||
std::vector<float> B = { 0.25f, -0.25f, -0.00f };
|
||||
std::vector<float> B = {0.25f, -0.25f, -0.00f};
|
||||
float B_scale = 8.0f / 256.0f;
|
||||
uint8_t B_zero_point = 96;
|
||||
float C_scale = 8.0f / 256.0f;
|
||||
uint8_t C_zero_point = 100;
|
||||
|
||||
RunQLinearMathTestFromFloat("QLinearMul", mul_function,
|
||||
B, {3, 1, 1}, B_scale, B_zero_point, A, {3, 7, 3}, A_scale, A_zero_point, C_scale, C_zero_point);
|
||||
B, {3, 1, 1}, B_scale, B_zero_point, A, {3, 7, 3}, A_scale, A_zero_point, C_scale, C_zero_point);
|
||||
}
|
||||
|
||||
TEST(QLinearBinaryOpTest, MulU8VectorScalarFull) {
|
||||
const std::vector<float>& A(A4Add);
|
||||
float A_scale = 8.0f / 256.0f;
|
||||
uint8_t A_zero_point = 128;
|
||||
std::vector<float> B = { 0.25f };
|
||||
std::vector<float> B = {0.25f};
|
||||
float B_scale = 8.0f / 256.0f;
|
||||
uint8_t B_zero_point = 96;
|
||||
float C_scale = 16.0f / 256.0f;
|
||||
uint8_t C_zero_point = 128;
|
||||
|
||||
RunQLinearMathTestFromFloat("QLinearMul", mul_function,
|
||||
A, {63}, A_scale, A_zero_point, B, {1}, B_scale, B_zero_point, C_scale, C_zero_point);
|
||||
A, {63}, A_scale, A_zero_point, B, {1}, B_scale, B_zero_point, C_scale, C_zero_point);
|
||||
}
|
||||
|
||||
TEST(QLinearBinaryOpTest, MulU8VectorScalarBroadcast) {
|
||||
const std::vector<float>& A(A4Add);
|
||||
float A_scale = 8.0f / 256.0f;
|
||||
uint8_t A_zero_point = 128;
|
||||
std::vector<float> B = { 0.25f, -0.25f, -0.00f };
|
||||
std::vector<float> B = {0.25f, -0.25f, -0.00f};
|
||||
float B_scale = 8.0f / 256.0f;
|
||||
uint8_t B_zero_point = 96;
|
||||
float C_scale = 16.0f / 256.0f;
|
||||
uint8_t C_zero_point = 128;
|
||||
|
||||
RunQLinearMathTestFromFloat("QLinearMul", mul_function,
|
||||
A, {3, 7, 3}, A_scale, A_zero_point, B, {1, 1, 3}, B_scale, B_zero_point, C_scale, C_zero_point);
|
||||
A, {3, 7, 3}, A_scale, A_zero_point, B, {1, 1, 3}, B_scale, B_zero_point, C_scale, C_zero_point);
|
||||
}
|
||||
|
||||
TEST(QLinearBinaryOpTest, MulS8VectorVectorFull) {
|
||||
|
|
@ -474,7 +528,7 @@ TEST(QLinearBinaryOpTest, MulS8VectorVectorFull) {
|
|||
int8_t C_zero_point = -16;
|
||||
|
||||
RunQLinearMathTestFromFloat("QLinearMul", mul_function,
|
||||
A, {63}, A_scale, A_zero_point, B, {63}, B_scale, B_zero_point, C_scale, C_zero_point);
|
||||
A, {63}, A_scale, A_zero_point, B, {63}, B_scale, B_zero_point, C_scale, C_zero_point);
|
||||
}
|
||||
|
||||
TEST(QLinearBinaryOpTest, MulS8VectorVectorBroadcast) {
|
||||
|
|
@ -482,73 +536,72 @@ TEST(QLinearBinaryOpTest, MulS8VectorVectorBroadcast) {
|
|||
float A_scale = 8.0f / 256.0f;
|
||||
int8_t A_zero_point = 0;
|
||||
std::vector<float> B = {
|
||||
4.00f, 0.25f, 0.00f, -0.25f, 0.50f, -0.25f, -0.00f, 0.25f,
|
||||
-1.50f, -2.25f, 2.50f, 3.75f, -3.75f, -4.00f, 5.00f, 5.50f,
|
||||
-0.50f, -1.25f, 0.75f, 1.25f, 2.25f
|
||||
};
|
||||
4.00f, 0.25f, 0.00f, -0.25f, 0.50f, -0.25f, -0.00f, 0.25f,
|
||||
-1.50f, -2.25f, 2.50f, 3.75f, -3.75f, -4.00f, 5.00f, 5.50f,
|
||||
-0.50f, -1.25f, 0.75f, 1.25f, 2.25f};
|
||||
float B_scale = 8.0f / 256.0f;
|
||||
int8_t B_zero_point = 0;
|
||||
float C_scale = 16.0f / 256.0f;
|
||||
int8_t C_zero_point = -16;
|
||||
|
||||
RunQLinearMathTestFromFloat("QLinearMul", mul_function,
|
||||
A, {3, 3, 7}, A_scale, A_zero_point, B, {3, 1, 7}, B_scale, B_zero_point, C_scale, C_zero_point);
|
||||
A, {3, 3, 7}, A_scale, A_zero_point, B, {3, 1, 7}, B_scale, B_zero_point, C_scale, C_zero_point);
|
||||
}
|
||||
|
||||
TEST(QLinearBinaryOpTest, MulS8ScalarVectorFull) {
|
||||
const std::vector<float>& A(A4Add);
|
||||
float A_scale = 8.0f / 256.0f;
|
||||
int8_t A_zero_point = 0;
|
||||
std::vector<float> B = { 0.25f };
|
||||
std::vector<float> B = {0.25f};
|
||||
float B_scale = 2.0f / 256.0f;
|
||||
int8_t B_zero_point = 16;
|
||||
float C_scale = 8.0f / 256.0f;
|
||||
int8_t C_zero_point = 10;
|
||||
|
||||
RunQLinearMathTestFromFloat("QLinearMul", mul_function,
|
||||
B, {1}, B_scale, B_zero_point, A, {63}, A_scale, A_zero_point, C_scale, C_zero_point);
|
||||
B, {1}, B_scale, B_zero_point, A, {63}, A_scale, A_zero_point, C_scale, C_zero_point);
|
||||
}
|
||||
|
||||
TEST(QLinearBinaryOpTest, MulS8ScalarVectorBroadcast) {
|
||||
const std::vector<float>& A(A4Add);
|
||||
float A_scale = 8.0f / 256.0f;
|
||||
int8_t A_zero_point = 0;
|
||||
std::vector<float> B = { 0.25f, -0.25f, -0.00f };
|
||||
std::vector<float> B = {0.25f, -0.25f, -0.00f};
|
||||
float B_scale = 2.0f / 256.0f;
|
||||
int8_t B_zero_point = 16;
|
||||
float C_scale = 8.0f / 256.0f;
|
||||
int8_t C_zero_point = 10;
|
||||
|
||||
RunQLinearMathTestFromFloat("QLinearMul", mul_function,
|
||||
B, {3, 1, 1}, B_scale, B_zero_point, A, {3, 7, 3}, A_scale, A_zero_point, C_scale, C_zero_point);
|
||||
B, {3, 1, 1}, B_scale, B_zero_point, A, {3, 7, 3}, A_scale, A_zero_point, C_scale, C_zero_point);
|
||||
}
|
||||
|
||||
TEST(QLinearBinaryOpTest, MulS8VectorScalarFull) {
|
||||
const std::vector<float>& A(A4Add);
|
||||
float A_scale = 8.0f / 256.0f;
|
||||
int8_t A_zero_point = 0;
|
||||
std::vector<float> B = { 0.25f };
|
||||
std::vector<float> B = {0.25f};
|
||||
float B_scale = 2.0f / 256.0f;
|
||||
int8_t B_zero_point = 16;
|
||||
float C_scale = 8.0f / 256.0f;
|
||||
int8_t C_zero_point = 10;
|
||||
|
||||
RunQLinearMathTestFromFloat("QLinearMul", mul_function,
|
||||
A, {63}, A_scale, A_zero_point, B, {1}, B_scale, B_zero_point, C_scale, C_zero_point);
|
||||
A, {63}, A_scale, A_zero_point, B, {1}, B_scale, B_zero_point, C_scale, C_zero_point);
|
||||
}
|
||||
|
||||
TEST(QLinearBinaryOpTest, MulS8VectorScalarBroadcast) {
|
||||
const std::vector<float>& A(A4Add);
|
||||
float A_scale = 8.0f / 256.0f;
|
||||
int8_t A_zero_point = 0;
|
||||
std::vector<float> B = { 0.25f, -0.25f, -0.00f };
|
||||
std::vector<float> B = {0.25f, -0.25f, -0.00f};
|
||||
float B_scale = 2.0f / 256.0f;
|
||||
int8_t B_zero_point = 16;
|
||||
float C_scale = 8.0f / 256.0f;
|
||||
int8_t C_zero_point = 10;
|
||||
|
||||
RunQLinearMathTestFromFloat("QLinearMul", mul_function,
|
||||
A, {3, 7, 3}, A_scale, A_zero_point, B, {1, 1, 3}, B_scale, B_zero_point, C_scale, C_zero_point);
|
||||
A, {3, 7, 3}, A_scale, A_zero_point, B, {1, 1, 3}, B_scale, B_zero_point, C_scale, C_zero_point);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
|
|
|||
Loading…
Reference in a new issue