#include // for uint32_t & uint64_t #include #include // for std::fabs #include "gtest/gtest.h" #include "caffe/blob.hpp" #include "caffe/common.hpp" #include "caffe/filler.hpp" #include "caffe/util/math_functions.hpp" #include "caffe/test/test_caffe_main.hpp" namespace caffe { template class MathFunctionsTest : public MultiDeviceTest { typedef typename TypeParam::Dtype Dtype; protected: MathFunctionsTest() : blob_bottom_(new Blob()), blob_top_(new Blob()) { } virtual void SetUp() { Caffe::set_random_seed(1701); this->blob_bottom_->Reshape(11, 17, 19, 23); this->blob_top_->Reshape(11, 17, 19, 23); // fill the values FillerParameter filler_param; GaussianFiller filler(filler_param); filler.Fill(this->blob_bottom_); filler.Fill(this->blob_top_); } virtual ~MathFunctionsTest() { delete blob_bottom_; delete blob_top_; } // http://en.wikipedia.org/wiki/Hamming_distance int ReferenceHammingDistance(const int n, const Dtype* x, const Dtype* y) { int dist = 0; uint64_t val; for (int i = 0; i < n; ++i) { if (sizeof(Dtype) == 8) { val = static_cast(x[i]) ^ static_cast(y[i]); } else if (sizeof(Dtype) == 4) { val = static_cast(x[i]) ^ static_cast(y[i]); } else { LOG(FATAL) << "Unrecognized Dtype size: " << sizeof(Dtype); } // Count the number of set bits while (val) { ++dist; val &= val - 1; } } return dist; } Blob* const blob_bottom_; Blob* const blob_top_; }; template class CPUMathFunctionsTest : public MathFunctionsTest > { }; TYPED_TEST_CASE(CPUMathFunctionsTest, TestDtypes); TYPED_TEST(CPUMathFunctionsTest, TestNothing) { // The first test case of a test suite takes the longest time // due to the set up overhead. } TYPED_TEST(CPUMathFunctionsTest, TestHammingDistance) { int n = this->blob_bottom_->count(); const TypeParam* x = this->blob_bottom_->cpu_data(); const TypeParam* y = this->blob_top_->cpu_data(); EXPECT_EQ(this->ReferenceHammingDistance(n, x, y), caffe_cpu_hamming_distance(n, x, y)); } TYPED_TEST(CPUMathFunctionsTest, TestAsum) { int n = this->blob_bottom_->count(); const TypeParam* x = this->blob_bottom_->cpu_data(); TypeParam std_asum = 0; for (int i = 0; i < n; ++i) { std_asum += std::fabs(x[i]); } TypeParam cpu_asum = caffe_cpu_asum(n, x); EXPECT_LT((cpu_asum - std_asum) / std_asum, 1e-2); } TYPED_TEST(CPUMathFunctionsTest, TestSign) { int n = this->blob_bottom_->count(); const TypeParam* x = this->blob_bottom_->cpu_data(); caffe_cpu_sign(n, x, this->blob_bottom_->mutable_cpu_diff()); const TypeParam* signs = this->blob_bottom_->cpu_diff(); for (int i = 0; i < n; ++i) { EXPECT_EQ(signs[i], x[i] > 0 ? 1 : (x[i] < 0 ? -1 : 0)); } } TYPED_TEST(CPUMathFunctionsTest, TestSgnbit) { int n = this->blob_bottom_->count(); const TypeParam* x = this->blob_bottom_->cpu_data(); caffe_cpu_sgnbit(n, x, this->blob_bottom_->mutable_cpu_diff()); const TypeParam* signbits = this->blob_bottom_->cpu_diff(); for (int i = 0; i < n; ++i) { EXPECT_EQ(signbits[i], x[i] < 0 ? 1 : 0); } } TYPED_TEST(CPUMathFunctionsTest, TestFabs) { int n = this->blob_bottom_->count(); const TypeParam* x = this->blob_bottom_->cpu_data(); caffe_abs(n, x, this->blob_bottom_->mutable_cpu_diff()); const TypeParam* abs_val = this->blob_bottom_->cpu_diff(); for (int i = 0; i < n; ++i) { EXPECT_EQ(abs_val[i], x[i] > 0 ? x[i] : -x[i]); } } TYPED_TEST(CPUMathFunctionsTest, TestScale) { int n = this->blob_bottom_->count(); TypeParam alpha = this->blob_bottom_->cpu_diff()[caffe_rng_rand() % this->blob_bottom_->count()]; caffe_cpu_scale(n, alpha, this->blob_bottom_->cpu_data(), this->blob_bottom_->mutable_cpu_diff()); const TypeParam* scaled = this->blob_bottom_->cpu_diff(); const TypeParam* x = this->blob_bottom_->cpu_data(); for (int i = 0; i < n; ++i) { EXPECT_EQ(scaled[i], x[i] * alpha); } } TYPED_TEST(CPUMathFunctionsTest, TestCopy) { const int n = this->blob_bottom_->count(); const TypeParam* bottom_data = this->blob_bottom_->cpu_data(); TypeParam* top_data = this->blob_top_->mutable_cpu_data(); caffe_copy(n, bottom_data, top_data); for (int i = 0; i < n; ++i) { EXPECT_EQ(bottom_data[i], top_data[i]); } } #ifndef CPU_ONLY template class GPUMathFunctionsTest : public MathFunctionsTest > { }; TYPED_TEST_CASE(GPUMathFunctionsTest, TestDtypes); // TODO: Fix caffe_gpu_hamming_distance and re-enable this test. TYPED_TEST(GPUMathFunctionsTest, DISABLED_TestHammingDistance) { int n = this->blob_bottom_->count(); const TypeParam* x = this->blob_bottom_->cpu_data(); const TypeParam* y = this->blob_top_->cpu_data(); int reference_distance = this->ReferenceHammingDistance(n, x, y); x = this->blob_bottom_->gpu_data(); y = this->blob_top_->gpu_data(); int computed_distance = caffe_gpu_hamming_distance(n, x, y); EXPECT_EQ(reference_distance, computed_distance); } TYPED_TEST(GPUMathFunctionsTest, TestAsum) { int n = this->blob_bottom_->count(); const TypeParam* x = this->blob_bottom_->cpu_data(); TypeParam std_asum = 0; for (int i = 0; i < n; ++i) { std_asum += std::fabs(x[i]); } TypeParam gpu_asum; caffe_gpu_asum(n, this->blob_bottom_->gpu_data(), &gpu_asum); EXPECT_LT((gpu_asum - std_asum) / std_asum, 1e-2); } TYPED_TEST(GPUMathFunctionsTest, TestSign) { int n = this->blob_bottom_->count(); caffe_gpu_sign(n, this->blob_bottom_->gpu_data(), this->blob_bottom_->mutable_gpu_diff()); const TypeParam* signs = this->blob_bottom_->cpu_diff(); const TypeParam* x = this->blob_bottom_->cpu_data(); for (int i = 0; i < n; ++i) { EXPECT_EQ(signs[i], x[i] > 0 ? 1 : (x[i] < 0 ? -1 : 0)); } } TYPED_TEST(GPUMathFunctionsTest, TestSgnbit) { int n = this->blob_bottom_->count(); caffe_gpu_sgnbit(n, this->blob_bottom_->gpu_data(), this->blob_bottom_->mutable_gpu_diff()); const TypeParam* signbits = this->blob_bottom_->cpu_diff(); const TypeParam* x = this->blob_bottom_->cpu_data(); for (int i = 0; i < n; ++i) { EXPECT_EQ(signbits[i], x[i] < 0 ? 1 : 0); } } TYPED_TEST(GPUMathFunctionsTest, TestFabs) { int n = this->blob_bottom_->count(); caffe_gpu_abs(n, this->blob_bottom_->gpu_data(), this->blob_bottom_->mutable_gpu_diff()); const TypeParam* abs_val = this->blob_bottom_->cpu_diff(); const TypeParam* x = this->blob_bottom_->cpu_data(); for (int i = 0; i < n; ++i) { EXPECT_EQ(abs_val[i], x[i] > 0 ? x[i] : -x[i]); } } TYPED_TEST(GPUMathFunctionsTest, TestScale) { int n = this->blob_bottom_->count(); TypeParam alpha = this->blob_bottom_->cpu_diff()[caffe_rng_rand() % this->blob_bottom_->count()]; caffe_gpu_scale(n, alpha, this->blob_bottom_->gpu_data(), this->blob_bottom_->mutable_gpu_diff()); const TypeParam* scaled = this->blob_bottom_->cpu_diff(); const TypeParam* x = this->blob_bottom_->cpu_data(); for (int i = 0; i < n; ++i) { EXPECT_EQ(scaled[i], x[i] * alpha); } } TYPED_TEST(GPUMathFunctionsTest, TestCopy) { const int n = this->blob_bottom_->count(); const TypeParam* bottom_data = this->blob_bottom_->gpu_data(); TypeParam* top_data = this->blob_top_->mutable_gpu_data(); caffe_copy(n, bottom_data, top_data); bottom_data = this->blob_bottom_->cpu_data(); top_data = this->blob_top_->mutable_cpu_data(); for (int i = 0; i < n; ++i) { EXPECT_EQ(bottom_data[i], top_data[i]); } } #endif } // namespace caffe