Files
tensorflow--tensorflow/tensorflow/lite/kernels/batch_matmul_test.cc
T
wehub-resource-sync 8a852e4b4e
cffconvert / validate (push) Has been skipped
License Check / license-check (push) Failing after 2s
chore: import upstream snapshot with attribution
2026-07-13 12:14:16 +08:00

1180 lines
44 KiB
C++

/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include <stddef.h>
#include <stdint.h>
#include <initializer_list>
#include <limits>
#include <map>
#include <numeric>
#include <type_traits>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/c/c_api_types.h"
#include "tensorflow/lite/c/common.h"
#include "tensorflow/lite/kernels/test_util.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/string_type.h"
namespace tflite {
namespace ops {
namespace builtin {
TfLiteRegistration* Register_BATCH_MATMUL_REF();
} // namespace builtin
} // namespace ops
namespace {
using ::testing::ElementsAre;
using ::testing::ElementsAreArray;
template <typename T>
tflite::TensorType GetTFLiteType() {
if (std::is_same<T, int8_t>::value) {
return TensorType_INT8;
}
if (std::is_same<T, int16_t>::value) {
return TensorType_INT16;
}
if (std::is_same<T, int32_t>::value) {
return TensorType_INT32;
}
return TensorType_FLOAT32;
}
template <typename T>
class BatchMatMulOpModel : public SingleOpModel {
public:
BatchMatMulOpModel(const TensorData& lhs, const TensorData& rhs,
bool adj_x = false, bool adj_y = false) {
lhs_id_ = AddInput(lhs);
rhs_id_ = AddInput(rhs);
output_id_ = AddOutput(GetTFLiteType<T>());
SetBuiltinOp(BuiltinOperator_BATCH_MATMUL,
BuiltinOptions_BatchMatMulOptions,
CreateBatchMatMulOptions(builder_, adj_x, adj_y).Union());
BuildInterpreter({GetShape(lhs_id_), GetShape(rhs_id_)});
}
int lhs() const { return lhs_id_; }
int rhs() const { return rhs_id_; }
std::vector<T> GetOutput() { return ExtractVector<T>(output_id_); }
std::vector<int32_t> GetOutputShape() { return GetTensorShape(output_id_); }
protected:
int lhs_id_;
int rhs_id_;
int output_id_;
};
TEST(BatchMatMulOpTest, Float32Test_Ones) {
BatchMatMulOpModel<float> model({TensorType_FLOAT32, {3, 2, 1, 4}},
{TensorType_FLOAT32, {3, 1, 4, 1}});
std::vector<float> lhs(24);
std::iota(lhs.begin(), lhs.end(), 1);
std::vector<float> rhs(12);
std::iota(rhs.begin(), rhs.end(), 1);
std::vector<float> res{30, 70, 278, 382, 782, 950};
model.PopulateTensor<float>(model.lhs(), lhs);
model.PopulateTensor<float>(model.rhs(), rhs);
ASSERT_EQ(model.Invoke(), kTfLiteOk);
EXPECT_THAT(model.GetOutput(), ElementsAreArray(res));
EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({3, 2, 1, 1}));
}
TEST(BatchMatMulOpTest, Float32Test_Flatten) {
BatchMatMulOpModel<float> model({TensorType_FLOAT32, {3, 2, 2, 4}},
{TensorType_FLOAT32, {3, 1, 4, 1}});
std::vector<float> lhs(48);
std::iota(lhs.begin(), lhs.end(), 1);
std::vector<float> rhs(12);
std::iota(rhs.begin(), rhs.end(), 1);
std::vector<float> res{30, 70, 110, 150, 486, 590,
694, 798, 1454, 1622, 1790, 1958};
model.PopulateTensor<float>(model.lhs(), lhs);
model.PopulateTensor<float>(model.rhs(), rhs);
ASSERT_EQ(model.Invoke(), kTfLiteOk);
EXPECT_THAT(model.GetOutput(), ElementsAreArray(res));
EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({3, 2, 2, 1}));
}
TEST(BatchMatMulOpTest, Float32Test_Simple) {
BatchMatMulOpModel<float> model({TensorType_FLOAT32, {1, 2, 3}},
{TensorType_FLOAT32, {1, 3, 4}});
model.PopulateTensor<float>(model.lhs(), {1, 2, 3, 4, 5, 6});
model.PopulateTensor<float>(model.rhs(),
{7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18});
ASSERT_EQ(model.Invoke(), kTfLiteOk);
EXPECT_THAT(model.GetOutput(),
Pointwise(FloatingPointEq(),
{74., 80., 86., 92., 173., 188., 203., 218.}));
EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 2, 4}));
}
TEST(BatchMatMulOpTest, Int8Test_Simple) {
BatchMatMulOpModel<int32_t> model({TensorType_INT8, {1, 2, 3}},
{TensorType_INT8, {1, 3, 4}});
model.PopulateTensor<int8_t>(model.lhs(), {1, 2, 3, 4, 5, 6});
model.PopulateTensor<int8_t>(model.rhs(),
{7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18});
ASSERT_EQ(model.Invoke(), kTfLiteOk);
EXPECT_THAT(model.GetOutput(),
ElementsAreArray({74, 80, 86, 92, 173, 188, 203, 218}));
EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 2, 4}));
}
TEST(BatchMatMulOpTest, Int8Test_LargeElement) {
BatchMatMulOpModel<int32_t> model({TensorType_INT8, {1, 2, 3}},
{TensorType_INT8, {1, 3, 4}});
model.PopulateTensor<int8_t>(model.lhs(), {121, 122, 123, 124, 125, 126});
model.PopulateTensor<int8_t>(model.rhs(), {117, 118, 119, 110, 111, 112, 113,
114, 115, 116, 117, 118});
ASSERT_EQ(model.Invoke(), kTfLiteOk);
EXPECT_THAT(model.GetOutput(),
ElementsAreArray(
{41844, 42210, 42576, 41732, 42873, 43248, 43623, 42758}));
EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 2, 4}));
}
TEST(BatchMatMulOpTest, Float32Test_SimpleRHSAdjoint) {
BatchMatMulOpModel<float> model({TensorType_FLOAT32, {1, 2, 3}},
{TensorType_FLOAT32, {1, 4, 3}}, false, true);
model.PopulateTensor<float>(model.lhs(), {1, 2, 3, 4, 5, 6});
model.PopulateTensor<float>(model.rhs(),
{7, 11, 15, 8, 12, 16, 9, 13, 17, 10, 14, 18});
ASSERT_EQ(model.Invoke(), kTfLiteOk);
EXPECT_THAT(model.GetOutput(),
Pointwise(FloatingPointEq(),
{74., 80., 86., 92., 173., 188., 203., 218.}));
EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 2, 4}));
}
TEST(BatchMatMulOpTest, Float32Test_SimpleLHSAdjoint) {
BatchMatMulOpModel<float> model({TensorType_FLOAT32, {1, 3, 2}},
{TensorType_FLOAT32, {1, 3, 4}}, true, false);
model.PopulateTensor<float>(model.lhs(), {1, 4, 2, 5, 3, 6});
model.PopulateTensor<float>(model.rhs(),
{7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18});
ASSERT_EQ(model.Invoke(), kTfLiteOk);
EXPECT_THAT(model.GetOutput(),
Pointwise(FloatingPointEq(),
{74., 80., 86., 92., 173., 188., 203., 218.}));
EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 2, 4}));
}
TEST(BatchMatMulOpTest, Float32Test_BatchSizeTwo) {
BatchMatMulOpModel<float> model({TensorType_FLOAT32, {2, 2, 3}},
{TensorType_FLOAT32, {2, 3, 4}});
model.PopulateTensor<float>(model.lhs(),
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12});
model.PopulateTensor<float>(model.rhs(),
{7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30});
ASSERT_EQ(model.Invoke(), kTfLiteOk);
EXPECT_THAT(model.GetOutput(),
Pointwise(FloatingPointEq(),
{74., 80., 86., 92., 173., 188., 203., 218., 560., 584.,
608., 632., 767., 800., 833., 866.}));
EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({2, 2, 4}));
}
TEST(BatchMatMulOpTest, Float32Test_Broadcast) {
BatchMatMulOpModel<float> model({TensorType_FLOAT32, {2, 2, 3}},
{TensorType_FLOAT32, {3, 4}});
model.PopulateTensor<float>(model.lhs(),
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12});
model.PopulateTensor<float>(model.rhs(),
{7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18});
ASSERT_EQ(model.Invoke(), kTfLiteOk);
EXPECT_THAT(model.GetOutput(),
Pointwise(FloatingPointEq(),
{74., 80., 86., 92., 173., 188., 203., 218., 272., 296.,
320., 344., 371., 404., 437., 470.}));
EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({2, 2, 4}));
}
TEST(BatchMatMulOpTest, Float32Test_BroadcastLHSAdjoint) {
BatchMatMulOpModel<float> model({TensorType_FLOAT32, {2, 3, 2}},
{TensorType_FLOAT32, {3, 4}}, true, false);
model.PopulateTensor<float>(model.lhs(),
{1, 4, 2, 5, 3, 6, 7, 10, 8, 11, 9, 12});
model.PopulateTensor<float>(model.rhs(),
{7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18});
ASSERT_EQ(model.Invoke(), kTfLiteOk);
EXPECT_THAT(model.GetOutput(),
Pointwise(FloatingPointEq(),
{74., 80., 86., 92., 173., 188., 203., 218., 272., 296.,
320., 344., 371., 404., 437., 470.}));
EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({2, 2, 4}));
}
TEST(BatchMatMulOpTest, Float32Test_Broadcast2) {
BatchMatMulOpModel<float> model({TensorType_FLOAT32, {2, 1, 3, 2}},
{TensorType_FLOAT32, {3, 2, 4}});
model.PopulateTensor<float>(model.lhs(),
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12});
model.PopulateTensor<float>(model.rhs(),
{7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30});
ASSERT_EQ(model.Invoke(), kTfLiteOk);
EXPECT_THAT(
model.GetOutput(),
Pointwise(
FloatingPointEq(),
{29., 32., 35., 38., 65., 72., 79., 86., 101., 112., 123.,
134., 53., 56., 59., 62., 121., 128., 135., 142., 189., 200.,
211., 222., 77., 80., 83., 86., 177., 184., 191., 198., 277.,
288., 299., 310., 137., 152., 167., 182., 173., 192., 211., 230.,
209., 232., 255., 278., 257., 272., 287., 302., 325., 344., 363.,
382., 393., 416., 439., 462., 377., 392., 407., 422., 477., 496.,
515., 534., 577., 600., 623., 646.}));
EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({2, 3, 3, 4}));
}
TEST(BatchMatMulOpTest, Float32Test_Broadcast2LHSAdjoint) {
BatchMatMulOpModel<float> model({TensorType_FLOAT32, {2, 1, 2, 3}},
{TensorType_FLOAT32, {3, 2, 4}}, true, false);
model.PopulateTensor<float>(model.lhs(),
{1, 3, 5, 2, 4, 6, 7, 9, 11, 8, 10, 12});
model.PopulateTensor<float>(model.rhs(),
{7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30});
ASSERT_EQ(model.Invoke(), kTfLiteOk);
EXPECT_THAT(
model.GetOutput(),
Pointwise(
FloatingPointEq(),
{29., 32., 35., 38., 65., 72., 79., 86., 101., 112., 123.,
134., 53., 56., 59., 62., 121., 128., 135., 142., 189., 200.,
211., 222., 77., 80., 83., 86., 177., 184., 191., 198., 277.,
288., 299., 310., 137., 152., 167., 182., 173., 192., 211., 230.,
209., 232., 255., 278., 257., 272., 287., 302., 325., 344., 363.,
382., 393., 416., 439., 462., 377., 392., 407., 422., 477., 496.,
515., 534., 577., 600., 623., 646.}));
EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({2, 3, 3, 4}));
}
TEST(BatchMatMulOpTest, Float32Test_Broadcast2RHSAdjoint) {
BatchMatMulOpModel<float> model({TensorType_FLOAT32, {2, 1, 3, 2}},
{TensorType_FLOAT32, {3, 4, 2}}, false, true);
model.PopulateTensor<float>(model.lhs(),
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12});
model.PopulateTensor<float>(model.rhs(),
{7, 11, 8, 12, 9, 13, 10, 14, 15, 19, 16, 20,
17, 21, 18, 22, 23, 27, 24, 28, 25, 29, 26, 30});
ASSERT_EQ(model.Invoke(), kTfLiteOk);
EXPECT_THAT(
model.GetOutput(),
Pointwise(
FloatingPointEq(),
{29., 32., 35., 38., 65., 72., 79., 86., 101., 112., 123.,
134., 53., 56., 59., 62., 121., 128., 135., 142., 189., 200.,
211., 222., 77., 80., 83., 86., 177., 184., 191., 198., 277.,
288., 299., 310., 137., 152., 167., 182., 173., 192., 211., 230.,
209., 232., 255., 278., 257., 272., 287., 302., 325., 344., 363.,
382., 393., 416., 439., 462., 377., 392., 407., 422., 477., 496.,
515., 534., 577., 600., 623., 646.}));
EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({2, 3, 3, 4}));
}
TEST(BatchMatMulOpTest, Float32Test_Broadcast2BothAdjoint) {
BatchMatMulOpModel<float> model({TensorType_FLOAT32, {2, 1, 2, 3}},
{TensorType_FLOAT32, {3, 4, 2}}, true, true);
model.PopulateTensor<float>(model.lhs(),
{1, 3, 5, 2, 4, 6, 7, 9, 11, 8, 10, 12});
model.PopulateTensor<float>(model.rhs(),
{7, 11, 8, 12, 9, 13, 10, 14, 15, 19, 16, 20,
17, 21, 18, 22, 23, 27, 24, 28, 25, 29, 26, 30});
ASSERT_EQ(model.Invoke(), kTfLiteOk);
EXPECT_THAT(
model.GetOutput(),
Pointwise(
FloatingPointEq(),
{29., 32., 35., 38., 65., 72., 79., 86., 101., 112., 123.,
134., 53., 56., 59., 62., 121., 128., 135., 142., 189., 200.,
211., 222., 77., 80., 83., 86., 177., 184., 191., 198., 277.,
288., 299., 310., 137., 152., 167., 182., 173., 192., 211., 230.,
209., 232., 255., 278., 257., 272., 287., 302., 325., 344., 363.,
382., 393., 416., 439., 462., 377., 392., 407., 422., 477., 496.,
515., 534., 577., 600., 623., 646.}));
EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({2, 3, 3, 4}));
}
TEST(BatchMatMulOpTest, Float32Test_Broadcast3DAdjXOptimization) {
BatchMatMulOpModel<float> model({TensorType_FLOAT32, {4, 3, 2, 3, 2}},
{TensorType_FLOAT32, {4, 3, 1, 3, 2}},
/*adj_x=*/true, /*adj_y=*/false);
std::vector<float> lhs(288);
for (int i = 0; i < 288; ++i) {
lhs[i] = (i % 12) + 1;
}
std::vector<float> rhs(72);
for (int i = 0; i < 72; ++i) {
rhs[i] = (i % 6) + 1;
}
std::vector<float> res_block{
35, 44, 44, 56, 89, 116, 98, 128,
};
std::vector<float> res;
for (int i = 0; i < 12; ++i) {
res.insert(res.end(), res_block.begin(), res_block.end());
}
model.PopulateTensor<float>(model.lhs(), lhs);
model.PopulateTensor<float>(model.rhs(), rhs);
ASSERT_EQ(model.Invoke(), kTfLiteOk);
EXPECT_THAT(model.GetOutput(), ElementsAreArray(res));
EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({4, 3, 2, 2, 2}));
}
TEST(BatchMatMulOpTest, Float32Test_BroadcastFromRHS) {
BatchMatMulOpModel<float> model({TensorType_FLOAT32, {4, 5}},
{TensorType_FLOAT32, {3, 1, 5, 2}});
model.PopulateTensor<float>(
model.lhs(),
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20});
model.PopulateTensor<float>(
model.rhs(),
{7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36});
ASSERT_EQ(model.Invoke(), kTfLiteOk);
EXPECT_THAT(
model.GetOutput(),
Pointwise(FloatingPointEq(),
{185., 200., 460., 500., 735., 800., 1010., 1100.,
335., 350., 860., 900., 1385., 1450., 1910., 2000.,
485., 500., 1260., 1300., 2035., 2100., 2810., 2900.}));
EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({3, 1, 4, 2}));
}
class ConstRHSBatchMatMulOpModel : public MultiOpModel {
public:
ConstRHSBatchMatMulOpModel(const TensorData& lhs,
std::initializer_list<int> rhs_shape,
std::initializer_list<float> rhs_data,
bool adj_x = false, bool adj_y = false) {
lhs_id_ = AddInput(lhs);
rhs_id_ = AddConstInput<float>(TensorType_FLOAT32, rhs_data, rhs_shape);
matmul_output_id_ = AddOutput(lhs.type);
std::vector<int> matmul_inputs{lhs_id_, rhs_id_};
std::vector<int> matmul_outputs{matmul_output_id_};
AddBuiltinOp(BuiltinOperator_BATCH_MATMUL,
BuiltinOptions_BatchMatMulOptions,
CreateBatchMatMulOptions(builder_, adj_x, adj_y).Union(),
matmul_inputs, matmul_outputs);
// Without following ops (not limited to neg), temporary allocation with
// kTfLiteArenaRw tends to re-claim the same memory across each evaluation,
// and no other ops will modify values at that memory address because no
// other memory allocations take place. Therefore, it's likely that results
// are correct even if constant transposed RHS is allocated with
// kTfLiteArenaRw. We thus use a dummy op to make sure constant transposed
// RHS behaves correctly.
neg_output_id_ = AddOutput(lhs.type);
std::vector<int> neg_inputs{matmul_output_id_};
std::vector<int> neg_outputs{neg_output_id_};
AddBuiltinOp(BuiltinOperator_NEG, BuiltinOptions_NegOptions,
CreateNegOptions(builder_).Union(), neg_inputs, neg_outputs);
BuildInterpreter({GetShape(lhs_id_), GetShape(rhs_id_)});
}
int lhs() const { return lhs_id_; }
std::vector<float> GetOutput() {
return ExtractVector<float>(neg_output_id_);
}
std::vector<int32_t> GetOutputShape() {
return GetTensorShape(neg_output_id_);
}
protected:
int lhs_id_;
int rhs_id_;
int matmul_output_id_;
int neg_output_id_;
};
TEST(ConstRHSBatchMatMulOpModel, RHSNotAdjoint) {
ConstRHSBatchMatMulOpModel model({TensorType_FLOAT32, {1, 6, 2}}, {2, 3},
{6, 3, 7, 4, 6, 9});
model.PopulateTensor<float>(model.lhs(),
{6, 3, 7, 4, 6, 9, 2, 6, 7, 4, 3, 7});
ASSERT_EQ(model.Invoke(), kTfLiteOk);
EXPECT_THAT(model.GetOutput(),
ElementsAreArray({-48, -36, -69, -58, -45, -85, -72, -72, -123,
-36, -42, -68, -58, -45, -85, -46, -51, -84}));
EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 6, 3}));
// Eval twice to make sure constant transposed RHS is persistent.
model.PopulateTensor<float>(model.lhs(),
{6, 3, 7, 4, 6, 9, 2, 6, 7, 4, 3, 7});
ASSERT_EQ(model.Invoke(), kTfLiteOk);
EXPECT_THAT(model.GetOutput(),
ElementsAreArray({-48, -36, -69, -58, -45, -85, -72, -72, -123,
-36, -42, -68, -58, -45, -85, -46, -51, -84}));
EXPECT_THAT(model.GetOutputShape(), ElementsAreArray({1, 6, 3}));
}
// In the hybrid model the weights are quantized int8. But the input
// and output are expected to be in float precision.
class HybridBatchMatMulOpModel : public SingleOpModel {
public:
HybridBatchMatMulOpModel(int units, int batches, const TensorData& lhs,
const TensorData& rhs,
const TensorData& output = {TensorType_FLOAT32},
bool asymmetric_quantize_inputs = true,
bool adj_x = false, bool adj_y = false)
: units_(units), batches_(batches) {
int total_input_size = 1;
for (size_t i = 0; i < lhs.shape.size(); ++i) {
total_input_size *= lhs.shape[i];
}
input_size_ = total_input_size / batches_;
lhs_id_ = AddInput(lhs);
rhs_id_ = AddInput(rhs);
output_id_ = AddOutput(output);
SetBuiltinOp(BuiltinOperator_BATCH_MATMUL,
BuiltinOptions_BatchMatMulOptions,
CreateBatchMatMulOptions(builder_, adj_x, adj_y,
asymmetric_quantize_inputs)
.Union());
BuildInterpreter({GetShape(lhs_id_), GetShape(rhs_id_)},
/*num_threads=*/-1,
/*allow_fp32_relax_to_fp16=*/false,
/*apply_delegate=*/false);
}
void SetWeights(const std::vector<float>& data) {
SymmetricQuantizeAndPopulate(rhs_id_, data);
AllocateAndDelegate(true);
}
void SetSignedWeights(std::initializer_list<float> f) {
SignedSymmetricQuantizeAndPopulate(rhs_id_, f);
AllocateAndDelegate(true);
}
void SetInput(const std::vector<float>& f) { PopulateTensor(lhs_id_, f); }
std::vector<float> GetOutput() { return ExtractVector<float>(output_id_); }
std::vector<int> GetOutputShape() { return GetTensorShape(output_id_); }
int input_size() { return input_size_; }
int num_units() { return units_; }
int num_batches() { return batches_; }
int lhs() const { return lhs_id_; }
int rhs() const { return rhs_id_; }
protected:
int lhs_id_;
int rhs_id_;
int output_id_;
int units_;
int batches_;
int input_size_;
};
TEST(HybridAsymmetricBatchMatMulOpTest, SimpleTestQuantizedInt8) {
HybridBatchMatMulOpModel m(
/*units=*/3, /*batches=*/2,
/*lhs=*/{TensorType_FLOAT32, {2, 10}},
/*rhs=*/{TensorType_INT8, {10, 3}, 0, 0, 10.0 / 127.0, 0});
m.SetSignedWeights({
1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5,
6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
});
m.SetInput({
11, 12, 13, 14, 15, 16, 17, 18, -19, -20, // batch 1, 0
11, 12, 13, 14, 15, 16, 17, -18, 19, -20, // batch 1, 1
});
ASSERT_EQ(m.Invoke(), kTfLiteOk);
EXPECT_THAT(m.GetOutput(), ElementsAreArray(ArrayFloatNear(
{
193,
193,
193,
247,
247,
247,
},
/*max_abs_err=*/3.f)));
EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({2, 3}));
}
TEST(HybridAsymmetricBatchMatMulOpTest, MultipleNumBatchQuantizedInt8) {
// need 4 scale factors
HybridBatchMatMulOpModel m(
/*units=*/10, /*batches=*/4,
/*lhs=*/{TensorType_FLOAT32, {1, 2, 2, 3}},
/*rhs=*/{TensorType_INT8, {3, 10}, 0, 0, 10.0 / 127.0, 0});
m.SetSignedWeights({
1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
});
m.SetInput({
11, 12, 13, // batch 1, 0
11, 12, 13, // batch 1, 1
11, 12, 13, // batch 1, 2
11, 12, 13, // batch 1, 3
});
ASSERT_EQ(m.Invoke(), kTfLiteOk);
EXPECT_THAT(m.GetOutput(),
ElementsAreArray(ArrayFloatNear(
{
73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
},
/*max_abs_err=*/0.64f)));
EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({1, 2, 2, 10}));
}
TEST(HybridAsymmetricBatchMatMulOpTest, RegressionTestQuantizedInt8) {
HybridBatchMatMulOpModel m(
/*units=*/10, /*batches=*/2,
/*lhs=*/{TensorType_FLOAT32, {2, 3}},
/*rhs=*/{TensorType_INT8, {3, 10}, 0, 0, 10.0 / 127.0, 0});
m.SetSignedWeights({
1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
});
m.SetInput({
11, 12, 13, // batch 1, 0
11, 12, 13, // batch 1, 1
});
ASSERT_EQ(m.Invoke(), kTfLiteOk);
EXPECT_THAT(m.GetOutput(), ElementsAreArray(ArrayFloatNear(
{
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
},
/*max_abs_err=*/0.64f)));
EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({2, 10}));
}
// Test if batch_size and num_units are set correctly in InitializeTemporaries.
// Intentionally set batches and units to be greater than accum dim size, since
// if batch_size/num_units is set to accum dim size which is wrong (instead of
// batches/units), scratch scaling_factors/accum_scratch will be allocated to
// smaller size than required and make wrong operation result, so that we can
// check this thoroughly.
TEST(HybridAsymmetricBatchMatMulOpTest,
TestQuantizedInt8BatchesAndUnitsGreaterThanAccumDimSize) {
HybridBatchMatMulOpModel m(
/*units=*/8, /*batches=*/6,
/*lhs=*/{TensorType_FLOAT32, {6, 3}},
/*rhs=*/{TensorType_INT8, {3, 8}, 0, 0, 10.0 / 127.0, 0});
m.SetSignedWeights(
{1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3});
m.SetInput({
11, 12, 13, // batch 1, 0
11, 12, 13, // batch 1, 1
11, 12, 13, // batch 1, 2
11, 12, 13, // batch 1, 3
11, 12, 13, // batch 1, 4
11, 12, 13, // batch 1, 5
});
ASSERT_EQ(m.Invoke(), kTfLiteOk);
EXPECT_THAT(
m.GetOutput(),
ElementsAreArray(ArrayFloatNear(
{74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74,
74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74,
74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74},
/*max_abs_err=*/0.15f)));
EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({6, 8}));
}
// Test if batch_size and num_units are set correctly in InitializeTemporaries,
// where adj_x is true.
TEST(HybridAsymmetricBatchMatMulOpTest,
TestQuantizedInt8BatchesAndUnitsGreaterThanAccumDimSizeAdjX) {
HybridBatchMatMulOpModel m(
/*units=*/8, /*batches=*/6,
/*lhs=*/{TensorType_FLOAT32, {3, 6}},
/*rhs=*/{TensorType_INT8, {3, 8}, 0, 0, 10.0 / 127.0, 0},
/*output=*/{TensorType_FLOAT32},
/*asymmetric_quantize_inputs=*/true,
/*adj_x=*/true);
m.SetSignedWeights(
{1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3});
m.SetInput(
{11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13});
ASSERT_EQ(m.Invoke(), kTfLiteOk);
EXPECT_THAT(
m.GetOutput(),
ElementsAreArray(ArrayFloatNear(
{74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74,
74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74,
74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74},
/*max_abs_err=*/0.15f)));
EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({6, 8}));
}
// Test if batch_size and num_units are set correctly in
// InitializeTemporaries, where adj_y is true.
TEST(HybridAsymmetricBatchMatMulOpTest,
TestQuantizedInt8BatchesAndUnitsGreaterThanAccumDimSizeAdjY) {
HybridBatchMatMulOpModel m(
/*units=*/8, /*batches=*/6,
/*lhs=*/{TensorType_FLOAT32, {6, 3}},
/*rhs=*/{TensorType_INT8, {8, 3}, 0, 0, 10.0 / 127.0, 0},
/*output=*/{TensorType_FLOAT32},
/*asymmetric_quantize_inputs=*/true,
/*adj_x=*/false,
/*adj_y=*/true);
m.SetSignedWeights(
{1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3});
m.SetInput({
11, 12, 13, // batch 1, 0
11, 12, 13, // batch 1, 1
11, 12, 13, // batch 1, 2
11, 12, 13, // batch 1, 3
11, 12, 13, // batch 1, 4
11, 12, 13, // batch 1, 5
});
ASSERT_EQ(m.Invoke(), kTfLiteOk);
EXPECT_THAT(
m.GetOutput(),
ElementsAreArray(ArrayFloatNear(
{74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74,
74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74,
74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74},
/*max_abs_err=*/0.15f)));
EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({6, 8}));
}
// Test if batch_size and num_units are set correctly in
// InitializeTemporaries, where both adj_x and adj_y are true.
TEST(HybridAsymmetricBatchMatMulOpTest,
TestQuantizedInt8BatchesAndUnitsGreaterThanAccumDimSizeAdjXAdjY) {
HybridBatchMatMulOpModel m(
/*units=*/8, /*batches=*/6,
/*lhs=*/{TensorType_FLOAT32, {3, 6}},
/*rhs=*/{TensorType_INT8, {8, 3}, 0, 0, 10.0 / 127.0, 0},
/*output=*/{TensorType_FLOAT32},
/*asymmetric_quantize_inputs=*/true,
/*adj_x=*/true,
/*adj_y=*/true);
m.SetSignedWeights(
{1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3});
m.SetInput(
{11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13});
ASSERT_EQ(m.Invoke(), kTfLiteOk);
EXPECT_THAT(
m.GetOutput(),
ElementsAreArray(ArrayFloatNear(
{74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74,
74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74,
74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74},
/*max_abs_err=*/0.15f)));
EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({6, 8}));
}
TEST(HybridAsymmetricBatchMatMulOpTest, QuantizedInt8BroadcastWeights) {
HybridBatchMatMulOpModel m(
/*units=*/3, /*batches=*/2,
/*lhs=*/{TensorType_FLOAT32, {2, 2, 10}},
/*rhs=*/{TensorType_INT8, {10, 3}, 0, 0, 10.0 / 127.0, 0});
m.SetSignedWeights({
1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5,
6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
});
m.SetInput({
1, 2, 3, 4, 5, 6, 7, 8, -9, -10, // batch 0, 0
1, 2, 3, 4, 5, 6, 7, -8, 9, -10, // batch 0, 1
11, 12, 13, 14, 15, 16, 17, 18, -19, -20, // batch 1, 0
11, 12, 13, 14, 15, 16, 17, -18, 19, -20, // batch 1, 1
});
ASSERT_EQ(m.Invoke(), kTfLiteOk);
EXPECT_THAT(m.GetOutput(), ElementsAreArray(ArrayFloatNear(
{
23, 23, 23, //
57, 57, 57, //
193, 193, 193, //
247, 247, 247, //
},
/*max_abs_err=*/3.f)));
EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({2, 2, 3}));
}
TEST(HybridAsymmetricBatchMatMulOpTest, QuantizedInt8BroadcastBigWeights) {
HybridBatchMatMulOpModel m(
/*units=*/9, /*batches=*/2,
/*lhs=*/{TensorType_FLOAT32, {2, 2, 10}},
/*rhs=*/{TensorType_INT8, {10, 9}, 0, 0, 10.0 / 127.0, 0});
m.SetSignedWeights({
1, 1, 1, 17, 17, 17, 26, 26, 26, //
2, 2, 2, 18, 18, 18, 27, 27, 27, //
3, 3, 3, 19, 19, 19, 28, 28, 28, //
4, 4, 4, 20, 20, 20, 29, 29, 29, //
5, 5, 5, 21, 21, 21, 30, 30, 30, //
6, 6, 6, 22, 22, 22, 31, 31, 31, //
7, 7, 7, 23, 23, 23, 32, 32, 32, //
8, 8, 8, 24, 24, 24, 33, 33, 33, //
9, 9, 9, 25, 25, 25, 34, 34, 34, //
10, 10, 10, 26, 26, 26, 35, 35, 35,
});
m.SetInput({
1, 2, 3, 4, 5, 6, 7, 8, -9, -10, // batch 0, 0
1, 2, 3, 4, 5, 6, 7, -8, 9, -10, // batch 0, 1
11, 12, 13, 14, 15, 16, 17, 18, -19, -20, // batch 1, 0
11, 12, 13, 14, 15, 16, 17, -18, 19, -20, // batch 1, 1
});
ASSERT_EQ(m.Invoke(), kTfLiteOk);
EXPECT_THAT(m.GetOutput(),
ElementsAreArray(ArrayFloatNear(
{
23, 23, 23, 295, 295, 295, 448, 448, 448, //
57, 57, 57, 361, 361, 361, 532, 532, 532, //
193, 193, 193, 1425, 1425, 1425, 2118, 2118, 2118, //
247, 247, 247, 1511, 1511, 1511, 2222, 2222, 2222 //
},
/*max_abs_err=*/10.0f)));
EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({2, 2, 9}));
}
TEST(HybridAsymmetricBatchMatMulOpTest, QuantizedInt8BroadcastInputs) {
HybridBatchMatMulOpModel m(
/*units=*/3, /*batches=*/2,
/*lhs=*/{TensorType_FLOAT32, {2, 10}},
/*rhs=*/{TensorType_INT8, {2, 10, 3}, 0, 0, 10.0 / 127.0, 0});
m.SetSignedWeights({
1, -3, 1, //
2, -2, 2, //
3, -1, 3, //
4, 0, 4, //
5, 1, 5, //
6, 2, 6, //
7, 3, 7, //
8, 4, 8, //
9, 5, 9, //
10, 6, 10, //
1, 1, 1, //
2, 2, 2, //
3, 3, 3, //
4, 4, 4, //
5, 5, 5, //
6, 6, 6, //
7, 7, 7, //
8, 8, 8, //
9, 9, 9, //
10, 10, 10,
});
m.SetInput({
1, 2, 3, 4, 5, 6, 7, 8, -9, -10, // batch 0, 0
1, 2, 3, 4, 5, 6, 7, -8, 9, -10, // batch 0, 1
});
ASSERT_EQ(m.Invoke(), kTfLiteOk);
EXPECT_THAT(m.GetOutput(), ElementsAreArray(ArrayFloatNear(
{
23, -45, 23, //
57, -19, 57, //
23, 23, 23, //
57, 57, 57, //
},
/*max_abs_err=*/1.5f)));
EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({2, 2, 3}));
}
TEST(HybridSymmetricBatchMatMulOpTest, SimpleTestQuantizedInt8) {
HybridBatchMatMulOpModel m(
/*units=*/3, /*batches=*/2,
/*lhs=*/{TensorType_FLOAT32, {2, 10}},
/*rhs=*/{TensorType_INT8, {10, 3}, 0, 0, 10.0 / 127.0, 0},
/*output=*/{TensorType_FLOAT32}, /*asymmetric_quantize_inputs=*/false);
m.SetSignedWeights({
1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5,
6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
});
m.SetInput({
11, 12, 13, 14, 15, 16, 17, 18, -19, -20, // batch 1, 0
11, 12, 13, 14, 15, 16, 17, -18, 19, -20, // batch 1, 1
});
ASSERT_EQ(m.Invoke(), kTfLiteOk);
EXPECT_THAT(m.GetOutput(), ElementsAreArray(ArrayFloatNear(
{
193,
193,
193,
247,
247,
247,
},
/*max_abs_err=*/1.5f)));
EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({2, 3}));
}
TEST(HybridSymmetricBatchMatMulOpTest, QuantizedInt8BroadcastWeights) {
HybridBatchMatMulOpModel m(
/*units=*/3, /*batches=*/2,
/*lhs=*/{TensorType_FLOAT32, {2, 2, 10}},
/*rhs=*/{TensorType_INT8, {10, 3}, 0, 0, 10.0 / 127.0, 0},
/*output=*/{TensorType_FLOAT32}, /*asymmetric_quantize_inputs=*/false);
m.SetSignedWeights({
1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5,
6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
});
m.SetInput({
1, 2, 3, 4, 5, 6, 7, 8, -9, -10, // batch 0, 0
1, 2, 3, 4, 5, 6, 7, -8, 9, -10, // batch 0, 1
11, 12, 13, 14, 15, 16, 17, 18, -19, -20, // batch 1, 0
11, 12, 13, 14, 15, 16, 17, -18, 19, -20, // batch 1, 1
});
ASSERT_EQ(m.Invoke(), kTfLiteOk);
EXPECT_THAT(m.GetOutput(), ElementsAreArray(ArrayFloatNear(
{
23, 23, 23, //
57, 57, 57, //
193, 193, 193, //
247, 247, 247, //
},
/*max_abs_err=*/1.5f)));
EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({2, 2, 3}));
}
TEST(HybridSymmetricBatchMatMulOpTest, QuantizedInt8BroadcastBigWeights) {
HybridBatchMatMulOpModel m(
/*units=*/9, /*batches=*/2,
/*lhs=*/{TensorType_FLOAT32, {2, 2, 10}},
/*rhs=*/{TensorType_INT8, {10, 9}, 0, 0, 10.0 / 127.0, 0},
{TensorType_FLOAT32}, false);
m.SetSignedWeights({
1, 1, 1, 17, 17, 17, 26, 26, 26, //
2, 2, 2, 18, 18, 18, 27, 27, 27, //
3, 3, 3, 19, 19, 19, 28, 28, 28, //
4, 4, 4, 20, 20, 20, 29, 29, 29, //
5, 5, 5, 21, 21, 21, 30, 30, 30, //
6, 6, 6, 22, 22, 22, 31, 31, 31, //
7, 7, 7, 23, 23, 23, 32, 32, 32, //
8, 8, 8, 24, 24, 24, 33, 33, 33, //
9, 9, 9, 25, 25, 25, 34, 34, 34, //
10, 10, 10, 26, 26, 26, 35, 35, 35,
});
m.SetInput({
1, 2, 3, 4, 5, 6, 7, 8, -9, -10, // batch 0, 0
1, 2, 3, 4, 5, 6, 7, -8, 9, -10, // batch 0, 1
11, 12, 13, 14, 15, 16, 17, 18, -19, -20, // batch 1, 0
11, 12, 13, 14, 15, 16, 17, -18, 19, -20, // batch 1, 1
});
ASSERT_EQ(m.Invoke(), kTfLiteOk);
EXPECT_THAT(m.GetOutput(),
ElementsAreArray(ArrayFloatNear(
{
23, 23, 23, 295, 295, 295, 448, 448, 448, //
57, 57, 57, 361, 361, 361, 532, 532, 532, //
193, 193, 193, 1425, 1425, 1425, 2118, 2118, 2118, //
247, 247, 247, 1511, 1511, 1511, 2222, 2222, 2222 //
},
/*max_abs_err=*/10.0f)));
EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({2, 2, 9}));
}
TEST(HybridSymmetricBatchMatMulOpTest, QuantizedInt8BroadcastInputs) {
HybridBatchMatMulOpModel m(
/*units=*/3, /*batches=*/2,
/*lhs=*/{TensorType_FLOAT32, {2, 10}},
/*rhs=*/{TensorType_INT8, {2, 10, 3}, 0, 0, 10.0 / 127.0, 0},
{TensorType_FLOAT32}, false);
m.SetSignedWeights({
1, -3, 1, //
2, -2, 2, //
3, -1, 3, //
4, 0, 4, //
5, 1, 5, //
6, 2, 6, //
7, 3, 7, //
8, 4, 8, //
9, 5, 9, //
10, 6, 10, //
1, 1, 1, //
2, 2, 2, //
3, 3, 3, //
4, 4, 4, //
5, 5, 5, //
6, 6, 6, //
7, 7, 7, //
8, 8, 8, //
9, 9, 9, //
10, 10, 10,
});
m.SetInput({
1, 2, 3, 4, 5, 6, 7, 8, -9, -10, // batch 0, 0
1, 2, 3, 4, 5, 6, 7, -8, 9, -10, // batch 0, 1
});
ASSERT_EQ(m.Invoke(), kTfLiteOk);
EXPECT_THAT(m.GetOutput(), ElementsAreArray(ArrayFloatNear(
{
23, -45, 23, //
57, -19, 57, //
23, 23, 23, //
57, 57, 57, //
},
/*max_abs_err=*/1.5f)));
EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({2, 2, 3}));
}
class QuantizedBatchMatMulOpModel : public SingleOpModel {
public:
QuantizedBatchMatMulOpModel(const TensorData& lhs, const TensorData& rhs,
const TensorData& output = {TensorType_INT8},
bool adj_x = false, bool adj_y = false) {
lhs_id_ = AddInput(lhs);
rhs_id_ = AddInput(rhs);
output_id_ = AddOutput(output);
SetBuiltinOp(BuiltinOperator_BATCH_MATMUL,
BuiltinOptions_BatchMatMulOptions,
CreateBatchMatMulOptions(builder_, adj_x, adj_y).Union());
BuildInterpreter({GetShape(lhs_id_), GetShape(rhs_id_)});
}
template <typename T>
void SetWeights(const std::vector<float>& data) {
QuantizeAndPopulate<T>(rhs_id_, data);
}
template <typename T>
void SetInput(const std::vector<float>& data) {
QuantizeAndPopulate<T>(lhs_id_, data);
}
template <typename T>
std::vector<T> GetOutput() {
return ExtractVector<T>(output_id_);
}
template <typename T>
std::vector<float> GetDequantizedOutput() {
return Dequantize<T>(ExtractVector<T>(output_id_), GetScale(output_id_),
GetZeroPoint(output_id_));
}
protected:
int lhs_id_;
int rhs_id_;
int output_id_;
};
TEST(QuantizedBatchMatMulOpTest, SimpleTestQuantizedInt8) {
QuantizedBatchMatMulOpModel m(
/*lhs=*/{TensorType_INT8, {2, 10}, -63.5, 64},
/*rhs=*/{TensorType_INT8, {10, 3}, -63.5, 64},
/*output=*/{TensorType_INT8, {}, -127, 128});
m.SetWeights<int8_t>({
1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5,
6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
});
m.SetInput<int8_t>({
1, 2, 3, 4, 5, 6, 7, 8, -9, -10, // b = 0
1, 2, 3, 4, 5, 6, 7, -8, 9, -10, // b = 1
});
ASSERT_EQ(m.Invoke(), kTfLiteOk);
EXPECT_THAT(m.GetDequantizedOutput<int8_t>(),
ElementsAreArray(ArrayFloatNear({23, 23, 23, 57, 57, 57})));
EXPECT_THAT(m.GetOutput<int8_t>(), ElementsAre(22, 22, 22, 56, 56, 56));
}
TEST(QuantizedBatchMatMulOpTest, SimpleTestQuantizedInt8AdjRHS) {
QuantizedBatchMatMulOpModel m(
/*lhs=*/{TensorType_INT8, {2, 10}, -63.5, 64},
/*rhs=*/{TensorType_INT8, {3, 10}, -63.5, 64},
/*output=*/{TensorType_INT8, {}, -127, 128}, false, true);
m.SetWeights<int8_t>({
1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5,
6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
});
m.SetInput<int8_t>({
1, 2, 3, 4, 5, 6, 7, 8, -9, -10, // b = 0
1, 2, 3, 4, 5, 6, 7, -8, 9, -10, // b = 1
});
ASSERT_EQ(m.Invoke(), kTfLiteOk);
EXPECT_THAT(m.GetDequantizedOutput<int8_t>(),
ElementsAreArray(ArrayFloatNear({14, 65, 128, 20, 95, 128})));
EXPECT_THAT(m.GetOutput<int8_t>(), ElementsAre(13, 64, 127, 19, 94, 127));
}
TEST(QuantizedBatchMatMulOpTest, SimpleTestQuantizedInt16) {
const float inputs_scale = 10.0 / std::numeric_limits<int16_t>::max();
const float output_scale = 1.0;
const int32_t zero_point = 0;
QuantizedBatchMatMulOpModel m(
/*lhs=*/{TensorType_INT16, {2, 10}, 0, 0, inputs_scale, zero_point},
/*rhs=*/{TensorType_INT16, {10, 3}, 0, 0, inputs_scale, zero_point},
/*output=*/{TensorType_INT16, {}, 0, 0, output_scale, zero_point});
m.SetWeights<int16_t>({
1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5,
6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
});
m.SetInput<int16_t>({
1, 2, 3, 4, 5, 6, 7, 8, -9, -10, // b = 0
1, 2, 3, 4, 5, 6, 7, -8, 9, -10, // b = 1
});
ASSERT_EQ(m.Invoke(), kTfLiteOk);
EXPECT_THAT(m.GetDequantizedOutput<int16_t>(),
ElementsAreArray(ArrayFloatNear({23, 23, 23, 57, 57, 57})));
EXPECT_THAT(m.GetOutput<int16_t>(), ElementsAre(23, 23, 23, 57, 57, 57));
}
TEST(QuantizedBatchMatMulOpTest, SimpleTestQuantizedInt16Int8) {
const float inputs_scale = 1.0;
const float weights_scale = 1.0;
const float output_scale = 1.0;
const int32_t zero_point = 0;
QuantizedBatchMatMulOpModel m(
/*lhs=*/{TensorType_INT16, {2, 10}, 0, 0, inputs_scale, zero_point},
/*rhs=*/{TensorType_INT8, {10, 3}, 0, 0, weights_scale, zero_point},
/*output=*/{TensorType_INT16, {}, 0, 0, output_scale, zero_point});
m.SetWeights<int8_t>({
1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5,
6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
});
m.SetInput<int16_t>({
1, 2, 3, 4, 5, 6, 7, 8, -9, -10, // b = 0
1, 2, 3, 4, 5, 6, 7, -8, 9, -10, // b = 1
});
ASSERT_EQ(m.Invoke(), kTfLiteOk);
EXPECT_THAT(m.GetDequantizedOutput<int16_t>(),
ElementsAreArray(ArrayFloatNear({23, 23, 23, 57, 57, 57})));
EXPECT_THAT(m.GetOutput<int16_t>(), ElementsAre(23, 23, 23, 57, 57, 57));
}
TEST(QuantizedBatchMatMulOpTest, SimpleTestQuantizedInt16Int8WithScales) {
const float inputs_scale = 0.5;
const float weights_scale = 2.0;
const float output_scale = 0.25;
const int32_t zero_point = 0;
QuantizedBatchMatMulOpModel m(
/*lhs=*/{TensorType_INT16, {2, 10}, 0, 0, inputs_scale, zero_point},
/*rhs=*/{TensorType_INT8, {10, 3}, 0, 0, weights_scale, zero_point},
/*output=*/{TensorType_INT16, {}, 0, 0, output_scale, zero_point});
m.SetWeights<int8_t>({
2, 2, 2, 4, 4, 4, 6, 6, 6, 8, 8, 8, 10, 10, 10,
12, 12, 12, 14, 14, 14, 16, 16, 16, 18, 18, 18, 20, 20, 20,
});
m.SetInput<int16_t>({
0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, -4.5, -5.0, // b = 0
0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, -4.0, 4.5, -5.0, // b = 1
});
ASSERT_EQ(m.Invoke(), kTfLiteOk);
// Accumulator for b=0: 23. Combined Scale = (0.5 * 2.0) / 0.25 = 4.0.
// Quantized output: 23 * 4 = 92.
// Accumulator for b=1: 57. Combined Scale = 4.0.
// Quantized output: 57 * 4 = 228.
EXPECT_THAT(m.GetOutput<int16_t>(), ElementsAre(92, 92, 92, 228, 228, 228));
EXPECT_THAT(m.GetDequantizedOutput<int16_t>(),
ElementsAreArray(ArrayFloatNear({23, 23, 23, 57, 57, 57})));
}
} // namespace
} // namespace tflite