Files
TheRock/rocm-libraries/projects/hipdnn/backend/tests/descriptors/TestBatchnormOperationFromNode.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

1134 lines
53 KiB
C++

// Copyright © Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#include "HipdnnOperationType.h"
#include "TestMacros.hpp"
#include "descriptors/BatchnormOperationDescriptor.hpp"
#include "descriptors/NodeFactory.hpp"
#include "descriptors/ScopedDescriptor.hpp"
#include "descriptors/TensorDescriptor.hpp"
#include "hipdnn_backend.h"
#include <gtest/gtest.h>
#include <hipdnn_flatbuffers_sdk/data_objects/batchnorm_attributes_generated.h>
#include <hipdnn_flatbuffers_sdk/data_objects/graph_generated.h>
#include <hipdnn_flatbuffers_sdk/data_objects/pointwise_attributes_generated.h>
#include <hipdnn_flatbuffers_sdk/data_objects/tensor_attributes_generated.h>
#include <hipdnn_test_sdk/constants/BatchnormConstants.hpp>
#include <array>
#include <memory>
#include <optional>
#include <unordered_map>
#include <vector>
using namespace hipdnn_backend;
using namespace hipdnn_flatbuffers_sdk::data_objects;
using namespace hipdnn_tests::constants;
// =============================================================================
// BatchnormOperationDescriptor::fromNode() Tests
// =============================================================================
class TestBatchnormOperationFromNode : public ::testing::Test
{
protected:
std::unordered_map<int64_t, std::shared_ptr<TensorDescriptor>> _tensorMap;
void SetUp() override
{
TensorAttributesT xAttrs;
xAttrs.uid = K_BATCHNORM_TENSOR_X_UID;
xAttrs.data_type = DataType::FLOAT;
xAttrs.dims = {K_BATCHNORM_TENSOR_X_DIMS.begin(), K_BATCHNORM_TENSOR_X_DIMS.end()};
xAttrs.strides = {K_BATCHNORM_TENSOR_X_STRIDES.begin(), K_BATCHNORM_TENSOR_X_STRIDES.end()};
_tensorMap[K_BATCHNORM_TENSOR_X_UID] = TensorDescriptor::fromFlatBuffer(xAttrs);
TensorAttributesT scaleAttrs;
scaleAttrs.uid = K_BATCHNORM_TENSOR_SCALE_UID;
scaleAttrs.data_type = DataType::FLOAT;
scaleAttrs.dims
= {K_BATCHNORM_TENSOR_SCALE_DIMS.begin(), K_BATCHNORM_TENSOR_SCALE_DIMS.end()};
scaleAttrs.strides
= {K_BATCHNORM_TENSOR_SCALE_STRIDES.begin(), K_BATCHNORM_TENSOR_SCALE_STRIDES.end()};
_tensorMap[K_BATCHNORM_TENSOR_SCALE_UID] = TensorDescriptor::fromFlatBuffer(scaleAttrs);
TensorAttributesT biasAttrs;
biasAttrs.uid = K_BATCHNORM_TENSOR_BIAS_UID;
biasAttrs.data_type = DataType::FLOAT;
biasAttrs.dims = {K_BATCHNORM_TENSOR_BIAS_DIMS.begin(), K_BATCHNORM_TENSOR_BIAS_DIMS.end()};
biasAttrs.strides
= {K_BATCHNORM_TENSOR_BIAS_STRIDES.begin(), K_BATCHNORM_TENSOR_BIAS_STRIDES.end()};
_tensorMap[K_BATCHNORM_TENSOR_BIAS_UID] = TensorDescriptor::fromFlatBuffer(biasAttrs);
TensorAttributesT epsilonAttrs;
epsilonAttrs.uid = K_BATCHNORM_TENSOR_EPSILON_UID;
epsilonAttrs.data_type = DataType::FLOAT;
epsilonAttrs.dims
= {K_BATCHNORM_TENSOR_EPSILON_DIMS.begin(), K_BATCHNORM_TENSOR_EPSILON_DIMS.end()};
epsilonAttrs.strides = {K_BATCHNORM_TENSOR_EPSILON_STRIDES.begin(),
K_BATCHNORM_TENSOR_EPSILON_STRIDES.end()};
_tensorMap[K_BATCHNORM_TENSOR_EPSILON_UID] = TensorDescriptor::fromFlatBuffer(epsilonAttrs);
TensorAttributesT yAttrs;
yAttrs.uid = K_BATCHNORM_TENSOR_Y_UID;
yAttrs.data_type = DataType::FLOAT;
yAttrs.dims = {K_BATCHNORM_TENSOR_Y_DIMS.begin(), K_BATCHNORM_TENSOR_Y_DIMS.end()};
yAttrs.strides = {K_BATCHNORM_TENSOR_Y_STRIDES.begin(), K_BATCHNORM_TENSOR_Y_STRIDES.end()};
_tensorMap[K_BATCHNORM_TENSOR_Y_UID] = TensorDescriptor::fromFlatBuffer(yAttrs);
TensorAttributesT prevRunningMeanAttrs;
prevRunningMeanAttrs.uid = K_BATCHNORM_TENSOR_PREV_RUNNING_MEAN_UID;
prevRunningMeanAttrs.data_type = DataType::FLOAT;
prevRunningMeanAttrs.dims = {K_BATCHNORM_TENSOR_PREV_RUNNING_MEAN_DIMS.begin(),
K_BATCHNORM_TENSOR_PREV_RUNNING_MEAN_DIMS.end()};
prevRunningMeanAttrs.strides = {K_BATCHNORM_TENSOR_PREV_RUNNING_MEAN_STRIDES.begin(),
K_BATCHNORM_TENSOR_PREV_RUNNING_MEAN_STRIDES.end()};
_tensorMap[K_BATCHNORM_TENSOR_PREV_RUNNING_MEAN_UID]
= TensorDescriptor::fromFlatBuffer(prevRunningMeanAttrs);
TensorAttributesT prevRunningVarianceAttrs;
prevRunningVarianceAttrs.uid = K_BATCHNORM_TENSOR_PREV_RUNNING_VARIANCE_UID;
prevRunningVarianceAttrs.data_type = DataType::FLOAT;
prevRunningVarianceAttrs.dims = {K_BATCHNORM_TENSOR_PREV_RUNNING_VARIANCE_DIMS.begin(),
K_BATCHNORM_TENSOR_PREV_RUNNING_VARIANCE_DIMS.end()};
prevRunningVarianceAttrs.strides
= {K_BATCHNORM_TENSOR_PREV_RUNNING_VARIANCE_STRIDES.begin(),
K_BATCHNORM_TENSOR_PREV_RUNNING_VARIANCE_STRIDES.end()};
_tensorMap[K_BATCHNORM_TENSOR_PREV_RUNNING_VARIANCE_UID]
= TensorDescriptor::fromFlatBuffer(prevRunningVarianceAttrs);
TensorAttributesT momentumAttrs;
momentumAttrs.uid = K_BATCHNORM_TENSOR_MOMENTUM_UID;
momentumAttrs.data_type = DataType::FLOAT;
momentumAttrs.dims
= {K_BATCHNORM_TENSOR_MOMENTUM_DIMS.begin(), K_BATCHNORM_TENSOR_MOMENTUM_DIMS.end()};
momentumAttrs.strides = {K_BATCHNORM_TENSOR_MOMENTUM_STRIDES.begin(),
K_BATCHNORM_TENSOR_MOMENTUM_STRIDES.end()};
_tensorMap[K_BATCHNORM_TENSOR_MOMENTUM_UID]
= TensorDescriptor::fromFlatBuffer(momentumAttrs);
TensorAttributesT meanAttrs;
meanAttrs.uid = K_BATCHNORM_TENSOR_MEAN_UID;
meanAttrs.data_type = DataType::FLOAT;
meanAttrs.dims = {K_BATCHNORM_TENSOR_MEAN_DIMS.begin(), K_BATCHNORM_TENSOR_MEAN_DIMS.end()};
meanAttrs.strides
= {K_BATCHNORM_TENSOR_MEAN_STRIDES.begin(), K_BATCHNORM_TENSOR_MEAN_STRIDES.end()};
_tensorMap[K_BATCHNORM_TENSOR_MEAN_UID] = TensorDescriptor::fromFlatBuffer(meanAttrs);
TensorAttributesT invVarianceAttrs;
invVarianceAttrs.uid = K_BATCHNORM_TENSOR_INV_VARIANCE_UID;
invVarianceAttrs.data_type = DataType::FLOAT;
invVarianceAttrs.dims = {K_BATCHNORM_TENSOR_INV_VARIANCE_DIMS.begin(),
K_BATCHNORM_TENSOR_INV_VARIANCE_DIMS.end()};
invVarianceAttrs.strides = {K_BATCHNORM_TENSOR_INV_VARIANCE_STRIDES.begin(),
K_BATCHNORM_TENSOR_INV_VARIANCE_STRIDES.end()};
_tensorMap[K_BATCHNORM_TENSOR_INV_VARIANCE_UID]
= TensorDescriptor::fromFlatBuffer(invVarianceAttrs);
TensorAttributesT nextRunningMeanAttrs;
nextRunningMeanAttrs.uid = K_BATCHNORM_TENSOR_NEXT_RUNNING_MEAN_UID;
nextRunningMeanAttrs.data_type = DataType::FLOAT;
nextRunningMeanAttrs.dims = {K_BATCHNORM_TENSOR_NEXT_RUNNING_MEAN_DIMS.begin(),
K_BATCHNORM_TENSOR_NEXT_RUNNING_MEAN_DIMS.end()};
nextRunningMeanAttrs.strides = {K_BATCHNORM_TENSOR_NEXT_RUNNING_MEAN_STRIDES.begin(),
K_BATCHNORM_TENSOR_NEXT_RUNNING_MEAN_STRIDES.end()};
_tensorMap[K_BATCHNORM_TENSOR_NEXT_RUNNING_MEAN_UID]
= TensorDescriptor::fromFlatBuffer(nextRunningMeanAttrs);
TensorAttributesT nextRunningVarianceAttrs;
nextRunningVarianceAttrs.uid = K_BATCHNORM_TENSOR_NEXT_RUNNING_VARIANCE_UID;
nextRunningVarianceAttrs.data_type = DataType::FLOAT;
nextRunningVarianceAttrs.dims = {K_BATCHNORM_TENSOR_NEXT_RUNNING_VARIANCE_DIMS.begin(),
K_BATCHNORM_TENSOR_NEXT_RUNNING_VARIANCE_DIMS.end()};
nextRunningVarianceAttrs.strides
= {K_BATCHNORM_TENSOR_NEXT_RUNNING_VARIANCE_STRIDES.begin(),
K_BATCHNORM_TENSOR_NEXT_RUNNING_VARIANCE_STRIDES.end()};
_tensorMap[K_BATCHNORM_TENSOR_NEXT_RUNNING_VARIANCE_UID]
= TensorDescriptor::fromFlatBuffer(nextRunningVarianceAttrs);
TensorAttributesT peerStatsAttrs0;
peerStatsAttrs0.uid = K_BATCHNORM_TENSOR_PEER_STAT_0_UID;
peerStatsAttrs0.data_type = DataType::FLOAT;
peerStatsAttrs0.dims
= {K_BATCHNORM_TENSOR_PEER_STAT_DIMS.begin(), K_BATCHNORM_TENSOR_PEER_STAT_DIMS.end()};
peerStatsAttrs0.strides = {K_BATCHNORM_TENSOR_PEER_STAT_STRIDES.begin(),
K_BATCHNORM_TENSOR_PEER_STAT_STRIDES.end()};
_tensorMap[K_BATCHNORM_TENSOR_PEER_STAT_0_UID]
= TensorDescriptor::fromFlatBuffer(peerStatsAttrs0);
TensorAttributesT peerStatsAttrs1;
peerStatsAttrs1.uid = K_BATCHNORM_TENSOR_PEER_STAT_1_UID;
peerStatsAttrs1.data_type = DataType::FLOAT;
peerStatsAttrs1.dims
= {K_BATCHNORM_TENSOR_PEER_STAT_DIMS.begin(), K_BATCHNORM_TENSOR_PEER_STAT_DIMS.end()};
peerStatsAttrs1.strides = {K_BATCHNORM_TENSOR_PEER_STAT_STRIDES.begin(),
K_BATCHNORM_TENSOR_PEER_STAT_STRIDES.end()};
_tensorMap[K_BATCHNORM_TENSOR_PEER_STAT_1_UID]
= TensorDescriptor::fromFlatBuffer(peerStatsAttrs1);
}
static hipdnn_flatbuffers_sdk::data_objects::BatchnormAttributesT createStandardBatchnormAttrs()
{
hipdnn_flatbuffers_sdk::data_objects::BatchnormAttributesT attrs;
attrs.x_tensor_uid = K_BATCHNORM_TENSOR_X_UID;
attrs.scale_tensor_uid = K_BATCHNORM_TENSOR_SCALE_UID;
attrs.bias_tensor_uid = K_BATCHNORM_TENSOR_BIAS_UID;
attrs.epsilon_tensor_uid = K_BATCHNORM_TENSOR_EPSILON_UID;
attrs.y_tensor_uid = K_BATCHNORM_TENSOR_Y_UID;
attrs.prev_running_mean_tensor_uid = K_BATCHNORM_TENSOR_PREV_RUNNING_MEAN_UID;
attrs.prev_running_variance_tensor_uid = K_BATCHNORM_TENSOR_PREV_RUNNING_VARIANCE_UID;
attrs.momentum_tensor_uid = K_BATCHNORM_TENSOR_MOMENTUM_UID;
attrs.mean_tensor_uid = K_BATCHNORM_TENSOR_MEAN_UID;
attrs.inv_variance_tensor_uid = K_BATCHNORM_TENSOR_INV_VARIANCE_UID;
attrs.next_running_mean_tensor_uid = K_BATCHNORM_TENSOR_NEXT_RUNNING_MEAN_UID;
attrs.next_running_variance_tensor_uid = K_BATCHNORM_TENSOR_NEXT_RUNNING_VARIANCE_UID;
attrs.peer_stats_tensor_uid
= {K_BATCHNORM_TENSOR_PEER_STAT_0_UID, K_BATCHNORM_TENSOR_PEER_STAT_1_UID};
return attrs;
}
static NodeT createStandardNode(DataType computeType = DataType::FLOAT)
{
NodeT node;
node.compute_data_type = computeType;
node.attributes.Set(createStandardBatchnormAttrs());
return node;
}
// Verifies that a packed tensor descriptor (retrieved via getAttribute) has the
// expected UID, data_type, dimensions, and strides.
static void verifyTensorDescriptor(hipdnnBackendDescriptor_t tensorDesc,
int64_t expectedUid,
hipdnnDataType_t expectedDataType,
const std::vector<int64_t>& expectedDims,
const std::vector<int64_t>& expectedStrides)
{
// Verify UID
int64_t uid = 0;
int64_t uidCount = 0;
tensorDesc->getAttribute(
HIPDNN_ATTR_TENSOR_UNIQUE_ID, HIPDNN_TYPE_INT64, 1, &uidCount, &uid);
EXPECT_EQ(uid, expectedUid);
// Verify data type
hipdnnDataType_t dataType = {};
int64_t dtCount = 0;
tensorDesc->getAttribute(
HIPDNN_ATTR_TENSOR_DATA_TYPE, HIPDNN_TYPE_DATA_TYPE, 1, &dtCount, &dataType);
EXPECT_EQ(dataType, expectedDataType);
// Verify dimensions
int64_t dimCount = 0;
tensorDesc->getAttribute(
HIPDNN_ATTR_TENSOR_DIMENSIONS, HIPDNN_TYPE_INT64, 0, &dimCount, nullptr);
ASSERT_EQ(dimCount, static_cast<int64_t>(expectedDims.size()));
std::vector<int64_t> dims(static_cast<size_t>(dimCount));
int64_t actualDimCount = 0;
tensorDesc->getAttribute(HIPDNN_ATTR_TENSOR_DIMENSIONS,
HIPDNN_TYPE_INT64,
dimCount,
&actualDimCount,
dims.data());
EXPECT_EQ(dims, expectedDims);
// Verify strides
int64_t strideCount = 0;
tensorDesc->getAttribute(
HIPDNN_ATTR_TENSOR_STRIDES, HIPDNN_TYPE_INT64, 0, &strideCount, nullptr);
ASSERT_EQ(strideCount, static_cast<int64_t>(expectedStrides.size()));
std::vector<int64_t> strides(static_cast<size_t>(strideCount));
int64_t actualStrideCount = 0;
tensorDesc->getAttribute(HIPDNN_ATTR_TENSOR_STRIDES,
HIPDNN_TYPE_INT64,
strideCount,
&actualStrideCount,
strides.data());
EXPECT_EQ(strides, expectedStrides);
}
};
TEST_F(TestBatchnormOperationFromNode, CreatesValidFinalizedDescriptor)
{
auto node = createStandardNode();
auto desc = BatchnormOperationDescriptor::fromNode(node, _tensorMap);
ASSERT_NE(desc, nullptr);
ASSERT_TRUE(desc->isFinalized());
ASSERT_EQ(desc->getType(), HIPDNN_BACKEND_OPERATION_BATCHNORM_DESCRIPTOR_EXT);
EXPECT_EQ(desc->getData().x_tensor_uid, K_BATCHNORM_TENSOR_X_UID);
}
TEST_F(TestBatchnormOperationFromNode, NodeFactoryDelegatesCorrectly)
{
auto node = createStandardNode();
// NodeFactory::createOperationFromNode delegates to fromNode internally.
// Verify the delegation produces a valid, correctly-typed descriptor.
auto graphOp = NodeFactory::createOperationFromNode(node, _tensorMap);
ASSERT_NE(graphOp, nullptr);
// Verify the factory dispatched to the correct operation type, then static_cast.
// Cannot use dynamic_pointer_cast: backend tests compile with -fno-rtti.
auto* op = graphOp->asGraphOperation();
ASSERT_NE(op, nullptr);
auto rebuiltNode = op->buildNode();
ASSERT_EQ(rebuiltNode->attributes.type, NodeAttributes::BatchnormAttributes);
auto desc = std::static_pointer_cast<BatchnormOperationDescriptor>(graphOp);
ASSERT_TRUE(desc->isFinalized());
// Verify all attributes are correctly populated via the delegated path
EXPECT_EQ(desc->getData().x_tensor_uid, K_BATCHNORM_TENSOR_X_UID);
EXPECT_EQ(desc->getData().scale_tensor_uid, K_BATCHNORM_TENSOR_SCALE_UID);
EXPECT_EQ(desc->getData().bias_tensor_uid, K_BATCHNORM_TENSOR_BIAS_UID);
EXPECT_EQ(desc->getData().epsilon_tensor_uid, K_BATCHNORM_TENSOR_EPSILON_UID);
EXPECT_EQ(desc->getData().y_tensor_uid, K_BATCHNORM_TENSOR_Y_UID);
EXPECT_EQ(desc->getData().prev_running_mean_tensor_uid,
K_BATCHNORM_TENSOR_PREV_RUNNING_MEAN_UID);
EXPECT_EQ(desc->getData().prev_running_variance_tensor_uid,
K_BATCHNORM_TENSOR_PREV_RUNNING_VARIANCE_UID);
EXPECT_EQ(desc->getData().momentum_tensor_uid, K_BATCHNORM_TENSOR_MOMENTUM_UID);
EXPECT_EQ(desc->getData().mean_tensor_uid, K_BATCHNORM_TENSOR_MEAN_UID);
EXPECT_EQ(desc->getData().inv_variance_tensor_uid, K_BATCHNORM_TENSOR_INV_VARIANCE_UID);
EXPECT_EQ(desc->getData().next_running_mean_tensor_uid,
K_BATCHNORM_TENSOR_NEXT_RUNNING_MEAN_UID);
EXPECT_EQ(desc->getData().next_running_variance_tensor_uid,
K_BATCHNORM_TENSOR_NEXT_RUNNING_VARIANCE_UID);
EXPECT_EQ(desc->getComputeDataType(), DataType::FLOAT);
EXPECT_EQ(desc->getXDesc()->getData().uid, K_BATCHNORM_TENSOR_X_UID);
EXPECT_EQ(desc->getScaleDesc()->getData().uid, K_BATCHNORM_TENSOR_SCALE_UID);
EXPECT_EQ(desc->getBiasDesc()->getData().uid, K_BATCHNORM_TENSOR_BIAS_UID);
EXPECT_EQ(desc->getEpsilonDesc()->getData().uid, K_BATCHNORM_TENSOR_EPSILON_UID);
EXPECT_EQ(desc->getYDesc()->getData().uid, K_BATCHNORM_TENSOR_Y_UID);
}
TEST_F(TestBatchnormOperationFromNode, PreservesComputeDataType)
{
auto node = createStandardNode(DataType::HALF);
auto desc = BatchnormOperationDescriptor::fromNode(node, _tensorMap);
ASSERT_EQ(desc->getComputeDataType(), DataType::HALF);
}
TEST_F(TestBatchnormOperationFromNode, SetsTensorReferences)
{
auto node = createStandardNode();
auto desc = BatchnormOperationDescriptor::fromNode(node, _tensorMap);
ASSERT_NE(desc->getXDesc(), nullptr);
EXPECT_EQ(desc->getXDesc()->getData().uid, K_BATCHNORM_TENSOR_X_UID);
ASSERT_NE(desc->getScaleDesc(), nullptr);
EXPECT_EQ(desc->getScaleDesc()->getData().uid, K_BATCHNORM_TENSOR_SCALE_UID);
ASSERT_NE(desc->getBiasDesc(), nullptr);
EXPECT_EQ(desc->getBiasDesc()->getData().uid, K_BATCHNORM_TENSOR_BIAS_UID);
ASSERT_NE(desc->getEpsilonDesc(), nullptr);
EXPECT_EQ(desc->getEpsilonDesc()->getData().uid, K_BATCHNORM_TENSOR_EPSILON_UID);
ASSERT_NE(desc->getYDesc(), nullptr);
EXPECT_EQ(desc->getYDesc()->getData().uid, K_BATCHNORM_TENSOR_Y_UID);
}
TEST_F(TestBatchnormOperationFromNode, TensorReferencesMatchTensorMap)
{
auto node = createStandardNode();
auto desc = BatchnormOperationDescriptor::fromNode(node, _tensorMap);
EXPECT_EQ(desc->getXDesc(), _tensorMap[K_BATCHNORM_TENSOR_X_UID]);
EXPECT_EQ(desc->getScaleDesc(), _tensorMap[K_BATCHNORM_TENSOR_SCALE_UID]);
EXPECT_EQ(desc->getBiasDesc(), _tensorMap[K_BATCHNORM_TENSOR_BIAS_UID]);
EXPECT_EQ(desc->getEpsilonDesc(), _tensorMap[K_BATCHNORM_TENSOR_EPSILON_UID]);
EXPECT_EQ(desc->getYDesc(), _tensorMap[K_BATCHNORM_TENSOR_Y_UID]);
}
TEST_F(TestBatchnormOperationFromNode, FailsWithMissingXTensor)
{
_tensorMap.erase(K_BATCHNORM_TENSOR_X_UID);
auto node = createStandardNode();
ASSERT_THROW_HIPDNN_STATUS(BatchnormOperationDescriptor::fromNode(node, _tensorMap),
HIPDNN_STATUS_INTERNAL_ERROR);
}
TEST_F(TestBatchnormOperationFromNode, FailsWithMissingScaleTensor)
{
_tensorMap.erase(K_BATCHNORM_TENSOR_SCALE_UID);
auto node = createStandardNode();
ASSERT_THROW_HIPDNN_STATUS(BatchnormOperationDescriptor::fromNode(node, _tensorMap),
HIPDNN_STATUS_INTERNAL_ERROR);
}
TEST_F(TestBatchnormOperationFromNode, FailsWithMissingBiasTensor)
{
_tensorMap.erase(K_BATCHNORM_TENSOR_BIAS_UID);
auto node = createStandardNode();
ASSERT_THROW_HIPDNN_STATUS(BatchnormOperationDescriptor::fromNode(node, _tensorMap),
HIPDNN_STATUS_INTERNAL_ERROR);
}
TEST_F(TestBatchnormOperationFromNode, FailsWithMissingEpsilonTensor)
{
_tensorMap.erase(K_BATCHNORM_TENSOR_EPSILON_UID);
auto node = createStandardNode();
ASSERT_THROW_HIPDNN_STATUS(BatchnormOperationDescriptor::fromNode(node, _tensorMap),
HIPDNN_STATUS_INTERNAL_ERROR);
}
TEST_F(TestBatchnormOperationFromNode, FailsWithMissingYTensor)
{
_tensorMap.erase(K_BATCHNORM_TENSOR_Y_UID);
auto node = createStandardNode();
ASSERT_THROW_HIPDNN_STATUS(BatchnormOperationDescriptor::fromNode(node, _tensorMap),
HIPDNN_STATUS_INTERNAL_ERROR);
}
TEST_F(TestBatchnormOperationFromNode, SucceedsWithOnlyRequiredTensors)
{
auto attrs = createStandardBatchnormAttrs();
attrs.prev_running_mean_tensor_uid = flatbuffers::nullopt;
attrs.prev_running_variance_tensor_uid = flatbuffers::nullopt;
attrs.momentum_tensor_uid = flatbuffers::nullopt;
attrs.mean_tensor_uid = flatbuffers::nullopt;
attrs.inv_variance_tensor_uid = flatbuffers::nullopt;
attrs.next_running_mean_tensor_uid = flatbuffers::nullopt;
attrs.next_running_variance_tensor_uid = flatbuffers::nullopt;
NodeT node;
node.compute_data_type = DataType::FLOAT;
node.attributes.Set(attrs);
auto desc = BatchnormOperationDescriptor::fromNode(node, _tensorMap);
ASSERT_NE(desc, nullptr);
ASSERT_TRUE(desc->isFinalized());
// Required tensor getters are non-null
EXPECT_NE(desc->getXDesc(), nullptr);
EXPECT_NE(desc->getScaleDesc(), nullptr);
EXPECT_NE(desc->getBiasDesc(), nullptr);
EXPECT_NE(desc->getEpsilonDesc(), nullptr);
EXPECT_NE(desc->getYDesc(), nullptr);
// Optional tensor getters are null
EXPECT_EQ(desc->getPrevRunningMeanDesc(), nullptr);
EXPECT_EQ(desc->getPrevRunningVarianceDesc(), nullptr);
EXPECT_EQ(desc->getMomentumDesc(), nullptr);
EXPECT_EQ(desc->getMeanDesc(), nullptr);
EXPECT_EQ(desc->getInvVarianceDesc(), nullptr);
EXPECT_EQ(desc->getNextRunningMeanDesc(), nullptr);
EXPECT_EQ(desc->getNextRunningVarianceDesc(), nullptr);
}
TEST_F(TestBatchnormOperationFromNode, FailsWhenOptionalPrevRunningMeanUidSetButTensorMissing)
{
_tensorMap.erase(K_BATCHNORM_TENSOR_PREV_RUNNING_MEAN_UID);
auto node = createStandardNode();
ASSERT_THROW_HIPDNN_STATUS(BatchnormOperationDescriptor::fromNode(node, _tensorMap),
HIPDNN_STATUS_INTERNAL_ERROR);
}
TEST_F(TestBatchnormOperationFromNode, FailsWhenOptionalPrevRunningVarianceUidSetButTensorMissing)
{
_tensorMap.erase(K_BATCHNORM_TENSOR_PREV_RUNNING_VARIANCE_UID);
auto node = createStandardNode();
ASSERT_THROW_HIPDNN_STATUS(BatchnormOperationDescriptor::fromNode(node, _tensorMap),
HIPDNN_STATUS_INTERNAL_ERROR);
}
TEST_F(TestBatchnormOperationFromNode, FailsWhenOptionalMomentumUidSetButTensorMissing)
{
_tensorMap.erase(K_BATCHNORM_TENSOR_MOMENTUM_UID);
auto node = createStandardNode();
ASSERT_THROW_HIPDNN_STATUS(BatchnormOperationDescriptor::fromNode(node, _tensorMap),
HIPDNN_STATUS_INTERNAL_ERROR);
}
TEST_F(TestBatchnormOperationFromNode, FailsWhenOptionalMeanUidSetButTensorMissing)
{
_tensorMap.erase(K_BATCHNORM_TENSOR_MEAN_UID);
auto node = createStandardNode();
ASSERT_THROW_HIPDNN_STATUS(BatchnormOperationDescriptor::fromNode(node, _tensorMap),
HIPDNN_STATUS_INTERNAL_ERROR);
}
TEST_F(TestBatchnormOperationFromNode, FailsWhenOptionalInvVarianceUidSetButTensorMissing)
{
_tensorMap.erase(K_BATCHNORM_TENSOR_INV_VARIANCE_UID);
auto node = createStandardNode();
ASSERT_THROW_HIPDNN_STATUS(BatchnormOperationDescriptor::fromNode(node, _tensorMap),
HIPDNN_STATUS_INTERNAL_ERROR);
}
TEST_F(TestBatchnormOperationFromNode, FailsWhenOptionalNextRunningMeanUidSetButTensorMissing)
{
_tensorMap.erase(K_BATCHNORM_TENSOR_NEXT_RUNNING_MEAN_UID);
auto node = createStandardNode();
ASSERT_THROW_HIPDNN_STATUS(BatchnormOperationDescriptor::fromNode(node, _tensorMap),
HIPDNN_STATUS_INTERNAL_ERROR);
}
TEST_F(TestBatchnormOperationFromNode, FailsWhenOptionalNextRunningVarianceUidSetButTensorMissing)
{
_tensorMap.erase(K_BATCHNORM_TENSOR_NEXT_RUNNING_VARIANCE_UID);
auto node = createStandardNode();
ASSERT_THROW_HIPDNN_STATUS(BatchnormOperationDescriptor::fromNode(node, _tensorMap),
HIPDNN_STATUS_INTERNAL_ERROR);
}
TEST_F(TestBatchnormOperationFromNode, GetTensorDescriptorsReturnsAllTensors)
{
auto node = createStandardNode();
auto desc = BatchnormOperationDescriptor::fromNode(node, _tensorMap);
auto tensors = desc->getTensorDescriptors();
// 5 required + 7 optional + 2 peer_stats = 14 total
ASSERT_EQ(tensors.size(), 14);
EXPECT_EQ(tensors[0]->getData().uid, K_BATCHNORM_TENSOR_X_UID);
EXPECT_EQ(tensors[1]->getData().uid, K_BATCHNORM_TENSOR_SCALE_UID);
EXPECT_EQ(tensors[2]->getData().uid, K_BATCHNORM_TENSOR_BIAS_UID);
EXPECT_EQ(tensors[3]->getData().uid, K_BATCHNORM_TENSOR_EPSILON_UID);
EXPECT_EQ(tensors[4]->getData().uid, K_BATCHNORM_TENSOR_Y_UID);
}
TEST_F(TestBatchnormOperationFromNode, BuildNodeRoundTrip)
{
auto node = createStandardNode();
auto desc = BatchnormOperationDescriptor::fromNode(node, _tensorMap);
auto rebuiltNode = desc->buildNode();
ASSERT_NE(rebuiltNode, nullptr);
ASSERT_EQ(rebuiltNode->compute_data_type, DataType::FLOAT);
ASSERT_EQ(rebuiltNode->attributes.type, NodeAttributes::BatchnormAttributes);
const auto* rebuiltAttrs = rebuiltNode->attributes.AsBatchnormAttributes();
ASSERT_NE(rebuiltAttrs, nullptr);
EXPECT_EQ(rebuiltAttrs->x_tensor_uid, K_BATCHNORM_TENSOR_X_UID);
EXPECT_EQ(rebuiltAttrs->scale_tensor_uid, K_BATCHNORM_TENSOR_SCALE_UID);
EXPECT_EQ(rebuiltAttrs->bias_tensor_uid, K_BATCHNORM_TENSOR_BIAS_UID);
EXPECT_EQ(rebuiltAttrs->epsilon_tensor_uid, K_BATCHNORM_TENSOR_EPSILON_UID);
EXPECT_EQ(rebuiltAttrs->y_tensor_uid, K_BATCHNORM_TENSOR_Y_UID);
EXPECT_EQ(rebuiltAttrs->prev_running_mean_tensor_uid, K_BATCHNORM_TENSOR_PREV_RUNNING_MEAN_UID);
EXPECT_EQ(rebuiltAttrs->prev_running_variance_tensor_uid,
K_BATCHNORM_TENSOR_PREV_RUNNING_VARIANCE_UID);
EXPECT_EQ(rebuiltAttrs->momentum_tensor_uid, K_BATCHNORM_TENSOR_MOMENTUM_UID);
EXPECT_EQ(rebuiltAttrs->mean_tensor_uid, K_BATCHNORM_TENSOR_MEAN_UID);
EXPECT_EQ(rebuiltAttrs->inv_variance_tensor_uid, K_BATCHNORM_TENSOR_INV_VARIANCE_UID);
EXPECT_EQ(rebuiltAttrs->next_running_mean_tensor_uid, K_BATCHNORM_TENSOR_NEXT_RUNNING_MEAN_UID);
EXPECT_EQ(rebuiltAttrs->next_running_variance_tensor_uid,
K_BATCHNORM_TENSOR_NEXT_RUNNING_VARIANCE_UID);
// Verify peer_stats round-trip
ASSERT_EQ(rebuiltAttrs->peer_stats_tensor_uid.size(), 2);
EXPECT_EQ(rebuiltAttrs->peer_stats_tensor_uid[0], K_BATCHNORM_TENSOR_PEER_STAT_0_UID);
EXPECT_EQ(rebuiltAttrs->peer_stats_tensor_uid[1], K_BATCHNORM_TENSOR_PEER_STAT_1_UID);
}
TEST_F(TestBatchnormOperationFromNode, BuildNodeRoundTripWithOnlyRequiredTensors)
{
auto attrs = createStandardBatchnormAttrs();
attrs.prev_running_mean_tensor_uid = flatbuffers::nullopt;
attrs.prev_running_variance_tensor_uid = flatbuffers::nullopt;
attrs.momentum_tensor_uid = flatbuffers::nullopt;
attrs.mean_tensor_uid = flatbuffers::nullopt;
attrs.inv_variance_tensor_uid = flatbuffers::nullopt;
attrs.next_running_mean_tensor_uid = flatbuffers::nullopt;
attrs.next_running_variance_tensor_uid = flatbuffers::nullopt;
attrs.peer_stats_tensor_uid.clear();
NodeT node;
node.compute_data_type = DataType::FLOAT;
node.attributes.Set(attrs);
auto desc = BatchnormOperationDescriptor::fromNode(node, _tensorMap);
auto rebuiltNode = desc->buildNode();
const auto* rebuiltAttrs = rebuiltNode->attributes.AsBatchnormAttributes();
ASSERT_NE(rebuiltAttrs, nullptr);
// Required tensors preserved
EXPECT_EQ(rebuiltAttrs->x_tensor_uid, K_BATCHNORM_TENSOR_X_UID);
EXPECT_EQ(rebuiltAttrs->scale_tensor_uid, K_BATCHNORM_TENSOR_SCALE_UID);
EXPECT_EQ(rebuiltAttrs->bias_tensor_uid, K_BATCHNORM_TENSOR_BIAS_UID);
EXPECT_EQ(rebuiltAttrs->epsilon_tensor_uid, K_BATCHNORM_TENSOR_EPSILON_UID);
EXPECT_EQ(rebuiltAttrs->y_tensor_uid, K_BATCHNORM_TENSOR_Y_UID);
// Optional tensors remain unset
EXPECT_FALSE(rebuiltAttrs->prev_running_mean_tensor_uid.has_value());
EXPECT_FALSE(rebuiltAttrs->prev_running_variance_tensor_uid.has_value());
EXPECT_FALSE(rebuiltAttrs->momentum_tensor_uid.has_value());
EXPECT_FALSE(rebuiltAttrs->mean_tensor_uid.has_value());
EXPECT_FALSE(rebuiltAttrs->inv_variance_tensor_uid.has_value());
EXPECT_FALSE(rebuiltAttrs->next_running_mean_tensor_uid.has_value());
EXPECT_FALSE(rebuiltAttrs->next_running_variance_tensor_uid.has_value());
// Peer stats empty
EXPECT_TRUE(rebuiltAttrs->peer_stats_tensor_uid.empty());
}
TEST_F(TestBatchnormOperationFromNode, NamePreservedFromNode)
{
auto node = createStandardNode();
node.name = "test_batchnorm_1";
auto desc = BatchnormOperationDescriptor::fromNode(node, _tensorMap);
int64_t count = 0;
desc->getAttribute(HIPDNN_ATTR_OPERATION_NAME_EXT, HIPDNN_TYPE_CHAR, 0, &count, nullptr);
ASSERT_EQ(count, static_cast<int64_t>(std::string("test_batchnorm_1").size() + 1));
std::vector<char> buffer(static_cast<size_t>(count));
int64_t actualCount = 0;
desc->getAttribute(
HIPDNN_ATTR_OPERATION_NAME_EXT, HIPDNN_TYPE_CHAR, count, &actualCount, buffer.data());
EXPECT_STREQ(buffer.data(), "test_batchnorm_1");
}
TEST_F(TestBatchnormOperationFromNode, EmptyNamePreservedFromNode)
{
auto node = createStandardNode();
auto desc = BatchnormOperationDescriptor::fromNode(node, _tensorMap);
int64_t count = 0;
desc->getAttribute(HIPDNN_ATTR_OPERATION_NAME_EXT, HIPDNN_TYPE_CHAR, 0, &count, nullptr);
EXPECT_EQ(count, 1);
}
TEST_F(TestBatchnormOperationFromNode, BuildNodePreservesName)
{
auto node = createStandardNode();
node.name = "test_build_name";
auto desc = BatchnormOperationDescriptor::fromNode(node, _tensorMap);
auto rebuiltNode = desc->buildNode();
ASSERT_NE(rebuiltNode, nullptr);
EXPECT_EQ(rebuiltNode->name, "test_build_name");
}
TEST_F(TestBatchnormOperationFromNode, GetAttributeWorksAfterFromNode)
{
auto node = createStandardNode();
auto desc = BatchnormOperationDescriptor::fromNode(node, _tensorMap);
// Verify compute type via getAttribute
hipdnnDataType_t computeType = {};
int64_t dtCount = 0;
desc->getAttribute(
HIPDNN_ATTR_BATCHNORM_COMP_TYPE_EXT, HIPDNN_TYPE_DATA_TYPE, 1, &dtCount, &computeType);
ASSERT_EQ(dtCount, 1);
EXPECT_EQ(computeType, HIPDNN_DATA_FLOAT);
// Verify operation type
hipdnnOperationType_ext_t opType = HIPDNN_OPERATION_TYPE_NOT_SET_EXT;
int64_t opTypeCount = 0;
desc->getAttribute(
HIPDNN_ATTR_OPERATION_TYPE_EXT, HIPDNN_TYPE_OPERATION_TYPE_EXT, 1, &opTypeCount, &opType);
ASSERT_EQ(opTypeCount, 1);
EXPECT_EQ(opType, HIPDNN_OPERATION_TYPE_BATCHNORM_EXT);
// Verify name (empty default from fixture, count==1 for null terminator)
int64_t nameCount = 0;
desc->getAttribute(HIPDNN_ATTR_OPERATION_NAME_EXT, HIPDNN_TYPE_CHAR, 0, &nameCount, nullptr);
EXPECT_EQ(nameCount, 1);
// --- Required tensor attributes ---
// X tensor
hipdnn_backend::ScopedDescriptor xScoped;
int64_t xCount = 0;
desc->getAttribute(HIPDNN_ATTR_OPERATION_BATCHNORM_X_EXT,
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
1,
&xCount,
static_cast<void*>(xScoped.getPtr()));
ASSERT_EQ(xCount, 1);
ASSERT_NE(xScoped.get(), nullptr);
verifyTensorDescriptor(
xScoped.get(),
K_BATCHNORM_TENSOR_X_UID,
HIPDNN_DATA_FLOAT,
{K_BATCHNORM_TENSOR_X_DIMS.begin(), K_BATCHNORM_TENSOR_X_DIMS.end()},
{K_BATCHNORM_TENSOR_X_STRIDES.begin(), K_BATCHNORM_TENSOR_X_STRIDES.end()});
// Scale tensor
hipdnn_backend::ScopedDescriptor scaleScoped;
int64_t scaleCount = 0;
desc->getAttribute(HIPDNN_ATTR_OPERATION_BATCHNORM_SCALE_EXT,
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
1,
&scaleCount,
static_cast<void*>(scaleScoped.getPtr()));
ASSERT_EQ(scaleCount, 1);
ASSERT_NE(scaleScoped.get(), nullptr);
verifyTensorDescriptor(
scaleScoped.get(),
K_BATCHNORM_TENSOR_SCALE_UID,
HIPDNN_DATA_FLOAT,
{K_BATCHNORM_TENSOR_SCALE_DIMS.begin(), K_BATCHNORM_TENSOR_SCALE_DIMS.end()},
{K_BATCHNORM_TENSOR_SCALE_STRIDES.begin(), K_BATCHNORM_TENSOR_SCALE_STRIDES.end()});
// Bias tensor
hipdnn_backend::ScopedDescriptor biasScoped;
int64_t biasCount = 0;
desc->getAttribute(HIPDNN_ATTR_OPERATION_BATCHNORM_BIAS_EXT,
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
1,
&biasCount,
static_cast<void*>(biasScoped.getPtr()));
ASSERT_EQ(biasCount, 1);
ASSERT_NE(biasScoped.get(), nullptr);
verifyTensorDescriptor(
biasScoped.get(),
K_BATCHNORM_TENSOR_BIAS_UID,
HIPDNN_DATA_FLOAT,
{K_BATCHNORM_TENSOR_BIAS_DIMS.begin(), K_BATCHNORM_TENSOR_BIAS_DIMS.end()},
{K_BATCHNORM_TENSOR_BIAS_STRIDES.begin(), K_BATCHNORM_TENSOR_BIAS_STRIDES.end()});
// Epsilon tensor
hipdnn_backend::ScopedDescriptor epsilonScoped;
int64_t epsilonCount = 0;
desc->getAttribute(HIPDNN_ATTR_OPERATION_BATCHNORM_EPSILON_EXT,
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
1,
&epsilonCount,
static_cast<void*>(epsilonScoped.getPtr()));
ASSERT_EQ(epsilonCount, 1);
ASSERT_NE(epsilonScoped.get(), nullptr);
verifyTensorDescriptor(
epsilonScoped.get(),
K_BATCHNORM_TENSOR_EPSILON_UID,
HIPDNN_DATA_FLOAT,
{K_BATCHNORM_TENSOR_EPSILON_DIMS.begin(), K_BATCHNORM_TENSOR_EPSILON_DIMS.end()},
{K_BATCHNORM_TENSOR_EPSILON_STRIDES.begin(), K_BATCHNORM_TENSOR_EPSILON_STRIDES.end()});
// Y tensor
hipdnn_backend::ScopedDescriptor yScoped;
int64_t yCount = 0;
desc->getAttribute(HIPDNN_ATTR_OPERATION_BATCHNORM_Y_EXT,
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
1,
&yCount,
static_cast<void*>(yScoped.getPtr()));
ASSERT_EQ(yCount, 1);
ASSERT_NE(yScoped.get(), nullptr);
verifyTensorDescriptor(
yScoped.get(),
K_BATCHNORM_TENSOR_Y_UID,
HIPDNN_DATA_FLOAT,
{K_BATCHNORM_TENSOR_Y_DIMS.begin(), K_BATCHNORM_TENSOR_Y_DIMS.end()},
{K_BATCHNORM_TENSOR_Y_STRIDES.begin(), K_BATCHNORM_TENSOR_Y_STRIDES.end()});
// --- Optional tensor attributes (all set in standard fixture) ---
// PrevRunningMean tensor
hipdnn_backend::ScopedDescriptor prevRunMeanScoped;
int64_t prevRunMeanCount = 0;
desc->getAttribute(HIPDNN_ATTR_OPERATION_BATCHNORM_PREV_RUNNING_MEAN_EXT,
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
1,
&prevRunMeanCount,
static_cast<void*>(prevRunMeanScoped.getPtr()));
ASSERT_EQ(prevRunMeanCount, 1);
ASSERT_NE(prevRunMeanScoped.get(), nullptr);
verifyTensorDescriptor(prevRunMeanScoped.get(),
K_BATCHNORM_TENSOR_PREV_RUNNING_MEAN_UID,
HIPDNN_DATA_FLOAT,
{K_BATCHNORM_TENSOR_PREV_RUNNING_MEAN_DIMS.begin(),
K_BATCHNORM_TENSOR_PREV_RUNNING_MEAN_DIMS.end()},
{K_BATCHNORM_TENSOR_PREV_RUNNING_MEAN_STRIDES.begin(),
K_BATCHNORM_TENSOR_PREV_RUNNING_MEAN_STRIDES.end()});
// PrevRunningVariance tensor
hipdnn_backend::ScopedDescriptor prevRunVarScoped;
int64_t prevRunVarCount = 0;
desc->getAttribute(HIPDNN_ATTR_OPERATION_BATCHNORM_PREV_RUNNING_VARIANCE_EXT,
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
1,
&prevRunVarCount,
static_cast<void*>(prevRunVarScoped.getPtr()));
ASSERT_EQ(prevRunVarCount, 1);
ASSERT_NE(prevRunVarScoped.get(), nullptr);
verifyTensorDescriptor(prevRunVarScoped.get(),
K_BATCHNORM_TENSOR_PREV_RUNNING_VARIANCE_UID,
HIPDNN_DATA_FLOAT,
{K_BATCHNORM_TENSOR_PREV_RUNNING_VARIANCE_DIMS.begin(),
K_BATCHNORM_TENSOR_PREV_RUNNING_VARIANCE_DIMS.end()},
{K_BATCHNORM_TENSOR_PREV_RUNNING_VARIANCE_STRIDES.begin(),
K_BATCHNORM_TENSOR_PREV_RUNNING_VARIANCE_STRIDES.end()});
// Momentum tensor
hipdnn_backend::ScopedDescriptor momentumScoped;
int64_t momentumCount = 0;
desc->getAttribute(HIPDNN_ATTR_OPERATION_BATCHNORM_MOMENTUM_EXT,
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
1,
&momentumCount,
static_cast<void*>(momentumScoped.getPtr()));
ASSERT_EQ(momentumCount, 1);
ASSERT_NE(momentumScoped.get(), nullptr);
verifyTensorDescriptor(
momentumScoped.get(),
K_BATCHNORM_TENSOR_MOMENTUM_UID,
HIPDNN_DATA_FLOAT,
{K_BATCHNORM_TENSOR_MOMENTUM_DIMS.begin(), K_BATCHNORM_TENSOR_MOMENTUM_DIMS.end()},
{K_BATCHNORM_TENSOR_MOMENTUM_STRIDES.begin(), K_BATCHNORM_TENSOR_MOMENTUM_STRIDES.end()});
// Mean tensor
hipdnn_backend::ScopedDescriptor meanScoped;
int64_t meanCount = 0;
desc->getAttribute(HIPDNN_ATTR_OPERATION_BATCHNORM_MEAN_EXT,
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
1,
&meanCount,
static_cast<void*>(meanScoped.getPtr()));
ASSERT_EQ(meanCount, 1);
ASSERT_NE(meanScoped.get(), nullptr);
verifyTensorDescriptor(
meanScoped.get(),
K_BATCHNORM_TENSOR_MEAN_UID,
HIPDNN_DATA_FLOAT,
{K_BATCHNORM_TENSOR_MEAN_DIMS.begin(), K_BATCHNORM_TENSOR_MEAN_DIMS.end()},
{K_BATCHNORM_TENSOR_MEAN_STRIDES.begin(), K_BATCHNORM_TENSOR_MEAN_STRIDES.end()});
// InvVariance tensor
hipdnn_backend::ScopedDescriptor invVarScoped;
int64_t invVarCount = 0;
desc->getAttribute(HIPDNN_ATTR_OPERATION_BATCHNORM_INV_VARIANCE_EXT,
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
1,
&invVarCount,
static_cast<void*>(invVarScoped.getPtr()));
ASSERT_EQ(invVarCount, 1);
ASSERT_NE(invVarScoped.get(), nullptr);
verifyTensorDescriptor(
invVarScoped.get(),
K_BATCHNORM_TENSOR_INV_VARIANCE_UID,
HIPDNN_DATA_FLOAT,
{K_BATCHNORM_TENSOR_INV_VARIANCE_DIMS.begin(), K_BATCHNORM_TENSOR_INV_VARIANCE_DIMS.end()},
{K_BATCHNORM_TENSOR_INV_VARIANCE_STRIDES.begin(),
K_BATCHNORM_TENSOR_INV_VARIANCE_STRIDES.end()});
// NextRunningMean tensor
hipdnn_backend::ScopedDescriptor nextRunMeanScoped;
int64_t nextRunMeanCount = 0;
desc->getAttribute(HIPDNN_ATTR_OPERATION_BATCHNORM_NEXT_RUNNING_MEAN_EXT,
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
1,
&nextRunMeanCount,
static_cast<void*>(nextRunMeanScoped.getPtr()));
ASSERT_EQ(nextRunMeanCount, 1);
ASSERT_NE(nextRunMeanScoped.get(), nullptr);
verifyTensorDescriptor(nextRunMeanScoped.get(),
K_BATCHNORM_TENSOR_NEXT_RUNNING_MEAN_UID,
HIPDNN_DATA_FLOAT,
{K_BATCHNORM_TENSOR_NEXT_RUNNING_MEAN_DIMS.begin(),
K_BATCHNORM_TENSOR_NEXT_RUNNING_MEAN_DIMS.end()},
{K_BATCHNORM_TENSOR_NEXT_RUNNING_MEAN_STRIDES.begin(),
K_BATCHNORM_TENSOR_NEXT_RUNNING_MEAN_STRIDES.end()});
// NextRunningVariance tensor
hipdnn_backend::ScopedDescriptor nextRunVarScoped;
int64_t nextRunVarCount = 0;
desc->getAttribute(HIPDNN_ATTR_OPERATION_BATCHNORM_NEXT_RUNNING_VARIANCE_EXT,
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
1,
&nextRunVarCount,
static_cast<void*>(nextRunVarScoped.getPtr()));
ASSERT_EQ(nextRunVarCount, 1);
ASSERT_NE(nextRunVarScoped.get(), nullptr);
verifyTensorDescriptor(nextRunVarScoped.get(),
K_BATCHNORM_TENSOR_NEXT_RUNNING_VARIANCE_UID,
HIPDNN_DATA_FLOAT,
{K_BATCHNORM_TENSOR_NEXT_RUNNING_VARIANCE_DIMS.begin(),
K_BATCHNORM_TENSOR_NEXT_RUNNING_VARIANCE_DIMS.end()},
{K_BATCHNORM_TENSOR_NEXT_RUNNING_VARIANCE_STRIDES.begin(),
K_BATCHNORM_TENSOR_NEXT_RUNNING_VARIANCE_STRIDES.end()});
// --- Peer stats tensor array ---
// Query count first
int64_t peerStatsCount = 0;
desc->getAttribute(HIPDNN_ATTR_OPERATION_BATCHNORM_PEER_STATS_EXT,
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
0,
&peerStatsCount,
nullptr);
ASSERT_EQ(peerStatsCount, 2);
// Retrieve both peer_stats descriptors
hipdnn_backend::ScopedDescriptor peerStats0Scoped;
hipdnn_backend::ScopedDescriptor peerStats1Scoped;
std::array<hipdnnBackendDescriptor_t, 2> peerStatsArray = {};
int64_t peerStatsRetrievedCount = 0;
desc->getAttribute(HIPDNN_ATTR_OPERATION_BATCHNORM_PEER_STATS_EXT,
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
2,
&peerStatsRetrievedCount,
static_cast<void*>(peerStatsArray.data()));
ASSERT_EQ(peerStatsRetrievedCount, 2);
// Transfer ownership to ScopedDescriptors
peerStats0Scoped = hipdnn_backend::ScopedDescriptor(peerStatsArray[0]);
peerStats1Scoped = hipdnn_backend::ScopedDescriptor(peerStatsArray[1]);
ASSERT_NE(peerStats0Scoped.get(), nullptr);
ASSERT_NE(peerStats1Scoped.get(), nullptr);
verifyTensorDescriptor(
peerStats0Scoped.get(),
K_BATCHNORM_TENSOR_PEER_STAT_0_UID,
HIPDNN_DATA_FLOAT,
{K_BATCHNORM_TENSOR_PEER_STAT_DIMS.begin(), K_BATCHNORM_TENSOR_PEER_STAT_DIMS.end()},
{K_BATCHNORM_TENSOR_PEER_STAT_STRIDES.begin(), K_BATCHNORM_TENSOR_PEER_STAT_STRIDES.end()});
verifyTensorDescriptor(
peerStats1Scoped.get(),
K_BATCHNORM_TENSOR_PEER_STAT_1_UID,
HIPDNN_DATA_FLOAT,
{K_BATCHNORM_TENSOR_PEER_STAT_DIMS.begin(), K_BATCHNORM_TENSOR_PEER_STAT_DIMS.end()},
{K_BATCHNORM_TENSOR_PEER_STAT_STRIDES.begin(), K_BATCHNORM_TENSOR_PEER_STAT_STRIDES.end()});
}
TEST_F(TestBatchnormOperationFromNode, OperationTypeAttributeReturnsCorrectValue)
{
auto node = createStandardNode();
auto desc = BatchnormOperationDescriptor::fromNode(node, _tensorMap);
hipdnnOperationType_ext_t opType = HIPDNN_OPERATION_TYPE_NOT_SET_EXT;
int64_t opTypeCount = 0;
desc->getAttribute(
HIPDNN_ATTR_OPERATION_TYPE_EXT, HIPDNN_TYPE_OPERATION_TYPE_EXT, 1, &opTypeCount, &opType);
ASSERT_EQ(opTypeCount, 1);
EXPECT_EQ(opType, HIPDNN_OPERATION_TYPE_BATCHNORM_EXT);
}
TEST_F(TestBatchnormOperationFromNode, OptionalTensorReferencesMatchTensorMap)
{
auto node = createStandardNode();
auto desc = BatchnormOperationDescriptor::fromNode(node, _tensorMap);
EXPECT_EQ(desc->getPrevRunningMeanDesc(), _tensorMap[K_BATCHNORM_TENSOR_PREV_RUNNING_MEAN_UID]);
EXPECT_EQ(desc->getPrevRunningVarianceDesc(),
_tensorMap[K_BATCHNORM_TENSOR_PREV_RUNNING_VARIANCE_UID]);
EXPECT_EQ(desc->getMomentumDesc(), _tensorMap[K_BATCHNORM_TENSOR_MOMENTUM_UID]);
EXPECT_EQ(desc->getMeanDesc(), _tensorMap[K_BATCHNORM_TENSOR_MEAN_UID]);
EXPECT_EQ(desc->getInvVarianceDesc(), _tensorMap[K_BATCHNORM_TENSOR_INV_VARIANCE_UID]);
EXPECT_EQ(desc->getNextRunningMeanDesc(), _tensorMap[K_BATCHNORM_TENSOR_NEXT_RUNNING_MEAN_UID]);
EXPECT_EQ(desc->getNextRunningVarianceDesc(),
_tensorMap[K_BATCHNORM_TENSOR_NEXT_RUNNING_VARIANCE_UID]);
}
TEST_F(TestBatchnormOperationFromNode, PeerStatsTensorReferencesPopulated)
{
auto node = createStandardNode();
auto desc = BatchnormOperationDescriptor::fromNode(node, _tensorMap);
auto tensors = desc->getTensorDescriptors();
// 5 required + 2 (mean, inv_var) + 5 (running stats) + 2 peer_stats = 14 total
ASSERT_EQ(tensors.size(), 14);
// Last 2 are peer_stats
EXPECT_EQ(tensors[12]->getData().uid, K_BATCHNORM_TENSOR_PEER_STAT_0_UID);
EXPECT_EQ(tensors[13]->getData().uid, K_BATCHNORM_TENSOR_PEER_STAT_1_UID);
// Verify they are the same shared_ptr instances from tensorMap
EXPECT_EQ(tensors[12], _tensorMap[K_BATCHNORM_TENSOR_PEER_STAT_0_UID]);
EXPECT_EQ(tensors[13], _tensorMap[K_BATCHNORM_TENSOR_PEER_STAT_1_UID]);
}
TEST_F(TestBatchnormOperationFromNode, SetsTensorReferencesWithFullValues)
{
auto node = createStandardNode();
auto desc = BatchnormOperationDescriptor::fromNode(node, _tensorMap);
// Required: X
ASSERT_NE(desc->getXDesc(), nullptr);
EXPECT_EQ(desc->getXDesc()->getData().uid, K_BATCHNORM_TENSOR_X_UID);
EXPECT_EQ(desc->getXDesc()->getData().data_type, DataType::FLOAT);
EXPECT_EQ(
desc->getXDesc()->getData().dims,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_X_DIMS.begin(), K_BATCHNORM_TENSOR_X_DIMS.end()}));
EXPECT_EQ(desc->getXDesc()->getData().strides,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_X_STRIDES.begin(),
K_BATCHNORM_TENSOR_X_STRIDES.end()}));
// Required: Scale
ASSERT_NE(desc->getScaleDesc(), nullptr);
EXPECT_EQ(desc->getScaleDesc()->getData().uid, K_BATCHNORM_TENSOR_SCALE_UID);
EXPECT_EQ(desc->getScaleDesc()->getData().data_type, DataType::FLOAT);
EXPECT_EQ(desc->getScaleDesc()->getData().dims,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_SCALE_DIMS.begin(),
K_BATCHNORM_TENSOR_SCALE_DIMS.end()}));
EXPECT_EQ(desc->getScaleDesc()->getData().strides,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_SCALE_STRIDES.begin(),
K_BATCHNORM_TENSOR_SCALE_STRIDES.end()}));
// Required: Bias
ASSERT_NE(desc->getBiasDesc(), nullptr);
EXPECT_EQ(desc->getBiasDesc()->getData().uid, K_BATCHNORM_TENSOR_BIAS_UID);
EXPECT_EQ(desc->getBiasDesc()->getData().data_type, DataType::FLOAT);
EXPECT_EQ(desc->getBiasDesc()->getData().dims,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_BIAS_DIMS.begin(),
K_BATCHNORM_TENSOR_BIAS_DIMS.end()}));
EXPECT_EQ(desc->getBiasDesc()->getData().strides,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_BIAS_STRIDES.begin(),
K_BATCHNORM_TENSOR_BIAS_STRIDES.end()}));
// Required: Epsilon
ASSERT_NE(desc->getEpsilonDesc(), nullptr);
EXPECT_EQ(desc->getEpsilonDesc()->getData().uid, K_BATCHNORM_TENSOR_EPSILON_UID);
EXPECT_EQ(desc->getEpsilonDesc()->getData().data_type, DataType::FLOAT);
EXPECT_EQ(desc->getEpsilonDesc()->getData().dims,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_EPSILON_DIMS.begin(),
K_BATCHNORM_TENSOR_EPSILON_DIMS.end()}));
EXPECT_EQ(desc->getEpsilonDesc()->getData().strides,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_EPSILON_STRIDES.begin(),
K_BATCHNORM_TENSOR_EPSILON_STRIDES.end()}));
// Required: Y
ASSERT_NE(desc->getYDesc(), nullptr);
EXPECT_EQ(desc->getYDesc()->getData().uid, K_BATCHNORM_TENSOR_Y_UID);
EXPECT_EQ(desc->getYDesc()->getData().data_type, DataType::FLOAT);
EXPECT_EQ(
desc->getYDesc()->getData().dims,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_Y_DIMS.begin(), K_BATCHNORM_TENSOR_Y_DIMS.end()}));
EXPECT_EQ(desc->getYDesc()->getData().strides,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_Y_STRIDES.begin(),
K_BATCHNORM_TENSOR_Y_STRIDES.end()}));
// Optional: PrevRunningMean
ASSERT_NE(desc->getPrevRunningMeanDesc(), nullptr);
EXPECT_EQ(desc->getPrevRunningMeanDesc()->getData().uid,
K_BATCHNORM_TENSOR_PREV_RUNNING_MEAN_UID);
EXPECT_EQ(desc->getPrevRunningMeanDesc()->getData().data_type, DataType::FLOAT);
EXPECT_EQ(desc->getPrevRunningMeanDesc()->getData().dims,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_PREV_RUNNING_MEAN_DIMS.begin(),
K_BATCHNORM_TENSOR_PREV_RUNNING_MEAN_DIMS.end()}));
EXPECT_EQ(desc->getPrevRunningMeanDesc()->getData().strides,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_PREV_RUNNING_MEAN_STRIDES.begin(),
K_BATCHNORM_TENSOR_PREV_RUNNING_MEAN_STRIDES.end()}));
// Optional: PrevRunningVariance
ASSERT_NE(desc->getPrevRunningVarianceDesc(), nullptr);
EXPECT_EQ(desc->getPrevRunningVarianceDesc()->getData().uid,
K_BATCHNORM_TENSOR_PREV_RUNNING_VARIANCE_UID);
EXPECT_EQ(desc->getPrevRunningVarianceDesc()->getData().data_type, DataType::FLOAT);
EXPECT_EQ(desc->getPrevRunningVarianceDesc()->getData().dims,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_PREV_RUNNING_VARIANCE_DIMS.begin(),
K_BATCHNORM_TENSOR_PREV_RUNNING_VARIANCE_DIMS.end()}));
EXPECT_EQ(desc->getPrevRunningVarianceDesc()->getData().strides,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_PREV_RUNNING_VARIANCE_STRIDES.begin(),
K_BATCHNORM_TENSOR_PREV_RUNNING_VARIANCE_STRIDES.end()}));
// Optional: Momentum
ASSERT_NE(desc->getMomentumDesc(), nullptr);
EXPECT_EQ(desc->getMomentumDesc()->getData().uid, K_BATCHNORM_TENSOR_MOMENTUM_UID);
EXPECT_EQ(desc->getMomentumDesc()->getData().data_type, DataType::FLOAT);
EXPECT_EQ(desc->getMomentumDesc()->getData().dims,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_MOMENTUM_DIMS.begin(),
K_BATCHNORM_TENSOR_MOMENTUM_DIMS.end()}));
EXPECT_EQ(desc->getMomentumDesc()->getData().strides,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_MOMENTUM_STRIDES.begin(),
K_BATCHNORM_TENSOR_MOMENTUM_STRIDES.end()}));
// Optional: Mean
ASSERT_NE(desc->getMeanDesc(), nullptr);
EXPECT_EQ(desc->getMeanDesc()->getData().uid, K_BATCHNORM_TENSOR_MEAN_UID);
EXPECT_EQ(desc->getMeanDesc()->getData().data_type, DataType::FLOAT);
EXPECT_EQ(desc->getMeanDesc()->getData().dims,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_MEAN_DIMS.begin(),
K_BATCHNORM_TENSOR_MEAN_DIMS.end()}));
EXPECT_EQ(desc->getMeanDesc()->getData().strides,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_MEAN_STRIDES.begin(),
K_BATCHNORM_TENSOR_MEAN_STRIDES.end()}));
// Optional: InvVariance
ASSERT_NE(desc->getInvVarianceDesc(), nullptr);
EXPECT_EQ(desc->getInvVarianceDesc()->getData().uid, K_BATCHNORM_TENSOR_INV_VARIANCE_UID);
EXPECT_EQ(desc->getInvVarianceDesc()->getData().data_type, DataType::FLOAT);
EXPECT_EQ(desc->getInvVarianceDesc()->getData().dims,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_INV_VARIANCE_DIMS.begin(),
K_BATCHNORM_TENSOR_INV_VARIANCE_DIMS.end()}));
EXPECT_EQ(desc->getInvVarianceDesc()->getData().strides,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_INV_VARIANCE_STRIDES.begin(),
K_BATCHNORM_TENSOR_INV_VARIANCE_STRIDES.end()}));
// Optional: NextRunningMean
ASSERT_NE(desc->getNextRunningMeanDesc(), nullptr);
EXPECT_EQ(desc->getNextRunningMeanDesc()->getData().uid,
K_BATCHNORM_TENSOR_NEXT_RUNNING_MEAN_UID);
EXPECT_EQ(desc->getNextRunningMeanDesc()->getData().data_type, DataType::FLOAT);
EXPECT_EQ(desc->getNextRunningMeanDesc()->getData().dims,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_NEXT_RUNNING_MEAN_DIMS.begin(),
K_BATCHNORM_TENSOR_NEXT_RUNNING_MEAN_DIMS.end()}));
EXPECT_EQ(desc->getNextRunningMeanDesc()->getData().strides,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_NEXT_RUNNING_MEAN_STRIDES.begin(),
K_BATCHNORM_TENSOR_NEXT_RUNNING_MEAN_STRIDES.end()}));
// Optional: NextRunningVariance
ASSERT_NE(desc->getNextRunningVarianceDesc(), nullptr);
EXPECT_EQ(desc->getNextRunningVarianceDesc()->getData().uid,
K_BATCHNORM_TENSOR_NEXT_RUNNING_VARIANCE_UID);
EXPECT_EQ(desc->getNextRunningVarianceDesc()->getData().data_type, DataType::FLOAT);
EXPECT_EQ(desc->getNextRunningVarianceDesc()->getData().dims,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_NEXT_RUNNING_VARIANCE_DIMS.begin(),
K_BATCHNORM_TENSOR_NEXT_RUNNING_VARIANCE_DIMS.end()}));
EXPECT_EQ(desc->getNextRunningVarianceDesc()->getData().strides,
(std::vector<int64_t>{K_BATCHNORM_TENSOR_NEXT_RUNNING_VARIANCE_STRIDES.begin(),
K_BATCHNORM_TENSOR_NEXT_RUNNING_VARIANCE_STRIDES.end()}));
}
TEST_F(TestBatchnormOperationFromNode, FailsWhenPeerStatsUidSetButTensorMissing)
{
_tensorMap.erase(K_BATCHNORM_TENSOR_PEER_STAT_0_UID);
auto node = createStandardNode();
ASSERT_THROW_HIPDNN_STATUS(BatchnormOperationDescriptor::fromNode(node, _tensorMap),
HIPDNN_STATUS_INTERNAL_ERROR);
}
TEST_F(TestBatchnormOperationFromNode, FailsWithMeanWithoutInvVariance)
{
auto attrs = createStandardBatchnormAttrs();
attrs.mean_tensor_uid = K_BATCHNORM_TENSOR_MEAN_UID;
attrs.inv_variance_tensor_uid = flatbuffers::nullopt;
NodeT node;
node.compute_data_type = DataType::FLOAT;
node.attributes.Set(attrs);
ASSERT_THROW_HIPDNN_STATUS(BatchnormOperationDescriptor::fromNode(node, _tensorMap),
HIPDNN_STATUS_BAD_PARAM);
}
TEST_F(TestBatchnormOperationFromNode, FailsWithPartialRunningStats)
{
auto attrs = createStandardBatchnormAttrs();
attrs.prev_running_mean_tensor_uid = K_BATCHNORM_TENSOR_PREV_RUNNING_MEAN_UID;
attrs.prev_running_variance_tensor_uid = flatbuffers::nullopt;
attrs.momentum_tensor_uid = flatbuffers::nullopt;
attrs.next_running_mean_tensor_uid = flatbuffers::nullopt;
attrs.next_running_variance_tensor_uid = flatbuffers::nullopt;
NodeT node;
node.compute_data_type = DataType::FLOAT;
node.attributes.Set(attrs);
ASSERT_THROW_HIPDNN_STATUS(BatchnormOperationDescriptor::fromNode(node, _tensorMap),
HIPDNN_STATUS_BAD_PARAM);
}
TEST_F(TestBatchnormOperationFromNode, FailsWithWrongAttributeType)
{
NodeT node;
node.compute_data_type = DataType::FLOAT;
node.attributes.Set(PointwiseAttributesT{});
ASSERT_THROW_HIPDNN_STATUS(BatchnormOperationDescriptor::fromNode(node, _tensorMap),
HIPDNN_STATUS_INTERNAL_ERROR);
}