[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:
Guoyu Wang 2021-03-24 11:56:53 -07:00 committed by GitHub
parent 540eac253e
commit 1c04eec2b1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 175 additions and 103 deletions

View file

@ -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;

View file

@ -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};

View file

@ -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();

View file

@ -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;

View file

@ -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