blob: c863b36805eee0a619a3a65a8de279968f6df105 [file] [log] [blame]
/**
* Copyright (c) 2016-present, Facebook, Inc.
*
* 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 <memory>
#include <vector>
#include <gtest/gtest.h>
#include "caffe2/core/blob.h"
#include "caffe2/core/context.h"
#include "caffe2/core/tensor.h"
#include "caffe2/proto/caffe2.pb.h"
#include "caffe2/utils/conversions.h"
#include "caffe2/utils/math.h"
namespace caffe2 {
TEST(MathTest, GemmNoTransNoTrans) {
DeviceOption option;
CPUContext cpu_context(option);
TensorCPU X(std::vector<int>{5, 10});
TensorCPU W(std::vector<int>{10, 6});
TensorCPU Y(std::vector<int>{5, 6});
EXPECT_EQ(X.size(), 50);
EXPECT_EQ(W.size(), 60);
math::Set<float, CPUContext>(X.size(), 1, X.mutable_data<float>(), &cpu_context);
math::Set<float, CPUContext>(W.size(), 1, W.mutable_data<float>(), &cpu_context);
EXPECT_EQ(Y.size(), 30);
for (int i = 0; i < X.size(); ++i) {
CHECK_EQ(X.data<float>()[i], 1);
}
for (int i = 0; i < W.size(); ++i) {
CHECK_EQ(W.data<float>()[i], 1);
}
const float kOne = 1.0;
const float kPointFive = 0.5;
const float kZero = 0.0;
math::Gemm<float, CPUContext>(CblasNoTrans, CblasNoTrans, 5, 6, 10, kOne,
X.data<float>(), W.data<float>(), kZero, Y.mutable_data<float>(),
&cpu_context);
EXPECT_EQ(Y.size(), 30);
for (int i = 0; i < Y.size(); ++i) {
CHECK_EQ(Y.data<float>()[i], 10) << i;
}
// Test Accumulate
math::Gemm<float, CPUContext>(CblasNoTrans, CblasNoTrans, 5, 6, 10, kOne,
X.data<float>(), W.data<float>(), kPointFive,
Y.mutable_data<float>(), &cpu_context);
EXPECT_EQ(Y.size(), 30);
for (int i = 0; i < Y.size(); ++i) {
CHECK_EQ(Y.data<float>()[i], 15) << i;
}
// Test Accumulate
math::Gemm<float, CPUContext>(CblasNoTrans, CblasNoTrans, 5, 6, 10,
kPointFive,
X.data<float>(), W.data<float>(), kOne, Y.mutable_data<float>(),
&cpu_context);
EXPECT_EQ(Y.size(), 30);
for (int i = 0; i < Y.size(); ++i) {
CHECK_EQ(Y.data<float>()[i], 20) << i;
}
}
TEST(MathTest, GemmNoTransTrans) {
DeviceOption option;
CPUContext cpu_context(option);
TensorCPU X(std::vector<int>{5, 10});
TensorCPU W(std::vector<int>{6, 10});
TensorCPU Y(std::vector<int>{5, 6});
EXPECT_EQ(X.size(), 50);
EXPECT_EQ(W.size(), 60);
math::Set<float, CPUContext>(X.size(), 1, X.mutable_data<float>(), &cpu_context);
math::Set<float, CPUContext>(W.size(), 1, W.mutable_data<float>(), &cpu_context);
EXPECT_EQ(Y.size(), 30);
for (int i = 0; i < X.size(); ++i) {
CHECK_EQ(X.data<float>()[i], 1);
}
for (int i = 0; i < W.size(); ++i) {
CHECK_EQ(W.data<float>()[i], 1);
}
const float kOne = 1.0;
const float kPointFive = 0.5;
const float kZero = 0.0;
math::Gemm<float, CPUContext>(CblasNoTrans, CblasTrans, 5, 6, 10, kOne,
X.data<float>(), W.data<float>(), kZero, Y.mutable_data<float>(),
&cpu_context);
EXPECT_EQ(Y.size(), 30);
for (int i = 0; i < Y.size(); ++i) {
CHECK_EQ(Y.data<float>()[i], 10) << i;
}
// Test Accumulate
math::Gemm<float, CPUContext>(CblasNoTrans, CblasTrans, 5, 6, 10, kOne,
X.data<float>(), W.data<float>(), kPointFive,
Y.mutable_data<float>(), &cpu_context);
EXPECT_EQ(Y.size(), 30);
for (int i = 0; i < Y.size(); ++i) {
CHECK_EQ(Y.data<float>()[i], 15) << i;
}
math::Gemm<float, CPUContext>(CblasNoTrans, CblasTrans, 5, 6, 10, kPointFive,
X.data<float>(), W.data<float>(), kOne, Y.mutable_data<float>(),
&cpu_context);
EXPECT_EQ(Y.size(), 30);
for (int i = 0; i < Y.size(); ++i) {
CHECK_EQ(Y.data<float>()[i], 20) << i;
}
}
namespace {
class GemmBatchedTest
: public testing::TestWithParam<testing::tuple<bool, bool>> {
protected:
void SetUp() override {
cpu_context_ = make_unique<CPUContext>(option_);
X_.Resize(std::vector<TIndex>{3, 5, 10});
W_.Resize(std::vector<TIndex>{3, 6, 10});
Y_.Resize(std::vector<TIndex>{3, 5, 6});
math::Set<float, CPUContext>(
X_.size(), 1, X_.mutable_data<float>(), cpu_context_.get());
math::Set<float, CPUContext>(
W_.size(), 1, W_.mutable_data<float>(), cpu_context_.get());
trans_X_ = std::get<0>(GetParam());
trans_W_ = std::get<1>(GetParam());
}
void RunGemmBatched(const float alpha, const float beta) {
math::GemmBatched(
trans_X_ ? CblasTrans : CblasNoTrans,
trans_W_ ? CblasTrans : CblasNoTrans,
3,
5,
6,
10,
alpha,
X_.template data<float>(),
W_.template data<float>(),
beta,
Y_.template mutable_data<float>(),
cpu_context_.get());
}
void VerifyOutput(const float value) const {
for (int i = 0; i < Y_.size(); ++i) {
EXPECT_FLOAT_EQ(value, Y_.template data<float>()[i]);
}
}
DeviceOption option_;
std::unique_ptr<CPUContext> cpu_context_;
TensorCPU X_;
TensorCPU W_;
TensorCPU Y_;
bool trans_X_;
bool trans_W_;
};
TEST_P(GemmBatchedTest, GemmBatchedFloatTest) {
RunGemmBatched(1.0f, 0.0f);
VerifyOutput(10.0f);
RunGemmBatched(1.0f, 0.5f);
VerifyOutput(15.0f);
RunGemmBatched(0.5f, 1.0f);
VerifyOutput(20.0f);
}
INSTANTIATE_TEST_CASE_P(
GemmBatchedTrans,
GemmBatchedTest,
testing::Combine(testing::Bool(), testing::Bool()));
} // namespace
TEST(MathTest, GemvNoTrans) {
DeviceOption option;
CPUContext cpu_context(option);
TensorCPU A(std::vector<int>{5, 10});
TensorCPU X(std::vector<int>{10});
TensorCPU Y(std::vector<int>{5});
EXPECT_EQ(A.size(), 50);
EXPECT_EQ(X.size(), 10);
math::Set<float, CPUContext>(A.size(), 1, A.mutable_data<float>(), &cpu_context);
math::Set<float, CPUContext>(X.size(), 1, X.mutable_data<float>(), &cpu_context);
EXPECT_EQ(Y.size(), 5);
for (int i = 0; i < A.size(); ++i) {
CHECK_EQ(A.data<float>()[i], 1);
}
for (int i = 0; i < X.size(); ++i) {
CHECK_EQ(X.data<float>()[i], 1);
}
const float kOne = 1.0;
const float kPointFive = 0.5;
const float kZero = 0.0;
math::Gemv<float, CPUContext>(CblasNoTrans, 5, 10, kOne, A.data<float>(), X.data<float>(),
kZero, Y.mutable_data<float>(), &cpu_context);
for (int i = 0; i < Y.size(); ++i) {
CHECK_EQ(Y.data<float>()[i], 10) << i;
}
// Test Accumulate
math::Gemv<float, CPUContext>(CblasNoTrans, 5, 10, kOne, A.data<float>(), X.data<float>(),
kPointFive, Y.mutable_data<float>(), &cpu_context);
for (int i = 0; i < Y.size(); ++i) {
CHECK_EQ(Y.data<float>()[i], 15) << i;
}
// Test Accumulate
math::Gemv<float, CPUContext>(CblasNoTrans, 5, 10, kPointFive, A.data<float>(),
X.data<float>(), kOne, Y.mutable_data<float>(),
&cpu_context);
for (int i = 0; i < Y.size(); ++i) {
CHECK_EQ(Y.data<float>()[i], 20) << i;
}
}
TEST(MathTest, GemvTrans) {
DeviceOption option;
CPUContext cpu_context(option);
TensorCPU A(std::vector<int>{6, 10});
TensorCPU X(std::vector<int>{6});
TensorCPU Y(std::vector<int>{10});
EXPECT_EQ(A.size(), 60);
EXPECT_EQ(X.size(), 6);
math::Set<float, CPUContext>(A.size(), 1, A.mutable_data<float>(), &cpu_context);
math::Set<float, CPUContext>(X.size(), 1, X.mutable_data<float>(), &cpu_context);
EXPECT_EQ(Y.size(), 10);
for (int i = 0; i < A.size(); ++i) {
CHECK_EQ(A.data<float>()[i], 1);
}
for (int i = 0; i < X.size(); ++i) {
CHECK_EQ(X.data<float>()[i], 1);
}
const float kOne = 1.0;
const float kPointFive = 0.5;
const float kZero = 0.0;
math::Gemv<float, CPUContext>(CblasTrans, 6, 10, kOne, A.data<float>(), X.data<float>(),
kZero, Y.mutable_data<float>(), &cpu_context);
for (int i = 0; i < Y.size(); ++i) {
CHECK_EQ(Y.data<float>()[i], 6) << i;
}
// Test Accumulate
math::Gemv<float, CPUContext>(CblasTrans, 6, 10, kOne, A.data<float>(), X.data<float>(),
kPointFive, Y.mutable_data<float>(), &cpu_context);
for (int i = 0; i < Y.size(); ++i) {
CHECK_EQ(Y.data<float>()[i], 9) << i;
}
// Test Accumulate
math::Gemv<float, CPUContext>(CblasTrans, 6, 10, kPointFive, A.data<float>(),
X.data<float>(), kOne, Y.mutable_data<float>(),
&cpu_context);
for (int i = 0; i < Y.size(); ++i) {
CHECK_EQ(Y.data<float>()[i], 12) << i;
}
}
using convert::cpu_half2float;
using convert::cpu_float2half_rn;
TEST(MathTest, FloatToHalfConversion) {
float a = 1.0f;
float b = 1.75f;
float c = 128.125f;
float converted_a = cpu_half2float(cpu_float2half_rn(a));
float converted_b = cpu_half2float(cpu_float2half_rn(b));
float converted_c = cpu_half2float(cpu_float2half_rn(c));
CHECK_EQ(a, converted_a);
CHECK_EQ(b, converted_b);
CHECK_EQ(c, converted_c);
}
TEST(MathTest, TranposeTest) {
DeviceOption option;
CPUContext cpu_context(option);
{
// Test for 1D transpose.
const std::vector<int> x_dims = {3};
const std::vector<int> y_dims = {3};
const std::vector<int> axes = {0};
TensorCPU X(x_dims);
TensorCPU Y(y_dims);
for (int i = 0; i < 3; ++i) {
X.mutable_data<float>()[i] = static_cast<float>(i + 1);
}
math::Transpose<float, CPUContext>(
1,
x_dims.data(),
y_dims.data(),
axes.data(),
3,
X.data<float>(),
Y.mutable_data<float>(),
&cpu_context);
for (int i = 0; i < 3; ++i) {
EXPECT_FLOAT_EQ(static_cast<float>(i + 1), Y.data<float>()[i]);
}
}
{
// Test for 2D transpose.
const std::vector<int> x_dims = {2, 3};
const std::vector<int> y_dims = {3, 2};
const std::vector<int> axes = {1, 0};
TensorCPU X(x_dims);
TensorCPU Y(y_dims);
for (int i = 0; i < 6; ++i) {
X.mutable_data<float>()[i] = static_cast<float>(i + 1);
}
math::Transpose<float, CPUContext>(
2,
x_dims.data(),
y_dims.data(),
axes.data(),
6,
X.data<float>(),
Y.mutable_data<float>(),
&cpu_context);
const std::vector<float> expected_output = {
1.0f, 4.0f, 2.0f, 5.0f, 3.0f, 6.0f};
for (int i = 0; i < 6; ++i) {
EXPECT_FLOAT_EQ(expected_output[i], Y.data<float>()[i]);
}
}
{
// Test for 3D transpose.
const std::vector<int> x_dims = {2, 2, 2};
const std::vector<int> y_dims = {2, 2, 2};
const std::vector<int> axes1 = {1, 2, 0};
TensorCPU X(x_dims);
TensorCPU Y(y_dims);
for (int i = 0; i < 8; ++i) {
X.mutable_data<float>()[i] = static_cast<float>(i + 1);
}
math::Transpose<float, CPUContext>(
3,
x_dims.data(),
y_dims.data(),
axes1.data(),
8,
X.data<float>(),
Y.mutable_data<float>(),
&cpu_context);
const std::vector<float> expected_output1 = {
1.0f, 5.0f, 2.0f, 6.0f, 3.0f, 7.0f, 4.0f, 8.0f};
for (int i = 0; i < 8; ++i) {
EXPECT_FLOAT_EQ(expected_output1[i], Y.data<float>()[i]);
}
const std::vector<int> axes2 = {1, 0, 2};
math::Set<float, CPUContext>(
Y.size(), 0.0f, Y.mutable_data<float>(), &cpu_context);
math::Transpose<float, CPUContext>(
3,
x_dims.data(),
y_dims.data(),
axes2.data(),
8,
X.data<float>(),
Y.mutable_data<float>(),
&cpu_context);
const std::vector<float> expected_output2 = {
1.0f, 2.0f, 5.0f, 6.0f, 3.0f, 4.0f, 7.0f, 8.0f};
for (int i = 0; i < 8; ++i) {
EXPECT_FLOAT_EQ(expected_output2[i], Y.data<float>()[i]);
}
}
}
} // namespace caffe2