Files
TheRock/rocm-libraries/projects/hipdnn/test_sdk/tests/utilities/TestCpuFpReferenceReduction.cpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

432 lines
14 KiB
C++

// Copyright © Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#include <gtest/gtest.h>
#include <hipdnn_data_sdk/types.hpp>
#include <hipdnn_data_sdk/utilities/ShapeUtilities.hpp>
#include <hipdnn_data_sdk/utilities/Tensor.hpp>
#include <hipdnn_flatbuffers_sdk/data_objects/reduction_attributes_generated.h>
#include <hipdnn_test_sdk/utilities/CpuFpReferenceReduction.hpp>
#include <hipdnn_test_sdk/utilities/detail/CpuFpReferenceUtilities.hpp>
#include <cmath>
#include <vector>
using namespace hipdnn_test_sdk::utilities;
using namespace hipdnn_flatbuffers_sdk::data_objects;
using namespace hipdnn_data_sdk::utilities;
using namespace hipdnn_data_sdk::types;
using hipdnn_test_sdk::detail::safeTestTypeCast;
namespace
{
template <typename Type>
Tensor<Type> createTensor(const std::vector<int64_t>& dims)
{
return Tensor<Type>(dims, generateStrides(dims));
}
template <typename Type>
void fillSequential(Tensor<Type>& tensor, float start = 1.0f)
{
const auto count = static_cast<int>(tensor.elementCount());
for(int i = 0; i < count; ++i)
{
tensor.memory().hostData()[i] = safeTestTypeCast<Type>(start + static_cast<float>(i));
}
}
template <typename Type>
void fillValues(Tensor<Type>& tensor, const std::vector<float>& values)
{
ASSERT_EQ(static_cast<size_t>(tensor.elementCount()), values.size());
for(size_t i = 0; i < values.size(); ++i)
{
tensor.memory().hostData()[i] = safeTestTypeCast<Type>(values[i]);
}
}
template <typename Type>
void expectTensorValues(const Tensor<Type>& tensor,
const std::vector<float>& expected,
float tolerance = 0.0f)
{
ASSERT_EQ(static_cast<size_t>(tensor.elementCount()), expected.size());
const auto* data = tensor.memory().hostData();
for(size_t idx = 0; idx < expected.size(); ++idx)
{
if(tolerance == 0.0f)
{
EXPECT_EQ(data[idx], static_cast<Type>(expected[idx]))
<< "Mismatch at flat index " << idx;
}
else
{
EXPECT_NEAR(static_cast<float>(data[idx]), expected[idx], tolerance)
<< "Mismatch at flat index " << idx;
}
}
}
} // namespace
/* ============================= Validation tests ============================= */
class TestCpuFpReferenceReduction : public ::testing::Test
{
};
TEST_F(TestCpuFpReferenceReduction, ValidateRankMismatch)
{
auto x = createTensor<float>({2, 3, 4});
auto y = createTensor<float>({2, 3});
EXPECT_THROW((CpuFpReferenceReduction::reduce<float, float, float>(x, y, ReductionMode::ADD)),
std::invalid_argument);
}
TEST_F(TestCpuFpReferenceReduction, ValidateInvalidYDim)
{
auto x = createTensor<float>({2, 3, 4});
auto y = createTensor<float>({2, 2, 4}); // dim 1: 2 != 3 and != 1
EXPECT_THROW((CpuFpReferenceReduction::reduce<float, float, float>(x, y, ReductionMode::ADD)),
std::invalid_argument);
}
TEST_F(TestCpuFpReferenceReduction, ValidateNoReducedDim)
{
auto x = createTensor<float>({2, 3, 4});
auto y = createTensor<float>({2, 3, 4}); // no dim reduced
EXPECT_THROW((CpuFpReferenceReduction::reduce<float, float, float>(x, y, ReductionMode::ADD)),
std::invalid_argument);
}
TEST_F(TestCpuFpReferenceReduction, NotSetModeThrows)
{
auto x = createTensor<float>({2, 3});
auto y = createTensor<float>({2, 1});
fillSequential(x);
EXPECT_THROW(
(CpuFpReferenceReduction::reduce<float, float, float>(x, y, ReductionMode::NOT_SET)),
std::invalid_argument);
}
/* ============================= Mode tests ============================= */
// All mode tests use X = [[1,2,3],[4,5,6]] shape (2,3), reduce dim 1 -> Y shape (2,1)
TEST_F(TestCpuFpReferenceReduction, Add)
{
auto x = createTensor<float>({2, 3});
auto y = createTensor<float>({2, 1});
fillSequential(x); // [1,2,3, 4,5,6]
CpuFpReferenceReduction::reduce<float, float, float>(x, y, ReductionMode::ADD);
// Y[0,0] = 1+2+3 = 6, Y[1,0] = 4+5+6 = 15
expectTensorValues(y, {6.0f, 15.0f});
}
TEST_F(TestCpuFpReferenceReduction, Mul)
{
auto x = createTensor<float>({2, 3});
auto y = createTensor<float>({2, 1});
fillSequential(x);
CpuFpReferenceReduction::reduce<float, float, float>(x, y, ReductionMode::MUL);
// Y[0,0] = 1*2*3 = 6, Y[1,0] = 4*5*6 = 120
expectTensorValues(y, {6.0f, 120.0f});
}
TEST_F(TestCpuFpReferenceReduction, Min)
{
auto x = createTensor<float>({2, 3});
auto y = createTensor<float>({2, 1});
fillSequential(x);
CpuFpReferenceReduction::reduce<float, float, float>(x, y, ReductionMode::MIN_OP);
// Y[0,0] = min(1,2,3) = 1, Y[1,0] = min(4,5,6) = 4
expectTensorValues(y, {1.0f, 4.0f});
}
TEST_F(TestCpuFpReferenceReduction, Max)
{
auto x = createTensor<float>({2, 3});
auto y = createTensor<float>({2, 1});
fillSequential(x);
CpuFpReferenceReduction::reduce<float, float, float>(x, y, ReductionMode::MAX_OP);
// Y[0,0] = max(1,2,3) = 3, Y[1,0] = max(4,5,6) = 6
expectTensorValues(y, {3.0f, 6.0f});
}
TEST_F(TestCpuFpReferenceReduction, Avg)
{
auto x = createTensor<float>({2, 3});
auto y = createTensor<float>({2, 1});
fillSequential(x);
CpuFpReferenceReduction::reduce<float, float, float>(x, y, ReductionMode::AVG);
// Y[0,0] = (1+2+3)/3 = 2, Y[1,0] = (4+5+6)/3 = 5
expectTensorValues(y, {2.0f, 5.0f});
}
TEST_F(TestCpuFpReferenceReduction, Amax)
{
auto x = createTensor<float>({2, 3});
auto y = createTensor<float>({2, 1});
fillValues(x, {-3.0f, 1.0f, 2.0f, -5.0f, 4.0f, 0.0f});
CpuFpReferenceReduction::reduce<float, float, float>(x, y, ReductionMode::AMAX);
// Y[0,0] = max(|-3|,|1|,|2|) = 3, Y[1,0] = max(|-5|,|4|,|0|) = 5
expectTensorValues(y, {3.0f, 5.0f});
}
TEST_F(TestCpuFpReferenceReduction, Norm1)
{
auto x = createTensor<float>({2, 3});
auto y = createTensor<float>({2, 1});
fillValues(x, {-1.0f, 2.0f, -3.0f, 4.0f, -5.0f, 6.0f});
CpuFpReferenceReduction::reduce<float, float, float>(x, y, ReductionMode::NORM1);
// Y[0,0] = |-1|+|2|+|-3| = 6, Y[1,0] = |4|+|-5|+|6| = 15
expectTensorValues(y, {6.0f, 15.0f});
}
TEST_F(TestCpuFpReferenceReduction, Norm2)
{
auto x = createTensor<float>({2, 3});
auto y = createTensor<float>({2, 1});
fillSequential(x);
CpuFpReferenceReduction::reduce<float, float, float>(x, y, ReductionMode::NORM2);
// Y[0,0] = sqrt(1+4+9) = sqrt(14), Y[1,0] = sqrt(16+25+36) = sqrt(77)
expectTensorValues(y, {std::sqrt(14.0f), std::sqrt(77.0f)}, 1e-5f);
}
TEST_F(TestCpuFpReferenceReduction, MulNoZeros)
{
auto x = createTensor<float>({2, 3});
auto y = createTensor<float>({2, 1});
fillValues(x, {2.0f, 0.0f, 3.0f, 0.0f, 5.0f, 4.0f});
CpuFpReferenceReduction::reduce<float, float, float>(x, y, ReductionMode::MUL_NO_ZEROS);
// Y[0,0] = 2*3 = 6 (skip 0), Y[1,0] = 5*4 = 20 (skip 0)
expectTensorValues(y, {6.0f, 20.0f});
}
TEST_F(TestCpuFpReferenceReduction, MinWithNegativeValues)
{
auto x = createTensor<float>({2, 3});
auto y = createTensor<float>({2, 1});
fillValues(x, {-3.0f, 1.0f, -7.0f, 4.0f, -2.0f, 5.0f});
CpuFpReferenceReduction::reduce<float, float, float>(x, y, ReductionMode::MIN_OP);
// Y[0,0] = min(-3, 1, -7) = -7, Y[1,0] = min(4, -2, 5) = -2
expectTensorValues(y, {-7.0f, -2.0f});
}
TEST_F(TestCpuFpReferenceReduction, MaxWithNegativeValues)
{
auto x = createTensor<float>({2, 3});
auto y = createTensor<float>({2, 1});
fillValues(x, {-3.0f, -1.0f, -7.0f, -4.0f, -2.0f, -5.0f});
CpuFpReferenceReduction::reduce<float, float, float>(x, y, ReductionMode::MAX_OP);
// Y[0,0] = max(-3, -1, -7) = -1, Y[1,0] = max(-4, -2, -5) = -2
expectTensorValues(y, {-1.0f, -2.0f});
}
/* ============================= Multi-dim reduction ============================= */
TEST_F(TestCpuFpReferenceReduction, AddReduceMultipleDims)
{
// X shape (2, 3, 4), Y shape (1, 3, 1) — reduce dims 0 and 2
auto x = createTensor<float>({2, 3, 4});
auto y = createTensor<float>({1, 3, 1});
fillSequential(x, 0.0f); // [0, 1, 2, ..., 23]
CpuFpReferenceReduction::reduce<float, float, float>(x, y, ReductionMode::ADD);
// Y[0,0,0] = sum(X[:,0,:]) = (0+1+2+3)+(12+13+14+15) = 60
// Y[0,1,0] = sum(X[:,1,:]) = (4+5+6+7)+(16+17+18+19) = 92
// Y[0,2,0] = sum(X[:,2,:]) = (8+9+10+11)+(20+21+22+23) = 124
expectTensorValues(y, {60.0f, 92.0f, 124.0f});
}
TEST_F(TestCpuFpReferenceReduction, AddReduceAllDims)
{
// X shape (2, 3), Y shape (1, 1) — reduce all dims
auto x = createTensor<float>({2, 3});
auto y = createTensor<float>({1, 1});
fillSequential(x); // [1, 2, 3, 4, 5, 6]
CpuFpReferenceReduction::reduce<float, float, float>(x, y, ReductionMode::ADD);
// Y[0,0] = 1+2+3+4+5+6 = 21
expectTensorValues(y, {21.0f});
}
TEST_F(TestCpuFpReferenceReduction, MaxReduceDim0)
{
// X shape (2, 3), Y shape (1, 3) — reduce dim 0
auto x = createTensor<float>({2, 3});
auto y = createTensor<float>({1, 3});
fillSequential(x); // [1,2,3, 4,5,6]
CpuFpReferenceReduction::reduce<float, float, float>(x, y, ReductionMode::MAX_OP);
// Y[0,0] = max(1,4) = 4, Y[0,1] = max(2,5) = 5, Y[0,2] = max(3,6) = 6
expectTensorValues(y, {4.0f, 5.0f, 6.0f});
}
TEST_F(TestCpuFpReferenceReduction, AddReduceSingleInnerAxis)
{
// X shape (2,3,4,8), Y shape (2,3,1,8) — reduce dim 2 only
//
// X = [[[[ 0, 1, 2, 3, 4, 5, 6, 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, 37, 38, 39],
// [ 40, 41, 42, 43, 44, 45, 46, 47],
// [ 48, 49, 50, 51, 52, 53, 54, 55],
// [ 56, 57, 58, 59, 60, 61, 62, 63]],
//
// [[ 64, 65, 66, 67, 68, 69, 70, 71],
// [ 72, 73, 74, 75, 76, 77, 78, 79],
// [ 80, 81, 82, 83, 84, 85, 86, 87],
// [ 88, 89, 90, 91, 92, 93, 94, 95]]],
//
//
// [[[ 96, 97, 98, 99, 100, 101, 102, 103],
// [104, 105, 106, 107, 108, 109, 110, 111],
// [112, 113, 114, 115, 116, 117, 118, 119],
// [120, 121, 122, 123, 124, 125, 126, 127]],
//
// [[128, 129, 130, 131, 132, 133, 134, 135],
// [136, 137, 138, 139, 140, 141, 142, 143],
// [144, 145, 146, 147, 148, 149, 150, 151],
// [152, 153, 154, 155, 156, 157, 158, 159]],
//
// [[160, 161, 162, 163, 164, 165, 166, 167],
// [168, 169, 170, 171, 172, 173, 174, 175],
// [176, 177, 178, 179, 180, 181, 182, 183],
// [184, 185, 186, 187, 188, 189, 190, 191]]]])
//
// Y = [[[[ 48, 52, 56, 60, 64, 68, 72, 76]],
//
// [[176, 180, 184, 188, 192, 196, 200, 204]],
//
// [[304, 308, 312, 316, 320, 324, 328, 332]]],
//
//
// [[[432, 436, 440, 444, 448, 452, 456, 460]],
//
// [[560, 564, 568, 572, 576, 580, 584, 588]],
//
// [[688, 692, 696, 700, 704, 708, 712, 716]]]])
auto x = createTensor<float>({2, 3, 4, 8});
auto y = createTensor<float>({2, 3, 1, 8});
// X[n,c,h,w] = n*96 + c*32 + h*8 + w
fillSequential(x, 0.0f);
CpuFpReferenceReduction::reduce<float, float, float>(x, y, ReductionMode::ADD);
// Y[n,c,0,w] = sum over h: X[n,c,h,w] for h in {0,1,2,3}
// Y[0,0,0,0] = X[0,0,0,0]+X[0,0,1,0]+X[0,0,2,0]+X[0,0,3,0] = 0+8+16+24 = 48
// clang-format off
expectTensorValues(y, {
48, 52, 56, 60, 64, 68, 72, 76,
176, 180, 184, 188, 192, 196, 200, 204,
304, 308, 312, 316, 320, 324, 328, 332,
432, 436, 440, 444, 448, 452, 456, 460,
560, 564, 568, 572, 576, 580, 584, 588,
688, 692, 696, 700, 704, 708, 712, 716,
});
// clang-format on
}
/* ============================= Multi-type tests ============================= */
template <typename T1, typename T2, typename T3>
struct ReductionTypeTuple
{
using XDataType = T1;
using YDataType = T2;
using ComputeDataType = T3;
};
using ReductionTypes = ::testing::Types<ReductionTypeTuple<float, float, float>,
ReductionTypeTuple<half, half, float>,
ReductionTypeTuple<bfloat16, bfloat16, float>,
ReductionTypeTuple<half, float, float>,
ReductionTypeTuple<bfloat16, float, float>>;
template <class T>
class CpuFpReferenceReductionTyped : public ::testing::Test
{
};
TYPED_TEST_SUITE(CpuFpReferenceReductionTyped, ReductionTypes, );
TYPED_TEST(CpuFpReferenceReductionTyped, AddReduceDim1)
{
using X = typename TypeParam::XDataType;
using Y = typename TypeParam::YDataType;
using C = typename TypeParam::ComputeDataType;
auto x = createTensor<X>({2, 3});
auto y = createTensor<Y>({2, 1});
fillSequential(x);
CpuFpReferenceReduction::reduce<X, Y, C>(x, y, ReductionMode::ADD);
expectTensorValues(y, {6.0f, 15.0f});
}
TYPED_TEST(CpuFpReferenceReductionTyped, MinReduceDim1)
{
using X = typename TypeParam::XDataType;
using Y = typename TypeParam::YDataType;
using C = typename TypeParam::ComputeDataType;
auto x = createTensor<X>({2, 3});
auto y = createTensor<Y>({2, 1});
fillSequential(x);
CpuFpReferenceReduction::reduce<X, Y, C>(x, y, ReductionMode::MIN_OP);
expectTensorValues(y, {1.0f, 4.0f});
}
TYPED_TEST(CpuFpReferenceReductionTyped, AddReduceMultipleDims)
{
using X = typename TypeParam::XDataType;
using Y = typename TypeParam::YDataType;
using C = typename TypeParam::ComputeDataType;
auto x = createTensor<X>({2, 3, 4});
auto y = createTensor<Y>({1, 3, 1});
fillSequential(x, 0.0f);
CpuFpReferenceReduction::reduce<X, Y, C>(x, y, ReductionMode::ADD);
expectTensorValues(y, {60.0f, 92.0f, 124.0f});
}