From 2e734a8d0d95791373203e93a38a8ba31291cda9 Mon Sep 17 00:00:00 2001 From: Brent Maas Date: Thu, 9 Apr 2026 13:20:59 +0000 Subject: [PATCH 1/7] Generated frontend code after fixing code generation mistakes --- .../include/HipdnnBackendAttributeName.h | 43 + .../include/HipdnnBackendDescriptorType.h | 7 + .../backend/include/HipdnnOperationType.h | 1 + .../backend/src/BackendEnumStringUtils.hpp | 26 + projects/hipdnn/backend/src/CMakeLists.txt | 1 + .../src/descriptors/DescriptorFactory.cpp | 4 + .../LayernormBackwardOperationDescriptor.cpp | 409 ++++++ .../LayernormBackwardOperationDescriptor.hpp | 120 ++ .../backend/src/descriptors/NodeFactory.cpp | 2 + .../backend/src/descriptors/NodeFactory.hpp | 1 + projects/hipdnn/backend/tests/CMakeLists.txt | 3 + .../TestGraphDescriptorLayernormBackward.cpp | 690 +++++++++++ ...stLayernormBackwardOperationDescriptor.cpp | 1102 +++++++++++++++++ ...TestLayernormBackwardOperationFromNode.cpp | 654 ++++++++++ .../include/hipdnn_frontend/Graph.hpp | 87 ++ .../LayernormBackwardAttributes.hpp | 250 ++++ .../detail/LayernormBackwardPacker.hpp | 103 ++ .../detail/LayernormBackwardUnpacker.hpp | 138 +++ .../detail/OperationUnpacker.hpp | 5 + .../node/LayernormBackwardNode.hpp | 248 ++++ .../include/hipdnn_frontend/node/NodeType.hpp | 3 +- projects/hipdnn/frontend/tests/CMakeLists.txt | 3 + .../tests/TestGraphLayernormBackward.cpp | 68 + .../tests/TestLayernormBackwardAttributes.cpp | 326 +++++ .../tests/TestLayernormBackwardNode.cpp | 346 ++++++ .../frontend/tests/TestOperationUnpacker.cpp | 12 + .../constants/LayernormBackwardConstants.hpp | 51 + projects/hipdnn/tests/frontend/CMakeLists.txt | 2 + ...tionLayernormBackwardDescriptorLifting.cpp | 419 +++++++ ...ionLayernormBackwardDescriptorLowering.cpp | 178 +++ .../configs/layernorm_backward.yaml | 168 +++ 31 files changed, 5469 insertions(+), 1 deletion(-) create mode 100644 projects/hipdnn/backend/src/descriptors/LayernormBackwardOperationDescriptor.cpp create mode 100644 projects/hipdnn/backend/src/descriptors/LayernormBackwardOperationDescriptor.hpp create mode 100644 projects/hipdnn/backend/tests/descriptors/TestGraphDescriptorLayernormBackward.cpp create mode 100644 projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationDescriptor.cpp create mode 100644 projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationFromNode.cpp create mode 100644 projects/hipdnn/frontend/include/hipdnn_frontend/attributes/LayernormBackwardAttributes.hpp create mode 100644 projects/hipdnn/frontend/include/hipdnn_frontend/detail/LayernormBackwardPacker.hpp create mode 100644 projects/hipdnn/frontend/include/hipdnn_frontend/detail/LayernormBackwardUnpacker.hpp create mode 100644 projects/hipdnn/frontend/include/hipdnn_frontend/node/LayernormBackwardNode.hpp create mode 100644 projects/hipdnn/frontend/tests/TestGraphLayernormBackward.cpp create mode 100644 projects/hipdnn/frontend/tests/TestLayernormBackwardAttributes.cpp create mode 100644 projects/hipdnn/frontend/tests/TestLayernormBackwardNode.cpp create mode 100644 projects/hipdnn/test_sdk/include/hipdnn_test_sdk/constants/LayernormBackwardConstants.hpp create mode 100644 projects/hipdnn/tests/frontend/IntegrationLayernormBackwardDescriptorLifting.cpp create mode 100644 projects/hipdnn/tests/frontend/IntegrationLayernormBackwardDescriptorLowering.cpp create mode 100644 projects/hipdnn/tools/DescriptorGenerator/configs/layernorm_backward.yaml diff --git a/projects/hipdnn/backend/include/HipdnnBackendAttributeName.h b/projects/hipdnn/backend/include/HipdnnBackendAttributeName.h index 1020da27b517..4d79a4506338 100644 --- a/projects/hipdnn/backend/include/HipdnnBackendAttributeName.h +++ b/projects/hipdnn/backend/include/HipdnnBackendAttributeName.h @@ -57,6 +57,8 @@ * - 3200-3299: Reduction operation attributes * - 3300-3399: Resample forward operation attributes * - 3400-3499: Shared resample descriptor attributes + * - 3500-3599: RMSNorm backward operation attributes + * - 3600-3699: Layernorm backward operation attributes * - 60000-60099: Knob info serialized value extension attributes * - 60100-60199: Knob choice serialized value extension attributes * - 60200-60299: Operation type extension attributes @@ -1257,6 +1259,47 @@ typedef enum /** @} */ + /** + * @name Layernorm Backward Operation Attributes (3600-3699) + * Attributes for HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR + * @{ + */ + + /** @brief Output gradient tensor for backward layernorm */ + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY = 3600, + + /** @brief Input tensor for backward layernorm */ + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X = 3601, + + /** @brief Scale tensor for backward layernorm */ + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE = 3602, + + /** @brief Mean tensor for backward layernorm */ + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN = 3603, + + /** @brief Inverse variance tensor for backward layernorm */ + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE = 3604, + + /** @brief Epsilon tensor for backward layernorm */ + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON = 3605, + + /** @brief Input gradient tensor for backward layernorm */ + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX = 3606, + + /** @brief Scale gradient tensor for backward layernorm */ + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE = 3607, + + /** @brief Bias gradient tensor for backward layernorm */ + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS = 3608, + + /** @brief Number of normalized dimensions for backward layernorm */ + HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT = 3609, + + /** @brief Compute type for backward layernorm */ + HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT = 3610, + + /** @} */ + /** * @name Extension Attributes (60000+) * hipDNN-specific extension attributes diff --git a/projects/hipdnn/backend/include/HipdnnBackendDescriptorType.h b/projects/hipdnn/backend/include/HipdnnBackendDescriptorType.h index e00d23ce28de..cbd555653453 100644 --- a/projects/hipdnn/backend/include/HipdnnBackendDescriptorType.h +++ b/projects/hipdnn/backend/include/HipdnnBackendDescriptorType.h @@ -313,4 +313,11 @@ typedef enum */ HIPDNN_BACKEND_PROFILING_CONTROL_EXT = 34, + /** + * @brief Layernorm backward operation descriptor + * + * Represents a backward layer normalization operation with output gradient (DY), input (X), scale, mean, inverse variance, input gradient (DX), scale gradient and mean gradient tensors, a backward layernorm operator, and a compute data type + */ + HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR = 35, + } hipdnnBackendDescriptorType_t; diff --git a/projects/hipdnn/backend/include/HipdnnOperationType.h b/projects/hipdnn/backend/include/HipdnnOperationType.h index 013b5d996bb5..fe46b54719c3 100644 --- a/projects/hipdnn/backend/include/HipdnnOperationType.h +++ b/projects/hipdnn/backend/include/HipdnnOperationType.h @@ -42,4 +42,5 @@ typedef enum HIPDNN_OPERATION_TYPE_REDUCTION_EXT = 17, ///< Reduction operation HIPDNN_OPERATION_TYPE_RESAMPLE_FWD = 18, ///< Resample forward operation HIPDNN_OPERATION_TYPE_RMSNORM_BACKWARD_EXT = 19, ///< RMS normalization backward + HIPDNN_OPERATION_TYPE_LAYERNORM_BACKWARD_EXT = 20, ///< Layer normalization backward } hipdnnOperationType_ext_t; diff --git a/projects/hipdnn/backend/src/BackendEnumStringUtils.hpp b/projects/hipdnn/backend/src/BackendEnumStringUtils.hpp index ebe15cbaca5c..2e585bbfd931 100644 --- a/projects/hipdnn/backend/src/BackendEnumStringUtils.hpp +++ b/projects/hipdnn/backend/src/BackendEnumStringUtils.hpp @@ -255,6 +255,8 @@ inline const char* hipdnnGetBackendDescriptorTypeName(hipdnnBackendDescriptorTyp return "HIPDNN_BACKEND_OPERATION_SDPA_FWD_DESCRIPTOR"; case HIPDNN_BACKEND_OPERATION_LAYERNORM_DESCRIPTOR_EXT: return "HIPDNN_BACKEND_OPERATION_LAYERNORM_DESCRIPTOR_EXT"; + case HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR: + return "HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR_EXT"; case HIPDNN_BACKEND_OPERATION_BLOCK_SCALE_QUANTIZE_DESCRIPTOR: return "HIPDNN_BACKEND_OPERATION_BLOCK_SCALE_QUANTIZE_DESCRIPTOR"; case HIPDNN_BACKEND_OPERATION_BATCHNORM_DESCRIPTOR_EXT: @@ -898,6 +900,30 @@ inline const char* hipdnnGetAttributeNameString(hipdnnBackendAttributeName_t att case HIPDNN_ATTR_RMSNORM_BACKWARD_COMP_TYPE_EXT: return "HIPDNN_ATTR_RMSNORM_BACKWARD_COMP_TYPE_EXT"; + // Layernorm backward operation attributes + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY: + return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY"; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X: + return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X"; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE: + return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE"; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN: + return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN"; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE: + return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE"; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON: + return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON"; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX: + return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX"; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE: + return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE"; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS: + return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS"; + case HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT: + return "HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT"; + case HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT: + return "HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT"; + // Operation extension attributes case HIPDNN_ATTR_OPERATION_NAME_EXT: return "HIPDNN_ATTR_OPERATION_NAME_EXT"; diff --git a/projects/hipdnn/backend/src/CMakeLists.txt b/projects/hipdnn/backend/src/CMakeLists.txt index de498fc7307e..a7b0c0c798eb 100644 --- a/projects/hipdnn/backend/src/CMakeLists.txt +++ b/projects/hipdnn/backend/src/CMakeLists.txt @@ -37,6 +37,7 @@ add_library( descriptors/KnobDescriptor.cpp descriptors/KnobSettingDescriptor.cpp descriptors/LayernormOperationDescriptor.cpp + descriptors/LayernormBackwardOperationDescriptor.cpp descriptors/MatmulOperationDescriptor.cpp descriptors/PointwiseOperationDescriptor.cpp descriptors/ProfilingControlDescriptor.cpp diff --git a/projects/hipdnn/backend/src/descriptors/DescriptorFactory.cpp b/projects/hipdnn/backend/src/descriptors/DescriptorFactory.cpp index 806e213f1f6e..b5d50aeb510f 100644 --- a/projects/hipdnn/backend/src/descriptors/DescriptorFactory.cpp +++ b/projects/hipdnn/backend/src/descriptors/DescriptorFactory.cpp @@ -21,6 +21,7 @@ #include "HipdnnException.hpp" #include "KnobDescriptor.hpp" #include "KnobSettingDescriptor.hpp" +#include "LayernormBackwardOperationDescriptor.hpp" #include "LayernormOperationDescriptor.hpp" #include "MatmulOperationDescriptor.hpp" #include "PointwiseOperationDescriptor.hpp" @@ -117,6 +118,9 @@ void DescriptorFactory::create(hipdnnBackendDescriptorType_t descriptorType, case HIPDNN_BACKEND_OPERATION_LAYERNORM_DESCRIPTOR_EXT: privateDesc = std::make_shared(); break; + case HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR: + privateDesc = std::make_shared(); + break; case HIPDNN_BACKEND_OPERATION_BATCHNORM_DESCRIPTOR_EXT: privateDesc = std::make_shared(); break; diff --git a/projects/hipdnn/backend/src/descriptors/LayernormBackwardOperationDescriptor.cpp b/projects/hipdnn/backend/src/descriptors/LayernormBackwardOperationDescriptor.cpp new file mode 100644 index 000000000000..17f108c48df4 --- /dev/null +++ b/projects/hipdnn/backend/src/descriptors/LayernormBackwardOperationDescriptor.cpp @@ -0,0 +1,409 @@ +// Copyright © Advanced Micro Devices, Inc., or its affiliates. +// SPDX-License-Identifier: MIT + +#include "LayernormBackwardOperationDescriptor.hpp" +#include "DescriptorAttributeUtils.hpp" +#include "HipdnnBackendDescriptorType.h" +#include "HipdnnException.hpp" +#include "HipdnnOperationType.h" +#include + +namespace hipdnn_backend +{ + +void LayernormBackwardOperationDescriptor::finalize() +{ + THROW_IF_NULL(_dyDesc, + HIPDNN_STATUS_BAD_PARAM, + "LayernormBackwardOperationDescriptor::finalize() failed: DY tensor not set"); + THROW_IF_NULL(_xDesc, + HIPDNN_STATUS_BAD_PARAM, + "LayernormBackwardOperationDescriptor::finalize() failed: X tensor not set"); + THROW_IF_NULL(_scaleDesc, + HIPDNN_STATUS_BAD_PARAM, + "LayernormBackwardOperationDescriptor::finalize() failed: SCALE tensor not set"); + THROW_IF_NULL(_dxDesc, + HIPDNN_STATUS_BAD_PARAM, + "LayernormBackwardOperationDescriptor::finalize() failed: DX tensor not set"); + THROW_IF_NULL(_dscaleDesc, + HIPDNN_STATUS_BAD_PARAM, + "LayernormBackwardOperationDescriptor::finalize() failed: DSCALE tensor not set"); + THROW_IF_NULL(_dbiasDesc, + HIPDNN_STATUS_BAD_PARAM, + "LayernormBackwardOperationDescriptor::finalize() failed: DBIAS tensor not set"); + THROW_IF_TRUE(_computeDataType == hipdnn_flatbuffers_sdk::data_objects::DataType::UNSET, + HIPDNN_STATUS_BAD_PARAM, + "LayernormBackwardOperationDescriptor::finalize() failed: compute data type not " + "set"); + + HipdnnBackendDescriptorImpl::finalize(); +} + +// ============================================================================ +// setAttribute +// ============================================================================ + +void LayernormBackwardOperationDescriptor::setAttribute(hipdnnBackendAttributeName_t attributeName, + hipdnnBackendAttributeType_t attributeType, + int64_t elementCount, + const void* arrayOfElements) +{ + THROW_IF_TRUE( + isFinalized(), + HIPDNN_STATUS_NOT_INITIALIZED, + "LayernormBackwardOperationDescriptor::setAttribute() failed: Already finalized."); + + switch(attributeName) + { + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY: + setTensorDescriptor(_dyDesc, + _data.dy_tensor_uid, + attributeType, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::setAttribute()"); + break; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X: + setTensorDescriptor(_xDesc, + _data.x_tensor_uid, + attributeType, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::setAttribute()"); + break; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE: + setTensorDescriptor(_scaleDesc, + _data.scale_tensor_uid, + attributeType, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::setAttribute()"); + break; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN: + setOptionalTensorDescriptor(_meanDesc, + _data.mean_tensor_uid, + attributeType, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::setAttribute()"); + break; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE: + setOptionalTensorDescriptor(_invVarianceDesc, + _data.inv_variance_tensor_uid, + attributeType, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::setAttribute()"); + break; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON: + setOptionalTensorDescriptor(_epsilonDesc, + _data.epsilon_tensor_uid, + attributeType, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::setAttribute()"); + break; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX: + setTensorDescriptor(_dxDesc, + _data.dx_tensor_uid, + attributeType, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::setAttribute()"); + break; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE: + setTensorDescriptor(_dscaleDesc, + _data.dscale_tensor_uid, + attributeType, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::setAttribute()"); + break; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS: + setTensorDescriptor(_dbiasDesc, + _data.dbias_tensor_uid, + attributeType, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::setAttribute()"); + break; + case HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT: + setScalar(_data.normalized_dim_count, + HIPDNN_TYPE_INT64, + attributeType, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::setAttribute()"); + break; + case HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT: + setDataType(_computeDataType, + attributeType, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::setAttribute()"); + break; + case HIPDNN_ATTR_OPERATION_NAME_EXT: + setString(_name, + attributeType, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::setAttribute()"); + break; + default: + throw HipdnnException( + HIPDNN_STATUS_NOT_SUPPORTED, + "LayernormBackwardOperationDescriptor::setAttribute: attributeName not " + "supported"); + } +} + +// ============================================================================ +// getAttribute +// ============================================================================ + +void LayernormBackwardOperationDescriptor::getAttribute(hipdnnBackendAttributeName_t attributeName, + hipdnnBackendAttributeType_t attributeType, + int64_t requestedElementCount, + int64_t* elementCount, + void* arrayOfElements) const +{ + THROW_IF_FALSE(isFinalized(), + HIPDNN_STATUS_NOT_INITIALIZED, + "LayernormBackwardOperationDescriptor::getAttribute() failed: Not finalized."); + + switch(attributeName) + { + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY: + getTensorDescriptor(_dyDesc, + attributeType, + requestedElementCount, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::getAttribute()"); + break; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X: + getTensorDescriptor(_xDesc, + attributeType, + requestedElementCount, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::getAttribute()"); + break; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE: + getTensorDescriptor(_scaleDesc, + attributeType, + requestedElementCount, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::getAttribute()"); + break; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN: + getOptionalTensorDescriptor(_meanDesc, + attributeType, + requestedElementCount, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::getAttribute()"); + break; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE: + getOptionalTensorDescriptor(_invVarianceDesc, + attributeType, + requestedElementCount, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::getAttribute()"); + break; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON: + getOptionalTensorDescriptor(_epsilonDesc, + attributeType, + requestedElementCount, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::getAttribute()"); + break; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX: + getTensorDescriptor(_dxDesc, + attributeType, + requestedElementCount, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::getAttribute()"); + break; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE: + getTensorDescriptor(_dscaleDesc, + attributeType, + requestedElementCount, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::getAttribute()"); + break; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS: + getTensorDescriptor(_dbiasDesc, + attributeType, + requestedElementCount, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::getAttribute()"); + break; + case HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT: + getScalar(_data.normalized_dim_count, + HIPDNN_TYPE_INT64, + attributeType, + requestedElementCount, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::getAttribute()"); + break; + case HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT: + getDataType(_computeDataType, + attributeType, + requestedElementCount, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::getAttribute()"); + break; + case HIPDNN_ATTR_OPERATION_NAME_EXT: + getString(_name, + attributeType, + requestedElementCount, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::getAttribute()"); + break; + case HIPDNN_ATTR_OPERATION_TYPE_EXT: + getOperationType(HIPDNN_OPERATION_TYPE_LAYERNORM_BACKWARD_EXT, + attributeType, + requestedElementCount, + elementCount, + arrayOfElements, + "LayernormBackwardOperationDescriptor::getAttribute()"); + break; + default: + throw HipdnnException( + HIPDNN_STATUS_NOT_SUPPORTED, + "LayernormBackwardOperationDescriptor::getAttribute: attributeName not " + "supported"); + } +} + +// ============================================================================ +// Other methods +// ============================================================================ + +std::vector> + LayernormBackwardOperationDescriptor::getTensorDescriptors() const +{ + std::vector> result = {_dyDesc, _xDesc, _scaleDesc}; + if(_meanDesc) + { + result.push_back(_meanDesc); + } + if(_invVarianceDesc) + { + result.push_back(_invVarianceDesc); + } + if(_epsilonDesc) + { + result.push_back(_epsilonDesc); + } + result.push_back(_dxDesc); + result.push_back(_dscaleDesc); + result.push_back(_dbiasDesc); + return result; +} + +std::unique_ptr + LayernormBackwardOperationDescriptor::buildNode() const +{ + auto node = std::make_unique(); + node->name = _name; + node->compute_data_type = _computeDataType; + node->attributes.Set(hipdnn_flatbuffers_sdk::data_objects::LayernormBackwardAttributesT(_data)); + return node; +} + +hipdnnBackendDescriptorType_t LayernormBackwardOperationDescriptor::getStaticType() +{ + return HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR; +} + +std::string LayernormBackwardOperationDescriptor::toString() const +{ + using hipdnn_data_sdk::utilities::vecToString; + std::string str = "LayernormBackwardOperationDescriptor: {"; + str += "name=" + _name; + str += ", dy_uid=" + std::to_string(_data.dy_tensor_uid); + str += ", x_uid=" + std::to_string(_data.x_tensor_uid); + str += ", scale_uid=" + std::to_string(_data.scale_tensor_uid); + str += ", mean_uid=" + + (_data.mean_tensor_uid ? std::to_string(*_data.mean_tensor_uid) : "nullopt"); + str += ", inv_variance_uid=" + + (_data.inv_variance_tensor_uid ? std::to_string(*_data.inv_variance_tensor_uid) + : "nullopt"); + str += ", epsilon_uid=" + + (_data.epsilon_tensor_uid ? std::to_string(*_data.epsilon_tensor_uid) : "nullopt"); + str += ", dx_uid=" + std::to_string(_data.dx_tensor_uid); + str += ", dscale_uid=" + std::to_string(_data.dscale_tensor_uid); + str += ", dbias_uid=" + std::to_string(_data.dbias_tensor_uid); + str += ", normalized_dim_count=" + std::to_string(_data.normalized_dim_count); + str += ", compute_data_type="; + str += hipdnn_flatbuffers_sdk::data_objects::EnumNameDataType(_computeDataType); + str += "}"; + return str; +} + +std::shared_ptr + LayernormBackwardOperationDescriptor::fromNode( + const hipdnn_flatbuffers_sdk::data_objects::NodeT& nodeT, + const std::unordered_map>& tensorMap) +{ + const auto* attrs = nodeT.attributes.AsLayernormBackwardAttributes(); + THROW_IF_NULL( + attrs, + HIPDNN_STATUS_INTERNAL_ERROR, + "LayernormBackwardOperationDescriptor::fromNode: LayernormBackwardAttributes is null"); + + auto desc = std::make_shared(); + desc->_data = *attrs; + desc->_computeDataType = nodeT.compute_data_type; + desc->_name = nodeT.name; + desc->_dyDesc = findTensorInMap( + tensorMap, attrs->dy_tensor_uid, "LayernormBackwardOperationDescriptor::fromNode: Dy"); + desc->_xDesc = findTensorInMap( + tensorMap, attrs->x_tensor_uid, "LayernormBackwardOperationDescriptor::fromNode: X"); + desc->_scaleDesc = findTensorInMap(tensorMap, + attrs->scale_tensor_uid, + "LayernormBackwardOperationDescriptor::fromNode: Scale"); + if(attrs->mean_tensor_uid) + { + desc->_meanDesc = findTensorInMap(tensorMap, + *attrs->mean_tensor_uid, + "LayernormBackwardOperationDescriptor::fromNode: Mean"); + } + if(attrs->inv_variance_tensor_uid) + { + desc->_invVarianceDesc + = findTensorInMap(tensorMap, + *attrs->inv_variance_tensor_uid, + "LayernormBackwardOperationDescriptor::fromNode: InvVariance"); + } + if(attrs->epsilon_tensor_uid) + { + desc->_epsilonDesc + = findTensorInMap(tensorMap, + *attrs->epsilon_tensor_uid, + "LayernormBackwardOperationDescriptor::fromNode: Epsilon"); + } + desc->_dxDesc = findTensorInMap( + tensorMap, attrs->dx_tensor_uid, "LayernormBackwardOperationDescriptor::fromNode: Dx"); + desc->_dscaleDesc = findTensorInMap(tensorMap, + attrs->dscale_tensor_uid, + "LayernormBackwardOperationDescriptor::fromNode: Dscale"); + desc->_dbiasDesc = findTensorInMap(tensorMap, + attrs->dbias_tensor_uid, + "LayernormBackwardOperationDescriptor::fromNode: Dbias"); + desc->finalize(); + return desc; +} + +} // namespace hipdnn_backend diff --git a/projects/hipdnn/backend/src/descriptors/LayernormBackwardOperationDescriptor.hpp b/projects/hipdnn/backend/src/descriptors/LayernormBackwardOperationDescriptor.hpp new file mode 100644 index 000000000000..c84b40f7a6ff --- /dev/null +++ b/projects/hipdnn/backend/src/descriptors/LayernormBackwardOperationDescriptor.hpp @@ -0,0 +1,120 @@ +// Copyright © Advanced Micro Devices, Inc., or its affiliates. +// SPDX-License-Identifier: MIT + +#pragma once + +#include "BackendDescriptor.hpp" +#include "IGraphOperation.hpp" +#include "TensorDescriptor.hpp" +#include +#include +#include + +namespace hipdnn_backend +{ + +class LayernormBackwardOperationDescriptor + : public HipdnnBackendDescriptorImpl, + public IGraphOperation +{ +public: + void finalize() override; + + void getAttribute(hipdnnBackendAttributeName_t attributeName, + hipdnnBackendAttributeType_t attributeType, + int64_t requestedElementCount, + int64_t* elementCount, + void* arrayOfElements) const override; + + void setAttribute(hipdnnBackendAttributeName_t attributeName, + hipdnnBackendAttributeType_t attributeType, + int64_t elementCount, + const void* arrayOfElements) override; + + // Direct access to the underlying T struct for OperationGraphBuilder + const hipdnn_flatbuffers_sdk::data_objects::LayernormBackwardAttributesT& getData() const + { + return _data; + } + + // Access to tensor descriptor references for graph building + std::shared_ptr getDyDesc() const + { + return _dyDesc; + } + std::shared_ptr getXDesc() const + { + return _xDesc; + } + std::shared_ptr getScaleDesc() const + { + return _scaleDesc; + } + std::shared_ptr getMeanDesc() const + { + return _meanDesc; + } + std::shared_ptr getInvVarianceDesc() const + { + return _invVarianceDesc; + } + std::shared_ptr getEpsilonDesc() const + { + return _epsilonDesc; + } + std::shared_ptr getDxDesc() const + { + return _dxDesc; + } + std::shared_ptr getDscaleDesc() const + { + return _dscaleDesc; + } + std::shared_ptr getDbiasDesc() const + { + return _dbiasDesc; + } + + // Get compute data type for the operation (used when building graph nodes) + hipdnn_flatbuffers_sdk::data_objects::DataType getComputeDataType() const + { + return _computeDataType; + } + + // IGraphOperation interface + std::vector> getTensorDescriptors() const override; + std::unique_ptr buildNode() const override; + + // Creates a finalized LayernormBackwardOperationDescriptor directly from a FlatBuffer NodeT. + // Casts nodeT.attributes to LayernormBackwardAttributes internally, then directly assigns + // the data struct, looks up tensor descriptors from the tensor map, and calls finalize(). + static std::shared_ptr + fromNode(const hipdnn_flatbuffers_sdk::data_objects::NodeT& nodeT, + const std::unordered_map>& tensorMap); + + static hipdnnBackendDescriptorType_t getStaticType(); + + std::string toString() const override; + +private: + hipdnn_flatbuffers_sdk::data_objects::LayernormBackwardAttributesT _data; + + // Store tensor descriptor references for validation and graph building + std::shared_ptr _dyDesc; + std::shared_ptr _xDesc; + std::shared_ptr _scaleDesc; + std::shared_ptr _meanDesc; + std::shared_ptr _invVarianceDesc; + std::shared_ptr _epsilonDesc; + std::shared_ptr _dxDesc; + std::shared_ptr _dscaleDesc; + std::shared_ptr _dbiasDesc; + + // Compute data type for this operation (stored at node level in graph) + hipdnn_flatbuffers_sdk::data_objects::DataType _computeDataType + = hipdnn_flatbuffers_sdk::data_objects::DataType::UNSET; + + std::string _name; +}; + +} // namespace hipdnn_backend diff --git a/projects/hipdnn/backend/src/descriptors/NodeFactory.cpp b/projects/hipdnn/backend/src/descriptors/NodeFactory.cpp index 6b1d4f91c9cc..85daecac1760 100644 --- a/projects/hipdnn/backend/src/descriptors/NodeFactory.cpp +++ b/projects/hipdnn/backend/src/descriptors/NodeFactory.cpp @@ -39,6 +39,8 @@ std::shared_ptr NodeFactory::createOperationFromNode( return CustomOpOperationDescriptor::fromNode(nodeT, tensorMap); case NodeAttributes::LayernormAttributes: return LayernormOperationDescriptor::fromNode(nodeT, tensorMap); + case NodeAttributes::LayernormBackwardAttributes: + return LayernormBackwardOperationDescriptor::fromNode(nodeT, tensorMap); case NodeAttributes::MatmulAttributes: return MatmulOperationDescriptor::fromNode(nodeT, tensorMap); case NodeAttributes::PointwiseAttributes: diff --git a/projects/hipdnn/backend/src/descriptors/NodeFactory.hpp b/projects/hipdnn/backend/src/descriptors/NodeFactory.hpp index b4b80e5eb408..18b2efd15e20 100644 --- a/projects/hipdnn/backend/src/descriptors/NodeFactory.hpp +++ b/projects/hipdnn/backend/src/descriptors/NodeFactory.hpp @@ -14,6 +14,7 @@ #include "ConvolutionWrwOperationDescriptor.hpp" #include "CustomOpOperationDescriptor.hpp" #include "IGraphOperation.hpp" +#include "LayernormBackwardOperationDescriptor.hpp" #include "LayernormOperationDescriptor.hpp" #include "MatmulOperationDescriptor.hpp" #include "PointwiseOperationDescriptor.hpp" diff --git a/projects/hipdnn/backend/tests/CMakeLists.txt b/projects/hipdnn/backend/tests/CMakeLists.txt index 54277446d87d..5a413cf573fc 100644 --- a/projects/hipdnn/backend/tests/CMakeLists.txt +++ b/projects/hipdnn/backend/tests/CMakeLists.txt @@ -60,6 +60,7 @@ add_executable( descriptors/TestBatchnormInferenceVarianceExtOperationFromNode.cpp descriptors/TestGraphDescriptorBatchnormInferenceVarianceExt.cpp descriptors/TestGraphDescriptorLayernorm.cpp + descriptors/TestGraphDescriptorLayernormBackward.cpp descriptors/TestGraphDescriptorOps.cpp descriptors/TestGraphDescriptorLifting.cpp descriptors/TestGraphDescriptorPointwise.cpp @@ -68,6 +69,8 @@ add_executable( descriptors/TestConvolutionBwdOperationFromNode.cpp descriptors/TestLayernormOperationDescriptor.cpp descriptors/TestLayernormOperationFromNode.cpp + descriptors/TestLayernormBackwardOperationDescriptor.cpp + descriptors/TestLayernormBackwardOperationFromNode.cpp descriptors/TestPointwiseOperationDescriptor.cpp descriptors/TestPointwiseOperationFromNode.cpp descriptors/TestReductionOperationDescriptor.cpp diff --git a/projects/hipdnn/backend/tests/descriptors/TestGraphDescriptorLayernormBackward.cpp b/projects/hipdnn/backend/tests/descriptors/TestGraphDescriptorLayernormBackward.cpp new file mode 100644 index 000000000000..518a9692b0ad --- /dev/null +++ b/projects/hipdnn/backend/tests/descriptors/TestGraphDescriptorLayernormBackward.cpp @@ -0,0 +1,690 @@ +// Copyright © Advanced Micro Devices, Inc., or its affiliates. +// SPDX-License-Identifier: MIT + +#include "DescriptorTestUtils.hpp" +#include "TensorDescriptorTestUtils.hpp" +#include "descriptors/GraphDescriptor.hpp" +#include "descriptors/LayernormBackwardOperationDescriptor.hpp" +#include "hipdnn_backend.h" +#include "mocks/MockHandle.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace hipdnn_backend; +using namespace hipdnn_backend::test_utilities; +using namespace hipdnn_flatbuffers_sdk::data_objects; +using namespace hipdnn_tests::constants; +using hipdnn_tests::toVec; + +namespace +{ + +// Helper: create a finalized LayernormBackwardOperationDescriptor from tensor descriptors +inline std::unique_ptr + createFinalizedLayernormBackwardOp(HipdnnBackendDescriptor* dyDesc, + HipdnnBackendDescriptor* xDesc, + HipdnnBackendDescriptor* scaleDesc, + HipdnnBackendDescriptor* meanDesc, + HipdnnBackendDescriptor* invVarianceDesc, + HipdnnBackendDescriptor* epsilonDesc, + HipdnnBackendDescriptor* dxDesc, + HipdnnBackendDescriptor* dscaleDesc, + HipdnnBackendDescriptor* dbiasDesc, + hipdnnDataType_t computeType = HIPDNN_DATA_FLOAT, + const std::string& name = "") +{ + auto wrapper = createDescriptor(); + auto desc = wrapper->asDescriptor(); + + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + static_cast(&dyDesc)); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + static_cast(&xDesc)); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + static_cast(&scaleDesc)); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + static_cast(&meanDesc)); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + static_cast(&invVarianceDesc)); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + static_cast(&epsilonDesc)); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + static_cast(&dxDesc)); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + static_cast(&dscaleDesc)); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + static_cast(&dbiasDesc)); + desc->setAttribute( + HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, HIPDNN_TYPE_DATA_TYPE, 1, &computeType); + + if(!name.empty()) + { + desc->setAttribute(HIPDNN_ATTR_OPERATION_NAME_EXT, + HIPDNN_TYPE_CHAR, + static_cast(name.size()), + name.data()); + } + + desc->finalize(); + return wrapper; +} + +class TestGraphDescriptorLayernormBackward : public ::testing::Test +{ +public: + std::shared_ptr getDescriptor() const + { + return _wrapper->asDescriptor(); + } + + void setHandle() const + { + auto desc = getDescriptor(); + hipdnnHandle_t handle = &_mockHandle; + desc->setAttribute(HIPDNN_ATTR_OPERATIONGRAPH_HANDLE, + HIPDNN_TYPE_HANDLE, + 1, + static_cast(&handle)); + } + + static const TensorAttributesT* findTensorByUid(const GraphT& graphT, int64_t uid) + { + for(const auto& tensor : graphT.tensors) + { + if(tensor->uid == uid) + { + return tensor.get(); + } + } + return nullptr; + } + + static void verifyTensor(const TensorAttributesT* tensor, + int64_t expectedUid, + const std::vector& expectedDims, + const std::vector& expectedStrides, + DataType expectedDataType, + bool expectedVirtual = false) + { + ASSERT_NE(tensor, nullptr) << "Tensor with UID " << expectedUid + << " not found"; // NOLINT(readability-implicit-bool-conversion) + EXPECT_EQ(tensor->uid, expectedUid); + EXPECT_EQ(tensor->dims, expectedDims); + EXPECT_EQ(tensor->strides, expectedStrides); + EXPECT_EQ(tensor->data_type, expectedDataType); + EXPECT_EQ(tensor->virtual_, expectedVirtual); + } + + static void verifyLayernormBackwardNode(const NodeT& node, + DataType expectedComputeType, + int64_t expectedDyUid, + int64_t expectedXUid, + int64_t expectedScaleUid, + int64_t expectedMeanUid, + int64_t expectedInvVarianceUid, + int64_t expectedEpsilonUid, + int64_t expectedDxUid, + int64_t expectedDscaleUid, + int64_t expectedDbiasUid) + { + EXPECT_EQ(node.compute_data_type, expectedComputeType); + ASSERT_EQ(node.attributes.type, NodeAttributes::LayernormBackwardAttributes); + + auto* attrs = node.attributes.AsLayernormBackwardAttributes(); + ASSERT_NE(attrs, nullptr); + + EXPECT_EQ(attrs->dy_tensor_uid, expectedDyUid); + EXPECT_EQ(attrs->x_tensor_uid, expectedXUid); + EXPECT_EQ(attrs->scale_tensor_uid, expectedScaleUid); + EXPECT_EQ(attrs->mean_tensor_uid, expectedMeanUid); + EXPECT_EQ(attrs->inv_variance_tensor_uid, expectedInvVarianceUid); + EXPECT_EQ(attrs->epsilon_tensor_uid, expectedEpsilonUid); + EXPECT_EQ(attrs->dx_tensor_uid, expectedDxUid); + EXPECT_EQ(attrs->dscale_tensor_uid, expectedDscaleUid); + EXPECT_EQ(attrs->dbias_tensor_uid, expectedDbiasUid); + } + +protected: + std::unique_ptr _wrapper = nullptr; + mutable MockHandle _mockHandle; + + void SetUp() override + { + _wrapper = createDescriptor(); + } + + void TearDown() override + { + _wrapper.reset(); + } +}; + +TEST_F(TestGraphDescriptorLayernormBackward, BuildFromSingleOperation) +{ + auto dyDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DY_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DY_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DY_STRIDES)); + auto xDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_X_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_X_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_X_STRIDES)); + auto scaleDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_SCALE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_STRIDES)); + auto meanDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_MEAN_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_STRIDES)); + auto invVarianceDesc + = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_STRIDES)); + auto epsilonDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_STRIDES)); + auto dxDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DX_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DX_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DX_STRIDES)); + auto dscaleDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_STRIDES)); + auto dbiasDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_STRIDES)); + auto opDesc = createFinalizedLayernormBackwardOp(dyDesc.get(), + xDesc.get(), + scaleDesc.get(), + meanDesc.get(), + invVarianceDesc.get(), + epsilonDesc.get(), + dxDesc.get(), + dscaleDesc.get(), + dbiasDesc.get()); + + auto desc = getDescriptor(); + setHandle(); + + std::array ops = {opDesc.get()}; + ASSERT_NO_THROW(desc->setAttribute(HIPDNN_ATTR_OPERATIONGRAPH_OPS, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + static_cast(ops.data()))); + ASSERT_NO_THROW(desc->finalize()); + + // Verify the built graph + auto serialized = desc->getSerializedGraph(); + ASSERT_NE(serialized.ptr, nullptr); + ASSERT_GT(serialized.size, 0UL); + + flatbuffers::Verifier verifier(static_cast(serialized.ptr), serialized.size); + ASSERT_TRUE(verifier.VerifyBuffer()); + + const auto graphT = UnPackGraph(serialized.ptr); + + ASSERT_EQ(graphT->nodes.size(), 1); + ASSERT_EQ(graphT->tensors.size(), 9); + + // Verify tensor attributes + verifyTensor(findTensorByUid(*graphT, K_LAYERNORMBACKWARD_TENSOR_DY_UID), + K_LAYERNORMBACKWARD_TENSOR_DY_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DY_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DY_STRIDES), + DataType::FLOAT); + verifyTensor(findTensorByUid(*graphT, K_LAYERNORMBACKWARD_TENSOR_X_UID), + K_LAYERNORMBACKWARD_TENSOR_X_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_X_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_X_STRIDES), + DataType::FLOAT); + verifyTensor(findTensorByUid(*graphT, K_LAYERNORMBACKWARD_TENSOR_SCALE_UID), + K_LAYERNORMBACKWARD_TENSOR_SCALE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_STRIDES), + DataType::FLOAT); + verifyTensor(findTensorByUid(*graphT, K_LAYERNORMBACKWARD_TENSOR_MEAN_UID), + K_LAYERNORMBACKWARD_TENSOR_MEAN_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_STRIDES), + DataType::FLOAT); + verifyTensor(findTensorByUid(*graphT, K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID), + K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_STRIDES), + DataType::FLOAT); + verifyTensor(findTensorByUid(*graphT, K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID), + K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_STRIDES), + DataType::FLOAT); + verifyTensor(findTensorByUid(*graphT, K_LAYERNORMBACKWARD_TENSOR_DX_UID), + K_LAYERNORMBACKWARD_TENSOR_DX_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DX_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DX_STRIDES), + DataType::FLOAT); + verifyTensor(findTensorByUid(*graphT, K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID), + K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_STRIDES), + DataType::FLOAT); + verifyTensor(findTensorByUid(*graphT, K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID), + K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_STRIDES), + DataType::FLOAT); + + // Verify node attributes + ASSERT_EQ(graphT->nodes[0]->attributes.type, NodeAttributes::LayernormBackwardAttributes); + + auto* attrs = graphT->nodes[0]->attributes.AsLayernormBackwardAttributes(); + ASSERT_NE(attrs, nullptr); + EXPECT_EQ(graphT->nodes[0]->compute_data_type, DataType::FLOAT); + + // Verify tensor UID references + EXPECT_EQ(attrs->dy_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DY_UID); + EXPECT_EQ(attrs->x_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_X_UID); + EXPECT_EQ(attrs->scale_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_SCALE_UID); + EXPECT_EQ(attrs->mean_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_MEAN_UID); + EXPECT_EQ(attrs->inv_variance_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID); + EXPECT_EQ(attrs->epsilon_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID); + EXPECT_EQ(attrs->dx_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DX_UID); + EXPECT_EQ(attrs->dscale_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID); + EXPECT_EQ(attrs->dbias_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID); + + // Verify default node name is empty + EXPECT_TRUE(graphT->nodes[0]->name.empty()); +} + +TEST_F(TestGraphDescriptorLayernormBackward, ComputeDataTypePreserved) +{ + auto dyDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DY_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DY_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DY_STRIDES)); + auto xDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_X_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_X_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_X_STRIDES)); + auto scaleDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_SCALE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_STRIDES)); + auto meanDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_MEAN_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_STRIDES)); + auto invVarianceDesc + = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_STRIDES)); + auto epsilonDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_STRIDES)); + auto dxDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DX_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DX_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DX_STRIDES)); + auto dscaleDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_STRIDES)); + auto dbiasDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_STRIDES)); + auto opDesc = createFinalizedLayernormBackwardOp(dyDesc.get(), + xDesc.get(), + scaleDesc.get(), + meanDesc.get(), + invVarianceDesc.get(), + epsilonDesc.get(), + dxDesc.get(), + dscaleDesc.get(), + dbiasDesc.get(), + HIPDNN_DATA_HALF); + + auto desc = getDescriptor(); + setHandle(); + + std::array ops = {opDesc.get()}; + desc->setAttribute(HIPDNN_ATTR_OPERATIONGRAPH_OPS, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + static_cast(ops.data())); + desc->finalize(); + + auto serialized = desc->getSerializedGraph(); + const auto graphT = UnPackGraph(serialized.ptr); + + ASSERT_EQ(graphT->nodes.size(), 1); + EXPECT_EQ(graphT->nodes[0]->compute_data_type, DataType::HALF); +} + +TEST_F(TestGraphDescriptorLayernormBackward, LayernormBackwardAttributesPreserved) +{ + auto dyDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DY_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DY_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DY_STRIDES)); + auto xDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_X_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_X_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_X_STRIDES)); + auto scaleDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_SCALE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_STRIDES)); + auto meanDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_MEAN_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_STRIDES)); + auto invVarianceDesc + = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_STRIDES)); + auto epsilonDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_STRIDES)); + auto dxDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DX_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DX_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DX_STRIDES)); + auto dscaleDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_STRIDES)); + auto dbiasDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_STRIDES)); + + // Create op with non-default parameters to test graph roundtrip + auto wrapper = createDescriptor(); + auto opDesc = wrapper->asDescriptor(); + + HipdnnBackendDescriptor* dyPtr = dyDesc.get(); + opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + static_cast(&dyPtr)); + HipdnnBackendDescriptor* xPtr = xDesc.get(); + opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + static_cast(&xPtr)); + HipdnnBackendDescriptor* scalePtr = scaleDesc.get(); + opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + static_cast(&scalePtr)); + HipdnnBackendDescriptor* meanPtr = meanDesc.get(); + opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + static_cast(&meanPtr)); + HipdnnBackendDescriptor* invVariancePtr = invVarianceDesc.get(); + opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + static_cast(&invVariancePtr)); + HipdnnBackendDescriptor* epsilonPtr = epsilonDesc.get(); + opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + static_cast(&epsilonPtr)); + HipdnnBackendDescriptor* dxPtr = dxDesc.get(); + opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + static_cast(&dxPtr)); + HipdnnBackendDescriptor* dscalePtr = dscaleDesc.get(); + opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + static_cast(&dscalePtr)); + HipdnnBackendDescriptor* dbiasPtr = dbiasDesc.get(); + opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + static_cast(&dbiasPtr)); + + auto computeType = HIPDNN_DATA_FLOAT; + opDesc->setAttribute( + HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, HIPDNN_TYPE_DATA_TYPE, 1, &computeType); + + // Set operation name + const std::string opName = "test_layernormbackward"; + opDesc->setAttribute(HIPDNN_ATTR_OPERATION_NAME_EXT, + HIPDNN_TYPE_CHAR, + static_cast(opName.size()), + opName.c_str()); + opDesc->finalize(); + + auto desc = getDescriptor(); + setHandle(); + + std::array ops = {wrapper.get()}; + desc->setAttribute(HIPDNN_ATTR_OPERATIONGRAPH_OPS, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + static_cast(ops.data())); + desc->finalize(); + + auto serialized = desc->getSerializedGraph(); + const auto graphT = UnPackGraph(serialized.ptr); + + ASSERT_EQ(graphT->nodes.size(), 1); + ASSERT_EQ(graphT->tensors.size(), 9); + + // Verify tensors + verifyTensor(findTensorByUid(*graphT, K_LAYERNORMBACKWARD_TENSOR_DY_UID), + K_LAYERNORMBACKWARD_TENSOR_DY_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DY_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DY_STRIDES), + DataType::FLOAT); + verifyTensor(findTensorByUid(*graphT, K_LAYERNORMBACKWARD_TENSOR_X_UID), + K_LAYERNORMBACKWARD_TENSOR_X_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_X_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_X_STRIDES), + DataType::FLOAT); + verifyTensor(findTensorByUid(*graphT, K_LAYERNORMBACKWARD_TENSOR_SCALE_UID), + K_LAYERNORMBACKWARD_TENSOR_SCALE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_STRIDES), + DataType::FLOAT); + verifyTensor(findTensorByUid(*graphT, K_LAYERNORMBACKWARD_TENSOR_MEAN_UID), + K_LAYERNORMBACKWARD_TENSOR_MEAN_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_STRIDES), + DataType::FLOAT); + verifyTensor(findTensorByUid(*graphT, K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID), + K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_STRIDES), + DataType::FLOAT); + verifyTensor(findTensorByUid(*graphT, K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID), + K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_STRIDES), + DataType::FLOAT); + verifyTensor(findTensorByUid(*graphT, K_LAYERNORMBACKWARD_TENSOR_DX_UID), + K_LAYERNORMBACKWARD_TENSOR_DX_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DX_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DX_STRIDES), + DataType::FLOAT); + verifyTensor(findTensorByUid(*graphT, K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID), + K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_STRIDES), + DataType::FLOAT); + verifyTensor(findTensorByUid(*graphT, K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID), + K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_STRIDES), + DataType::FLOAT); + + // Verify node with non-default attribute values + verifyLayernormBackwardNode(*graphT->nodes[0], + DataType::FLOAT, + K_LAYERNORMBACKWARD_TENSOR_DY_UID, + K_LAYERNORMBACKWARD_TENSOR_X_UID, + K_LAYERNORMBACKWARD_TENSOR_SCALE_UID, + K_LAYERNORMBACKWARD_TENSOR_MEAN_UID, + K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID, + K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID, + K_LAYERNORMBACKWARD_TENSOR_DX_UID, + K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID, + K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID); + + // Verify operation name + EXPECT_EQ(graphT->nodes[0]->name, "test_layernormbackward"); +} + +TEST_F(TestGraphDescriptorLayernormBackward, OperationNamePreservedInSerialization) +{ + auto dyDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DY_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DY_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DY_STRIDES)); + auto xDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_X_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_X_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_X_STRIDES)); + auto scaleDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_SCALE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_STRIDES)); + auto meanDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_MEAN_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_STRIDES)); + auto invVarianceDesc + = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_STRIDES)); + auto epsilonDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_STRIDES)); + auto dxDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DX_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DX_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DX_STRIDES)); + auto dscaleDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_STRIDES)); + auto dbiasDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_STRIDES)); + auto opDesc = createFinalizedLayernormBackwardOp(dyDesc.get(), + xDesc.get(), + scaleDesc.get(), + meanDesc.get(), + invVarianceDesc.get(), + epsilonDesc.get(), + dxDesc.get(), + dscaleDesc.get(), + dbiasDesc.get(), + HIPDNN_DATA_FLOAT, + "test_layernormbackward_name"); + + auto desc = getDescriptor(); + setHandle(); + + std::array ops = {opDesc.get()}; + desc->setAttribute(HIPDNN_ATTR_OPERATIONGRAPH_OPS, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + static_cast(ops.data())); + desc->finalize(); + + auto serialized = desc->getSerializedGraph(); + const auto graphT = UnPackGraph(serialized.ptr); + + ASSERT_EQ(graphT->nodes.size(), 1u); + EXPECT_EQ(graphT->nodes[0]->name, "test_layernormbackward_name"); +} + +TEST_F(TestGraphDescriptorLayernormBackward, OperationNameRoundTripThroughLifting) +{ + auto dyDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DY_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DY_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DY_STRIDES)); + auto xDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_X_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_X_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_X_STRIDES)); + auto scaleDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_SCALE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_STRIDES)); + auto meanDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_MEAN_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_STRIDES)); + auto invVarianceDesc + = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_STRIDES)); + auto epsilonDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_STRIDES)); + auto dxDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DX_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DX_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DX_STRIDES)); + auto dscaleDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_STRIDES)); + auto dbiasDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_STRIDES)); + auto opDesc = createFinalizedLayernormBackwardOp(dyDesc.get(), + xDesc.get(), + scaleDesc.get(), + meanDesc.get(), + invVarianceDesc.get(), + epsilonDesc.get(), + dxDesc.get(), + dscaleDesc.get(), + dbiasDesc.get(), + HIPDNN_DATA_FLOAT, + "test_layernormbackward_lifting"); + + auto desc = getDescriptor(); + setHandle(); + + std::array ops = {opDesc.get()}; + desc->setAttribute(HIPDNN_ATTR_OPERATIONGRAPH_OPS, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + static_cast(ops.data())); + desc->finalize(); + + // Serialize the graph + auto serialized = desc->getSerializedGraph(); + std::vector bytes(static_cast(serialized.ptr), + static_cast(serialized.ptr) + serialized.size); + + // Deserialize into a new GraphDescriptor (lifting path) + auto liftedWrapper = createDescriptor(); + auto liftedDesc = liftedWrapper->asDescriptor(); + liftedDesc->deserializeGraph(bytes.data(), bytes.size()); + + hipdnnHandle_t handle = &_mockHandle; + liftedDesc->setAttribute(HIPDNN_ATTR_OPERATIONGRAPH_HANDLE, + HIPDNN_TYPE_HANDLE, + 1, + static_cast(&handle)); + liftedDesc->finalize(); + + // Re-serialize and verify name survived the round-trip + auto reSerialized = liftedDesc->getSerializedGraph(); + auto graphT = UnPackGraph(reSerialized.ptr); + + ASSERT_EQ(graphT->nodes.size(), 1u); + EXPECT_EQ(graphT->nodes[0]->name, "test_layernormbackward_lifting"); +} + +} // namespace diff --git a/projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationDescriptor.cpp b/projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationDescriptor.cpp new file mode 100644 index 000000000000..a48fa9eb21f7 --- /dev/null +++ b/projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationDescriptor.cpp @@ -0,0 +1,1102 @@ +// Copyright © Advanced Micro Devices, Inc., or its affiliates. +// SPDX-License-Identifier: MIT + +#include "DescriptorTestUtils.hpp" +#include "HipdnnOperationType.h" +#include "TensorDescriptorTestUtils.hpp" +#include "TestMacros.hpp" +#include "descriptors/LayernormBackwardOperationDescriptor.hpp" +#include "descriptors/TensorDescriptor.hpp" +#include "hipdnn_backend.h" + +#include +#include +#include +#include +#include + +#include + +#include +#include + +using namespace hipdnn_backend; +using namespace hipdnn_backend::test_utilities; +using namespace hipdnn_flatbuffers_sdk::data_objects; +using namespace hipdnn_tests::constants; +using hipdnn_tests::toVec; + +class TestLayernormBackwardOperationDescriptor : public ::testing::Test +{ +public: + std::shared_ptr getDescriptor() const + { + return _wrapper->asDescriptor(); + } + + void setTensors() const + { + auto desc = getDescriptor(); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dyDesc); + desc->setAttribute( + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_xDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_scaleDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_meanDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_invVarianceDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_epsilonDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dxDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dscaleDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dbiasDesc); + } + + void setLayernormBackwardParams() const + { + auto desc = getDescriptor(); + } + + void setRequiredAttributes() const + { + setTensors(); + setLayernormBackwardParams(); + auto computeType = HIPDNN_DATA_FLOAT; + getDescriptor()->setAttribute( + HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, HIPDNN_TYPE_DATA_TYPE, 1, &computeType); + } + + void makeFinalized() const + { + setRequiredAttributes(); + getDescriptor()->finalize(); + } + +protected: + std::unique_ptr _wrapper = nullptr; + std::unique_ptr _dyDesc = nullptr; + std::unique_ptr _xDesc = nullptr; + std::unique_ptr _scaleDesc = nullptr; + std::unique_ptr _meanDesc = nullptr; + std::unique_ptr _invVarianceDesc = nullptr; + std::unique_ptr _epsilonDesc = nullptr; + std::unique_ptr _dxDesc = nullptr; + std::unique_ptr _dscaleDesc = nullptr; + std::unique_ptr _dbiasDesc = nullptr; + std::unique_ptr _unfinalizedTensor = nullptr; + + void SetUp() override + { + _wrapper = createDescriptor(); + _dyDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DY_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DY_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DY_STRIDES)); + _xDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_X_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_X_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_X_STRIDES)); + _scaleDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_SCALE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_STRIDES)); + _meanDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_MEAN_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_STRIDES)); + _invVarianceDesc + = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_STRIDES)); + _epsilonDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_STRIDES)); + _dxDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DX_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DX_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DX_STRIDES)); + _dscaleDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_STRIDES)); + _dbiasDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID, + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_DIMS), + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_STRIDES)); + _unfinalizedTensor = createDescriptor(); + } + + void TearDown() override + { + _wrapper.reset(); + _dyDesc.reset(); + _xDesc.reset(); + _scaleDesc.reset(); + _meanDesc.reset(); + _invVarianceDesc.reset(); + _epsilonDesc.reset(); + _dxDesc.reset(); + _dscaleDesc.reset(); + _dbiasDesc.reset(); + _unfinalizedTensor.reset(); + } +}; + +// ============================================================================= +// Lifecycle Tests +// ============================================================================= + +TEST_F(TestLayernormBackwardOperationDescriptor, CreateDescriptor) +{ + auto desc = getDescriptor(); + ASSERT_NE(desc, nullptr); + ASSERT_FALSE(desc->isFinalized()); + ASSERT_EQ(desc->getType(), HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeWithRequiredAttributes) +{ + setRequiredAttributes(); + ASSERT_NO_THROW(getDescriptor()->finalize()); + ASSERT_TRUE(getDescriptor()->isFinalized()); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeFailsWithoutDyTensor) +{ + auto desc = getDescriptor(); + desc->setAttribute( + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_xDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_scaleDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_meanDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_invVarianceDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_epsilonDesc); + desc->setAttribute( + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dxDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dscaleDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dbiasDesc); + setLayernormBackwardParams(); + + ASSERT_THROW_HIPDNN_STATUS(desc->finalize(), HIPDNN_STATUS_BAD_PARAM); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeFailsWithoutXTensor) +{ + auto desc = getDescriptor(); + desc->setAttribute( + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dyDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_scaleDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_meanDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_invVarianceDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_epsilonDesc); + desc->setAttribute( + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dxDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dscaleDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dbiasDesc); + setLayernormBackwardParams(); + + ASSERT_THROW_HIPDNN_STATUS(desc->finalize(), HIPDNN_STATUS_BAD_PARAM); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeFailsWithoutScaleTensor) +{ + auto desc = getDescriptor(); + desc->setAttribute( + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dyDesc); + desc->setAttribute( + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_xDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_meanDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_invVarianceDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_epsilonDesc); + desc->setAttribute( + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dxDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dscaleDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dbiasDesc); + setLayernormBackwardParams(); + + ASSERT_THROW_HIPDNN_STATUS(desc->finalize(), HIPDNN_STATUS_BAD_PARAM); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeFailsWithoutDxTensor) +{ + auto desc = getDescriptor(); + desc->setAttribute( + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dyDesc); + desc->setAttribute( + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_xDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_scaleDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_meanDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_invVarianceDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_epsilonDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dscaleDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dbiasDesc); + setLayernormBackwardParams(); + + ASSERT_THROW_HIPDNN_STATUS(desc->finalize(), HIPDNN_STATUS_BAD_PARAM); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeFailsWithoutDscaleTensor) +{ + auto desc = getDescriptor(); + desc->setAttribute( + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dyDesc); + desc->setAttribute( + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_xDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_scaleDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_meanDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_invVarianceDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_epsilonDesc); + desc->setAttribute( + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dxDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dbiasDesc); + setLayernormBackwardParams(); + + ASSERT_THROW_HIPDNN_STATUS(desc->finalize(), HIPDNN_STATUS_BAD_PARAM); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeFailsWithoutDbiasTensor) +{ + auto desc = getDescriptor(); + desc->setAttribute( + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dyDesc); + desc->setAttribute( + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_xDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_scaleDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_meanDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_invVarianceDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_epsilonDesc); + desc->setAttribute( + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dxDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dscaleDesc); + setLayernormBackwardParams(); + + ASSERT_THROW_HIPDNN_STATUS(desc->finalize(), HIPDNN_STATUS_BAD_PARAM); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeFailsWithoutComputeType) +{ + setTensors(); + setLayernormBackwardParams(); + ASSERT_THROW_HIPDNN_STATUS(getDescriptor()->finalize(), HIPDNN_STATUS_BAD_PARAM); +} + +// ============================================================================= +// SetAttribute Tests - Tensor Descriptors +// ============================================================================= + +TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorDy) +{ + auto desc = getDescriptor(); + ASSERT_NO_THROW(desc->setAttribute( + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dyDesc)); + + // Verify UID extracted via getData() + ASSERT_EQ(desc->getData().dy_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DY_UID); + ASSERT_NE(desc->getDyDesc(), nullptr); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorX) +{ + auto desc = getDescriptor(); + ASSERT_NO_THROW(desc->setAttribute( + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_xDesc)); + + ASSERT_EQ(desc->getData().x_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_X_UID); + ASSERT_NE(desc->getXDesc(), nullptr); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorScale) +{ + auto desc = getDescriptor(); + ASSERT_NO_THROW(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_scaleDesc)); + + ASSERT_EQ(desc->getData().scale_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_SCALE_UID); + ASSERT_NE(desc->getScaleDesc(), nullptr); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorMean) +{ + auto desc = getDescriptor(); + ASSERT_NO_THROW(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_meanDesc)); + + ASSERT_EQ(desc->getData().mean_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_MEAN_UID); + ASSERT_NE(desc->getMeanDesc(), nullptr); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorInvVariance) +{ + auto desc = getDescriptor(); + ASSERT_NO_THROW(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_invVarianceDesc)); + + ASSERT_EQ(desc->getData().inv_variance_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID); + ASSERT_NE(desc->getInvVarianceDesc(), nullptr); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorEpsilon) +{ + auto desc = getDescriptor(); + ASSERT_NO_THROW(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_epsilonDesc)); + + ASSERT_EQ(desc->getData().epsilon_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID); + ASSERT_NE(desc->getEpsilonDesc(), nullptr); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorDx) +{ + auto desc = getDescriptor(); + ASSERT_NO_THROW(desc->setAttribute( + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dxDesc)); + + ASSERT_EQ(desc->getData().dx_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DX_UID); + ASSERT_NE(desc->getDxDesc(), nullptr); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorDscale) +{ + auto desc = getDescriptor(); + ASSERT_NO_THROW(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dscaleDesc)); + + ASSERT_EQ(desc->getData().dscale_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID); + ASSERT_NE(desc->getDscaleDesc(), nullptr); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorDbias) +{ + auto desc = getDescriptor(); + ASSERT_NO_THROW(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dbiasDesc)); + + ASSERT_EQ(desc->getData().dbias_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID); + ASSERT_NE(desc->getDbiasDesc(), nullptr); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorFailsNotFinalized) +{ + auto desc = getDescriptor(); + ASSERT_THROW_HIPDNN_STATUS(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_unfinalizedTensor), + HIPDNN_STATUS_BAD_PARAM_NOT_FINALIZED); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorFailsWrongType) +{ + auto desc = getDescriptor(); + ASSERT_THROW_HIPDNN_STATUS( + desc->setAttribute( + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, HIPDNN_TYPE_INT64, 1, &_dyDesc), + HIPDNN_STATUS_BAD_PARAM); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorFailsWrongElementCount) +{ + auto desc = getDescriptor(); + ASSERT_THROW_HIPDNN_STATUS(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 2, + &_dyDesc), + HIPDNN_STATUS_BAD_PARAM); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorFailsNullPointer) +{ + auto desc = getDescriptor(); + ASSERT_THROW_HIPDNN_STATUS(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + nullptr), + HIPDNN_STATUS_BAD_PARAM_NULL_POINTER); +} + +// ============================================================================= +// SetAttribute Tests - Data Fields +// ============================================================================= + +TEST_F(TestLayernormBackwardOperationDescriptor, SetComputeDataType) +{ + auto desc = getDescriptor(); + auto computeType = HIPDNN_DATA_FLOAT; + + ASSERT_NO_THROW(desc->setAttribute( + HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, HIPDNN_TYPE_DATA_TYPE, 1, &computeType)); + + ASSERT_EQ(desc->getComputeDataType(), DataType::FLOAT); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, SetComputeDataTypeWrongElementCount) +{ + auto desc = getDescriptor(); + auto computeType = HIPDNN_DATA_FLOAT; + + ASSERT_THROW_HIPDNN_STATUS( + desc->setAttribute( + HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, HIPDNN_TYPE_DATA_TYPE, 2, &computeType), + HIPDNN_STATUS_BAD_PARAM); +} + +// ============================================================================= +// SetAttribute Error Cases +// ============================================================================= + +TEST_F(TestLayernormBackwardOperationDescriptor, SetAttributeFailsAfterFinalize) +{ + makeFinalized(); + auto desc = getDescriptor(); + + ASSERT_THROW_HIPDNN_STATUS(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dyDesc), + HIPDNN_STATUS_NOT_INITIALIZED); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, SetAttributeUnsupported) +{ + auto desc = getDescriptor(); + int64_t dummy = 0; + + ASSERT_THROW_HIPDNN_STATUS( + desc->setAttribute(HIPDNN_ATTR_ENGINEHEUR_MODE, HIPDNN_TYPE_INT64, 1, &dummy), + HIPDNN_STATUS_NOT_SUPPORTED); +} + +// ============================================================================= +// GetAttribute Tests - Tensor Descriptors +// ============================================================================= + +TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeTensorDescriptor) +{ + makeFinalized(); + auto desc = getDescriptor(); + + HipdnnBackendDescriptor* retrievedDy = nullptr; + int64_t elementCount = 0; + ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &elementCount, + static_cast(&retrievedDy))); + + ASSERT_EQ(elementCount, 1); + ASSERT_NE(retrievedDy, nullptr); + const std::unique_ptr guardDy(retrievedDy); +} + +// ============================================================================= +// GetAttribute Tests - Data Fields +// ============================================================================= + +TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeComputeType) +{ + auto desc = getDescriptor(); + setRequiredAttributes(); + auto computeType = HIPDNN_DATA_HALF; + desc->setAttribute( + HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, HIPDNN_TYPE_DATA_TYPE, 1, &computeType); + desc->finalize(); + + hipdnnDataType_t retrieved = HIPDNN_DATA_FLOAT; + int64_t elementCount = 0; + ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, + HIPDNN_TYPE_DATA_TYPE, + 1, + &elementCount, + &retrieved)); + + ASSERT_EQ(retrieved, HIPDNN_DATA_HALF); + ASSERT_EQ(elementCount, 1); +} + +// ============================================================================= +// GetAttribute Error Cases +// ============================================================================= + +TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeFailsBeforeFinalize) +{ + auto desc = getDescriptor(); + setRequiredAttributes(); + + HipdnnBackendDescriptor* dummy = nullptr; + ASSERT_THROW_HIPDNN_STATUS(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + nullptr, + &dummy), + HIPDNN_STATUS_NOT_INITIALIZED); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeFailsNullPointer) +{ + makeFinalized(); + auto desc = getDescriptor(); + + ASSERT_THROW_HIPDNN_STATUS(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + nullptr, + nullptr), + HIPDNN_STATUS_BAD_PARAM_NULL_POINTER); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeUnsupported) +{ + makeFinalized(); + auto desc = getDescriptor(); + int64_t dummy = 0; + + ASSERT_THROW_HIPDNN_STATUS( + desc->getAttribute(HIPDNN_ATTR_ENGINEHEUR_MODE, HIPDNN_TYPE_INT64, 1, nullptr, &dummy), + HIPDNN_STATUS_NOT_SUPPORTED); +} + +// ============================================================================= +// GetAttribute Query Mode Tests +// ============================================================================= + +TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeTensorDyQueryReturnsOne) +{ + makeFinalized(); + auto desc = getDescriptor(); + + int64_t elementCount = 0; + ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 0, + &elementCount, + nullptr)); + ASSERT_EQ(elementCount, 1); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeTensorXQueryReturnsOne) +{ + makeFinalized(); + auto desc = getDescriptor(); + + int64_t elementCount = 0; + ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 0, + &elementCount, + nullptr)); + ASSERT_EQ(elementCount, 1); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeTensorScaleQueryReturnsOne) +{ + makeFinalized(); + auto desc = getDescriptor(); + + int64_t elementCount = 0; + ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 0, + &elementCount, + nullptr)); + ASSERT_EQ(elementCount, 1); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeTensorMeanQueryReturnsOne) +{ + makeFinalized(); + auto desc = getDescriptor(); + + int64_t elementCount = 0; + ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 0, + &elementCount, + nullptr)); + ASSERT_EQ(elementCount, 1); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeTensorInvVarianceQueryReturnsOne) +{ + makeFinalized(); + auto desc = getDescriptor(); + + int64_t elementCount = 0; + ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 0, + &elementCount, + nullptr)); + ASSERT_EQ(elementCount, 1); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeTensorEpsilonQueryReturnsOne) +{ + makeFinalized(); + auto desc = getDescriptor(); + + int64_t elementCount = 0; + ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 0, + &elementCount, + nullptr)); + ASSERT_EQ(elementCount, 1); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeTensorDxQueryReturnsOne) +{ + makeFinalized(); + auto desc = getDescriptor(); + + int64_t elementCount = 0; + ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 0, + &elementCount, + nullptr)); + ASSERT_EQ(elementCount, 1); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeTensorDscaleQueryReturnsOne) +{ + makeFinalized(); + auto desc = getDescriptor(); + + int64_t elementCount = 0; + ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 0, + &elementCount, + nullptr)); + ASSERT_EQ(elementCount, 1); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeTensorDbiasQueryReturnsOne) +{ + makeFinalized(); + auto desc = getDescriptor(); + + int64_t elementCount = 0; + ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 0, + &elementCount, + nullptr)); + ASSERT_EQ(elementCount, 1); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeComputeTypeQueryReturnsOne) +{ + makeFinalized(); + auto desc = getDescriptor(); + + int64_t elementCount = 0; + ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, + HIPDNN_TYPE_DATA_TYPE, + 0, + &elementCount, + nullptr)); + ASSERT_EQ(elementCount, 1); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeTensorQueryFailsNullElementCount) +{ + makeFinalized(); + auto desc = getDescriptor(); + + ASSERT_THROW_HIPDNN_STATUS(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 0, + nullptr, + nullptr), + HIPDNN_STATUS_BAD_PARAM_NULL_POINTER); +} + +// ============================================================================= +// Accessor Tests +// ============================================================================= + +TEST_F(TestLayernormBackwardOperationDescriptor, FinalizePreservesTensorReferences) +{ + makeFinalized(); + auto desc = getDescriptor(); + + // Verify the tensor descriptors are preserved + ASSERT_NE(desc->getDyDesc(), nullptr); + ASSERT_NE(desc->getXDesc(), nullptr); + ASSERT_NE(desc->getScaleDesc(), nullptr); + ASSERT_NE(desc->getMeanDesc(), nullptr); + ASSERT_NE(desc->getInvVarianceDesc(), nullptr); + ASSERT_NE(desc->getEpsilonDesc(), nullptr); + ASSERT_NE(desc->getDxDesc(), nullptr); + ASSERT_NE(desc->getDscaleDesc(), nullptr); + ASSERT_NE(desc->getDbiasDesc(), nullptr); + + // Verify UIDs match + ASSERT_EQ(desc->getDyDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DY_UID); + ASSERT_EQ(desc->getXDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_X_UID); + ASSERT_EQ(desc->getScaleDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_SCALE_UID); + ASSERT_EQ(desc->getMeanDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_MEAN_UID); + ASSERT_EQ(desc->getInvVarianceDesc()->getData().uid, + K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID); + ASSERT_EQ(desc->getEpsilonDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID); + ASSERT_EQ(desc->getDxDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DX_UID); + ASSERT_EQ(desc->getDscaleDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID); + ASSERT_EQ(desc->getDbiasDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID); +} + +// ============================================================================= +// ToString Test +// ============================================================================= + +TEST_F(TestLayernormBackwardOperationDescriptor, ToStringContainsExpectedInfo) +{ + setRequiredAttributes(); + auto desc = getDescriptor(); + + const std::string str = desc->toString(); + ASSERT_NE(str.find("LayernormBackwardOperationDescriptor"), std::string::npos); + ASSERT_NE(str.find("dy_uid=" + std::to_string(K_LAYERNORMBACKWARD_TENSOR_DY_UID)), + std::string::npos); + ASSERT_NE(str.find("x_uid=" + std::to_string(K_LAYERNORMBACKWARD_TENSOR_X_UID)), + std::string::npos); + ASSERT_NE(str.find("scale_uid=" + std::to_string(K_LAYERNORMBACKWARD_TENSOR_SCALE_UID)), + std::string::npos); + ASSERT_NE(str.find("mean_uid=" + std::to_string(K_LAYERNORMBACKWARD_TENSOR_MEAN_UID)), + std::string::npos); + ASSERT_NE( + str.find("inv_variance_uid=" + std::to_string(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID)), + std::string::npos); + ASSERT_NE(str.find("epsilon_uid=" + std::to_string(K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID)), + std::string::npos); + ASSERT_NE(str.find("dx_uid=" + std::to_string(K_LAYERNORMBACKWARD_TENSOR_DX_UID)), + std::string::npos); + ASSERT_NE(str.find("dscale_uid=" + std::to_string(K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID)), + std::string::npos); + ASSERT_NE(str.find("dbias_uid=" + std::to_string(K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID)), + std::string::npos); + ASSERT_NE(str.find("compute_data_type="), std::string::npos); +} + +// ============================================================================= +// IGraphOperation Interface Tests +// ============================================================================= + +TEST_F(TestLayernormBackwardOperationDescriptor, GetTensorDescriptorsReturnsAllTensors) +{ + makeFinalized(); + auto desc = getDescriptor(); + + auto tensors = desc->getTensorDescriptors(); + ASSERT_EQ(tensors.size(), 9); + ASSERT_EQ(tensors[0]->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DY_UID); + ASSERT_EQ(tensors[1]->getData().uid, K_LAYERNORMBACKWARD_TENSOR_X_UID); + ASSERT_EQ(tensors[2]->getData().uid, K_LAYERNORMBACKWARD_TENSOR_SCALE_UID); + ASSERT_EQ(tensors[3]->getData().uid, K_LAYERNORMBACKWARD_TENSOR_MEAN_UID); + ASSERT_EQ(tensors[4]->getData().uid, K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID); + ASSERT_EQ(tensors[5]->getData().uid, K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID); + ASSERT_EQ(tensors[6]->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DX_UID); + ASSERT_EQ(tensors[7]->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID); + ASSERT_EQ(tensors[8]->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, BuildNodeProducesCorrectNodeT) +{ + setRequiredAttributes(); + + auto desc = getDescriptor(); + auto computeType = HIPDNN_DATA_FLOAT; + desc->setAttribute( + HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, HIPDNN_TYPE_DATA_TYPE, 1, &computeType); + desc->finalize(); + + auto node = desc->buildNode(); + ASSERT_NE(node, nullptr); + ASSERT_EQ(node->compute_data_type, DataType::FLOAT); + ASSERT_EQ(node->attributes.type, NodeAttributes::LayernormBackwardAttributes); + + auto* attrs = node->attributes.AsLayernormBackwardAttributes(); + ASSERT_NE(attrs, nullptr); + ASSERT_EQ(attrs->dy_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DY_UID); + ASSERT_EQ(attrs->x_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_X_UID); + ASSERT_EQ(attrs->scale_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_SCALE_UID); + ASSERT_EQ(attrs->mean_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_MEAN_UID); + ASSERT_EQ(attrs->inv_variance_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID); + ASSERT_EQ(attrs->epsilon_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID); + ASSERT_EQ(attrs->dx_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DX_UID); + ASSERT_EQ(attrs->dscale_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID); + ASSERT_EQ(attrs->dbias_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, BuildNodeWithHalfComputeType) +{ + setRequiredAttributes(); + + auto desc = getDescriptor(); + auto computeType = HIPDNN_DATA_HALF; + desc->setAttribute( + HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, HIPDNN_TYPE_DATA_TYPE, 1, &computeType); + desc->finalize(); + + auto node = desc->buildNode(); + ASSERT_NE(node, nullptr); + ASSERT_EQ(node->compute_data_type, DataType::HALF); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, + GetTensorDescriptorsOrderIsDyXScaleMeanInvVarianceEpsilonDxDscaleDbias) +{ + makeFinalized(); + auto desc = getDescriptor(); + + auto tensors = desc->getTensorDescriptors(); + ASSERT_EQ(tensors.size(), 9); + // Verify ordering: [DY, X, SCALE, MEAN, INV_VARIANCE, EPSILON, DX, DSCALE, DBIAS] matches UIDs [10, 11, 12, 13, 14, 15, 16, 17, 18] + EXPECT_EQ(tensors[0], desc->getDyDesc()); + EXPECT_EQ(tensors[1], desc->getXDesc()); + EXPECT_EQ(tensors[2], desc->getScaleDesc()); + EXPECT_EQ(tensors[3], desc->getMeanDesc()); + EXPECT_EQ(tensors[4], desc->getInvVarianceDesc()); + EXPECT_EQ(tensors[5], desc->getEpsilonDesc()); + EXPECT_EQ(tensors[6], desc->getDxDesc()); + EXPECT_EQ(tensors[7], desc->getDscaleDesc()); + EXPECT_EQ(tensors[8], desc->getDbiasDesc()); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, TryAsInterfaceReturnsValidGraphOp) +{ + makeFinalized(); + + auto graphOp = _wrapper->tryAsGraphOperation(); + ASSERT_NE(graphOp, nullptr); + + // Verify the returned interface is the same underlying object + auto tensors = graphOp->getTensorDescriptors(); + ASSERT_EQ(tensors.size(), 9); + ASSERT_EQ(tensors[0]->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DY_UID); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, TryAsInterfaceReturnsNullForWrongType) +{ + // TensorDescriptor does not implement IGraphOperation + auto graphOp = _dyDesc->tryAsGraphOperation(); + EXPECT_EQ(graphOp, nullptr); +} + +// ============================================================================= +// Operation Name Tests +// ============================================================================= + +TEST_F(TestLayernormBackwardOperationDescriptor, SetAttributeNameSuccess) +{ + auto desc = getDescriptor(); + const std::string name = "test_layernormbackward_op"; + + ASSERT_NO_THROW(desc->setAttribute(HIPDNN_ATTR_OPERATION_NAME_EXT, + HIPDNN_TYPE_CHAR, + static_cast(name.size()), + name.c_str())); + + // Finalize and verify name round-trips + setRequiredAttributes(); + desc->finalize(); + + int64_t count = 0; + desc->getAttribute(HIPDNN_ATTR_OPERATION_NAME_EXT, HIPDNN_TYPE_CHAR, 0, &count, nullptr); + ASSERT_EQ(count, static_cast(name.size() + 1)); + + std::vector buffer(static_cast(count)); + int64_t actualCount = 0; + desc->getAttribute( + HIPDNN_ATTR_OPERATION_NAME_EXT, HIPDNN_TYPE_CHAR, count, &actualCount, buffer.data()); + EXPECT_STREQ(buffer.data(), "test_layernormbackward_op"); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeNameQueryReturnsSizeInclNull) +{ + auto desc = getDescriptor(); + const std::string name = "my_op"; + desc->setAttribute(HIPDNN_ATTR_OPERATION_NAME_EXT, + HIPDNN_TYPE_CHAR, + static_cast(name.size()), + name.c_str()); + setRequiredAttributes(); + desc->finalize(); + + int64_t count = 0; + desc->getAttribute(HIPDNN_ATTR_OPERATION_NAME_EXT, HIPDNN_TYPE_CHAR, 0, &count, nullptr); + EXPECT_EQ(count, static_cast(name.size() + 1)); +} + +// ============================================================================= +// Operation Type Tests +// ============================================================================= + +TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeOperationTypeReturnsCorrectType) +{ + makeFinalized(); + auto desc = getDescriptor(); + + hipdnnOperationType_ext_t opType = HIPDNN_OPERATION_TYPE_NOT_SET_EXT; + int64_t elementCount = 0; + ASSERT_NO_THROW(desc->getAttribute( + HIPDNN_ATTR_OPERATION_TYPE_EXT, HIPDNN_TYPE_OPERATION_TYPE_EXT, 1, &elementCount, &opType)); + + ASSERT_EQ(elementCount, 1); + EXPECT_EQ(opType, HIPDNN_OPERATION_TYPE_LAYERNORM_BACKWARD_EXT); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeOperationTypeQueryReturnsOne) +{ + makeFinalized(); + auto desc = getDescriptor(); + + int64_t elementCount = 0; + ASSERT_NO_THROW(desc->getAttribute( + HIPDNN_ATTR_OPERATION_TYPE_EXT, HIPDNN_TYPE_OPERATION_TYPE_EXT, 0, &elementCount, nullptr)); + ASSERT_EQ(elementCount, 1); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, BuildNodePreservesName) +{ + setRequiredAttributes(); + auto desc = getDescriptor(); + + const std::string opName = "test_layernormbackward"; + desc->setAttribute(HIPDNN_ATTR_OPERATION_NAME_EXT, + HIPDNN_TYPE_CHAR, + static_cast(opName.size()), + opName.c_str()); + auto computeType = HIPDNN_DATA_FLOAT; + desc->setAttribute( + HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, HIPDNN_TYPE_DATA_TYPE, 1, &computeType); + desc->finalize(); + + auto node = desc->buildNode(); + ASSERT_NE(node, nullptr); + EXPECT_EQ(node->name, "test_layernormbackward"); +} diff --git a/projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationFromNode.cpp b/projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationFromNode.cpp new file mode 100644 index 000000000000..57f5af7306f1 --- /dev/null +++ b/projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationFromNode.cpp @@ -0,0 +1,654 @@ +// Copyright © Advanced Micro Devices, Inc., or its affiliates. +// SPDX-License-Identifier: MIT + +#include "HipdnnOperationType.h" +#include "TensorDescriptorTestUtils.hpp" +#include "TestMacros.hpp" +#include "descriptors/LayernormBackwardOperationDescriptor.hpp" +#include "descriptors/NodeFactory.hpp" +#include "descriptors/ScopedDescriptor.hpp" +#include "descriptors/TensorDescriptor.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace hipdnn_backend; +using namespace hipdnn_flatbuffers_sdk::data_objects; +using namespace hipdnn_tests::constants; +using hipdnn_backend::test_utilities::verifyTensorDescriptor; +using hipdnn_tests::toVec; + +// ============================================================================= +// LayernormBackwardOperationDescriptor::fromNode() Tests +// ============================================================================= + +class TestLayernormBackwardOperationFromNode : public ::testing::Test +{ +protected: + std::unordered_map> _tensorMap; + + void SetUp() override + { + TensorAttributesT dyAttrs; + dyAttrs.uid = K_LAYERNORMBACKWARD_TENSOR_DY_UID; + dyAttrs.data_type = DataType::FLOAT; + dyAttrs.dims = toVec(K_LAYERNORMBACKWARD_TENSOR_DY_DIMS); + dyAttrs.strides = toVec(K_LAYERNORMBACKWARD_TENSOR_DY_STRIDES); + + _tensorMap[K_LAYERNORMBACKWARD_TENSOR_DY_UID] = TensorDescriptor::fromFlatBuffer(dyAttrs); + TensorAttributesT xAttrs; + xAttrs.uid = K_LAYERNORMBACKWARD_TENSOR_X_UID; + xAttrs.data_type = DataType::FLOAT; + xAttrs.dims = toVec(K_LAYERNORMBACKWARD_TENSOR_X_DIMS); + xAttrs.strides = toVec(K_LAYERNORMBACKWARD_TENSOR_X_STRIDES); + + _tensorMap[K_LAYERNORMBACKWARD_TENSOR_X_UID] = TensorDescriptor::fromFlatBuffer(xAttrs); + TensorAttributesT scaleAttrs; + scaleAttrs.uid = K_LAYERNORMBACKWARD_TENSOR_SCALE_UID; + scaleAttrs.data_type = DataType::FLOAT; + scaleAttrs.dims = toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_DIMS); + scaleAttrs.strides = toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_STRIDES); + + _tensorMap[K_LAYERNORMBACKWARD_TENSOR_SCALE_UID] + = TensorDescriptor::fromFlatBuffer(scaleAttrs); + TensorAttributesT meanAttrs; + meanAttrs.uid = K_LAYERNORMBACKWARD_TENSOR_MEAN_UID; + meanAttrs.data_type = DataType::FLOAT; + meanAttrs.dims = toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_DIMS); + meanAttrs.strides = toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_STRIDES); + + _tensorMap[K_LAYERNORMBACKWARD_TENSOR_MEAN_UID] + = TensorDescriptor::fromFlatBuffer(meanAttrs); + TensorAttributesT invVarianceAttrs; + invVarianceAttrs.uid = K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID; + invVarianceAttrs.data_type = DataType::FLOAT; + invVarianceAttrs.dims = toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_DIMS); + invVarianceAttrs.strides = toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_STRIDES); + + _tensorMap[K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID] + = TensorDescriptor::fromFlatBuffer(invVarianceAttrs); + TensorAttributesT epsilonAttrs; + epsilonAttrs.uid = K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID; + epsilonAttrs.data_type = DataType::FLOAT; + epsilonAttrs.dims = toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_DIMS); + epsilonAttrs.strides = toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_STRIDES); + + _tensorMap[K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID] + = TensorDescriptor::fromFlatBuffer(epsilonAttrs); + TensorAttributesT dxAttrs; + dxAttrs.uid = K_LAYERNORMBACKWARD_TENSOR_DX_UID; + dxAttrs.data_type = DataType::FLOAT; + dxAttrs.dims = toVec(K_LAYERNORMBACKWARD_TENSOR_DX_DIMS); + dxAttrs.strides = toVec(K_LAYERNORMBACKWARD_TENSOR_DX_STRIDES); + + _tensorMap[K_LAYERNORMBACKWARD_TENSOR_DX_UID] = TensorDescriptor::fromFlatBuffer(dxAttrs); + TensorAttributesT dscaleAttrs; + dscaleAttrs.uid = K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID; + dscaleAttrs.data_type = DataType::FLOAT; + dscaleAttrs.dims = toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_DIMS); + dscaleAttrs.strides = toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_STRIDES); + + _tensorMap[K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID] + = TensorDescriptor::fromFlatBuffer(dscaleAttrs); + TensorAttributesT dbiasAttrs; + dbiasAttrs.uid = K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID; + dbiasAttrs.data_type = DataType::FLOAT; + dbiasAttrs.dims = toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_DIMS); + dbiasAttrs.strides = toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_STRIDES); + + _tensorMap[K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID] + = TensorDescriptor::fromFlatBuffer(dbiasAttrs); + } + + static hipdnn_flatbuffers_sdk::data_objects::LayernormBackwardAttributesT + createStandardLayernormBackwardAttrs() + { + hipdnn_flatbuffers_sdk::data_objects::LayernormBackwardAttributesT attrs; + attrs.dy_tensor_uid = K_LAYERNORMBACKWARD_TENSOR_DY_UID; + attrs.x_tensor_uid = K_LAYERNORMBACKWARD_TENSOR_X_UID; + attrs.scale_tensor_uid = K_LAYERNORMBACKWARD_TENSOR_SCALE_UID; + attrs.mean_tensor_uid = K_LAYERNORMBACKWARD_TENSOR_MEAN_UID; + attrs.inv_variance_tensor_uid = K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID; + attrs.epsilon_tensor_uid = K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID; + attrs.dx_tensor_uid = K_LAYERNORMBACKWARD_TENSOR_DX_UID; + attrs.dscale_tensor_uid = K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID; + attrs.dbias_tensor_uid = K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID; + attrs.normalized_dim_count = 3; + return attrs; + } + + static NodeT createStandardNode(DataType computeType = DataType::FLOAT) + { + NodeT node; + node.compute_data_type = computeType; + node.attributes.Set(createStandardLayernormBackwardAttrs()); + return node; + } +}; + +TEST_F(TestLayernormBackwardOperationFromNode, CreatesValidFinalizedDescriptor) +{ + auto node = createStandardNode(); + auto desc = LayernormBackwardOperationDescriptor::fromNode(node, _tensorMap); + + ASSERT_NE(desc, nullptr); + ASSERT_TRUE(desc->isFinalized()); + ASSERT_EQ(desc->getType(), HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR); + EXPECT_EQ(desc->getData().dy_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DY_UID); +} + +TEST_F(TestLayernormBackwardOperationFromNode, 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); + const auto rebuiltNode = op->buildNode(); + ASSERT_EQ(rebuiltNode->attributes.type, NodeAttributes::LayernormBackwardAttributes); + auto desc = std::static_pointer_cast(graphOp); + ASSERT_TRUE(desc->isFinalized()); + + // Verify all attributes are correctly populated via the delegated path + EXPECT_EQ(desc->getData().dy_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DY_UID); + EXPECT_EQ(desc->getData().x_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_X_UID); + EXPECT_EQ(desc->getData().scale_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_SCALE_UID); + EXPECT_EQ(desc->getData().mean_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_MEAN_UID); + EXPECT_EQ(desc->getData().inv_variance_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID); + EXPECT_EQ(desc->getData().epsilon_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID); + EXPECT_EQ(desc->getData().dx_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DX_UID); + EXPECT_EQ(desc->getData().dscale_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID); + EXPECT_EQ(desc->getData().dbias_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID); + EXPECT_EQ(desc->getComputeDataType(), DataType::FLOAT); + EXPECT_EQ(desc->getDyDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DY_UID); + EXPECT_EQ(desc->getXDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_X_UID); + EXPECT_EQ(desc->getScaleDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_SCALE_UID); + EXPECT_EQ(desc->getDxDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DX_UID); + EXPECT_EQ(desc->getDscaleDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID); + EXPECT_EQ(desc->getDbiasDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID); + EXPECT_EQ(desc->getMeanDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_MEAN_UID); + EXPECT_EQ(desc->getInvVarianceDesc()->getData().uid, + K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID); + EXPECT_EQ(desc->getEpsilonDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID); +} + +TEST_F(TestLayernormBackwardOperationFromNode, PreservesComputeDataType) +{ + auto node = createStandardNode(DataType::HALF); + auto desc = LayernormBackwardOperationDescriptor::fromNode(node, _tensorMap); + + ASSERT_EQ(desc->getComputeDataType(), DataType::HALF); +} + +TEST_F(TestLayernormBackwardOperationFromNode, SetsTensorReferences) +{ + auto node = createStandardNode(); + auto desc = LayernormBackwardOperationDescriptor::fromNode(node, _tensorMap); + + ASSERT_NE(desc->getDyDesc(), nullptr); + EXPECT_EQ(desc->getDyDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DY_UID); + ASSERT_NE(desc->getXDesc(), nullptr); + EXPECT_EQ(desc->getXDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_X_UID); + ASSERT_NE(desc->getScaleDesc(), nullptr); + EXPECT_EQ(desc->getScaleDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_SCALE_UID); + ASSERT_NE(desc->getDxDesc(), nullptr); + EXPECT_EQ(desc->getDxDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DX_UID); + ASSERT_NE(desc->getDscaleDesc(), nullptr); + EXPECT_EQ(desc->getDscaleDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID); + ASSERT_NE(desc->getDbiasDesc(), nullptr); + EXPECT_EQ(desc->getDbiasDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID); + ASSERT_NE(desc->getMeanDesc(), nullptr); + EXPECT_EQ(desc->getMeanDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_MEAN_UID); + ASSERT_NE(desc->getInvVarianceDesc(), nullptr); + EXPECT_EQ(desc->getInvVarianceDesc()->getData().uid, + K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID); + ASSERT_NE(desc->getEpsilonDesc(), nullptr); + EXPECT_EQ(desc->getEpsilonDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID); +} + +TEST_F(TestLayernormBackwardOperationFromNode, TensorReferencesMatchTensorMap) +{ + auto node = createStandardNode(); + auto desc = LayernormBackwardOperationDescriptor::fromNode(node, _tensorMap); + + EXPECT_EQ(desc->getDyDesc(), _tensorMap[K_LAYERNORMBACKWARD_TENSOR_DY_UID]); + EXPECT_EQ(desc->getXDesc(), _tensorMap[K_LAYERNORMBACKWARD_TENSOR_X_UID]); + EXPECT_EQ(desc->getScaleDesc(), _tensorMap[K_LAYERNORMBACKWARD_TENSOR_SCALE_UID]); + EXPECT_EQ(desc->getDxDesc(), _tensorMap[K_LAYERNORMBACKWARD_TENSOR_DX_UID]); + EXPECT_EQ(desc->getDscaleDesc(), _tensorMap[K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID]); + EXPECT_EQ(desc->getDbiasDesc(), _tensorMap[K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID]); + EXPECT_EQ(desc->getMeanDesc(), _tensorMap[K_LAYERNORMBACKWARD_TENSOR_MEAN_UID]); + EXPECT_EQ(desc->getInvVarianceDesc(), _tensorMap[K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID]); + EXPECT_EQ(desc->getEpsilonDesc(), _tensorMap[K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID]); +} + +TEST_F(TestLayernormBackwardOperationFromNode, SetsTensorReferencesWithFullValues) +{ + auto node = createStandardNode(); + auto desc = LayernormBackwardOperationDescriptor::fromNode(node, _tensorMap); + + ASSERT_NE(desc->getDyDesc(), nullptr); + EXPECT_EQ(desc->getDyDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DY_UID); + EXPECT_EQ(desc->getDyDesc()->getData().data_type, DataType::FLOAT); + EXPECT_EQ(desc->getDyDesc()->getData().dims, (std::vector{16, 64, 32, 32})); + EXPECT_EQ(desc->getDyDesc()->getData().strides, (std::vector{65536, 1024, 32, 1})); + + ASSERT_NE(desc->getXDesc(), nullptr); + EXPECT_EQ(desc->getXDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_X_UID); + EXPECT_EQ(desc->getXDesc()->getData().data_type, DataType::FLOAT); + EXPECT_EQ(desc->getXDesc()->getData().dims, (std::vector{16, 64, 32, 32})); + EXPECT_EQ(desc->getXDesc()->getData().strides, (std::vector{65536, 1024, 32, 1})); + + ASSERT_NE(desc->getScaleDesc(), nullptr); + EXPECT_EQ(desc->getScaleDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_SCALE_UID); + EXPECT_EQ(desc->getScaleDesc()->getData().data_type, DataType::FLOAT); + EXPECT_EQ(desc->getScaleDesc()->getData().dims, (std::vector{1, 64, 32, 32})); + EXPECT_EQ(desc->getScaleDesc()->getData().strides, (std::vector{65536, 1024, 32, 1})); + + ASSERT_NE(desc->getDxDesc(), nullptr); + EXPECT_EQ(desc->getDxDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DX_UID); + EXPECT_EQ(desc->getDxDesc()->getData().data_type, DataType::FLOAT); + EXPECT_EQ(desc->getDxDesc()->getData().dims, (std::vector{16, 64, 32, 32})); + EXPECT_EQ(desc->getDxDesc()->getData().strides, (std::vector{65536, 1024, 32, 1})); + + ASSERT_NE(desc->getDscaleDesc(), nullptr); + EXPECT_EQ(desc->getDscaleDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID); + EXPECT_EQ(desc->getDscaleDesc()->getData().data_type, DataType::FLOAT); + EXPECT_EQ(desc->getDscaleDesc()->getData().dims, (std::vector{1, 64, 32, 32})); + EXPECT_EQ(desc->getDscaleDesc()->getData().strides, (std::vector{65536, 1024, 32, 1})); + + ASSERT_NE(desc->getDbiasDesc(), nullptr); + EXPECT_EQ(desc->getDbiasDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID); + EXPECT_EQ(desc->getDbiasDesc()->getData().data_type, DataType::FLOAT); + EXPECT_EQ(desc->getDbiasDesc()->getData().dims, (std::vector{1, 64, 32, 32})); + EXPECT_EQ(desc->getDbiasDesc()->getData().strides, (std::vector{65536, 1024, 32, 1})); + + ASSERT_NE(desc->getMeanDesc(), nullptr); + EXPECT_EQ(desc->getMeanDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_MEAN_UID); + EXPECT_EQ(desc->getMeanDesc()->getData().data_type, DataType::FLOAT); + EXPECT_EQ(desc->getMeanDesc()->getData().dims, (std::vector{16, 1, 1, 1})); + EXPECT_EQ(desc->getMeanDesc()->getData().strides, (std::vector{1, 1, 1, 1})); + + ASSERT_NE(desc->getInvVarianceDesc(), nullptr); + EXPECT_EQ(desc->getInvVarianceDesc()->getData().uid, + K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID); + EXPECT_EQ(desc->getInvVarianceDesc()->getData().data_type, DataType::FLOAT); + EXPECT_EQ(desc->getInvVarianceDesc()->getData().dims, (std::vector{16, 1, 1, 1})); + EXPECT_EQ(desc->getInvVarianceDesc()->getData().strides, (std::vector{1, 1, 1, 1})); + + ASSERT_NE(desc->getEpsilonDesc(), nullptr); + EXPECT_EQ(desc->getEpsilonDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID); + EXPECT_EQ(desc->getEpsilonDesc()->getData().data_type, DataType::FLOAT); + EXPECT_EQ(desc->getEpsilonDesc()->getData().dims, (std::vector{1})); + EXPECT_EQ(desc->getEpsilonDesc()->getData().strides, (std::vector{1})); +} + +TEST_F(TestLayernormBackwardOperationFromNode, FailsWithMissingDyTensor) +{ + _tensorMap.erase(K_LAYERNORMBACKWARD_TENSOR_DY_UID); + auto node = createStandardNode(); + + ASSERT_THROW_HIPDNN_STATUS(LayernormBackwardOperationDescriptor::fromNode(node, _tensorMap), + HIPDNN_STATUS_INTERNAL_ERROR); +} + +TEST_F(TestLayernormBackwardOperationFromNode, FailsWithMissingXTensor) +{ + _tensorMap.erase(K_LAYERNORMBACKWARD_TENSOR_X_UID); + auto node = createStandardNode(); + + ASSERT_THROW_HIPDNN_STATUS(LayernormBackwardOperationDescriptor::fromNode(node, _tensorMap), + HIPDNN_STATUS_INTERNAL_ERROR); +} + +TEST_F(TestLayernormBackwardOperationFromNode, FailsWithMissingScaleTensor) +{ + _tensorMap.erase(K_LAYERNORMBACKWARD_TENSOR_SCALE_UID); + auto node = createStandardNode(); + + ASSERT_THROW_HIPDNN_STATUS(LayernormBackwardOperationDescriptor::fromNode(node, _tensorMap), + HIPDNN_STATUS_INTERNAL_ERROR); +} + +TEST_F(TestLayernormBackwardOperationFromNode, FailsWithMissingDxTensor) +{ + _tensorMap.erase(K_LAYERNORMBACKWARD_TENSOR_DX_UID); + auto node = createStandardNode(); + + ASSERT_THROW_HIPDNN_STATUS(LayernormBackwardOperationDescriptor::fromNode(node, _tensorMap), + HIPDNN_STATUS_INTERNAL_ERROR); +} + +TEST_F(TestLayernormBackwardOperationFromNode, FailsWithMissingDscaleTensor) +{ + _tensorMap.erase(K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID); + auto node = createStandardNode(); + + ASSERT_THROW_HIPDNN_STATUS(LayernormBackwardOperationDescriptor::fromNode(node, _tensorMap), + HIPDNN_STATUS_INTERNAL_ERROR); +} + +TEST_F(TestLayernormBackwardOperationFromNode, FailsWithMissingDbiasTensor) +{ + _tensorMap.erase(K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID); + auto node = createStandardNode(); + + ASSERT_THROW_HIPDNN_STATUS(LayernormBackwardOperationDescriptor::fromNode(node, _tensorMap), + HIPDNN_STATUS_INTERNAL_ERROR); +} + +TEST_F(TestLayernormBackwardOperationFromNode, SucceedsWithOnlyRequiredTensors) +{ + auto attrs = createStandardLayernormBackwardAttrs(); + attrs.mean_tensor_uid = flatbuffers::nullopt; + attrs.inv_variance_tensor_uid = flatbuffers::nullopt; + attrs.epsilon_tensor_uid = flatbuffers::nullopt; + + NodeT node; + node.compute_data_type = DataType::FLOAT; + node.attributes.Set(attrs); + + auto desc = LayernormBackwardOperationDescriptor::fromNode(node, _tensorMap); + ASSERT_NE(desc, nullptr); + ASSERT_TRUE(desc->isFinalized()); + + // Required tensor getters are non-null + EXPECT_NE(desc->getDyDesc(), nullptr); + EXPECT_NE(desc->getXDesc(), nullptr); + EXPECT_NE(desc->getScaleDesc(), nullptr); + EXPECT_NE(desc->getDxDesc(), nullptr); + EXPECT_NE(desc->getDscaleDesc(), nullptr); + EXPECT_NE(desc->getDbiasDesc(), nullptr); + // Optional tensor getters are null + EXPECT_EQ(desc->getMeanDesc(), nullptr); + EXPECT_EQ(desc->getInvVarianceDesc(), nullptr); + EXPECT_EQ(desc->getEpsilonDesc(), nullptr); +} + +TEST_F(TestLayernormBackwardOperationFromNode, FailsWhenOptionalMeanUidSetButTensorMissing) +{ + _tensorMap.erase(K_LAYERNORMBACKWARD_TENSOR_MEAN_UID); + auto node = createStandardNode(); + + ASSERT_THROW_HIPDNN_STATUS(LayernormBackwardOperationDescriptor::fromNode(node, _tensorMap), + HIPDNN_STATUS_INTERNAL_ERROR); +} + +TEST_F(TestLayernormBackwardOperationFromNode, FailsWhenOptionalInvVarianceUidSetButTensorMissing) +{ + _tensorMap.erase(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID); + auto node = createStandardNode(); + + ASSERT_THROW_HIPDNN_STATUS(LayernormBackwardOperationDescriptor::fromNode(node, _tensorMap), + HIPDNN_STATUS_INTERNAL_ERROR); +} + +TEST_F(TestLayernormBackwardOperationFromNode, FailsWhenOptionalEpsilonUidSetButTensorMissing) +{ + _tensorMap.erase(K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID); + auto node = createStandardNode(); + + ASSERT_THROW_HIPDNN_STATUS(LayernormBackwardOperationDescriptor::fromNode(node, _tensorMap), + HIPDNN_STATUS_INTERNAL_ERROR); +} + +TEST_F(TestLayernormBackwardOperationFromNode, GetTensorDescriptorsReturnsAllTensors) +{ + auto node = createStandardNode(); + auto desc = LayernormBackwardOperationDescriptor::fromNode(node, _tensorMap); + + auto tensors = desc->getTensorDescriptors(); + ASSERT_EQ(tensors.size(), 9); + EXPECT_EQ(tensors[0]->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DY_UID); + EXPECT_EQ(tensors[1]->getData().uid, K_LAYERNORMBACKWARD_TENSOR_X_UID); + EXPECT_EQ(tensors[2]->getData().uid, K_LAYERNORMBACKWARD_TENSOR_SCALE_UID); + EXPECT_EQ(tensors[3]->getData().uid, K_LAYERNORMBACKWARD_TENSOR_MEAN_UID); + EXPECT_EQ(tensors[4]->getData().uid, K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID); + EXPECT_EQ(tensors[5]->getData().uid, K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID); + EXPECT_EQ(tensors[6]->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DX_UID); + EXPECT_EQ(tensors[7]->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID); + EXPECT_EQ(tensors[8]->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID); +} + +TEST_F(TestLayernormBackwardOperationFromNode, BuildNodeRoundTrip) +{ + auto node = createStandardNode(); + auto desc = LayernormBackwardOperationDescriptor::fromNode(node, _tensorMap); + + const auto rebuiltNode = desc->buildNode(); + ASSERT_NE(rebuiltNode, nullptr); + ASSERT_EQ(rebuiltNode->compute_data_type, DataType::FLOAT); + ASSERT_EQ(rebuiltNode->attributes.type, NodeAttributes::LayernormBackwardAttributes); + + const auto* rebuiltAttrs = rebuiltNode->attributes.AsLayernormBackwardAttributes(); + ASSERT_NE(rebuiltAttrs, nullptr); + EXPECT_EQ(rebuiltAttrs->dy_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DY_UID); + EXPECT_EQ(rebuiltAttrs->x_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_X_UID); + EXPECT_EQ(rebuiltAttrs->scale_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_SCALE_UID); + EXPECT_EQ(rebuiltAttrs->mean_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_MEAN_UID); + EXPECT_EQ(rebuiltAttrs->inv_variance_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID); + EXPECT_EQ(rebuiltAttrs->epsilon_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID); + EXPECT_EQ(rebuiltAttrs->dx_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DX_UID); + EXPECT_EQ(rebuiltAttrs->dscale_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID); + EXPECT_EQ(rebuiltAttrs->dbias_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID); +} + +TEST_F(TestLayernormBackwardOperationFromNode, GetAttributeWorksAfterFromNode) +{ + auto node = createStandardNode(); + auto desc = LayernormBackwardOperationDescriptor::fromNode(node, _tensorMap); + + // Verify compute type + hipdnnDataType_t computeType = {}; + int64_t dtCount = 0; + desc->getAttribute(HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, + HIPDNN_TYPE_DATA_TYPE, + 1, + &dtCount, + &computeType); + ASSERT_EQ(computeType, HIPDNN_DATA_FLOAT); + + // Verify dy tensor + hipdnn_backend::ScopedDescriptor dyScoped; + int64_t dyCount = 0; + desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &dyCount, + static_cast(dyScoped.getPtr())); + ASSERT_EQ(dyCount, 1); + ASSERT_NE(dyScoped.get(), nullptr); + verifyTensorDescriptor(dyScoped.get(), + K_LAYERNORMBACKWARD_TENSOR_DY_UID, + HIPDNN_DATA_FLOAT, + {16, 64, 32, 32}, + {65536, 1024, 32, 1}); + + // Verify x tensor + hipdnn_backend::ScopedDescriptor xScoped; + int64_t xCount = 0; + desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &xCount, + static_cast(xScoped.getPtr())); + ASSERT_EQ(xCount, 1); + ASSERT_NE(xScoped.get(), nullptr); + verifyTensorDescriptor(xScoped.get(), + K_LAYERNORMBACKWARD_TENSOR_X_UID, + HIPDNN_DATA_FLOAT, + {16, 64, 32, 32}, + {65536, 1024, 32, 1}); + + // Verify scale tensor + hipdnn_backend::ScopedDescriptor scaleScoped; + int64_t scaleCount = 0; + desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &scaleCount, + static_cast(scaleScoped.getPtr())); + ASSERT_EQ(scaleCount, 1); + ASSERT_NE(scaleScoped.get(), nullptr); + verifyTensorDescriptor(scaleScoped.get(), + K_LAYERNORMBACKWARD_TENSOR_SCALE_UID, + HIPDNN_DATA_FLOAT, + {1, 64, 32, 32}, + {65536, 1024, 32, 1}); + + // Verify dx tensor + hipdnn_backend::ScopedDescriptor dxScoped; + int64_t dxCount = 0; + desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &dxCount, + static_cast(dxScoped.getPtr())); + ASSERT_EQ(dxCount, 1); + ASSERT_NE(dxScoped.get(), nullptr); + verifyTensorDescriptor(dxScoped.get(), + K_LAYERNORMBACKWARD_TENSOR_DX_UID, + HIPDNN_DATA_FLOAT, + {16, 64, 32, 32}, + {65536, 1024, 32, 1}); + + // Verify dscale tensor + hipdnn_backend::ScopedDescriptor dscaleScoped; + int64_t dscaleCount = 0; + desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &dscaleCount, + static_cast(dscaleScoped.getPtr())); + ASSERT_EQ(dscaleCount, 1); + ASSERT_NE(dscaleScoped.get(), nullptr); + verifyTensorDescriptor(dscaleScoped.get(), + K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID, + HIPDNN_DATA_FLOAT, + {1, 64, 32, 32}, + {65536, 1024, 32, 1}); + + // Verify dbias tensor + hipdnn_backend::ScopedDescriptor dbiasScoped; + int64_t dbiasCount = 0; + desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &dbiasCount, + static_cast(dbiasScoped.getPtr())); + ASSERT_EQ(dbiasCount, 1); + ASSERT_NE(dbiasScoped.get(), nullptr); + verifyTensorDescriptor(dbiasScoped.get(), + K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID, + HIPDNN_DATA_FLOAT, + {1, 64, 32, 32}, + {65536, 1024, 32, 1}); + + // Verify mean tensor (optional) + hipdnn_backend::ScopedDescriptor meanScoped; + int64_t meanCount = 0; + desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &meanCount, + static_cast(meanScoped.getPtr())); + ASSERT_EQ(meanCount, 1); + ASSERT_NE(meanScoped.get(), nullptr); + verifyTensorDescriptor(meanScoped.get(), + K_LAYERNORMBACKWARD_TENSOR_MEAN_UID, + HIPDNN_DATA_FLOAT, + {16, 1, 1, 1}, + {1, 1, 1, 1}); + + // Verify inv_variance tensor (optional) + hipdnn_backend::ScopedDescriptor invVarianceScoped; + int64_t invVarianceCount = 0; + desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &invVarianceCount, + static_cast(invVarianceScoped.getPtr())); + ASSERT_EQ(invVarianceCount, 1); + ASSERT_NE(invVarianceScoped.get(), nullptr); + verifyTensorDescriptor(invVarianceScoped.get(), + K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID, + HIPDNN_DATA_FLOAT, + {16, 1, 1, 1}, + {1, 1, 1, 1}); + + // Verify epsilon tensor (optional) + hipdnn_backend::ScopedDescriptor epsilonScoped; + int64_t epsilonCount = 0; + desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &epsilonCount, + static_cast(epsilonScoped.getPtr())); + ASSERT_EQ(epsilonCount, 1); + ASSERT_NE(epsilonScoped.get(), nullptr); + verifyTensorDescriptor( + epsilonScoped.get(), K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID, HIPDNN_DATA_FLOAT, {1}, {1}); + + // 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_LAYERNORM_BACKWARD_EXT); +} + +TEST_F(TestLayernormBackwardOperationFromNode, NamePreservedFromNode) +{ + auto node = createStandardNode(); + node.name = "test_layernormbackward_1"; + + auto desc = LayernormBackwardOperationDescriptor::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(std::string("test_layernormbackward_1").size() + 1)); + + std::vector buffer(static_cast(count)); + int64_t actualCount = 0; + desc->getAttribute( + HIPDNN_ATTR_OPERATION_NAME_EXT, HIPDNN_TYPE_CHAR, count, &actualCount, buffer.data()); + EXPECT_STREQ(buffer.data(), "test_layernormbackward_1"); +} + +TEST_F(TestLayernormBackwardOperationFromNode, EmptyNamePreservedFromNode) +{ + auto node = createStandardNode(); + auto desc = LayernormBackwardOperationDescriptor::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(TestLayernormBackwardOperationFromNode, BuildNodePreservesName) +{ + auto node = createStandardNode(); + node.name = "test_build_name"; + + auto desc = LayernormBackwardOperationDescriptor::fromNode(node, _tensorMap); + const auto rebuiltNode = desc->buildNode(); + + ASSERT_NE(rebuiltNode, nullptr); + EXPECT_EQ(rebuiltNode->name, "test_build_name"); +} diff --git a/projects/hipdnn/frontend/include/hipdnn_frontend/Graph.hpp b/projects/hipdnn/frontend/include/hipdnn_frontend/Graph.hpp index b2f105d0a8f9..952e2ca594d4 100644 --- a/projects/hipdnn/frontend/include/hipdnn_frontend/Graph.hpp +++ b/projects/hipdnn/frontend/include/hipdnn_frontend/Graph.hpp @@ -88,6 +88,7 @@ #include #include #include +#include #include #include #include @@ -124,6 +125,7 @@ #include #include #include +#include #include #include #include @@ -4899,6 +4901,91 @@ class Graph : public INode return {y, mean, invVariance}; } + /** @brief Layer normalization backward pass + * + * Computes gradients with respect to input, scale, and bias. + * + * Formula: + * @code + * x_hat = (x - mean) * invVariance + * dscale = sum(rstd * dy * (x - mean)) // sum over normalized dims + * dbias = sum(dy) // sum over normalized dims + * a = rstd³ * (sum(dy * scale * x) - sum(dy * scale) * mean) / m // sum over normalized dims + * b = rstd * sum(dy * scale) / m - a * mean // sum over normalized dims + * dx = rstd * dy * scale - a * x - b + * @endcode + * where m = product of normalized dims. + * + * @param dy Upstream gradient (loss gradient w.r.t. output, same shape as x) + * @param x Original input from forward pass + * @param scale Scale (gamma) + * @param attributes Configuration; optional epsilon and optionally set saved mean and inverse + * variance from the forward pass via set_saved_mean_and_inv_variance() + * @return Array of 3 output tensors: + * - [0] dx: Gradient w.r.t. input (same shape as x) + * - [1] dscale: Gradient w.r.t. scale + * - [2] dbias: Gradient w.r.t. bias + * + * @see hipdnn_frontend::graph::LayernormBackwardAttributes + */ + // NOLINTBEGIN(readability-identifier-naming) + std::array, 3> + layernorm_backward(std::shared_ptr dy, + std::shared_ptr x, + std::shared_ptr scale, + LayernormBackwardAttributes attributes) + // NOLINTEND(readability-identifier-naming) + { + if(attributes.get_name().empty()) + { + attributes.set_name("LayernormBackward_" + std::to_string(_sub_nodes.size())); + } + if(dy->get_name().empty()) + { + dy->set_name(attributes.get_name() + "::DY"); + } + if(x->get_name().empty()) + { + x->set_name(attributes.get_name() + "::X"); + } + if(scale->get_name().empty()) + { + scale->set_name(attributes.get_name() + "::SCALE"); + } + + auto mean = attributes.get_epsilon(); + if(mean && mean->get_name().empty()) + { + mean->set_name(attributes.get_name() + "::MEAN"); + } + auto invVariance = attributes.get_inv_variance(); + if(invVariance && invVariance->get_name().empty()) + { + invVariance->set_name(attributes.get_name() + "::INV_VARIANCE"); + } + auto epsilon = attributes.get_epsilon(); + if(epsilon && epsilon->get_name().empty()) + { + epsilon->set_name(attributes.get_name() + "::EPSILON"); + } + + auto dx = outputTensor(attributes.get_name() + "::DX"); + auto dscale = outputTensor(attributes.get_name() + "::DSCALE"); + auto dbias = outputTensor(attributes.get_name() + "::DBIAS"); + + attributes.set_dy(std::move(dy)); + attributes.set_x(std::move(x)); + attributes.set_scale(std::move(scale)); + attributes.set_dx(dx); + attributes.set_dscale(dscale); + attributes.set_dbias(dbias); + + _sub_nodes.emplace_back( + std::make_shared(std::move(attributes), graph_attributes)); + + return {dx, dscale, dbias}; + } + /** @brief RMS normalization forward pass * * Normalizes the input using the root mean square across the channel diff --git a/projects/hipdnn/frontend/include/hipdnn_frontend/attributes/LayernormBackwardAttributes.hpp b/projects/hipdnn/frontend/include/hipdnn_frontend/attributes/LayernormBackwardAttributes.hpp new file mode 100644 index 000000000000..d50318718fce --- /dev/null +++ b/projects/hipdnn/frontend/include/hipdnn_frontend/attributes/LayernormBackwardAttributes.hpp @@ -0,0 +1,250 @@ +// Copyright (c) Advanced Micro Devices, Inc., or its affiliates. +// SPDX-License-Identifier: MIT + +/** + * @file LayernormBackwardAttributes.hpp + * @brief Attributes for LayernormBackward operations + * + * This file defines the LayernormBackwardAttributes class for configuring + * LayernormBackward operations in hipDNN computational graphs. + */ + +#pragma once + +#include "Attributes.hpp" +#include "TensorAttributes.hpp" +#include +#include + +namespace hipdnn_frontend::graph +{ + +/** + * @class LayernormBackwardAttributes + * @brief Configuration for LayernormBackward operations + */ +class LayernormBackwardAttributes : public Attributes +{ +public: + LayernormBackwardAttributes() = default; + + /// Input tensor identifiers + enum class InputNames + { + DY = 0, + X = 1, + SCALE = 2, + MEAN = 3, + INV_VARIANCE = 4, + EPSILON = 5 + }; + typedef InputNames input_names; ///< @brief Type alias for InputNames + + /// Output tensor identifiers + enum class OutputNames + { + DX = 0, + DSCALE = 1, + DBIAS = 2 + }; + typedef OutputNames output_names; ///< @brief Type alias for OutputNames + + std::unordered_map> inputs; ///< Input tensors + std::unordered_map> outputs; ///< Output tensors + + // NOLINTBEGIN(readability-identifier-naming) + int64_t normalized_dim_count = 0; ///< Normalized Dim Count + // NOLINTEND(readability-identifier-naming) + + /// @brief Get the dy input tensor + // NOLINTNEXTLINE(readability-identifier-naming) + std::shared_ptr get_dy() const + { + return getInput(InputNames::DY); + } + /// @brief Get the x input tensor + // NOLINTNEXTLINE(readability-identifier-naming) + std::shared_ptr get_x() const + { + return getInput(InputNames::X); + } + /// @brief Get the scale input tensor + // NOLINTNEXTLINE(readability-identifier-naming) + std::shared_ptr get_scale() const + { + return getInput(InputNames::SCALE); + } + /// @brief Get the mean input tensor + // NOLINTNEXTLINE(readability-identifier-naming) + std::shared_ptr get_mean() const + { + return getInput(InputNames::MEAN); + } + /// @brief Get the inv_variance input tensor + // NOLINTNEXTLINE(readability-identifier-naming) + std::shared_ptr get_inv_variance() const + { + return getInput(InputNames::INV_VARIANCE); + } + /// @brief Get the epsilon input tensor + // NOLINTNEXTLINE(readability-identifier-naming) + std::shared_ptr get_epsilon() const + { + return getInput(InputNames::EPSILON); + } + /// @brief Get the dx output tensor + // NOLINTNEXTLINE(readability-identifier-naming) + std::shared_ptr get_dx() const + { + return getOutput(OutputNames::DX); + } + /// @brief Get the dscale output tensor + // NOLINTNEXTLINE(readability-identifier-naming) + std::shared_ptr get_dscale() const + { + return getOutput(OutputNames::DSCALE); + } + /// @brief Get the dbias output tensor + // NOLINTNEXTLINE(readability-identifier-naming) + std::shared_ptr get_dbias() const + { + return getOutput(OutputNames::DBIAS); + } + + /// @brief Set the dy input tensor (move) + // NOLINTNEXTLINE(readability-identifier-naming) + LayernormBackwardAttributes& set_dy(std::shared_ptr&& value) + { + return setInput(InputNames::DY, std::move(value)); + } + /// @brief Set the dy input tensor (copy) + // NOLINTNEXTLINE(readability-identifier-naming) + LayernormBackwardAttributes& set_dy(const std::shared_ptr& value) + { + return setInput(InputNames::DY, value); + } + /// @brief Set the x input tensor (move) + // NOLINTNEXTLINE(readability-identifier-naming) + LayernormBackwardAttributes& set_x(std::shared_ptr&& value) + { + return setInput(InputNames::X, std::move(value)); + } + /// @brief Set the x input tensor (copy) + // NOLINTNEXTLINE(readability-identifier-naming) + LayernormBackwardAttributes& set_x(const std::shared_ptr& value) + { + return setInput(InputNames::X, value); + } + /// @brief Set the scale input tensor (move) + // NOLINTNEXTLINE(readability-identifier-naming) + LayernormBackwardAttributes& set_scale(std::shared_ptr&& value) + { + return setInput(InputNames::SCALE, std::move(value)); + } + /// @brief Set the scale input tensor (copy) + // NOLINTNEXTLINE(readability-identifier-naming) + LayernormBackwardAttributes& set_scale(const std::shared_ptr& value) + { + return setInput(InputNames::SCALE, value); + } + /// @brief Set the mean input tensor (move) + // NOLINTNEXTLINE(readability-identifier-naming) + LayernormBackwardAttributes& set_mean(std::shared_ptr&& value) + { + return setInput(InputNames::MEAN, std::move(value)); + } + /// @brief Set the mean input tensor (copy) + // NOLINTNEXTLINE(readability-identifier-naming) + LayernormBackwardAttributes& set_mean(const std::shared_ptr& value) + { + return setInput(InputNames::MEAN, value); + } + /// @brief Set the inv_variance input tensor (move) + // NOLINTNEXTLINE(readability-identifier-naming) + LayernormBackwardAttributes& set_inv_variance(std::shared_ptr&& value) + { + return setInput(InputNames::INV_VARIANCE, std::move(value)); + } + /// @brief Set the inv_variance input tensor (copy) + // NOLINTNEXTLINE(readability-identifier-naming) + LayernormBackwardAttributes& set_inv_variance(const std::shared_ptr& value) + { + return setInput(InputNames::INV_VARIANCE, value); + } + /// @brief Set the epsilon input tensor (move) + // NOLINTNEXTLINE(readability-identifier-naming) + LayernormBackwardAttributes& set_epsilon(std::shared_ptr&& value) + { + return setInput(InputNames::EPSILON, std::move(value)); + } + /// @brief Set the epsilon input tensor (copy) + // NOLINTNEXTLINE(readability-identifier-naming) + LayernormBackwardAttributes& set_epsilon(const std::shared_ptr& value) + { + return setInput(InputNames::EPSILON, value); + } + /// @brief Set the dx output tensor (move) + // NOLINTNEXTLINE(readability-identifier-naming) + LayernormBackwardAttributes& set_dx(std::shared_ptr&& value) + { + return setOutput(OutputNames::DX, std::move(value)); + } + /// @brief Set the dx output tensor (copy) + // NOLINTNEXTLINE(readability-identifier-naming) + LayernormBackwardAttributes& set_dx(const std::shared_ptr& value) + { + return setOutput(OutputNames::DX, value); + } + /// @brief Set the dscale output tensor (move) + // NOLINTNEXTLINE(readability-identifier-naming) + LayernormBackwardAttributes& set_dscale(std::shared_ptr&& value) + { + return setOutput(OutputNames::DSCALE, std::move(value)); + } + /// @brief Set the dscale output tensor (copy) + // NOLINTNEXTLINE(readability-identifier-naming) + LayernormBackwardAttributes& set_dscale(const std::shared_ptr& value) + { + return setOutput(OutputNames::DSCALE, value); + } + /// @brief Set the dbias output tensor (move) + // NOLINTNEXTLINE(readability-identifier-naming) + LayernormBackwardAttributes& set_dbias(std::shared_ptr&& value) + { + return setOutput(OutputNames::DBIAS, std::move(value)); + } + /// @brief Set the dbias output tensor (copy) + // NOLINTNEXTLINE(readability-identifier-naming) + LayernormBackwardAttributes& set_dbias(const std::shared_ptr& value) + { + return setOutput(OutputNames::DBIAS, value); + } + + // NOLINTNEXTLINE(readability-identifier-naming) + LayernormBackwardAttributes& set_normalized_dim_count(int64_t value) + { + normalized_dim_count = value; + return *this; + } + + // NOLINTNEXTLINE(readability-identifier-naming) + int64_t get_normalized_dim_count() const + { + return normalized_dim_count; + } + + LayernormBackwardAttributes& + set_saved_mean_and_inv_variance(const std::shared_ptr& mean, // NOLINT + const std::shared_ptr& invVariance) + { + return set_mean(mean).set_inv_variance(invVariance); + } + + LayernormBackwardAttributes& + set_saved_mean_and_inv_variance(std::shared_ptr&& mean, // NOLINT + std::shared_ptr&& invVariance) + { + return set_mean(std::move(mean)).set_inv_variance(std::move(invVariance)); + } +}; +} // namespace hipdnn_frontend::graph diff --git a/projects/hipdnn/frontend/include/hipdnn_frontend/detail/LayernormBackwardPacker.hpp b/projects/hipdnn/frontend/include/hipdnn_frontend/detail/LayernormBackwardPacker.hpp new file mode 100644 index 000000000000..cc20a124503e --- /dev/null +++ b/projects/hipdnn/frontend/include/hipdnn_frontend/detail/LayernormBackwardPacker.hpp @@ -0,0 +1,103 @@ +// Copyright © Advanced Micro Devices, Inc., or its affiliates. +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +namespace hipdnn_frontend::detail +{ + +// Builds a layernormbackward operation descriptor from LayernormBackwardAttributes. +// Tensor descriptors are created/deduplicated via ensureAndSetTensorRef. +inline Error createLayernormBackwardOperation( + const graph::LayernormBackwardAttributes& attributes, + std::unordered_map& tensorDescs, + std::vector& operations) +{ + // Create operation descriptor + ScopedHipdnnBackendDescriptor opDesc(HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR); + if(!opDesc.valid()) + { + return {ErrorCode::HIPDNN_BACKEND_ERROR, + "Failed to create layernormbackward operation descriptor"}; + } + + // Create tensor descriptors (if needed) and set them on the operation + HIPDNN_CHECK_ERROR(ensureAndSetTensorRef(opDesc.get(), + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + attributes.get_dy(), + tensorDescs, + "layernormbackward DY")); + HIPDNN_CHECK_ERROR(ensureAndSetTensorRef(opDesc.get(), + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, + attributes.get_x(), + tensorDescs, + "layernormbackward X")); + HIPDNN_CHECK_ERROR(ensureAndSetTensorRef(opDesc.get(), + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + attributes.get_scale(), + tensorDescs, + "layernormbackward SCALE")); + HIPDNN_CHECK_ERROR(ensureAndSetOptionalTensorRef(opDesc.get(), + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + attributes.get_mean(), + tensorDescs, + "layernormbackward MEAN")); + HIPDNN_CHECK_ERROR( + ensureAndSetOptionalTensorRef(opDesc.get(), + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + attributes.get_inv_variance(), + tensorDescs, + "layernormbackward INV_VARIANCE")); + HIPDNN_CHECK_ERROR( + ensureAndSetOptionalTensorRef(opDesc.get(), + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + attributes.get_epsilon(), + tensorDescs, + "layernormbackward EPSILON")); + HIPDNN_CHECK_ERROR(ensureAndSetTensorRef(opDesc.get(), + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, + attributes.get_dx(), + tensorDescs, + "layernormbackward DX")); + HIPDNN_CHECK_ERROR(ensureAndSetTensorRef(opDesc.get(), + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + attributes.get_dscale(), + tensorDescs, + "layernormbackward DSCALE")); + HIPDNN_CHECK_ERROR(ensureAndSetTensorRef(opDesc.get(), + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + attributes.get_dbias(), + tensorDescs, + "layernormbackward DBIAS")); + + // Set layernormbackward parameters + HIPDNN_CHECK_ERROR(setDescriptorAttrScalar(opDesc.get(), + HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT, + HIPDNN_TYPE_INT64, + attributes.get_normalized_dim_count(), + "layernormbackward normalized_dim_count")); + + HIPDNN_CHECK_ERROR(setDescriptorAttrDataType(opDesc.get(), + HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, + attributes.compute_data_type, + "layernormbackward compute data type")); + + // Set operation name if provided + auto& opName = attributes.get_name(); + if(!opName.empty()) + { + HIPDNN_CHECK_ERROR(setDescriptorAttrString( + opDesc.get(), HIPDNN_ATTR_OPERATION_NAME_EXT, opName, "operation name")); + } + + // Finalize operation descriptor + HIPDNN_CHECK_ERROR(finalizeDescriptor(opDesc.get(), "layernormbackward operation descriptor")); + + operations.push_back(std::move(opDesc)); + return {}; +} + +} // namespace hipdnn_frontend::detail diff --git a/projects/hipdnn/frontend/include/hipdnn_frontend/detail/LayernormBackwardUnpacker.hpp b/projects/hipdnn/frontend/include/hipdnn_frontend/detail/LayernormBackwardUnpacker.hpp new file mode 100644 index 000000000000..2d1f1068899f --- /dev/null +++ b/projects/hipdnn/frontend/include/hipdnn_frontend/detail/LayernormBackwardUnpacker.hpp @@ -0,0 +1,138 @@ +// Copyright © Advanced Micro Devices, Inc., or its affiliates. +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include + +namespace hipdnn_frontend::detail +{ + +[[nodiscard]] inline Error unpackLayernormBackwardOperation( + hipdnnBackendDescriptor_t opDesc, + std::unordered_map>& tensorMap, + graph::LayernormBackwardAttributes& attributes) +{ + // Unpack dy tensor + std::shared_ptr dyTensor; + HIPDNN_CHECK_ERROR(unpackAndRegisterTensor(opDesc, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + tensorMap, + dyTensor, + "layernormbackward DY tensor")); + attributes.set_dy(dyTensor); + + // Unpack x tensor + std::shared_ptr xTensor; + HIPDNN_CHECK_ERROR(unpackAndRegisterTensor(opDesc, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, + tensorMap, + xTensor, + "layernormbackward X tensor")); + attributes.set_x(xTensor); + + // Unpack scale tensor + std::shared_ptr scaleTensor; + HIPDNN_CHECK_ERROR(unpackAndRegisterTensor(opDesc, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + tensorMap, + scaleTensor, + "layernormbackward SCALE tensor")); + attributes.set_scale(scaleTensor); + + // Unpack mean tensor + std::shared_ptr meanTensor; + HIPDNN_CHECK_ERROR(unpackOptionalTensor(opDesc, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + tensorMap, + meanTensor, + "layernormbackward MEAN tensor")); + if(meanTensor) + { + attributes.set_mean(meanTensor); + } + + // Unpack inv_variance tensor + std::shared_ptr invVarianceTensor; + HIPDNN_CHECK_ERROR(unpackOptionalTensor(opDesc, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + tensorMap, + invVarianceTensor, + "layernormbackward INV_VARIANCE tensor")); + if(invVarianceTensor) + { + attributes.set_inv_variance(invVarianceTensor); + } + + // Unpack epsilon tensor + std::shared_ptr epsilonTensor; + HIPDNN_CHECK_ERROR(unpackOptionalTensor(opDesc, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + tensorMap, + epsilonTensor, + "layernormbackward EPSILON tensor")); + if(epsilonTensor) + { + attributes.set_epsilon(epsilonTensor); + } + + // Unpack dx tensor + std::shared_ptr dxTensor; + HIPDNN_CHECK_ERROR(unpackAndRegisterTensor(opDesc, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, + tensorMap, + dxTensor, + "layernormbackward DX tensor")); + attributes.set_dx(dxTensor); + + // Unpack dscale tensor + std::shared_ptr dscaleTensor; + HIPDNN_CHECK_ERROR(unpackAndRegisterTensor(opDesc, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + tensorMap, + dscaleTensor, + "layernormbackward DSCALE tensor")); + attributes.set_dscale(dscaleTensor); + + // Unpack dbias tensor + std::shared_ptr dbiasTensor; + HIPDNN_CHECK_ERROR(unpackAndRegisterTensor(opDesc, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + tensorMap, + dbiasTensor, + "layernormbackward DBIAS tensor")); + attributes.set_dbias(dbiasTensor); + + // Unpack normalized_dim_count + int64_t normalizedDimCount = 0; + HIPDNN_CHECK_ERROR(getDescriptorAttrScalar(opDesc, + HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT, + HIPDNN_TYPE_INT64, + normalizedDimCount, + "layernormbackward normalized_dim_count")); + attributes.set_normalized_dim_count(normalizedDimCount); + + // Unpack compute data type + auto [dt, dtErr] = unpackGraphDataType(opDesc, + HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, + "layernormbackward compute data type"); + if(dtErr.is_bad()) + { + return dtErr; + } + attributes.set_compute_data_type(dt); + + // Unpack operation name + std::string opName; + HIPDNN_CHECK_ERROR( + getDescriptorAttrString(opDesc, HIPDNN_ATTR_OPERATION_NAME_EXT, opName, "operation name")); + attributes.set_name(opName); + + return {}; +} + +} // namespace hipdnn_frontend::detail diff --git a/projects/hipdnn/frontend/include/hipdnn_frontend/detail/OperationUnpacker.hpp b/projects/hipdnn/frontend/include/hipdnn_frontend/detail/OperationUnpacker.hpp index e10c42107150..5506d053ba4a 100644 --- a/projects/hipdnn/frontend/include/hipdnn_frontend/detail/OperationUnpacker.hpp +++ b/projects/hipdnn/frontend/include/hipdnn_frontend/detail/OperationUnpacker.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -134,6 +135,10 @@ namespace hipdnn_frontend::detail return { std::make_shared(graph::ResampleFwdAttributes{}, graphAttrs), {}}; + case HIPDNN_OPERATION_TYPE_LAYERNORM_BACKWARD_EXT: + return {std::make_shared(graph::LayernormBackwardAttributes{}, + graphAttrs), + {}}; default: return {nullptr, {ErrorCode::HIPDNN_BACKEND_ERROR, diff --git a/projects/hipdnn/frontend/include/hipdnn_frontend/node/LayernormBackwardNode.hpp b/projects/hipdnn/frontend/include/hipdnn_frontend/node/LayernormBackwardNode.hpp new file mode 100644 index 000000000000..370f9d555f59 --- /dev/null +++ b/projects/hipdnn/frontend/include/hipdnn_frontend/node/LayernormBackwardNode.hpp @@ -0,0 +1,248 @@ +// Copyright (c) Advanced Micro Devices, Inc., or its affiliates. +// SPDX-License-Identifier: MIT +#pragma once + +#include "Node.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace hipdnn_frontend::graph +{ +class LayernormBackwardNode : public BaseNode +{ +public: + LayernormBackwardAttributes attributes; + + LayernormBackwardNode(LayernormBackwardAttributes&& attrs, const GraphAttributes& graphAttrs) + : BaseNode(graphAttrs) + , attributes(std::move(attrs)) + { + } + + Error unpack_from_descriptor( + hipdnnBackendDescriptor_t opDesc, + std::unordered_map>& tensorMap) override + { + LayernormBackwardAttributes attrs; + HIPDNN_CHECK_ERROR(detail::unpackLayernormBackwardOperation(opDesc, tensorMap, attrs)); + attributes = std::move(attrs); + return {}; + } + + Error pre_validate_node() const override + { + // Validate required tensor pointers + HIPDNN_RETURN_IF_FALSE(attributes.get_dy(), + ErrorCode::ATTRIBUTE_NOT_SET, + "LayernormBackwardNode missing dy (input) for pre-validation"); + + HIPDNN_RETURN_IF_FALSE(attributes.get_x(), + ErrorCode::ATTRIBUTE_NOT_SET, + "LayernormBackwardNode missing x (input) for pre-validation"); + + HIPDNN_RETURN_IF_FALSE(attributes.get_scale(), + ErrorCode::ATTRIBUTE_NOT_SET, + "LayernormBackwardNode missing scale (input) for pre-validation"); + + HIPDNN_RETURN_IF_FALSE(attributes.get_dx(), + ErrorCode::ATTRIBUTE_NOT_SET, + "LayernormBackwardNode missing dx (output) for pre-validation"); + + HIPDNN_RETURN_IF_FALSE(attributes.get_dscale(), + ErrorCode::ATTRIBUTE_NOT_SET, + "LayernormBackwardNode missing dscale (output) for pre-validation"); + + HIPDNN_RETURN_IF_FALSE(attributes.get_dbias(), + ErrorCode::ATTRIBUTE_NOT_SET, + "LayernormBackwardNode missing dbias (output) for pre-validation"); + + return {}; + } + + Error infer_properties_node() override + { + // Validate required tensor pointers + HIPDNN_RETURN_IF_FALSE(attributes.get_dy(), + ErrorCode::ATTRIBUTE_NOT_SET, + "LayernormBackwardNode missing dy for setting properties"); + + HIPDNN_RETURN_IF_FALSE(attributes.get_x(), + ErrorCode::ATTRIBUTE_NOT_SET, + "LayernormBackwardNode missing x for setting properties"); + + HIPDNN_RETURN_IF_FALSE(attributes.get_scale(), + ErrorCode::ATTRIBUTE_NOT_SET, + "LayernormBackwardNode missing scale for setting properties"); + + HIPDNN_RETURN_IF_FALSE(attributes.get_dx(), + ErrorCode::ATTRIBUTE_NOT_SET, + "LayernormBackwardNode missing dx for setting properties"); + + HIPDNN_RETURN_IF_FALSE(attributes.get_dscale(), + ErrorCode::ATTRIBUTE_NOT_SET, + "LayernormBackwardNode missing dscale for setting properties"); + + HIPDNN_RETURN_IF_FALSE(attributes.get_dbias(), + ErrorCode::ATTRIBUTE_NOT_SET, + "LayernormBackwardNode missing dbias for setting properties"); + + HIPDNN_CHECK_ERROR(attributes.fill_from_context(graph_attributes)); + + if(attributes.get_dx()->get_dim().empty()) + { + attributes.get_dx()->set_dim(attributes.get_x()->get_dim()); + } + if(attributes.get_dx()->get_stride().empty()) + { + attributes.get_dx()->set_stride(attributes.get_x()->get_stride()); + } + + if(attributes.get_dscale()->get_dim().empty()) + { + attributes.get_dscale()->set_dim(attributes.get_scale()->get_dim()); + } + if(attributes.get_dscale()->get_stride().empty()) + { + attributes.get_dscale()->set_stride(attributes.get_scale()->get_stride()); + } + + if(attributes.get_dbias()->get_dim().empty()) + { + attributes.get_dbias()->set_dim(attributes.get_scale()->get_dim()); + } + if(attributes.get_dbias()->get_stride().empty()) + { + attributes.get_dbias()->set_stride(attributes.get_scale()->get_stride()); + } + + auto dxTensor = attributes.get_dx(); + auto dscaleTensor = attributes.get_dscale(); + auto dbiasTensor = attributes.get_dbias(); + auto dyTensor = attributes.get_dy(); + auto scaleTensor = attributes.get_scale(); + + // Infer output strides if not set + if(dxTensor->get_stride().empty()) + { + auto& dyStrides = dyTensor->get_stride(); + auto& dxDims = dxTensor->get_dim(); + + HIPDNN_RETURN_IF_TRUE( + dyStrides.empty(), + ErrorCode::ATTRIBUTE_NOT_SET, + "LayernormBackwardNode: Cannot infer output strides - missing input strides"); + + HIPDNN_RETURN_IF_TRUE( + dxDims.empty(), + ErrorCode::ATTRIBUTE_NOT_SET, + "LayernormBackwardNode: Cannot infer output strides - missing output dimensions"); + + HIPDNN_RETURN_IF_NE(dyStrides.size(), + dxDims.size(), + ErrorCode::ATTRIBUTE_NOT_SET, + "LayernormBackwardNode: Stride dimension mismatch between input " + "and output tensors"); + + auto strideOrder = hipdnn_data_sdk::utilities::extractStrideOrder(dyStrides); + auto dxStrides = hipdnn_data_sdk::utilities::generateStrides(dxDims, strideOrder); + dxTensor->set_stride(dxStrides); + } + + if(dscaleTensor->get_stride().empty()) + { + auto& scaleStrides = scaleTensor->get_stride(); + auto& dscaleDims = dscaleTensor->get_dim(); + + HIPDNN_RETURN_IF_TRUE(scaleStrides.empty(), + ErrorCode::ATTRIBUTE_NOT_SET, + "LayernormBackwardNode: Cannot infer gradient scale strides - " + "missing scale strides"); + + HIPDNN_RETURN_IF_TRUE(dscaleDims.empty(), + ErrorCode::ATTRIBUTE_NOT_SET, + "LayernormBackwardNode: Cannot infer gradient scale strides - " + "missing gradient scale dimensions"); + + HIPDNN_RETURN_IF_NE(scaleStrides.size(), + dscaleDims.size(), + ErrorCode::ATTRIBUTE_NOT_SET, + "LayernormBackwardNode: Stride dimension mismatch between scale " + "and gradient scale tensors"); + + auto strideOrder = hipdnn_data_sdk::utilities::extractStrideOrder(scaleStrides); + auto dscaleStrides + = hipdnn_data_sdk::utilities::generateStrides(dscaleDims, strideOrder); + dscaleTensor->set_stride(dscaleStrides); + } + + if(dbiasTensor->get_stride().empty()) + { + auto& scaleStrides = scaleTensor->get_stride(); + auto& dbiasDims = dbiasTensor->get_dim(); + + HIPDNN_RETURN_IF_TRUE(scaleStrides.empty(), + ErrorCode::ATTRIBUTE_NOT_SET, + "LayernormBackwardNode: Cannot infer gradient bias strides - " + "missing scale strides"); + + HIPDNN_RETURN_IF_TRUE(dbiasDims.empty(), + ErrorCode::ATTRIBUTE_NOT_SET, + "LayernormBackwardNode: Cannot infer gradient bias strides - " + "missing gradient bias dimensions"); + + HIPDNN_RETURN_IF_NE(scaleStrides.size(), + dbiasDims.size(), + ErrorCode::ATTRIBUTE_NOT_SET, + "LayernormBackwardNode: Stride dimension mismatch between scale " + "and gradient bias tensors"); + + auto strideOrder = hipdnn_data_sdk::utilities::extractStrideOrder(scaleStrides); + auto dbiasStrides = hipdnn_data_sdk::utilities::generateStrides(dbiasDims, strideOrder); + dbiasTensor->set_stride(dbiasStrides); + } + + // Infer normalized dimension count if not available + if(attributes.get_normalized_dim_count() <= 0) + { + if(attributes.get_x()->get_dim().size() + == attributes.get_scale() + ->get_dim() + .size()) // Dimensions not used by scale have been set to 1 + { + int64_t normalizedDimCount = 1; + for(int64_t i = static_cast(attributes.get_scale()->get_dim().size()) - 1; + i >= 0; + --i) + { + if(attributes.get_scale()->get_dim()[static_cast(i)] == 1) + { + break; + } + normalizedDimCount + = static_cast(attributes.get_scale()->get_dim().size()) - i; + } + attributes.set_normalized_dim_count(normalizedDimCount); + } + else // Dimensions not used by scale have been omitted + { + attributes.set_normalized_dim_count( + static_cast(attributes.get_scale()->get_dim().size())); + } + } + + return {}; + } + + Error create_operation( + std::unordered_map& tensorDescs, + std::vector& operations) const override + { + return detail::createLayernormBackwardOperation(attributes, tensorDescs, operations); + } +}; +} diff --git a/projects/hipdnn/frontend/include/hipdnn_frontend/node/NodeType.hpp b/projects/hipdnn/frontend/include/hipdnn_frontend/node/NodeType.hpp index e0cfe0a03eb3..7a42d8708469 100644 --- a/projects/hipdnn/frontend/include/hipdnn_frontend/node/NodeType.hpp +++ b/projects/hipdnn/frontend/include/hipdnn_frontend/node/NodeType.hpp @@ -30,7 +30,8 @@ enum class NodeType CUSTOM_OP = 16, REDUCTION = 17, RESAMPLE_FWD = 18, - RMS_NORM_BACKWARD = 19 + RMS_NORM_BACKWARD = 19, + LAYERNORM_BACKWARD = 20 }; } // namespace hipdnn_frontend::graph diff --git a/projects/hipdnn/frontend/tests/CMakeLists.txt b/projects/hipdnn/frontend/tests/CMakeLists.txt index 761d6d1c03b6..82e2f9d04b15 100644 --- a/projects/hipdnn/frontend/tests/CMakeLists.txt +++ b/projects/hipdnn/frontend/tests/CMakeLists.txt @@ -60,6 +60,9 @@ add_executable( TestKnobUnpacker.cpp TestLayernormAttributes.cpp TestLayerNormNode.cpp + TestLayernormBackwardAttributes.cpp + TestLayernormBackwardNode.cpp + TestGraphLayernormBackward.cpp TestMatmulAttributes.cpp TestMatmulNode.cpp TestNode.cpp diff --git a/projects/hipdnn/frontend/tests/TestGraphLayernormBackward.cpp b/projects/hipdnn/frontend/tests/TestGraphLayernormBackward.cpp new file mode 100644 index 000000000000..04bc8d8c1590 --- /dev/null +++ b/projects/hipdnn/frontend/tests/TestGraphLayernormBackward.cpp @@ -0,0 +1,68 @@ +// Copyright © Advanced Micro Devices, Inc., or its affiliates. +// SPDX-License-Identifier: MIT +#include +#include +#include +#include + +#include +#include + +using namespace hipdnn_frontend; +using namespace hipdnn_frontend::graph; + +TEST(TestGraphLayernormBackward, BuildGraph) +{ + Graph graph; + graph.set_compute_data_type(DataType::FLOAT) + .set_io_data_type(DataType::FLOAT) + .set_intermediate_data_type(DataType::FLOAT); + + // Create attributes + LayernormBackwardAttributes attributes; + attributes.set_name("LayernormBackwardNode"); + + // Create input tensors + auto dy = std::make_shared(); + dy->set_dim({16, 64, 32, 32}).set_stride({65536, 1024, 32, 1}).set_data_type(DataType::FLOAT); + + auto x = std::make_shared(); + x->set_dim({16, 64, 32, 32}).set_stride({65536, 1024, 32, 1}).set_data_type(DataType::FLOAT); + + auto scale = std::make_shared(); + scale->set_dim({1, 64, 32, 32}).set_stride({65536, 1024, 32, 1}).set_data_type(DataType::FLOAT); + + auto mean = std::make_shared(); + mean->set_dim({16, 1, 1, 1}).set_stride({1, 1, 1, 1}).set_data_type(DataType::FLOAT); + attributes.set_mean(std::move(mean)); + + auto invVariance = std::make_shared(); + invVariance->set_dim({16, 1, 1, 1}).set_stride({1, 1, 1, 1}).set_data_type(DataType::FLOAT); + attributes.set_inv_variance(std::move(invVariance)); + + auto epsilon = std::make_shared(); + epsilon->set_dim({1}).set_stride({1}).set_data_type(DataType::FLOAT); + attributes.set_epsilon(std::move(epsilon)); + + // Call graph method + auto results = graph.layernorm_backward(dy, x, scale, attributes); + + // Verify returned dx tensor is non-null + ASSERT_NE(results[0], nullptr); + EXPECT_EQ(results[0]->get_name(), "LayernormBackwardNode::DX"); + EXPECT_TRUE(results[0]->get_is_virtual()); + + // Verify returned dscale tensor is non-null + ASSERT_NE(results[1], nullptr); + EXPECT_EQ(results[1]->get_name(), "LayernormBackwardNode::DSCALE"); + EXPECT_TRUE(results[1]->get_is_virtual()); + + // Verify returned dbias tensor is non-null + ASSERT_NE(results[2], nullptr); + EXPECT_EQ(results[2]->get_name(), "LayernormBackwardNode::DBIAS"); + EXPECT_TRUE(results[2]->get_is_virtual()); + + // Verify graph validates successfully + auto validationResult = graph.validate(); + EXPECT_TRUE(validationResult.is_good()) << validationResult.get_message(); +} diff --git a/projects/hipdnn/frontend/tests/TestLayernormBackwardAttributes.cpp b/projects/hipdnn/frontend/tests/TestLayernormBackwardAttributes.cpp new file mode 100644 index 000000000000..6aa6bab45fdc --- /dev/null +++ b/projects/hipdnn/frontend/tests/TestLayernormBackwardAttributes.cpp @@ -0,0 +1,326 @@ +// Copyright © Advanced Micro Devices, Inc., or its affiliates. +// SPDX-License-Identifier: MIT +#include +#include +#include + +#include + +using namespace hipdnn_frontend::graph; + +// --- Test suite: TestLayernormBackwardAttributes --- + +TEST(TestLayernormBackwardAttributes, CreateLayernormBackwardAttributes) +{ + LayernormBackwardAttributes attrs; + + // Set all tensors + auto dyTensor = std::make_shared(); + dyTensor->set_uid(10); + attrs.set_dy(dyTensor); + auto xTensor = std::make_shared(); + xTensor->set_uid(11); + attrs.set_x(xTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_uid(12); + attrs.set_scale(scaleTensor); + auto meanTensor = std::make_shared(); + meanTensor->set_uid(13); + attrs.set_mean(meanTensor); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_uid(14); + attrs.set_inv_variance(invVarianceTensor); + auto epsilonTensor = std::make_shared(); + epsilonTensor->set_uid(15); + attrs.set_epsilon(epsilonTensor); + auto dxTensor = std::make_shared(); + dxTensor->set_uid(16); + attrs.set_dx(dxTensor); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_uid(17); + attrs.set_dscale(dscaleTensor); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_uid(18); + attrs.set_dbias(dbiasTensor); + + // Set data fields + + // Verify tensor getters + EXPECT_NE(attrs.get_dy(), nullptr); + EXPECT_EQ(attrs.get_dy()->get_uid(), 10); + EXPECT_NE(attrs.get_x(), nullptr); + EXPECT_EQ(attrs.get_x()->get_uid(), 11); + EXPECT_NE(attrs.get_scale(), nullptr); + EXPECT_EQ(attrs.get_scale()->get_uid(), 12); + EXPECT_NE(attrs.get_mean(), nullptr); + EXPECT_EQ(attrs.get_mean()->get_uid(), 13); + EXPECT_NE(attrs.get_inv_variance(), nullptr); + EXPECT_EQ(attrs.get_inv_variance()->get_uid(), 14); + EXPECT_NE(attrs.get_epsilon(), nullptr); + EXPECT_EQ(attrs.get_epsilon()->get_uid(), 15); + EXPECT_NE(attrs.get_dx(), nullptr); + EXPECT_EQ(attrs.get_dx()->get_uid(), 16); + EXPECT_NE(attrs.get_dscale(), nullptr); + EXPECT_EQ(attrs.get_dscale()->get_uid(), 17); + EXPECT_NE(attrs.get_dbias(), nullptr); + EXPECT_EQ(attrs.get_dbias()->get_uid(), 18); + + // Verify data field getters +} + +TEST(TestLayernormBackwardAttributes, DefaultValues) +{ + const LayernormBackwardAttributes attrs; + + // Tensors should be null by default + EXPECT_EQ(attrs.get_dy(), nullptr); + EXPECT_EQ(attrs.get_x(), nullptr); + EXPECT_EQ(attrs.get_scale(), nullptr); + EXPECT_EQ(attrs.get_mean(), nullptr); + EXPECT_EQ(attrs.get_inv_variance(), nullptr); + EXPECT_EQ(attrs.get_epsilon(), nullptr); + EXPECT_EQ(attrs.get_dx(), nullptr); + EXPECT_EQ(attrs.get_dscale(), nullptr); + EXPECT_EQ(attrs.get_dbias(), nullptr); + + // Vector fields should be empty by default +} + +TEST(TestLayernormBackwardAttributes, SetDyMove) +{ + LayernormBackwardAttributes attrs; + + auto dyTensor = std::make_shared(); + dyTensor->set_uid(10).set_name("MovedDyTensor").set_data_type(hipdnn_frontend::DataType::FLOAT); + + // Store the raw pointer before moving + auto rawPtr = dyTensor.get(); + + attrs.set_dy(std::move(dyTensor)); + + // After move, original should be nullptr + EXPECT_EQ(dyTensor, nullptr); + + // The moved tensor should be accessible through the getter + auto retrievedTensor = attrs.get_dy(); + EXPECT_EQ(retrievedTensor.get(), rawPtr); +} + +TEST(TestLayernormBackwardAttributes, SetXMove) +{ + LayernormBackwardAttributes attrs; + + auto xTensor = std::make_shared(); + xTensor->set_uid(11).set_name("MovedXTensor").set_data_type(hipdnn_frontend::DataType::FLOAT); + + // Store the raw pointer before moving + auto rawPtr = xTensor.get(); + + attrs.set_x(std::move(xTensor)); + + // After move, original should be nullptr + EXPECT_EQ(xTensor, nullptr); + + // The moved tensor should be accessible through the getter + auto retrievedTensor = attrs.get_x(); + EXPECT_EQ(retrievedTensor.get(), rawPtr); +} + +TEST(TestLayernormBackwardAttributes, SetScaleMove) +{ + LayernormBackwardAttributes attrs; + + auto scaleTensor = std::make_shared(); + scaleTensor->set_uid(12) + .set_name("MovedScaleTensor") + .set_data_type(hipdnn_frontend::DataType::FLOAT); + + // Store the raw pointer before moving + auto rawPtr = scaleTensor.get(); + + attrs.set_scale(std::move(scaleTensor)); + + // After move, original should be nullptr + EXPECT_EQ(scaleTensor, nullptr); + + // The moved tensor should be accessible through the getter + auto retrievedTensor = attrs.get_scale(); + EXPECT_EQ(retrievedTensor.get(), rawPtr); +} + +TEST(TestLayernormBackwardAttributes, SetMeanMove) +{ + LayernormBackwardAttributes attrs; + + auto meanTensor = std::make_shared(); + meanTensor->set_uid(13) + .set_name("MovedMeanTensor") + .set_data_type(hipdnn_frontend::DataType::FLOAT); + + // Store the raw pointer before moving + auto rawPtr = meanTensor.get(); + + attrs.set_mean(std::move(meanTensor)); + + // After move, original should be nullptr + EXPECT_EQ(meanTensor, nullptr); + + // The moved tensor should be accessible through the getter + auto retrievedTensor = attrs.get_mean(); + EXPECT_EQ(retrievedTensor.get(), rawPtr); +} + +TEST(TestLayernormBackwardAttributes, SetInvVarianceMove) +{ + LayernormBackwardAttributes attrs; + + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_uid(14) + .set_name("MovedInvVarianceTensor") + .set_data_type(hipdnn_frontend::DataType::FLOAT); + + // Store the raw pointer before moving + auto rawPtr = invVarianceTensor.get(); + + attrs.set_inv_variance(std::move(invVarianceTensor)); + + // After move, original should be nullptr + EXPECT_EQ(invVarianceTensor, nullptr); + + // The moved tensor should be accessible through the getter + auto retrievedTensor = attrs.get_inv_variance(); + EXPECT_EQ(retrievedTensor.get(), rawPtr); +} + +TEST(TestLayernormBackwardAttributes, SetEpsilonMove) +{ + LayernormBackwardAttributes attrs; + + auto epsilonTensor = std::make_shared(); + epsilonTensor->set_uid(15) + .set_name("MovedEpsilonTensor") + .set_data_type(hipdnn_frontend::DataType::FLOAT); + + // Store the raw pointer before moving + auto rawPtr = epsilonTensor.get(); + + attrs.set_epsilon(std::move(epsilonTensor)); + + // After move, original should be nullptr + EXPECT_EQ(epsilonTensor, nullptr); + + // The moved tensor should be accessible through the getter + auto retrievedTensor = attrs.get_epsilon(); + EXPECT_EQ(retrievedTensor.get(), rawPtr); +} + +TEST(TestLayernormBackwardAttributes, SetDxMove) +{ + LayernormBackwardAttributes attrs; + + auto dxTensor = std::make_shared(); + dxTensor->set_uid(16).set_name("MovedDxTensor").set_data_type(hipdnn_frontend::DataType::FLOAT); + + // Store the raw pointer before moving + auto rawPtr = dxTensor.get(); + + attrs.set_dx(std::move(dxTensor)); + + // After move, original should be nullptr + EXPECT_EQ(dxTensor, nullptr); + + // The moved tensor should be accessible through the getter + auto retrievedTensor = attrs.get_dx(); + EXPECT_EQ(retrievedTensor.get(), rawPtr); +} + +TEST(TestLayernormBackwardAttributes, SetDscaleMove) +{ + LayernormBackwardAttributes attrs; + + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_uid(17) + .set_name("MovedDscaleTensor") + .set_data_type(hipdnn_frontend::DataType::FLOAT); + + // Store the raw pointer before moving + auto rawPtr = dscaleTensor.get(); + + attrs.set_dscale(std::move(dscaleTensor)); + + // After move, original should be nullptr + EXPECT_EQ(dscaleTensor, nullptr); + + // The moved tensor should be accessible through the getter + auto retrievedTensor = attrs.get_dscale(); + EXPECT_EQ(retrievedTensor.get(), rawPtr); +} + +TEST(TestLayernormBackwardAttributes, SetDbiasMove) +{ + LayernormBackwardAttributes attrs; + + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_uid(18) + .set_name("MovedDbiasTensor") + .set_data_type(hipdnn_frontend::DataType::FLOAT); + + // Store the raw pointer before moving + auto rawPtr = dbiasTensor.get(); + + attrs.set_dbias(std::move(dbiasTensor)); + + // After move, original should be nullptr + EXPECT_EQ(dbiasTensor, nullptr); + + // The moved tensor should be accessible through the getter + auto retrievedTensor = attrs.get_dbias(); + EXPECT_EQ(retrievedTensor.get(), rawPtr); +} + +TEST(TestLayernormBackwardAttributes, SetTensorsConstRef) +{ + LayernormBackwardAttributes attrs; + + // Create tensors + auto dyTensor = std::make_shared(); + dyTensor->set_uid(10).set_name("DyConstRef"); + auto xTensor = std::make_shared(); + xTensor->set_uid(11).set_name("XConstRef"); + auto scaleTensor = std::make_shared(); + scaleTensor->set_uid(12).set_name("ScaleConstRef"); + auto meanTensor = std::make_shared(); + meanTensor->set_uid(13).set_name("MeanConstRef"); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_uid(14).set_name("InvVarianceConstRef"); + auto epsilonTensor = std::make_shared(); + epsilonTensor->set_uid(15).set_name("EpsilonConstRef"); + auto dxTensor = std::make_shared(); + dxTensor->set_uid(16).set_name("DxConstRef"); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_uid(17).set_name("DscaleConstRef"); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_uid(18).set_name("DbiasConstRef"); + + // Set using const reference (copy) + attrs.set_dy(dyTensor); + attrs.set_x(xTensor); + attrs.set_scale(scaleTensor); + attrs.set_mean(meanTensor); + attrs.set_inv_variance(invVarianceTensor); + attrs.set_epsilon(epsilonTensor); + attrs.set_dx(dxTensor); + attrs.set_dscale(dscaleTensor); + attrs.set_dbias(dbiasTensor); + + // Original tensors should still be valid + EXPECT_NE(dyTensor, nullptr); + EXPECT_NE(xTensor, nullptr); + EXPECT_NE(scaleTensor, nullptr); + EXPECT_NE(meanTensor, nullptr); + EXPECT_NE(invVarianceTensor, nullptr); + EXPECT_NE(epsilonTensor, nullptr); + EXPECT_NE(dxTensor, nullptr); + EXPECT_NE(dscaleTensor, nullptr); + EXPECT_NE(dbiasTensor, nullptr); +} diff --git a/projects/hipdnn/frontend/tests/TestLayernormBackwardNode.cpp b/projects/hipdnn/frontend/tests/TestLayernormBackwardNode.cpp new file mode 100644 index 000000000000..e1d523ce6a53 --- /dev/null +++ b/projects/hipdnn/frontend/tests/TestLayernormBackwardNode.cpp @@ -0,0 +1,346 @@ +// Copyright © Advanced Micro Devices, Inc., or its affiliates. +// SPDX-License-Identifier: MIT +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace hipdnn_frontend; +using namespace hipdnn_frontend::graph; + +// --- Helper: create fully configured attributes for a valid node --- +namespace +{ + +LayernormBackwardAttributes createValidAttributes() +{ + LayernormBackwardAttributes attrs; + + auto dyTensor = std::make_shared(); + dyTensor->set_dim({16, 64, 32, 32}); + dyTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dy(dyTensor); + auto xTensor = std::make_shared(); + xTensor->set_dim({16, 64, 32, 32}); + xTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_x(xTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_dim({1, 64, 32, 32}); + scaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_scale(scaleTensor); + auto dxTensor = std::make_shared(); + dxTensor->set_dim({16, 64, 32, 32}); + dxTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dx(dxTensor); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_dim({1, 64, 32, 32}); + dscaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dscale(dscaleTensor); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_dim({1, 64, 32, 32}); + dbiasTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dbias(dbiasTensor); + + return attrs; +} + +} // namespace + +// --- GetNodeType --- + +TEST(TestLayernormBackwardNode, GetNodeTypeReturnsLayernormBackward) +{ + const GraphAttributes graphAttrs; + const LayernormBackwardNode node(LayernormBackwardAttributes{}, graphAttrs); + EXPECT_EQ(node.getNodeType(), NodeType::LAYERNORM_BACKWARD); +} + +// --- PreValidateNode (success case) --- + +TEST(TestLayernormBackwardNode, PreValidateNode) +{ + auto attrs = createValidAttributes(); + + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.pre_validate_node(); + EXPECT_EQ(error.code, error_code_t::OK) << error.err_msg; +} + +// --- PreValidateNode: missing required tensors --- + +TEST(TestLayernormBackwardNode, PreValidateNodeMissingDyTensor) +{ + LayernormBackwardAttributes attrs; + + // Set all required tensors except dy + auto xTensor = std::make_shared(); + xTensor->set_dim({16, 64, 32, 32}); + xTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_x(xTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_dim({1, 64, 32, 32}); + scaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_scale(scaleTensor); + auto dxTensor = std::make_shared(); + dxTensor->set_dim({16, 64, 32, 32}); + dxTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dx(dxTensor); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_dim({1, 64, 32, 32}); + dscaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dscale(dscaleTensor); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_dim({1, 64, 32, 32}); + dbiasTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dbias(dbiasTensor); + + // dy tensor is missing + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.pre_validate_node(); + EXPECT_EQ(error.code, error_code_t::ATTRIBUTE_NOT_SET); +} + +TEST(TestLayernormBackwardNode, PreValidateNodeMissingXTensor) +{ + LayernormBackwardAttributes attrs; + + // Set all required tensors except x + auto dyTensor = std::make_shared(); + dyTensor->set_dim({16, 64, 32, 32}); + dyTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dy(dyTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_dim({1, 64, 32, 32}); + scaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_scale(scaleTensor); + auto dxTensor = std::make_shared(); + dxTensor->set_dim({16, 64, 32, 32}); + dxTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dx(dxTensor); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_dim({1, 64, 32, 32}); + dscaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dscale(dscaleTensor); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_dim({1, 64, 32, 32}); + dbiasTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dbias(dbiasTensor); + + // x tensor is missing + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.pre_validate_node(); + EXPECT_EQ(error.code, error_code_t::ATTRIBUTE_NOT_SET); +} + +TEST(TestLayernormBackwardNode, PreValidateNodeMissingScaleTensor) +{ + LayernormBackwardAttributes attrs; + + // Set all required tensors except scale + auto dyTensor = std::make_shared(); + dyTensor->set_dim({16, 64, 32, 32}); + dyTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dy(dyTensor); + auto xTensor = std::make_shared(); + xTensor->set_dim({16, 64, 32, 32}); + xTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_x(xTensor); + auto dxTensor = std::make_shared(); + dxTensor->set_dim({16, 64, 32, 32}); + dxTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dx(dxTensor); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_dim({1, 64, 32, 32}); + dscaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dscale(dscaleTensor); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_dim({1, 64, 32, 32}); + dbiasTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dbias(dbiasTensor); + + // scale tensor is missing + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.pre_validate_node(); + EXPECT_EQ(error.code, error_code_t::ATTRIBUTE_NOT_SET); +} + +TEST(TestLayernormBackwardNode, PreValidateNodeMissingDxTensor) +{ + LayernormBackwardAttributes attrs; + + // Set all required tensors except dx + auto dyTensor = std::make_shared(); + dyTensor->set_dim({16, 64, 32, 32}); + dyTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dy(dyTensor); + auto xTensor = std::make_shared(); + xTensor->set_dim({16, 64, 32, 32}); + xTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_x(xTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_dim({1, 64, 32, 32}); + scaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_scale(scaleTensor); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_dim({1, 64, 32, 32}); + dscaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dscale(dscaleTensor); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_dim({1, 64, 32, 32}); + dbiasTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dbias(dbiasTensor); + + // dx tensor is missing + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.pre_validate_node(); + EXPECT_EQ(error.code, error_code_t::ATTRIBUTE_NOT_SET); +} + +TEST(TestLayernormBackwardNode, PreValidateNodeMissingDscaleTensor) +{ + LayernormBackwardAttributes attrs; + + // Set all required tensors except dscale + auto dyTensor = std::make_shared(); + dyTensor->set_dim({16, 64, 32, 32}); + dyTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dy(dyTensor); + auto xTensor = std::make_shared(); + xTensor->set_dim({16, 64, 32, 32}); + xTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_x(xTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_dim({1, 64, 32, 32}); + scaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_scale(scaleTensor); + auto dxTensor = std::make_shared(); + dxTensor->set_dim({16, 64, 32, 32}); + dxTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dx(dxTensor); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_dim({1, 64, 32, 32}); + dbiasTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dbias(dbiasTensor); + + // dscale tensor is missing + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.pre_validate_node(); + EXPECT_EQ(error.code, error_code_t::ATTRIBUTE_NOT_SET); +} + +TEST(TestLayernormBackwardNode, PreValidateNodeMissingDbiasTensor) +{ + LayernormBackwardAttributes attrs; + + // Set all required tensors except dbias + auto dyTensor = std::make_shared(); + dyTensor->set_dim({16, 64, 32, 32}); + dyTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dy(dyTensor); + auto xTensor = std::make_shared(); + xTensor->set_dim({16, 64, 32, 32}); + xTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_x(xTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_dim({1, 64, 32, 32}); + scaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_scale(scaleTensor); + auto dxTensor = std::make_shared(); + dxTensor->set_dim({16, 64, 32, 32}); + dxTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dx(dxTensor); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_dim({1, 64, 32, 32}); + dscaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dscale(dscaleTensor); + + // dbias tensor is missing + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.pre_validate_node(); + EXPECT_EQ(error.code, error_code_t::ATTRIBUTE_NOT_SET); +} + +TEST(TestLayernormBackwardNode, PreValidateNodeAllValuesSet) +{ + auto attrs = createValidAttributes(); + + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.pre_validate_node(); + EXPECT_EQ(error.code, error_code_t::OK) << error.err_msg; +} + +// --- InferPropertiesNode --- + +TEST(TestLayernormBackwardNode, InferPropertiesNode) +{ + auto attrs = createValidAttributes(); + + const GraphAttributes graphAttributes; + LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.infer_properties_node(); + // Stub implementation: verify the method can be called without error + EXPECT_EQ(error.code, error_code_t::OK) << error.err_msg; +} + +// --- GatherHipdnnTensors --- + +TEST(TestLayernormBackwardNode, GatherHipdnnTensor) +{ + LayernormBackwardAttributes attrs; + + auto dyTensor = std::make_shared(); + dyTensor->set_uid(10).set_name("DyTensor"); + attrs.set_dy(dyTensor); + auto xTensor = std::make_shared(); + xTensor->set_uid(11).set_name("XTensor"); + attrs.set_x(xTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_uid(12).set_name("ScaleTensor"); + attrs.set_scale(scaleTensor); + auto dxTensor = std::make_shared(); + dxTensor->set_uid(16).set_name("DxTensor"); + attrs.set_dx(dxTensor); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_uid(17).set_name("DscaleTensor"); + attrs.set_dscale(dscaleTensor); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_uid(18).set_name("DbiasTensor"); + attrs.set_dbias(dbiasTensor); + + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + std::unordered_set> allTensors; + + node.gather_hipdnn_tensors(allTensors); + + EXPECT_TRUE(allTensors.find(dyTensor) != allTensors.end()); + EXPECT_TRUE(allTensors.find(xTensor) != allTensors.end()); + EXPECT_TRUE(allTensors.find(scaleTensor) != allTensors.end()); + EXPECT_TRUE(allTensors.find(dxTensor) != allTensors.end()); + EXPECT_TRUE(allTensors.find(dscaleTensor) != allTensors.end()); + EXPECT_TRUE(allTensors.find(dbiasTensor) != allTensors.end()); + EXPECT_EQ(allTensors.size(), 6u); +} diff --git a/projects/hipdnn/frontend/tests/TestOperationUnpacker.cpp b/projects/hipdnn/frontend/tests/TestOperationUnpacker.cpp index b4ccd091082b..d0b43b94a9fc 100644 --- a/projects/hipdnn/frontend/tests/TestOperationUnpacker.cpp +++ b/projects/hipdnn/frontend/tests/TestOperationUnpacker.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,7 @@ #include #include +#include "HipdnnOperationType.h" #include "fake_backend/MockHipdnnBackend.hpp" using namespace hipdnn_frontend; @@ -477,6 +479,16 @@ TEST(TestCreateNodeForType, CreatesReductionNode) EXPECT_NE(typedNode, nullptr); } +TEST(TestCreateNodeForType, CreatesLayernormBackwardNode) +{ + const GraphAttributes graphAttrs; + auto [node, err] = createNodeForType(HIPDNN_OPERATION_TYPE_LAYERNORM_BACKWARD_EXT, graphAttrs); + EXPECT_EQ(err.code, ErrorCode::OK); + ASSERT_NE(node, nullptr); + auto typedNode = std::dynamic_pointer_cast(node); + EXPECT_NE(typedNode, nullptr); +} + TEST(TestCreateNodeForType, ReturnsErrorForUnsupportedType) { const GraphAttributes graphAttrs; diff --git a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/constants/LayernormBackwardConstants.hpp b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/constants/LayernormBackwardConstants.hpp new file mode 100644 index 000000000000..f8b3ba026d4e --- /dev/null +++ b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/constants/LayernormBackwardConstants.hpp @@ -0,0 +1,51 @@ +// Copyright © Advanced Micro Devices, Inc., or its affiliates. +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +namespace hipdnn_tests::constants +{ + +// Standard LayernormBackward constants for testing get/set of valid operations. +// These represent "any valid layernormbackward" — specific values are not significant. + +constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_DY_UID = 10; +constexpr std::array K_LAYERNORMBACKWARD_TENSOR_DY_DIMS = {16, 64, 32, 32}; +constexpr std::array K_LAYERNORMBACKWARD_TENSOR_DY_STRIDES = {65536, 1024, 32, 1}; + +constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_X_UID = 11; +constexpr std::array K_LAYERNORMBACKWARD_TENSOR_X_DIMS = {16, 64, 32, 32}; +constexpr std::array K_LAYERNORMBACKWARD_TENSOR_X_STRIDES = {65536, 1024, 32, 1}; + +constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_SCALE_UID = 12; +constexpr std::array K_LAYERNORMBACKWARD_TENSOR_SCALE_DIMS = {1, 64, 32, 32}; +constexpr std::array K_LAYERNORMBACKWARD_TENSOR_SCALE_STRIDES = {65536, 1024, 32, 1}; + +constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_MEAN_UID = 13; +constexpr std::array K_LAYERNORMBACKWARD_TENSOR_MEAN_DIMS = {16, 1, 1, 1}; +constexpr std::array K_LAYERNORMBACKWARD_TENSOR_MEAN_STRIDES = {1, 1, 1, 1}; + +constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID = 14; +constexpr std::array K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_DIMS = {16, 1, 1, 1}; +constexpr std::array K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_STRIDES = {1, 1, 1, 1}; + +constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID = 15; +constexpr std::array K_LAYERNORMBACKWARD_TENSOR_EPSILON_DIMS = {1}; +constexpr std::array K_LAYERNORMBACKWARD_TENSOR_EPSILON_STRIDES = {1}; + +constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_DX_UID = 16; +constexpr std::array K_LAYERNORMBACKWARD_TENSOR_DX_DIMS = {16, 64, 32, 32}; +constexpr std::array K_LAYERNORMBACKWARD_TENSOR_DX_STRIDES = {65536, 1024, 32, 1}; + +constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID = 17; +constexpr std::array K_LAYERNORMBACKWARD_TENSOR_DSCALE_DIMS = {1, 64, 32, 32}; +constexpr std::array K_LAYERNORMBACKWARD_TENSOR_DSCALE_STRIDES = {65536, 1024, 32, 1}; + +constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID = 18; +constexpr std::array K_LAYERNORMBACKWARD_TENSOR_DBIAS_DIMS = {1, 64, 32, 32}; +constexpr std::array K_LAYERNORMBACKWARD_TENSOR_DBIAS_STRIDES = {65536, 1024, 32, 1}; + +} // namespace hipdnn_tests::constants diff --git a/projects/hipdnn/tests/frontend/CMakeLists.txt b/projects/hipdnn/tests/frontend/CMakeLists.txt index 5062d4807834..7ce31a2d05a8 100644 --- a/projects/hipdnn/tests/frontend/CMakeLists.txt +++ b/projects/hipdnn/tests/frontend/CMakeLists.txt @@ -47,6 +47,8 @@ add_executable( IntegrationLayerNormDescriptorLowering.cpp IntegrationLayernormForward.cpp IntegrationMalformedVersionPlugin.cpp + IntegrationLayernormBackwardDescriptorLowering.cpp + IntegrationLayernormBackwardDescriptorLifting.cpp IntegrationMatmul.cpp IntegrationMatmulDescriptorLifting.cpp IntegrationMatmulDescriptorLowering.cpp diff --git a/projects/hipdnn/tests/frontend/IntegrationLayernormBackwardDescriptorLifting.cpp b/projects/hipdnn/tests/frontend/IntegrationLayernormBackwardDescriptorLifting.cpp new file mode 100644 index 000000000000..a3b82d392720 --- /dev/null +++ b/projects/hipdnn/tests/frontend/IntegrationLayernormBackwardDescriptorLifting.cpp @@ -0,0 +1,419 @@ +// Copyright © Advanced Micro Devices, Inc., or its affiliates. +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace hipdnn_frontend; +using namespace hipdnn_frontend::graph; +using namespace hipdnn_tests::constants; +using hipdnn_tests::IntegrationTestFixture; +using hipdnn_tests::liftGraph; +using hipdnn_tests::liftGraphWithoutFinalization; +using hipdnn_tests::TestableGraphLifting; +using hipdnn_tests::toVec; + +namespace +{ + +// Lifts a frontend graph via build_operation_graph(handle), then +// reconstructs it with fromBackendDescriptor() for verification. +class IntegrationLayernormBackwardDescriptorLifting : public IntegrationTestFixture +{ +protected: + /// Builds a standard LayernormBackward graph for round-trip testing. + static std::shared_ptr buildGraph() + { + auto graph = std::make_shared(); + graph->set_name("LayernormBackwardLiftingTestGraph") + .set_compute_data_type(DataType::FLOAT) + .set_intermediate_data_type(DataType::FLOAT) + .set_io_data_type(DataType::FLOAT); + + LayernormBackwardAttributes attrs; + attrs.set_name("test_op"); + + auto dy = std::make_shared(); + dy->set_uid(K_LAYERNORMBACKWARD_TENSOR_DY_UID) + .set_name("dy") + .set_data_type(DataType::FLOAT); + dy->set_dim(toVec(K_LAYERNORMBACKWARD_TENSOR_DY_DIMS)) + .set_stride(toVec(K_LAYERNORMBACKWARD_TENSOR_DY_STRIDES)); + + auto x = std::make_shared(); + x->set_uid(K_LAYERNORMBACKWARD_TENSOR_X_UID).set_name("x").set_data_type(DataType::FLOAT); + x->set_dim(toVec(K_LAYERNORMBACKWARD_TENSOR_X_DIMS)) + .set_stride(toVec(K_LAYERNORMBACKWARD_TENSOR_X_STRIDES)); + + auto scale = std::make_shared(); + scale->set_uid(K_LAYERNORMBACKWARD_TENSOR_SCALE_UID) + .set_name("scale") + .set_data_type(DataType::FLOAT); + scale->set_dim(toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_DIMS)) + .set_stride(toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_STRIDES)); + + auto mean = std::make_shared(); + mean->set_uid(K_LAYERNORMBACKWARD_TENSOR_MEAN_UID) + .set_name("mean") + .set_data_type(DataType::FLOAT); + mean->set_dim(toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_DIMS)) + .set_stride(toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_STRIDES)); + attrs.set_mean(std::move(mean)); + + auto invVariance = std::make_shared(); + invVariance->set_uid(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID) + .set_name("inv_variance") + .set_data_type(DataType::FLOAT); + invVariance->set_dim(toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_DIMS)) + .set_stride(toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_STRIDES)); + attrs.set_inv_variance(std::move(invVariance)); + + auto epsilon = std::make_shared(); + epsilon->set_uid(K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID) + .set_name("epsilon") + .set_data_type(DataType::FLOAT); + epsilon->set_dim(toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_DIMS)) + .set_stride(toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_STRIDES)); + attrs.set_epsilon(std::move(epsilon)); + + auto results = graph->layernorm_backward(dy, x, scale, attrs); + results[0]->set_uid(K_LAYERNORMBACKWARD_TENSOR_DX_UID).set_output(true).set_name("dx"); + results[1] + ->set_uid(K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID) + .set_output(true) + .set_name("dscale"); + results[2] + ->set_uid(K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID) + .set_output(true) + .set_name("dbias"); + + return graph; + } +}; + +// Builds a standard LayernormBackward graph, lowers via build_operation_graph(handle), +// lifts back with fromBackendDescriptor(), and performs comprehensive field-by-field +// validation of graph data types, tensor attributes, and operation parameters. +TEST_F(IntegrationLayernormBackwardDescriptorLifting, BasicLayernormBackwardRoundTrip) +{ + auto originalGraph = buildGraph(); + + auto liftedGraph = liftGraph(*originalGraph, _handle); + ASSERT_NE(liftedGraph, nullptr); + + // Verify graph-level data types + EXPECT_EQ(liftedGraph->get_compute_data_type(), DataType::FLOAT); + EXPECT_EQ(liftedGraph->get_intermediate_data_type(), DataType::FLOAT); + EXPECT_EQ(liftedGraph->get_io_data_type(), DataType::FLOAT); + + // Verify tensors by UID + auto tensorMap = liftedGraph->getTensorsByUid(); + ASSERT_EQ(tensorMap.size(), 9u); + + // Verify dy tensor + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_DY_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DY_UID]->get_uid(), + K_LAYERNORMBACKWARD_TENSOR_DY_UID); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DY_UID]->get_dim(), + toVec(K_LAYERNORMBACKWARD_TENSOR_DY_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DY_UID]->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_DY_STRIDES)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DY_UID]->get_data_type(), DataType::FLOAT); + + // Verify x tensor + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_X_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_X_UID]->get_uid(), + K_LAYERNORMBACKWARD_TENSOR_X_UID); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_X_UID]->get_dim(), + toVec(K_LAYERNORMBACKWARD_TENSOR_X_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_X_UID]->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_X_STRIDES)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_X_UID]->get_data_type(), DataType::FLOAT); + + // Verify scale tensor + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_SCALE_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_SCALE_UID]->get_uid(), + K_LAYERNORMBACKWARD_TENSOR_SCALE_UID); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_SCALE_UID]->get_dim(), + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_SCALE_UID]->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_STRIDES)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_SCALE_UID]->get_data_type(), DataType::FLOAT); + + // Verify dx tensor + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_DX_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DX_UID]->get_uid(), + K_LAYERNORMBACKWARD_TENSOR_DX_UID); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DX_UID]->get_dim(), + toVec(K_LAYERNORMBACKWARD_TENSOR_DX_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DX_UID]->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_DX_STRIDES)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DX_UID]->get_data_type(), DataType::FLOAT); + + // Verify dscale tensor + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID]->get_uid(), + K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID]->get_dim(), + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID]->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_STRIDES)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID]->get_data_type(), DataType::FLOAT); + + // Verify dbias tensor + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID]->get_uid(), + K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID]->get_dim(), + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID]->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_STRIDES)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID]->get_data_type(), DataType::FLOAT); + + // Verify sub-node count and type + auto& subNodes = liftedGraph->getSubNodes(); + ASSERT_EQ(subNodes.size(), 1u) + << "Expected 1 operation node in lifted graph"; // NOLINT(readability-implicit-bool-conversion) + + auto* opNode = dynamic_cast(subNodes[0].get()); + ASSERT_NE(opNode, nullptr) + << "Expected a LayernormBackwardNode"; // NOLINT(readability-implicit-bool-conversion) + + // Verify operation name + EXPECT_EQ(opNode->attributes.get_name(), "test_op"); +} + +// After lifting, verifies tensor objects in the node attributes are the same +// shared_ptr instances as in the tensor map (pointer equality). +TEST_F(IntegrationLayernormBackwardDescriptorLifting, LayernormBackwardTensorSharingPreserved) +{ + auto originalGraph = buildGraph(); + + auto liftedGraph = liftGraph(*originalGraph, _handle); + ASSERT_NE(liftedGraph, nullptr); + + auto tensorMap = liftedGraph->getTensorsByUid(); + + auto& subNodes = liftedGraph->getSubNodes(); + ASSERT_EQ(subNodes.size(), 1u); + + auto* opNode = dynamic_cast(subNodes[0].get()); + ASSERT_NE(opNode, nullptr); + + // Verify dy tensor sharing + EXPECT_EQ(opNode->attributes.get_dy()->get_uid(), K_LAYERNORMBACKWARD_TENSOR_DY_UID); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DY_UID].get(), + opNode->attributes.get_dy().get()); + // Verify x tensor sharing + EXPECT_EQ(opNode->attributes.get_x()->get_uid(), K_LAYERNORMBACKWARD_TENSOR_X_UID); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_X_UID].get(), opNode->attributes.get_x().get()); + // Verify scale tensor sharing + EXPECT_EQ(opNode->attributes.get_scale()->get_uid(), K_LAYERNORMBACKWARD_TENSOR_SCALE_UID); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_SCALE_UID].get(), + opNode->attributes.get_scale().get()); + // Verify dx tensor sharing + EXPECT_EQ(opNode->attributes.get_dx()->get_uid(), K_LAYERNORMBACKWARD_TENSOR_DX_UID); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DX_UID].get(), + opNode->attributes.get_dx().get()); + // Verify dscale tensor sharing + EXPECT_EQ(opNode->attributes.get_dscale()->get_uid(), K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID].get(), + opNode->attributes.get_dscale().get()); + // Verify dbias tensor sharing + EXPECT_EQ(opNode->attributes.get_dbias()->get_uid(), K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID].get(), + opNode->attributes.get_dbias().get()); +} + +// Builds a LayernormBackward graph, serializes to binary, creates a backend descriptor +// from bytes (no handle, no finalize), calls fromBackendDescriptor(), and verifies +// all fields survive the backend C API serialization path. +TEST_F(IntegrationLayernormBackwardDescriptorLifting, LayernormBackwardLiftWithoutFinalization) +{ + auto originalGraph = buildGraph(); + + auto liftedGraph = liftGraphWithoutFinalization(*originalGraph); + ASSERT_NE(liftedGraph, nullptr); + + // Verify graph-level data types + EXPECT_EQ(liftedGraph->get_compute_data_type(), DataType::FLOAT); + EXPECT_EQ(liftedGraph->get_intermediate_data_type(), DataType::FLOAT); + EXPECT_EQ(liftedGraph->get_io_data_type(), DataType::FLOAT); + + // Verify the lifted graph has 1 operation node + auto& subNodes = liftedGraph->getSubNodes(); + ASSERT_EQ(subNodes.size(), 1u); + + auto* opNode = dynamic_cast(subNodes[0].get()); + ASSERT_NE(opNode, nullptr); + + // Verify operation name + EXPECT_EQ(opNode->attributes.get_name(), "test_op"); + + // Verify tensor dims and strides + auto tensorMap = liftedGraph->getTensorsByUid(); + ASSERT_EQ(tensorMap.size(), 9u); + + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_DY_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DY_UID]->get_dim(), + toVec(K_LAYERNORMBACKWARD_TENSOR_DY_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DY_UID]->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_DY_STRIDES)); + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_X_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_X_UID]->get_dim(), + toVec(K_LAYERNORMBACKWARD_TENSOR_X_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_X_UID]->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_X_STRIDES)); + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_SCALE_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_SCALE_UID]->get_dim(), + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_SCALE_UID]->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_STRIDES)); + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_DX_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DX_UID]->get_dim(), + toVec(K_LAYERNORMBACKWARD_TENSOR_DX_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DX_UID]->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_DX_STRIDES)); + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID]->get_dim(), + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID]->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_STRIDES)); + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID]->get_dim(), + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID]->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_STRIDES)); +} + +// Builds a LayernormBackward graph without calling set_uid() on any tensor, +// lowers to backend, lifts, and verifies all auto-assigned UIDs are +// distinct and survive the round-trip. +TEST_F(IntegrationLayernormBackwardDescriptorLifting, AutoAssignedUidsPreservedInLiftingRoundTrip) +{ + LayernormBackwardAttributes attrs; + attrs.set_name("test_auto_uid"); + + auto graph = std::make_shared(); + graph->set_name("LayernormBackwardAutoUidLiftTest") + .set_compute_data_type(DataType::FLOAT) + .set_intermediate_data_type(DataType::FLOAT) + .set_io_data_type(DataType::FLOAT); + + auto dy = std::make_shared(); + dy->set_name("dy").set_data_type(DataType::FLOAT); + dy->set_dim(toVec(K_LAYERNORMBACKWARD_TENSOR_DY_DIMS)) + .set_stride(toVec(K_LAYERNORMBACKWARD_TENSOR_DY_STRIDES)); + + auto x = std::make_shared(); + x->set_name("x").set_data_type(DataType::FLOAT); + x->set_dim(toVec(K_LAYERNORMBACKWARD_TENSOR_X_DIMS)) + .set_stride(toVec(K_LAYERNORMBACKWARD_TENSOR_X_STRIDES)); + + auto scale = std::make_shared(); + scale->set_name("scale").set_data_type(DataType::FLOAT); + scale->set_dim(toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_DIMS)) + .set_stride(toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_STRIDES)); + + auto mean = std::make_shared(); + mean->set_name("mean").set_data_type(DataType::FLOAT); + mean->set_dim(toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_DIMS)) + .set_stride(toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_STRIDES)); + attrs.set_mean(std::move(mean)); + + auto invVariance = std::make_shared(); + invVariance->set_name("inv_variance").set_data_type(DataType::FLOAT); + invVariance->set_dim(toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_DIMS)) + .set_stride(toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_STRIDES)); + attrs.set_inv_variance(std::move(invVariance)); + + auto epsilon = std::make_shared(); + epsilon->set_name("epsilon").set_data_type(DataType::FLOAT); + epsilon->set_dim(toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_DIMS)) + .set_stride(toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_STRIDES)); + attrs.set_epsilon(std::move(epsilon)); + + auto results = graph->layernorm_backward(dy, x, scale, attrs); + results[0]->set_output(true).set_name("dx"); + results[1]->set_output(true).set_name("dscale"); + results[2]->set_output(true).set_name("dbias"); + + auto liftedGraph = liftGraph(*graph, _handle); + ASSERT_NE(liftedGraph, nullptr); + + // Verify the tensor map has the expected number of tensors + auto tensorMap = liftedGraph->getTensorsByUid(); + ASSERT_EQ(tensorMap.size(), 9u); + + // Verify all UIDs are distinct + std::vector uids; + uids.reserve(tensorMap.size()); + for(const auto& [uid, tensor] : tensorMap) + { + uids.push_back(uid); + } + std::sort(uids.begin(), uids.end()); + ASSERT_EQ(std::adjacent_find(uids.begin(), uids.end()), uids.end()) + << "Found duplicate auto-assigned UIDs"; // NOLINT(readability-implicit-bool-conversion) + + // Verify sub-node tensor UIDs are distinct via the node attributes + auto& subNodes = liftedGraph->getSubNodes(); + ASSERT_EQ(subNodes.size(), 1u); + + auto* opNode = dynamic_cast(subNodes[0].get()); + ASSERT_NE(opNode, nullptr); + + std::set nodeUids; + ASSERT_NE(opNode->attributes.get_dy(), nullptr); + nodeUids.insert(opNode->attributes.get_dy()->get_uid()); + ASSERT_NE(opNode->attributes.get_x(), nullptr); + nodeUids.insert(opNode->attributes.get_x()->get_uid()); + ASSERT_NE(opNode->attributes.get_scale(), nullptr); + nodeUids.insert(opNode->attributes.get_scale()->get_uid()); + ASSERT_NE(opNode->attributes.get_dx(), nullptr); + nodeUids.insert(opNode->attributes.get_dx()->get_uid()); + ASSERT_NE(opNode->attributes.get_dscale(), nullptr); + nodeUids.insert(opNode->attributes.get_dscale()->get_uid()); + ASSERT_NE(opNode->attributes.get_dbias(), nullptr); + nodeUids.insert(opNode->attributes.get_dbias()->get_uid()); + ASSERT_EQ(nodeUids.size(), 6u) + << "Node tensor UIDs are not all distinct"; // NOLINT(readability-implicit-bool-conversion) + + // Verify tensor dims survived the round trip + EXPECT_EQ(opNode->attributes.get_dy()->get_dim(), toVec(K_LAYERNORMBACKWARD_TENSOR_DY_DIMS)); + EXPECT_EQ(opNode->attributes.get_dy()->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_DY_STRIDES)); + EXPECT_EQ(opNode->attributes.get_x()->get_dim(), toVec(K_LAYERNORMBACKWARD_TENSOR_X_DIMS)); + EXPECT_EQ(opNode->attributes.get_x()->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_X_STRIDES)); + EXPECT_EQ(opNode->attributes.get_scale()->get_dim(), + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_DIMS)); + EXPECT_EQ(opNode->attributes.get_scale()->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_STRIDES)); + EXPECT_EQ(opNode->attributes.get_dx()->get_dim(), toVec(K_LAYERNORMBACKWARD_TENSOR_DX_DIMS)); + EXPECT_EQ(opNode->attributes.get_dx()->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_DX_STRIDES)); + EXPECT_EQ(opNode->attributes.get_dscale()->get_dim(), + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_DIMS)); + EXPECT_EQ(opNode->attributes.get_dscale()->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_STRIDES)); + EXPECT_EQ(opNode->attributes.get_dbias()->get_dim(), + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_DIMS)); + EXPECT_EQ(opNode->attributes.get_dbias()->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_STRIDES)); +} + +} // namespace diff --git a/projects/hipdnn/tests/frontend/IntegrationLayernormBackwardDescriptorLowering.cpp b/projects/hipdnn/tests/frontend/IntegrationLayernormBackwardDescriptorLowering.cpp new file mode 100644 index 000000000000..79ba1bb24379 --- /dev/null +++ b/projects/hipdnn/tests/frontend/IntegrationLayernormBackwardDescriptorLowering.cpp @@ -0,0 +1,178 @@ +// Copyright © Advanced Micro Devices, Inc., or its affiliates. +// SPDX-License-Identifier: MIT + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace hipdnn_frontend; +using namespace hipdnn_frontend::graph; +using namespace hipdnn_tests::constants; +using hipdnn_tests::buildTensorMap; +using hipdnn_tests::IntegrationTestFixture; +using hipdnn_tests::lowerAndDeserialize; +using hipdnn_tests::TestableGraphLowering; +using hipdnn_tests::toVec; +using DataTypeSdk = hipdnn_flatbuffers_sdk::data_objects::DataType; +using NodeAttrType = hipdnn_flatbuffers_sdk::data_objects::NodeAttributes; + +namespace +{ + +// Lowers a frontend graph via build_operation_graph_via_descriptors, then +// retrieves the serialized graph and deserializes it for verification. +class IntegrationLayernormBackwardDescriptorLowering : public IntegrationTestFixture +{ +protected: + /// Builds and lowers a graph, returning the deserialized GraphT. + /// Callers set up attrs before calling; this creates tensors, calls the + /// graph method, validates, lowers, serializes, and deserializes. + hipdnn_flatbuffers_sdk::data_objects::GraphT + buildAndDeserialize(LayernormBackwardAttributes& attrs) + { + auto graph = std::make_shared(); + graph->set_name("LayernormBackwardIntegrationTest") + .set_compute_data_type(DataType::FLOAT) + .set_intermediate_data_type(DataType::FLOAT) + .set_io_data_type(DataType::FLOAT); + + auto dy = std::make_shared(); + dy->set_uid(K_LAYERNORMBACKWARD_TENSOR_DY_UID) + .set_name("dy") + .set_data_type(DataType::FLOAT); + dy->set_dim(toVec(K_LAYERNORMBACKWARD_TENSOR_DY_DIMS)) + .set_stride(toVec(K_LAYERNORMBACKWARD_TENSOR_DY_STRIDES)); + + auto x = std::make_shared(); + x->set_uid(K_LAYERNORMBACKWARD_TENSOR_X_UID).set_name("x").set_data_type(DataType::FLOAT); + x->set_dim(toVec(K_LAYERNORMBACKWARD_TENSOR_X_DIMS)) + .set_stride(toVec(K_LAYERNORMBACKWARD_TENSOR_X_STRIDES)); + + auto scale = std::make_shared(); + scale->set_uid(K_LAYERNORMBACKWARD_TENSOR_SCALE_UID) + .set_name("scale") + .set_data_type(DataType::FLOAT); + scale->set_dim(toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_DIMS)) + .set_stride(toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_STRIDES)); + + auto mean = std::make_shared(); + mean->set_uid(K_LAYERNORMBACKWARD_TENSOR_MEAN_UID) + .set_name("mean") + .set_data_type(DataType::FLOAT); + mean->set_dim(toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_DIMS)) + .set_stride(toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_STRIDES)); + attrs.set_mean(std::move(mean)); + + auto invVariance = std::make_shared(); + invVariance->set_uid(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID) + .set_name("inv_variance") + .set_data_type(DataType::FLOAT); + invVariance->set_dim(toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_DIMS)) + .set_stride(toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_STRIDES)); + attrs.set_inv_variance(std::move(invVariance)); + + auto epsilon = std::make_shared(); + epsilon->set_uid(K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID) + .set_name("epsilon") + .set_data_type(DataType::FLOAT); + epsilon->set_dim(toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_DIMS)) + .set_stride(toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_STRIDES)); + attrs.set_epsilon(std::move(epsilon)); + + auto [dx, dscale, dbias] = graph->layernorm_backward(dy, x, scale, attrs); + dx->set_uid(K_LAYERNORMBACKWARD_TENSOR_DX_UID).set_output(true).set_name("dx"); + dx->set_dim(toVec(K_LAYERNORMBACKWARD_TENSOR_DX_DIMS)) + .set_stride(toVec(K_LAYERNORMBACKWARD_TENSOR_DX_STRIDES)); + dscale->set_uid(K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID).set_output(true).set_name("dscale"); + dscale->set_dim(toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_DIMS)) + .set_stride(toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_STRIDES)); + dbias->set_uid(K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID).set_output(true).set_name("dbias"); + dbias->set_dim(toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_DIMS)) + .set_stride(toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_STRIDES)); + + return lowerAndDeserialize(*graph, _handle); + } +}; + +// Lowering round-trip: builds a graph, lowers via descriptors, and verifies +// the deserialized FlatBuffer attributes match. +TEST_F(IntegrationLayernormBackwardDescriptorLowering, LayernormBackwardLoweringRoundTrip) +{ + LayernormBackwardAttributes attrs; + attrs.set_name("test_op"); + + auto graphT = buildAndDeserialize(attrs); + + // Verify tensors + ASSERT_EQ(graphT.tensors.size(), 9u); + + // Verify tensor attributes + auto tensorMap = buildTensorMap(graphT); + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_DY_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DY_UID]->dims, + toVec(K_LAYERNORMBACKWARD_TENSOR_DY_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DY_UID]->strides, + toVec(K_LAYERNORMBACKWARD_TENSOR_DY_STRIDES)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DY_UID]->data_type, DataTypeSdk::FLOAT); + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_X_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_X_UID]->dims, + toVec(K_LAYERNORMBACKWARD_TENSOR_X_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_X_UID]->strides, + toVec(K_LAYERNORMBACKWARD_TENSOR_X_STRIDES)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_X_UID]->data_type, DataTypeSdk::FLOAT); + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_SCALE_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_SCALE_UID]->dims, + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_SCALE_UID]->strides, + toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_STRIDES)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_SCALE_UID]->data_type, DataTypeSdk::FLOAT); + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_DX_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DX_UID]->dims, + toVec(K_LAYERNORMBACKWARD_TENSOR_DX_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DX_UID]->strides, + toVec(K_LAYERNORMBACKWARD_TENSOR_DX_STRIDES)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DX_UID]->data_type, DataTypeSdk::FLOAT); + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID]->dims, + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID]->strides, + toVec(K_LAYERNORMBACKWARD_TENSOR_DSCALE_STRIDES)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID]->data_type, DataTypeSdk::FLOAT); + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID]->dims, + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID]->strides, + toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_STRIDES)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID]->data_type, DataTypeSdk::FLOAT); + + // Verify operation node + ASSERT_EQ(graphT.nodes.size(), 1u); + auto& node = graphT.nodes[0]; + EXPECT_EQ(node->compute_data_type, DataTypeSdk::FLOAT); + + auto* opNode = node->attributes.AsLayernormBackwardAttributes(); + ASSERT_NE(opNode, nullptr); + + // Verify required tensor UIDs + EXPECT_EQ(opNode->dy_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DY_UID); + EXPECT_EQ(opNode->x_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_X_UID); + EXPECT_EQ(opNode->scale_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_SCALE_UID); + EXPECT_EQ(opNode->dx_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DX_UID); + EXPECT_EQ(opNode->dscale_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID); + EXPECT_EQ(opNode->dbias_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID); + + // Verify operation name preserved through lowering + EXPECT_EQ(node->name, "test_op"); +} + +} // namespace diff --git a/projects/hipdnn/tools/DescriptorGenerator/configs/layernorm_backward.yaml b/projects/hipdnn/tools/DescriptorGenerator/configs/layernorm_backward.yaml new file mode 100644 index 000000000000..ea27a829c878 --- /dev/null +++ b/projects/hipdnn/tools/DescriptorGenerator/configs/layernorm_backward.yaml @@ -0,0 +1,168 @@ +operation: + name: "LayernormBackward" + class_name: "LayernormBackwardOperationDescriptor" + fbs_table: "LayernormBackwardAttributes" + fbs_generated_header: "layernorm_backward_attributes_generated.h" + + descriptor_type: + enum_name: "HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR" + + operation_attr_prefix: "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD" + + frontend: + packer_function: "createLayernormBackwardOperation" + unpacker_function: "unpackLayernormBackwardOperation" + node_class: "LayernormBackwardNode" + attributes_class: "LayernormBackwardAttributes" + + inputs: + - name: "dy" + enum_name: "DY" + required: true + getter_name: "get_dy" + setter_name: "set_dy" + - name: "x" + enum_name: "X" + required: true + getter_name: "get_x" + setter_name: "set_x" + - name: "scale" + enum_name: "SCALE" + required: true + getter_name: "get_scale" + setter_name: "set_scale" + - name: "mean" + enum_name: "MEAN" + required: false + getter_name: "get_mean" + setter_name: "set_mean" + - name: "inv_variance" + enum_name: "INV_VARIANCE" + required: false + getter_name: "get_inv_variance" + setter_name: "set_inv_variance" + - name: "epsilon" + enum_name: "EPSILON" + required: false + getter_name: "get_epsilon" + setter_name: "set_epsilon" + outputs: + - name: "dx" + enum_name: "DX" + required: true + getter_name: "get_dx" + setter_name: "set_dx" + - name: "dscale" + enum_name: "DSCALE" + required: true + getter_name: "get_dscale" + setter_name: "set_dscale" + - name: "dbias" + enum_name: "DBIAS" + required: true + getter_name: "get_dbias" + setter_name: "set_dbias" + graph_method_name: "layernorm_backward" + graph_method_params: + - name: "dy" + tensor_name: "dy" + - name: "x" + tensor_name: "x" + - name: "scale" + tensor_name: "scale" + - name: "mean" + tensor_name: "mean" + - name: "inv_variance" + tensor_name: "inv_variance" + - name: "epsilon" + tensor_name: "epsilon" + graph_return_type: "array" + graph_return_outputs: + - "dx" + - "dscale" + - "dbias" + + tensor_fields: + - name: "dy" + fbs_field: "dy_tensor_uid" + attr_suffix: "DY" + required: true + frontend_getter: "get_dy()" + + - name: "x" + fbs_field: "x_tensor_uid" + attr_suffix: "X" + required: true + frontend_getter: "get_x()" + + - name: "scale" + fbs_field: "scale_tensor_uid" + attr_suffix: "SCALE" + required: true + frontend_getter: "get_scale()" + + - name: "mean" + fbs_field: "mean_tensor_uid" + attr_suffix: "MEAN" + required: false + frontend_getter: "get_mean()" + + - name: "inv_variance" + fbs_field: "inv_variance_tensor_uid" + attr_suffix: "INV_VARIANCE" + required: false + frontend_getter: "get_inv_variance()" + + - name: "epsilon" + fbs_field: "epsilon_tensor_uid" + attr_suffix: "EPSILON" + required: false + frontend_getter: "get_epsilon()" + + - name: "dx" + fbs_field: "dx_tensor_uid" + attr_suffix: "DX" + required: true + frontend_getter: "get_dx()" + + - name: "dscale" + fbs_field: "dscale_tensor_uid" + attr_suffix: "DSCALE" + required: true + frontend_getter: "get_dscale()" + + - name: "dbias" + fbs_field: "dbias_tensor_uid" + attr_suffix: "DBIAS" + required: true + frontend_getter: "get_dbias()" + + data_fields: + - name: "normalized_dim_count" + fbs_field: "normalized_dim_count" + attr_name: "HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT" + type: "scalar_int64" + required: true + frontend_getter: "get_normalized_dim_count()" + + tensor_array_fields: [] + + has_compute_data_type: true + compute_data_type_attr: "HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT" + + operation_type_enum: "HIPDNN_OPERATION_TYPE_LAYERNORM_BACKWARD" + + test_data: + tensor_uids: { dy: 10, x: 11, scale: 12, mean: 13, inv_variance: 14, epsilon: 15, dx: 16, dscale: 17, dbias: 18} + tensor_configs: + dy: { dims: [16, 64, 32, 32], strides: [65536, 1024, 32, 1] } + x: { dims: [16, 64, 32, 32], strides: [65536, 1024, 32, 1] } + scale: { dims: [1, 64, 32, 32], strides: [65536, 1024, 32, 1] } + mean: { dims: [16, 1, 1, 1], strides: [1, 1, 1, 1] } + inv_variance: { dims: [16, 1, 1, 1], strides: [1, 1, 1, 1] } + epsilon: { dims: [1], strides: [1] } + dx: { dims: [16, 64, 32, 32], strides: [65536, 1024, 32, 1] } + dscale: { dims: [1, 64, 32, 32], strides: [65536, 1024, 32, 1] } + dbias: { dims: [1, 64, 32, 32], strides: [65536, 1024, 32, 1] } + field_values: + normalized_dim_count: 3 From 3b7a5a8876054de9387cae460aee995e62647d4f Mon Sep 17 00:00:00 2001 From: Brent Maas Date: Mon, 20 Apr 2026 09:37:41 +0000 Subject: [PATCH 2/7] Layernorm backward CPU reference --- .../include/hipdnn_frontend/Graph.hpp | 2 +- .../utilities/CpuFpReferenceLayernorm.hpp | 190 ++- .../CpuReferenceGraphExecutor.hpp | 3 + .../detail/LayernormBpropPlan.hpp | 247 +++ .../detail/LayernormBpropSignatureKey.hpp | 197 +++ .../detail/PlanBuilderRegistry.hpp | 2 +- .../detail/PlanRegistrySignatureKey.hpp | 2 + projects/hipdnn/test_sdk/tests/CMakeLists.txt | 3 + .../TestCpuFpReferenceLayernormBackward.cpp | 1415 +++++++++++++++++ .../LayernormGraphUtils.hpp | 149 ++ .../LayernormTensorBundles.hpp | 27 + .../TestLayernormBpropPlan.cpp | 453 ++++++ .../TestLayernormBpropSignatureKey.cpp | 121 ++ projects/hipdnn/tests/frontend/CMakeLists.txt | 3 +- .../frontend/IntegrationLayernormBackward.cpp | 411 +++++ 15 files changed, 3220 insertions(+), 5 deletions(-) create mode 100644 projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormBpropPlan.hpp create mode 100644 projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormBpropSignatureKey.hpp create mode 100644 projects/hipdnn/test_sdk/tests/utilities/TestCpuFpReferenceLayernormBackward.cpp create mode 100644 projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestLayernormBpropPlan.cpp create mode 100644 projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestLayernormBpropSignatureKey.cpp create mode 100644 projects/hipdnn/tests/frontend/IntegrationLayernormBackward.cpp diff --git a/projects/hipdnn/frontend/include/hipdnn_frontend/Graph.hpp b/projects/hipdnn/frontend/include/hipdnn_frontend/Graph.hpp index 952e2ca594d4..522c1627af0e 100644 --- a/projects/hipdnn/frontend/include/hipdnn_frontend/Graph.hpp +++ b/projects/hipdnn/frontend/include/hipdnn_frontend/Graph.hpp @@ -4953,7 +4953,7 @@ class Graph : public INode scale->set_name(attributes.get_name() + "::SCALE"); } - auto mean = attributes.get_epsilon(); + auto mean = attributes.get_mean(); if(mean && mean->get_name().empty()) { mean->set_name(attributes.get_name() + "::MEAN"); diff --git a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/CpuFpReferenceLayernorm.hpp b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/CpuFpReferenceLayernorm.hpp index c7a80d7a8a14..8a67c8206b1d 100644 --- a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/CpuFpReferenceLayernorm.hpp +++ b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/CpuFpReferenceLayernorm.hpp @@ -3,6 +3,7 @@ #pragma once +#include "hipdnn_data_sdk/utilities/ShapeUtilities.hpp" #include #include #include @@ -46,8 +47,8 @@ class CpuFpReferenceLayernorm const hipdnn_data_sdk::utilities::TensorBase* scale, const hipdnn_data_sdk::utilities::TensorBase* bias, hipdnn_data_sdk::utilities::TensorBase& y, - double epsilon, - int64_t normalizedDimCount, + const double epsilon, + const int64_t normalizedDimCount, hipdnn_data_sdk::utilities::TensorBase* mean = nullptr, hipdnn_data_sdk::utilities::TensorBase* rstd = nullptr) { @@ -188,6 +189,191 @@ class CpuFpReferenceLayernorm } } + // Layer normalization backward pass. + // Calculates the gradients for a normalization over the last `normalizedDimCount` dimensions of the input tensor X + // + // For input dY, X with shape [d0, d1, ..., d_{n-1}] and normalizedDimCount = k: + // - Batch dimensions: [d0, ..., d_{n-k-1}] + // - Normalized dimensions: [d_{n-k}, ..., d_{n-1}] + // - Stage 1 (backward values): + // For each batch position b: + // For each element dy_b_n, x_b_n, scale_n (n = 1, 2, ..., N): + // sum_dy_scale_x_n = sum_dy_scale_x_{n-1} + dy_b_n * scale_n * x_b_n + // sum_dy_scale_n = sum_dy_scale_{n-1} + dy_b_n * scale_n + // a = rstd_b * rstd_b * rstd_b * (sum_dy_scale_x - sum_dy_scale * mean_b) / N + // b = rstd_b * sum_dy_scale / N - a * mean_b + // For each element dy_b_n, x_b_n, dx_b_n (n = 1, 2, ..., N): + // dx_b_n = rstd_b * dy_b_n * scale_n - a * x_b_n - b + // - Stage 2 (backward weights): + // For each normalized position n: + // For each element dy_n_b, x_n_b, mean_b, rstd_b (b = 1, 2, ...): + // dscale_sum_b = dscale_sum_{b-1} + dy_n_b * (x_n_b - mean_b) * rstd_b + // dbias_sum_b = dbias_sum_{b-1} + dy_n_b + // dscale_n = dscale_sum_b + // dbias_n = dbias_sum_b + // + // Scale and bias have shape matching the normalized dimensions. + // Mean and rstd inputs, if provided, have shape matching the batch dimensions. + template + static void bprop(const hipdnn_data_sdk::utilities::TensorBase& dy, + const hipdnn_data_sdk::utilities::TensorBase& x, + const hipdnn_data_sdk::utilities::TensorBase& scale, + const hipdnn_data_sdk::utilities::TensorBase& mean, + const hipdnn_data_sdk::utilities::TensorBase& rstd, + hipdnn_data_sdk::utilities::TensorBase& dx, + hipdnn_data_sdk::utilities::TensorBase& dscale, + hipdnn_data_sdk::utilities::TensorBase& dbias, + [[maybe_unused]] const double epsilon, + const int64_t normalizedDimCount) + { + const auto& dims = dy.dims(); + auto ndim = static_cast(dims.size()); + + if(ndim < 1) + { + throw std::runtime_error("Layernorm bprop requires at least 1D tensor."); + } + + if(normalizedDimCount < 1 || normalizedDimCount > ndim) + { + throw std::runtime_error( + "normalizedDimCount must be between 1 and the number of tensor dimensions."); + } + + if(scale.dims().size() != dscale.dims().size() + || scale.dims().size() != dbias.dims().size()) + { + throw std::runtime_error("Scale, dscale and dbias tensors must have the same rank."); + } + + if(mean.dims().size() != rstd.dims().size()) + { + throw std::runtime_error("Mean and rstd tensors must have the same rank."); + } + + for(auto d : dims) + { + if(d <= 0) + { + throw std::runtime_error( + "Dimensions must all be positive (no zero-size dimensions)."); + } + } + + // Split dimensions into batch dims and normalized dims + std::vector batchDims; + int64_t batchDimsSize = 1; + std::vector normalizedDims; + int64_t normalizedDimsSize = 1; + if(scale.dims().size() == dims.size() && mean.dims().size() == dims.size()) // Pad with ones + { + batchDims = std::vector(dims.size(), 1); + normalizedDims = std::vector(dims.size(), 1); + for(size_t i = 0; i < dims.size(); ++i) + { + if(static_cast(i) < ndim - normalizedDimCount) + { + batchDims[i] = dims[i]; + batchDimsSize *= dims[i]; + } + else + { + normalizedDims[i] = dims[i]; + normalizedDimsSize *= dims[i]; + } + } + } + else // Don't pad with ones + { + batchDims + = std::vector(dims.begin(), dims.begin() + (ndim - normalizedDimCount)); + for(auto d : batchDims) + { + batchDimsSize *= d; + } + normalizedDims + = std::vector(dims.begin() + (ndim - normalizedDimCount), dims.end()); + for(auto d : normalizedDims) + { + normalizedDimsSize *= d; + } + } + + // If batchDims is empty (entire tensor is normalized), use a single scalar iteration + if(batchDims.empty()) + { + batchDims.push_back(1); + } + + // Pass 1: backward values + auto layernormBpropValuesFunc = [&](const std::vector& batchIndices) { + auto sumDyScaleX = static_cast(0.0); + auto sumDyScale = static_cast(0.0); + hipdnn_data_sdk::utilities::iterateAlongDimensions( + normalizedDims, [&](const std::vector& normIndices) { + auto fullIndices + = buildFullIndices(batchIndices, normIndices, ndim, normalizedDimCount); + auto dyVal = static_cast(dy.getHostValue(fullIndices)); + auto scaleVal = static_cast(scale.getHostValue(normIndices)); + auto xVal = static_cast(x.getHostValue(fullIndices)); + + sumDyScaleX += dyVal * scaleVal * xVal; + sumDyScale += dyVal * scaleVal; + }); + auto meanVal = static_cast(mean.getHostValue(batchIndices)); + auto rstdVal = static_cast(rstd.getHostValue(batchIndices)); + auto a = rstdVal * rstdVal * rstdVal * (sumDyScaleX - sumDyScale * meanVal) + / static_cast(normalizedDimsSize); + auto b = rstdVal * sumDyScale / static_cast(normalizedDimsSize) + - a * meanVal; + hipdnn_data_sdk::utilities::iterateAlongDimensions( + normalizedDims, [&](const std::vector& normIndices) { + auto fullIndices + = buildFullIndices(batchIndices, normIndices, ndim, normalizedDimCount); + auto dyVal = static_cast(dy.getHostValue(fullIndices)); + auto scaleVal = static_cast(scale.getHostValue(normIndices)); + auto xVal = static_cast(x.getHostValue(fullIndices)); + auto dxVal = rstdVal * dyVal * scaleVal - a * xVal - b; + dx.setHostValue(static_cast(dxVal), fullIndices); + }); + }; + + // Pass 2: backward weights + auto layernormBpropWeightsFunc = [&](const std::vector& normIndices) { + auto dscaleVal = static_cast(0.0); + auto dbiasVal = static_cast(0.0); + hipdnn_data_sdk::utilities::iterateAlongDimensions( + batchDims, [&](const std::vector& batchIndices) { + auto fullIndices + = buildFullIndices(batchIndices, normIndices, ndim, normalizedDimCount); + auto dyVal = static_cast(dy.getHostValue(fullIndices)); + auto xVal = static_cast(x.getHostValue(fullIndices)); + auto meanVal = static_cast(mean.getHostValue(batchIndices)); + auto rstdVal = static_cast(rstd.getHostValue(batchIndices)); + dscaleVal += dyVal * (xVal - meanVal) * rstdVal; + dbiasVal += dyVal; + }); + dscale.setHostValue(static_cast(dscaleVal), normIndices); + dbias.setHostValue(static_cast(dbiasVal), normIndices); + }; + + // Parallelize over batch dimensions + auto parallelValuesFunc = hipdnn_test_sdk::detail::makeParallelTensorFunctor( + layernormBpropValuesFunc, batchDims); + parallelValuesFunc(std::thread::hardware_concurrency()); + auto parallelWeightsFunc = hipdnn_test_sdk::detail::makeParallelTensorFunctor( + layernormBpropWeightsFunc, normalizedDims); + parallelWeightsFunc(std::thread::hardware_concurrency()); + + dx.memory().markHostModified(); + dscale.memory().markHostModified(); + dbias.memory().markHostModified(); + } + private: // Build full N-dimensional indices from batch indices and normalized indices. // For a tensor of ndim dimensions with the last normalizedDimCount being normalized: diff --git a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/CpuReferenceGraphExecutor.hpp b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/CpuReferenceGraphExecutor.hpp index 73e2346b02b6..6893f4351c2e 100644 --- a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/CpuReferenceGraphExecutor.hpp +++ b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/CpuReferenceGraphExecutor.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -164,6 +165,8 @@ class CpuReferenceGraphExecutor return detail::ConvolutionWrwSignatureKey(node, tensorMap, computeType); case hipdnn_flatbuffers_sdk::data_objects::NodeAttributes::LayernormAttributes: return detail::LayernormFpropSignatureKey(node, tensorMap); + case hipdnn_flatbuffers_sdk::data_objects::NodeAttributes::LayernormBackwardAttributes: + return detail::LayernormBpropSignatureKey(node, tensorMap); case hipdnn_flatbuffers_sdk::data_objects::NodeAttributes::MatmulAttributes: return detail::MatmulSignatureKey(node, tensorMap, computeType); case hipdnn_flatbuffers_sdk::data_objects::NodeAttributes::RMSNormAttributes: diff --git a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormBpropPlan.hpp b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormBpropPlan.hpp new file mode 100644 index 000000000000..3ebbe8b287c4 --- /dev/null +++ b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormBpropPlan.hpp @@ -0,0 +1,247 @@ +// Copyright © Advanced Micro Devices, Inc., or its affiliates. +// SPDX-License-Identifier: MIT + +#pragma once + +#include "hipdnn_flatbuffers_sdk/data_objects/tensor_attributes_generated.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace hipdnn_test_sdk::detail +{ + +struct LayernormBpropParams +{ + LayernormBpropParams() = default; + LayernormBpropParams( + const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes& dyAttributes, + const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes& xAttributes, + const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes& scaleAttributes, + const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes& meanAttributes, + const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes& invVarianceAttributes, + const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes& dxAttributes, + const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes& dscaleAttributes, + const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes& dbiasAttributes, + const int64_t normalizedDimCount, + const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes* epsilonAttributes = nullptr) + : dyTensor(unpackTensorAttributes(dyAttributes)) + , xTensor(unpackTensorAttributes(xAttributes)) + , scaleTensor(unpackTensorAttributes(scaleAttributes)) + , meanTensor(unpackTensorAttributes(meanAttributes)) + , invVarianceTensor(unpackTensorAttributes(invVarianceAttributes)) + , dxTensor(unpackTensorAttributes(dxAttributes)) + , dscaleTensor(unpackTensorAttributes(dscaleAttributes)) + , dbiasTensor(unpackTensorAttributes(dbiasAttributes)) + , normalizedDimCount(normalizedDimCount) + , epsilonTensor(epsilonAttributes != nullptr + ? std::make_optional(unpackTensorAttributes(*epsilonAttributes)) + : std::nullopt) + { + } + + hipdnn_flatbuffers_sdk::data_objects::TensorAttributesT dyTensor; + hipdnn_flatbuffers_sdk::data_objects::TensorAttributesT xTensor; + hipdnn_flatbuffers_sdk::data_objects::TensorAttributesT scaleTensor; + hipdnn_flatbuffers_sdk::data_objects::TensorAttributesT meanTensor; + hipdnn_flatbuffers_sdk::data_objects::TensorAttributesT invVarianceTensor; + hipdnn_flatbuffers_sdk::data_objects::TensorAttributesT dxTensor; + hipdnn_flatbuffers_sdk::data_objects::TensorAttributesT dscaleTensor; + hipdnn_flatbuffers_sdk::data_objects::TensorAttributesT dbiasTensor; + int64_t normalizedDimCount; + std::optional epsilonTensor; +}; + +template +class LayernormBpropPlan : public IGraphNodePlanExecutor +{ +public: + LayernormBpropPlan(LayernormBpropParams&& params) + : _params(std::move(params)) + { + } + + std::vector getOutputTensorIds() const override + { + return {_params.dxTensor.uid, _params.dscaleTensor.uid, _params.dbiasTensor.uid}; + } + + void execute(const std::unordered_map& variantPack) override + { + auto shallowDyTensor = createShallowTensor( + _params.dyTensor, variantPack.at(_params.dyTensor.uid)); + + auto shallowXTensor + = createShallowTensor(_params.xTensor, variantPack.at(_params.xTensor.uid)); + + auto shallowScaleTensor = createShallowTensor( + _params.scaleTensor, variantPack.at(_params.scaleTensor.uid)); + + auto shallowMeanTensor = createShallowTensor( + _params.meanTensor, variantPack.at(_params.meanTensor.uid)); + + auto shallowInvVarianceTensor = createShallowTensor( + _params.invVarianceTensor, variantPack.at(_params.invVarianceTensor.uid)); + + auto shallowDxTensor = createShallowTensor( + _params.dxTensor, variantPack.at(_params.dxTensor.uid)); + + auto shallowDscaleTensor = createShallowTensor( + _params.dscaleTensor, variantPack.at(_params.dscaleTensor.uid)); + + auto shallowDbiasTensor = createShallowTensor( + _params.dbiasTensor, variantPack.at(_params.dbiasTensor.uid)); + + // Extract epsilon from pass-by-value tensor (cast to double) + double epsilon = 1e-5; + if(_params.epsilonTensor.has_value()) + { + epsilon = hipdnn_flatbuffers_sdk::utilities::extractDoubleFromTensorValue( + _params.epsilonTensor.value(), "Epsilon"); + } + + utilities::CpuFpReferenceLayernorm::bprop(*shallowDyTensor, + *shallowXTensor, + *shallowScaleTensor, + *shallowMeanTensor, + *shallowInvVarianceTensor, + *shallowDxTensor, + *shallowDscaleTensor, + *shallowDbiasTensor, + epsilon, + _params.normalizedDimCount); + } + +private: + LayernormBpropParams _params; +}; + +template +class LayernormBpropPlanBuilder : public IGraphNodePlanBuilder +{ +public: + using DyDataType = utilities::DataTypeToNative; + using ScaleBiasDataType = utilities::DataTypeToNative; + using MeanInvVarianceDataType = utilities::DataTypeToNative; + using OutputDataType = utilities::DataTypeToNative; + using ComputeDataType = utilities::DataTypeToNative; + + bool isApplicable( + const hipdnn_flatbuffers_sdk::data_objects::Node& node, + const std::unordered_map& + tensorMap) const override + { + if(node.compute_data_type() != ComputeDataTypeEnum) + { + return false; + } + + const auto* nodeAttributes = node.attributes_as_LayernormBackwardAttributes(); + if(nodeAttributes == nullptr) + { + return false; + } + + // Check required tensors + CHECK_TENSOR_EXISTS(tensorMap, nodeAttributes->dy_tensor_uid()); + CHECK_TENSOR_EXISTS(tensorMap, nodeAttributes->x_tensor_uid()); + CHECK_TENSOR_EXISTS(tensorMap, nodeAttributes->scale_tensor_uid()); + if(!nodeAttributes->mean_tensor_uid().has_value()) + { + return false; + } + CHECK_TENSOR_EXISTS(tensorMap, nodeAttributes->mean_tensor_uid().value()); + if(!nodeAttributes->inv_variance_tensor_uid().has_value()) + { + return false; + } + CHECK_TENSOR_EXISTS(tensorMap, nodeAttributes->inv_variance_tensor_uid().value()); + CHECK_TENSOR_EXISTS(tensorMap, nodeAttributes->dx_tensor_uid()); + CHECK_TENSOR_EXISTS(tensorMap, nodeAttributes->dscale_tensor_uid()); + CHECK_TENSOR_EXISTS(tensorMap, nodeAttributes->dbias_tensor_uid()); + + CHECK_TENSOR_TYPE(tensorMap, nodeAttributes->dy_tensor_uid(), DyDataTypeEnum); + CHECK_TENSOR_TYPE(tensorMap, nodeAttributes->x_tensor_uid(), DyDataTypeEnum); + CHECK_TENSOR_TYPE(tensorMap, nodeAttributes->scale_tensor_uid(), ScaleBiasDataTypeEnum); + CHECK_TENSOR_TYPE( + tensorMap, nodeAttributes->mean_tensor_uid().value(), MeanInvVarianceDataTypeEnum); + CHECK_TENSOR_TYPE(tensorMap, + nodeAttributes->inv_variance_tensor_uid().value(), + MeanInvVarianceDataTypeEnum); + CHECK_TENSOR_TYPE(tensorMap, nodeAttributes->dx_tensor_uid(), OutputDataTypeEnum); + CHECK_TENSOR_TYPE(tensorMap, nodeAttributes->dscale_tensor_uid(), OutputDataTypeEnum); + CHECK_TENSOR_TYPE(tensorMap, nodeAttributes->dbias_tensor_uid(), OutputDataTypeEnum); + + // Optional epsilon tensor + if(nodeAttributes->epsilon_tensor_uid().has_value()) + { + CHECK_OPTIONAL_TENSOR_EXISTS(tensorMap, nodeAttributes->epsilon_tensor_uid()); + } + + return true; + } + + std::unique_ptr + buildNodePlan(const hipdnn_flatbuffers_sdk::flatbuffer_utilities::IGraph& graph, + const hipdnn_flatbuffers_sdk::data_objects::Node& node) const override + { + const auto* nodeAttributes = node.attributes_as_LayernormBackwardAttributes(); + if(nodeAttributes == nullptr) + { + throw std::runtime_error("Node attributes are not of type LayernormBackwardAttributes"); + } + if(!nodeAttributes->mean_tensor_uid().has_value()) + { + throw std::runtime_error("Node attributes are missing mean tensor UID"); + } + if(!nodeAttributes->inv_variance_tensor_uid().has_value()) + { + throw std::runtime_error("Node attributes are missing inv variance tensor UID"); + } + + const auto& tensorMap = graph.getTensorMap(); + + const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes* epsilon = nullptr; + + if(nodeAttributes->epsilon_tensor_uid().has_value()) + { + epsilon = tensorMap.at(nodeAttributes->epsilon_tensor_uid().value()); + } + + LayernormBpropParams params( + *tensorMap.at(nodeAttributes->dy_tensor_uid()), + *tensorMap.at(nodeAttributes->x_tensor_uid()), + *tensorMap.at(nodeAttributes->scale_tensor_uid()), + *tensorMap.at(nodeAttributes->mean_tensor_uid().value()), + *tensorMap.at(nodeAttributes->inv_variance_tensor_uid().value()), + *tensorMap.at(nodeAttributes->dx_tensor_uid()), + *tensorMap.at(nodeAttributes->dscale_tensor_uid()), + *tensorMap.at(nodeAttributes->dbias_tensor_uid()), + nodeAttributes->normalized_dim_count(), + epsilon); + + return std::make_unique>(std::move(params)); + } +}; +} // namespace hipdnn_test_sdk::detail diff --git a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormBpropSignatureKey.hpp b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormBpropSignatureKey.hpp new file mode 100644 index 000000000000..036de947c252 --- /dev/null +++ b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormBpropSignatureKey.hpp @@ -0,0 +1,197 @@ +// Copyright © Advanced Micro Devices, Inc., or its affiliates. +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include + +namespace hipdnn_test_sdk::detail +{ + +struct LayernormBpropSignatureKey +{ + const hipdnn_flatbuffers_sdk::data_objects::NodeAttributes nodeType + = hipdnn_flatbuffers_sdk::data_objects::NodeAttributes::LayernormAttributes; + hipdnn_flatbuffers_sdk::data_objects::DataType dyDataType; + hipdnn_flatbuffers_sdk::data_objects::DataType scaleBiasDataType; + hipdnn_flatbuffers_sdk::data_objects::DataType meanInvVarianceDataType; + hipdnn_flatbuffers_sdk::data_objects::DataType outputDataType; + hipdnn_flatbuffers_sdk::data_objects::DataType computeDataType; + + LayernormBpropSignatureKey() = default; + constexpr LayernormBpropSignatureKey( + hipdnn_flatbuffers_sdk::data_objects::DataType dy, + hipdnn_flatbuffers_sdk::data_objects::DataType scaleBias, + hipdnn_flatbuffers_sdk::data_objects::DataType meanInvVariance, + hipdnn_flatbuffers_sdk::data_objects::DataType output, + hipdnn_flatbuffers_sdk::data_objects::DataType compute) + : dyDataType(dy) + , scaleBiasDataType(scaleBias) + , meanInvVarianceDataType(meanInvVariance) + , outputDataType(output) + , computeDataType(compute) + { + } + + LayernormBpropSignatureKey( + const hipdnn_flatbuffers_sdk::data_objects::Node& node, + const std::unordered_map& + tensorMap) + { + const auto* nodeAttributes = node.attributes_as_LayernormBackwardAttributes(); + if(nodeAttributes == nullptr) + { + throw std::runtime_error( + "Node attributes could not be cast to LayernormBackwardAttributes"); + } + + auto dyTensorAttr = tensorMap.at(nodeAttributes->dy_tensor_uid()); + auto xTensorAttr = tensorMap.at(nodeAttributes->x_tensor_uid()); + auto dxTensorAttr = tensorMap.at(nodeAttributes->dx_tensor_uid()); + + if(dyTensorAttr == nullptr || xTensorAttr == nullptr || dxTensorAttr == nullptr) + { + throw std::runtime_error("One or more tensor attributes could not be found in the map, " + "failed to construct key"); + } + + dyDataType = dyTensorAttr->data_type(); + outputDataType = dxTensorAttr->data_type(); + computeDataType = node.compute_data_type(); + + // Scale/bias type: use scale tensor type + auto scaleTensorAttr = tensorMap.at(nodeAttributes->scale_tensor_uid()); + scaleBiasDataType = scaleTensorAttr->data_type(); + + // Mean/inv_variance type: use mean if present, otherwise default to IO type (dy type) + if(nodeAttributes->mean_tensor_uid().has_value()) + { + auto meanTensorAttr = tensorMap.at(nodeAttributes->mean_tensor_uid().value()); + meanInvVarianceDataType = meanTensorAttr->data_type(); + } + else + { + meanInvVarianceDataType = dyDataType; + } + } + + std::size_t operator()(const LayernormBpropSignatureKey& k) const noexcept + { + return k.hashSelf(); + } + + constexpr std::size_t hashSelf() const + { + return static_cast(static_cast(nodeType)) + ^ (static_cast(static_cast(dyDataType)) << 4) + ^ (static_cast(static_cast(scaleBiasDataType)) << 8) + ^ (static_cast(static_cast(meanInvVarianceDataType)) << 12) + ^ (static_cast(static_cast(outputDataType)) << 16) + ^ (static_cast(static_cast(computeDataType)) << 20); + } + + bool operator==(const LayernormBpropSignatureKey& other) const noexcept + { + return nodeType == other.nodeType && dyDataType == other.dyDataType + && scaleBiasDataType == other.scaleBiasDataType + && meanInvVarianceDataType == other.meanInvVarianceDataType + && outputDataType == other.outputDataType + && computeDataType == other.computeDataType; + } + + static std::unordered_map, + LayernormBpropSignatureKey> + getPlanBuilders() + { + std::unordered_map, + LayernormBpropSignatureKey> + map; + + addPlanBuilder(map); + addPlanBuilder(map); + addPlanBuilder(map); + addPlanBuilder(map); + addPlanBuilder(map); + addPlanBuilder(map); + addPlanBuilder(map); + // MIOpen-compatible: all tensors same type, compute in float + addPlanBuilder(map); + addPlanBuilder(map); + + return map; + } + + template + static void addPlanBuilder(std::unordered_map, + LayernormBpropSignatureKey>& map) + { + map[LayernormBpropSignatureKey(DyDataTypeEnum, + ScaleBiasDataTypeEnum, + MeanInvVarianceDataTypeEnum, + OutputDataTypeEnum, + ComputeDataTypeEnum)] + = std::make_unique>(); + } +}; + +inline std::ostream& operator<<(std::ostream& os, const LayernormBpropSignatureKey& key) +{ + os << "Layernorm(dyX=" << key.dyDataType << ", scale=" << key.scaleBiasDataType + << ", meanInvVar=" << key.meanInvVarianceDataType << ", dxDscaleDbias=" << key.outputDataType + << ", compute=" << key.computeDataType << ")"; + return os; +} + +} // namespace hipdnn_test_sdk::detail diff --git a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/PlanBuilderRegistry.hpp b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/PlanBuilderRegistry.hpp index 287c422443c2..525c3faf257f 100644 --- a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/PlanBuilderRegistry.hpp +++ b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/PlanBuilderRegistry.hpp @@ -3,7 +3,6 @@ #pragma once -#include #include #include @@ -13,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/PlanRegistrySignatureKey.hpp b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/PlanRegistrySignatureKey.hpp index 6974249ff437..0a3129330283 100644 --- a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/PlanRegistrySignatureKey.hpp +++ b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/PlanRegistrySignatureKey.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,7 @@ using PlanRegistrySignatureKey = std::variant +#include +#include +#include +#include +#include +#include + +using namespace hipdnn_test_sdk::utilities; +using namespace hipdnn_data_sdk::utilities; +using namespace hipdnn_data_sdk::types; +using hipdnn_test_sdk::detail::safeTestTypeCast; + +// ============================================================================ +// Hand-computed golden reference tests +// ============================================================================ + +TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropSanityValidation2D) +{ + // Input shape: [2, 4] — normalize over last dim (4 features per sample) + // dy = [[-1, 2, -3, 4], + // [2, -4, 6, -8]] + // x = [[1, 2, 3, 4], + // [2, 4, 6, 8]] + // + // Sample 0: mean=2.5, var=1.25, rstd=1/sqrt(1.25+1e-5)=0.894423613312618 + // a = 1.7888329159619083 + // b = -3.5776586765921525 + // dx = [-4.7998926705705713e-05, 3.577687297918808, -7.15538175116928, 3.5777159192454633] + // + // Sample 1: mean=5.0, var=5.0, rstd=1/sqrt(5.0+1e-5)=0.4472131482870333 + // a = -0.8944245077250512 + // b = 3.5776962420511893 + // dx = [2.3999731674440028e-05, -3.5777033974472507, 7.155408583743517, -3.577710552843312] + // + // dscale = [-1.341640786446209, 0.8944271909641394, 1.341640786446209, -5.366563145784836] + // dbias = [1, -2, 3, -4] + + Tensor dy({2, 4}); + Tensor x({2, 4}); + Tensor scale({4}); + Tensor mean({2}); + Tensor rstd({2}); + Tensor dx({2, 4}); + Tensor dscale({4}); + Tensor dbias({4}); + + dy.setHostValue(-1.0, 0, 0); + dy.setHostValue(2.0, 0, 1); + dy.setHostValue(-3.0, 0, 2); + dy.setHostValue(4.0, 0, 3); + dy.setHostValue(2.0, 1, 0); + dy.setHostValue(-4.0, 1, 1); + dy.setHostValue(6.0, 1, 2); + dy.setHostValue(-8.0, 1, 3); + + x.setHostValue(1.0, 0, 0); + x.setHostValue(2.0, 0, 1); + x.setHostValue(3.0, 0, 2); + x.setHostValue(4.0, 0, 3); + x.setHostValue(2.0, 1, 0); + x.setHostValue(4.0, 1, 1); + x.setHostValue(6.0, 1, 2); + x.setHostValue(8.0, 1, 3); + + mean.setHostValue(2.5, 0); + mean.setHostValue(5.0, 1); + + rstd.setHostValue(1.0 / std::sqrt(1.25 + LAYERNORM_DEFAULT_EPSILON), 0); + rstd.setHostValue(1.0 / std::sqrt(5.0 + LAYERNORM_DEFAULT_EPSILON), 1); + + scale.fillWithValue(2.0); + + CpuFpReferenceLayernorm::bprop( + dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + + auto tolerance = layernorm::getTolerance(); + + // Sample 0 outputs + EXPECT_NEAR(dx.getHostValue(0, 0), -2.1465994991753945e-05, tolerance); + EXPECT_NEAR(dx.getHostValue(0, 1), 3.577687297918808, tolerance); + EXPECT_NEAR(dx.getHostValue(0, 2), -7.15538175116928, tolerance); + EXPECT_NEAR(dx.getHostValue(0, 3), 3.5777159192454633, tolerance); + + // Sample 1 outputs + EXPECT_NEAR(dx.getHostValue(1, 0), 5.366547046303793e-06, tolerance); + EXPECT_NEAR(dx.getHostValue(1, 1), -3.5777033974472507, tolerance); + EXPECT_NEAR(dx.getHostValue(1, 2), 7.155408583743517, tolerance); + EXPECT_NEAR(dx.getHostValue(1, 3), -3.577710552843312, tolerance); + + // dscale + EXPECT_NEAR(dscale.getHostValue(0), -1.3416434697532726, tolerance); + EXPECT_NEAR(dscale.getHostValue(1), 0.8944289798355152, tolerance); + EXPECT_NEAR(dscale.getHostValue(2), 1.3416434697532726, tolerance); + EXPECT_NEAR(dscale.getHostValue(3), -5.366573879013091, tolerance); + + // dbias + EXPECT_NEAR(dbias.getHostValue(0), 1.0, tolerance); + EXPECT_NEAR(dbias.getHostValue(1), -2.0, tolerance); + EXPECT_NEAR(dbias.getHostValue(2), 3.0, tolerance); + EXPECT_NEAR(dbias.getHostValue(3), -4.0, tolerance); +} + +TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropSanityValidation3D) +{ + // Input shape: [1, 2, 3] — normalize over last 2 dims (2x3 = 6 features) + // dy = [[[-1, 2, -3], [4, -5, 6]]] + // x = [[[1, 2, 3], [4, 5, 6]]] + // + // mean = (1+2+3+4+5+6)/6 = 3.5 + // var = ((1-3.5)^2 + (2-3.5)^2 + (3-3.5)^2 + (4-3.5)^2 + (5-3.5)^2 + (6-3.5)^2) / 6 + // = (6.25 + 2.25 + 0.25 + 0.25 + 2.25 + 6.25) / 6 = 17.5 / 6 = 2.9166666666666665 + // rstd = 1/sqrt(2.9166666666666665 + 1e-5) = 0.5855390399887689 + // + // With scale=1, bias=0 (identity affine): + // a = rstd³ * (sum(dy * scale * x) - sum(dy * scale) * mean) / 6 + // b = rstd * sum(dy * scale) / 6 - a * mean + // dx = rstd * dy * scale - a * x - b + // dscale = sum(dy * (x - mean) * rstd) + // dbias = sum(dy) + + Tensor dy({1, 2, 3}); + Tensor x({1, 2, 3}); + Tensor scale({2, 3}); + Tensor mean({1}); + Tensor rstd({1}); + Tensor dx({1, 2, 3}); + Tensor dscale({2, 3}); + Tensor dbias({2, 3}); + + dy.setHostValue(-1.0, 0, 0, 0); + dy.setHostValue(2.0, 0, 0, 1); + dy.setHostValue(-3.0, 0, 0, 2); + dy.setHostValue(4.0, 0, 1, 0); + dy.setHostValue(-5.0, 0, 1, 1); + dy.setHostValue(6.0, 0, 1, 2); + + x.setHostValue(1.0, 0, 0, 0); + x.setHostValue(2.0, 0, 0, 1); + x.setHostValue(3.0, 0, 0, 2); + x.setHostValue(4.0, 0, 1, 0); + x.setHostValue(5.0, 0, 1, 1); + x.setHostValue(6.0, 0, 1, 2); + + scale.fillWithValue(1.0); + + mean.setHostValue(3.5, 0); + rstd.setHostValue(0.5855390399887689, 0); + + CpuFpReferenceLayernorm::bprop( + dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 2); + + Tensor dxRef({1, 2, 3}); + Tensor dscaleRef({2, 3}); + Tensor dbiasRef({2, 3}); + + dxRef.setHostValue(-3.0113333097103734e-06, 0, 0, 0); + dxRef.setHostValue(1.4052918891730595, 0, 0, 1); + dxRef.setHostValue(-1.8737255302307223, 0, 0, 2); + dxRef.setHostValue(1.8737255302307223, 0, 1, 0); + dxRef.setHostValue(-3.7474480491281357, 0, 1, 1); + dxRef.setHostValue(2.342159171288385, 0, 1, 2); + + dscaleRef.setHostValue(1.4638475999719223, 0, 0); + dscaleRef.setHostValue(-1.7566171199663065, 0, 1); + dscaleRef.setHostValue(0.8783085599831533, 0, 2); + dscaleRef.setHostValue(1.1710780799775378, 1, 0); + dscaleRef.setHostValue(-4.391542799915767, 1, 1); + dscaleRef.setHostValue(8.783085599831534, 1, 2); + + dbiasRef.setHostValue(-1.0, 0, 0); + dbiasRef.setHostValue(2.0, 0, 1); + dbiasRef.setHostValue(-3.0, 0, 2); + dbiasRef.setHostValue(4.0, 1, 0); + dbiasRef.setHostValue(-5.0, 1, 1); + dbiasRef.setHostValue(6.0, 1, 2); + + auto tolerance = layernorm::getTolerance(); + + // Verify each output element: y = (x - mean) * rstd + for(int i = 0; i < 2; i++) + { + for(int j = 0; j < 3; j++) + { + EXPECT_NEAR(dx.getHostValue(0, i, j), dxRef.getHostValue(0, i, j), tolerance); + EXPECT_NEAR(dscale.getHostValue(i, j), dscaleRef.getHostValue(i, j), tolerance); + EXPECT_NEAR(dbias.getHostValue(i, j), dbiasRef.getHostValue(i, j), tolerance); + } + } +} + +// ============================================================================ +// Corner case: all zeros input +// ============================================================================ + +TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropAllZeros) +{ + // All-zero input: mean=0, var=0, rstd=1/sqrt(epsilon) + // a = rstd³ * (sum(dy * scale * x) - sum(dy * scale) * mean) / 4 = 0 + // b = rstd * sum(dy * scale) / 4 - a * mean = 0 + // dx = rstd * dy * scale - a * x - b = 0 + // dscale = sum(dy * (x - mean) * rstd) = 0 + // dbias = sum(dy) = 0 + + Tensor dy({2, 4}); + Tensor x({2, 4}); + Tensor scale({4}); + Tensor mean({2}); + Tensor rstd({2}); + Tensor dx({2, 4}); + Tensor dscale({4}); + Tensor dbias({4}); + + dy.fillWithValue(0.0); + x.fillWithValue(0.0); + mean.fillWithValue(0.0); + rstd.fillWithValue(1.0 / std::sqrt(LAYERNORM_DEFAULT_EPSILON)); + + for(int i = 0; i < 4; i++) + { + scale.setHostValue(2.0, i); + } + + CpuFpReferenceLayernorm::bprop( + dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + + auto tolerance = layernorm::getTolerance(); + + for(int f = 0; f < 4; f++) + { + EXPECT_NEAR(dscale.getHostValue(f), 0.0, tolerance); + EXPECT_NEAR(dbias.getHostValue(f), 0.0, tolerance); + for(int b = 0; b < 2; b++) + { + EXPECT_NEAR(dx.getHostValue(b, f), 0.0, tolerance); + } + } +} + +// ============================================================================ +// Corner case: all ones input +// ============================================================================ + +TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropAllOnes) +{ + // All-one input: mean=1, var=0, rstd=1/sqrt(epsilon) + // a = rstd³ * (sum(dy * scale * x) - sum(dy * scale) * mean) / 5 = 0 + // b = rstd * sum(dy * scale) / 5 - a * mean = sum(scale) / std::sqrt(epsilon) / 5 = 1.5 / std::sqrt(epsilon) + // dx = rstd * dy * scale - a * x - b = scale / std::sqrt(epsilon) - sum(scale) / std::sqrt(epsilon) / 5 = 0 + // dscale = sum(dy * (x - mean) * rstd) = 0 + // dbias = sum(dy) = 3 + + Tensor dy({3, 5}); + Tensor x({3, 5}); + Tensor scale({5}); + Tensor mean({3}); + Tensor rstd({3}); + Tensor dx({3, 5}); + Tensor dscale({5}); + Tensor dbias({5}); + + dy.fillWithValue(1.0); + x.fillWithValue(1.0); + scale.fillWithValue(1.5); + mean.fillWithValue(1.0); + rstd.fillWithValue(1.0 / std::sqrt(LAYERNORM_DEFAULT_EPSILON)); + + CpuFpReferenceLayernorm::bprop( + dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + + auto tolerance = layernorm::getTolerance(); + + for(int f = 0; f < 5; f++) + { + EXPECT_NEAR(dscale.getHostValue(f), 0.0, tolerance); + EXPECT_NEAR(dbias.getHostValue(f), 3.0, tolerance); + for(int b = 0; b < 3; b++) + { + EXPECT_NEAR(dx.getHostValue(b, f), 0.0, tolerance); + } + } +} + +// ============================================================================ +// Corner case: constant input (all same non-trivial value) +// ============================================================================ + +TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropConstantInput) +{ + // All elements = 7.0, scale = 1.0: mean=7, var=0 + // a = rstd³ * (sum(dy * scale * x) - sum(dy * scale) * mean) / 3 = 0 + // b = rstd * sum(dy * scale) / 3 - a * mean = sum(dy) / std::sqrt(epsilon) / 3 = 7.0 / std::sqrt(epsilon) + // dx = rstd * dy * scale - a * x - b = dy / std::sqrt(epsilon) - sum(dy) / std::sqrt(epsilon) / 3 = 0 + // dscale = sum(dy * (x - mean) * rstd) = 0 + // dbias = sum(dy) = 14.0 + + Tensor dy({2, 3}); + Tensor x({2, 3}); + Tensor scale({3}); + Tensor mean({2}); + Tensor rstd({2}); + Tensor dx({2, 3}); + Tensor dscale({3}); + Tensor dbias({3}); + + dy.fillWithValue(7.0); + x.fillWithValue(7.0); + scale.fillWithValue(1.0); + mean.fillWithValue(7.0); + rstd.fillWithValue(1.0 / std::sqrt(LAYERNORM_DEFAULT_EPSILON)); + + CpuFpReferenceLayernorm::bprop( + dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + + auto tolerance = layernorm::getTolerance(); + + for(int f = 0; f < 3; f++) + { + EXPECT_NEAR(dscale.getHostValue(f), 0.0, tolerance); + EXPECT_NEAR(dbias.getHostValue(f), 14.0, tolerance); + for(int b = 0; b < 2; b++) + { + EXPECT_NEAR(dx.getHostValue(b, f), 0.0, tolerance); + } + } +} + +// ============================================================================ +// Corner case: single element per normalized group +// ============================================================================ + +TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropSingleFeature) +{ + // Shape [3, 1]: normalize over dim of size 1 + // mean = x, var = 0 + // a = rstd³ * (dy * scale * x - dy * scale * mean) = 0 + // b = rstd * dy * scale - a * mean = scale / std::sqrt(epsilon) = 2.0 * dy / std::sqrt(epsilon) + // dx = rstd * dy * scale - a * x - b = 2.0 * dy / std::sqrt(epsilon) - 2.0 * dy / std::sqrt(epsilon) = 0 + // dscale = sum(dy * (x - mean) * rstd) = 0 + // dbias = sum(dy) = -2 + + Tensor dy({3, 1}); + Tensor x({3, 1}); + Tensor scale({1}); + Tensor mean({3}); + Tensor rstd({3}); + Tensor dx({3, 1}); + Tensor dscale({1}); + Tensor dbias({1}); + + dy.setHostValue(-5.0, 0, 0); + dy.setHostValue(3.0, 1, 0); + dy.setHostValue(0.0, 2, 0); + + x.setHostValue(5.0, 0, 0); + x.setHostValue(-3.0, 1, 0); + x.setHostValue(0.0, 2, 0); + + scale.setHostValue(2.0, 0); + + mean.setHostValue(5.0, 0); + mean.setHostValue(-3.0, 1); + mean.setHostValue(0.0, 2); + + rstd.fillWithValue(1.0 / std::sqrt(LAYERNORM_DEFAULT_EPSILON)); + + CpuFpReferenceLayernorm::bprop( + dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + + auto tolerance = layernorm::getTolerance(); + + EXPECT_NEAR(dx.getHostValue(0, 0), 0.0, tolerance); + EXPECT_NEAR(dx.getHostValue(1, 0), 0.0, tolerance); + EXPECT_NEAR(dx.getHostValue(2, 0), 0.0, tolerance); + + EXPECT_NEAR(dscale.getHostValue(0), 0.0, tolerance); + + EXPECT_NEAR(dbias.getHostValue(0), -2.0, tolerance); +} + +// ============================================================================ +// Corner case: negative values and mixed signs +// ============================================================================ + +TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropNegativeAndMixedValues) +{ + // Test with negative and mixed-sign values to ensure correct handling + // dy = [-1, -3, 3, 1] + // x = [-3, 1, -1, 3] + // mean = 0 + // var = 5 + // rstd = 1/sqrt(5 + 1e-5) + // a = rstd³ * (sum(dy * scale * x) - sum(dy * scale) * mean) / 4 = 0 + // b = rstd * sum(dy * scale) / 4 - a * mean = sum(scale) / std::sqrt(epsilon) / 4 = 0 + // dx = rstd * dy * scale - a * x - b = dy / std::sqrt(5 + epsilon) = [-1, -3, 3, 1] / std::sqrt(5 + epsilon) + // dscale = dy * (x - mean) * rstd = [3, -3, -3, 3] / std::sqrt(5 + epsilon) + // dbias = dy = [-1, -3, 3, 1] + + Tensor dy({1, 4}); + Tensor x({1, 4}); + Tensor scale({4}); + Tensor mean({1}); + Tensor rstd({1}); + Tensor dx({1, 4}); + Tensor dscale({4}); + Tensor dbias({4}); + + dy.setHostValue(-1.0, 0, 0); + dy.setHostValue(-3.0, 0, 1); + dy.setHostValue(3.0, 0, 2); + dy.setHostValue(1.0, 0, 3); + + x.setHostValue(-3.0, 0, 0); + x.setHostValue(1.0, 0, 1); + x.setHostValue(-1.0, 0, 2); + x.setHostValue(3.0, 0, 3); + + scale.fillWithValue(1.0); + mean.fillWithValue(0.0); + rstd.fillWithValue(1.0 / std::sqrt(5.0 + LAYERNORM_DEFAULT_EPSILON)); + + CpuFpReferenceLayernorm::bprop( + dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + + auto tolerance = layernorm::getTolerance(); + + // dx and dbias should be antisymmetric: dx[0]=-dx[3], dx[1]=-dx[2] + EXPECT_NEAR(dx.getHostValue(0, 0), -dx.getHostValue(0, 3), tolerance); + EXPECT_NEAR(dx.getHostValue(0, 1), -dx.getHostValue(0, 2), tolerance); + EXPECT_NEAR(dbias.getHostValue(0), -dbias.getHostValue(3), tolerance); + EXPECT_NEAR(dbias.getHostValue(1), -dbias.getHostValue(2), tolerance); + + // dscale should be symmetric: dscale[0]=dscale[3], dscale[1]=dscale[2] + EXPECT_NEAR(dscale.getHostValue(0), dscale.getHostValue(3), tolerance); + EXPECT_NEAR(dscale.getHostValue(1), dscale.getHostValue(2), tolerance); + + // And the actual values + EXPECT_NEAR(dx.getHostValue(0, 0), -1.0 * rstd.getHostValue(0), tolerance); + EXPECT_NEAR(dx.getHostValue(0, 1), -3.0 * rstd.getHostValue(0), tolerance); + EXPECT_NEAR(dx.getHostValue(0, 2), 3.0 * rstd.getHostValue(0), tolerance); + EXPECT_NEAR(dx.getHostValue(0, 3), 1.0 * rstd.getHostValue(0), tolerance); + + EXPECT_NEAR(dscale.getHostValue(0), 3.0 * rstd.getHostValue(0), tolerance); + EXPECT_NEAR(dscale.getHostValue(1), -3.0 * rstd.getHostValue(0), tolerance); + EXPECT_NEAR(dscale.getHostValue(2), -3.0 * rstd.getHostValue(0), tolerance); + EXPECT_NEAR(dscale.getHostValue(3), 3.0 * rstd.getHostValue(0), tolerance); + + EXPECT_NEAR(dbias.getHostValue(0), -1.0, tolerance); + EXPECT_NEAR(dbias.getHostValue(1), -3.0, tolerance); + EXPECT_NEAR(dbias.getHostValue(2), 3.0, tolerance); + EXPECT_NEAR(dbias.getHostValue(3), 1.0, tolerance); +} + +// ============================================================================ +// Numerical stability: large values with small variance +// ============================================================================ + +TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropLargeValueNumericalStability) +{ + // Values clustered around 1e15 with small relative differences. + // Naive one-pass (E[x²] - E[x]²) would suffer catastrophic cancellation here + // because E[x²] ≈ 1e30 and E[x]² ≈ 1e30, so their difference loses all precision. + // Welford's algorithm computes variance from centered deltas, avoiding this. + // + // dy = [-1e15 - 1, -1e15 - 2, -1e15 - 3, -1e15 - 4] + // x = [1e15 + 1, 1e15 + 2, 1e15 + 3, 1e15 + 4] + // mean = 1e15 + 2.5 + // var = 1.25 (same as small-value case, independent of offset) + // rstd = 1/sqrt(1.25 + 1e-5) + // a = rstd³ * (sum(dy * scale * x) - sum(dy * scale) * mean) / 4 = 0 + // b = rstd * sum(dy * scale) / 4 - a * mean = (-1e15 - 2.5) / std::sqrt(1.25 + 1e-5) = -894423613312620.2 + // dx = rstd * dy * scale - a * x - b = [1.375, 0.5, -0.375, -1.375] + // dscale = dy * (x - mean) * rstd = [1.5e+15 + 1.5, 5.0e+14 + 1.0, -5.0e+14 - 1.5, -1.5e+15 - 6.0] / std::sqrt(1.25 + 1e-5) = [1.34163542e+15, 4.47211807e+14, -4.47211807e+14, -1.34163542e+15] + // dbias = dy = [-1e15 - 1, -1e15 - 2, -1e15 - 3, -1e15 - 4] + + Tensor dy({1, 4}); + Tensor x({1, 4}); + Tensor scale({4}); + Tensor mean({1}); + Tensor rstd({1}); + Tensor dx({1, 4}); + Tensor dscale({4}); + Tensor dbias({4}); + + const double offset = 1.0e15; + dy.setHostValue(-offset - 1.0, 0, 0); + dy.setHostValue(-offset - 2.0, 0, 1); + dy.setHostValue(-offset - 3.0, 0, 2); + dy.setHostValue(-offset - 4.0, 0, 3); + + x.setHostValue(offset + 1.0, 0, 0); + x.setHostValue(offset + 2.0, 0, 1); + x.setHostValue(offset + 3.0, 0, 2); + x.setHostValue(offset + 4.0, 0, 3); + + scale.fillWithValue(1.0); + mean.fillWithValue(offset + 2.5); + rstd.fillWithValue(1.0 / std::sqrt(1.25 + LAYERNORM_DEFAULT_EPSILON)); + + CpuFpReferenceLayernorm::bprop( + dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + + auto tolerance = layernorm::getTolerance(); + + // Normalized output should match the small-value case exactly + EXPECT_NEAR(dx.getHostValue(0, 0), 1.375, tolerance); + EXPECT_NEAR(dx.getHostValue(0, 1), 0.5, tolerance); + EXPECT_NEAR(dx.getHostValue(0, 2), -0.375, tolerance); + EXPECT_NEAR(dx.getHostValue(0, 3), -1.375, tolerance); + + EXPECT_NEAR(dscale.getHostValue(0), (1.5e15 + 1.5) * rstd.getHostValue(0), tolerance); + EXPECT_NEAR(dscale.getHostValue(1), (0.5e15 + 1.0) * rstd.getHostValue(0), tolerance); + EXPECT_NEAR(dscale.getHostValue(2), (-0.5e15 - 1.5) * rstd.getHostValue(0), tolerance); + EXPECT_NEAR(dscale.getHostValue(3), (-1.5e15 - 6.0) * rstd.getHostValue(0), tolerance); + + EXPECT_NEAR(dbias.getHostValue(0), -1.0e15 - 1.0, tolerance); + EXPECT_NEAR(dbias.getHostValue(1), -1.0e15 - 2.0, tolerance); + EXPECT_NEAR(dbias.getHostValue(2), -1.0e15 - 3.0, tolerance); + EXPECT_NEAR(dbias.getHostValue(3), -1.0e15 - 4.0, tolerance); +} + +// ============================================================================ +// Realistic shape: typical transformer hidden dim +// ============================================================================ + +TEST(TestCpuFpReferenceLayernormBackwardFp32, BpropTypicalTransformerShape) +{ + // Batch=2, SeqLen=8, Hidden=64 — normalize over last dim (hidden) + Tensor dy({2, 8, 64}); + Tensor x({2, 8, 64}); + Tensor scale({64}); + Tensor mean({2, 8}); + Tensor rstd({2, 8}); + Tensor dx({2, 8, 64}); + Tensor dscale({64}); + Tensor dbias({64}); + + dy.fillWithRandomValues(-1.0f, 1.0f, 42); + x.fillWithRandomValues(-1.0f, 1.0f, 43); + scale.fillWithValue(1.0f); + for(size_t i = 0; i < 2; ++i) + { + for(size_t j = 0; j < 8; ++j) + { + float batchMean = 0.0f; + for(size_t k = 0; k < 64; ++k) + { + batchMean += x.getHostValue(i, j, k); + } + batchMean /= 64.0f; + mean.setHostValue(batchMean, i, j); + float batchVariance = 0.0f; + for(size_t k = 0; k < 64; ++k) + { + const float diff = x.getHostValue(i, j, k) - batchMean; + batchVariance += diff * diff; + } + batchVariance /= 64.0f; + rstd.setHostValue( + 1.0f / std::sqrt(batchVariance + static_cast(LAYERNORM_DEFAULT_EPSILON)), + i, + j); + } + } + + CpuFpReferenceLayernorm::bprop( + dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + + auto tolerance = layernorm::getTolerance(); + + for(size_t i = 0; i < 2; ++i) + { + for(size_t j = 0; j < 8; ++j) + { + const float batchRstd = rstd.getHostValue(i, j); + float sumDyScaleX = 0.0f; + float sumDyScale = 0.0f; + for(size_t k = 0; k < 64; ++k) + { + sumDyScaleX + += dy.getHostValue(i, j, k) * scale.getHostValue(k) * x.getHostValue(i, j, k); + sumDyScale += dy.getHostValue(i, j, k) * scale.getHostValue(k); + } + const float a = batchRstd * batchRstd * batchRstd + * (sumDyScaleX - sumDyScale * mean.getHostValue(i, j)) / 64.0f; + const float b = batchRstd * sumDyScale / 64.0f - a * mean.getHostValue(i, j); + for(size_t k = 0; k < 64; ++k) + { + const float outDx = batchRstd * dy.getHostValue(i, j, k) * scale.getHostValue(k) + - a * x.getHostValue(i, j, k) - b; + EXPECT_NEAR(outDx, dx.getHostValue(i, j, k), tolerance); + } + } + } + for(size_t k = 0; k < 64; ++k) + { + float batchDscale = 0.0f; + float batchDbias = 0.0f; + for(size_t i = 0; i < 2; ++i) + { + for(size_t j = 0; j < 8; ++j) + { + batchDscale += dy.getHostValue(i, j, k) + * (x.getHostValue(i, j, k) - mean.getHostValue(i, j)) + * rstd.getHostValue(i, j); + batchDbias += dy.getHostValue(i, j, k); + } + } + EXPECT_NEAR(batchDscale, dscale.getHostValue(k), tolerance); + EXPECT_NEAR(batchDbias, dbias.getHostValue(k), tolerance); + } +} + +// ============================================================================ +// Type compatibility: various type combinations +// ============================================================================ + +template +struct TypePair +{ + using First = T1; + using Second = T2; +}; + +using LayernormBpropTypes = ::testing::Types, + TypePair, + TypePair, + TypePair>; + +template +class CpuFpReferenceLayernormBpropTyped : public ::testing::Test +{ +}; + +TYPED_TEST_SUITE(CpuFpReferenceLayernormBpropTyped, LayernormBpropTypes, ); + +TYPED_TEST(CpuFpReferenceLayernormBpropTyped, BpropRunsWithoutError) +{ + using DyType = typename TypeParam::First; + using ScaleType = typename TypeParam::Second; + + Tensor dy({2, 8, 32}); + Tensor x({2, 8, 32}); + Tensor scale({32}); + Tensor mean({2, 8}); + Tensor rstd({2, 8}); + Tensor dx({2, 8, 32}); + Tensor dscale({32}); + Tensor dbias({32}); + + dy.fillWithValue(safeTestTypeCast(0.0)); + x.fillWithValue(safeTestTypeCast(1.0)); + scale.fillWithValue(safeTestTypeCast(1.0)); + mean.fillWithValue(safeTestTypeCast(1.0)); + rstd.fillWithValue(safeTestTypeCast(1.0 / std::sqrt(LAYERNORM_DEFAULT_EPSILON))); + + // Should not throw + CpuFpReferenceLayernorm::bprop( + dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + + auto tolerance = layernorm::getTolerance(); + + // All zeroes dy -> all dx, dscale and dbias are zero + for(size_t k = 0; k < 32; ++k) + { + for(size_t i = 0; i < 2; ++i) + { + for(size_t j = 0; j < 8; ++j) + { + EXPECT_NEAR(static_cast(dx.getHostValue(i, j, k)), 0.0f, tolerance); + } + } + EXPECT_NEAR(static_cast(dscale.getHostValue(k)), 0.0f, tolerance); + EXPECT_NEAR(static_cast(dbias.getHostValue(k)), 0.0f, tolerance); + } +} + +// ============================================================================ +// Multi-dim normalization: normalize over last 2 dims +// ============================================================================ + +TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropNormalizeLastTwoDims) +{ + // Shape [2, 3, 4], normalize over last 2 dims (3x4 = 12 features) + // Each of the 2 batches is independently normalized over 12 elements + + Tensor dy({2, 3, 4}); + Tensor x({2, 3, 4}); + Tensor scale({3, 4}); + Tensor mean({2}); + Tensor rstd({2}); + Tensor dx({2, 3, 4}); + Tensor dscale({3, 4}); + Tensor dbias({3, 4}); + + // Batch 0: dy values 0.1..1.2, x values 1..12, mean = (1+2+...+12)/12 = 78/12 = 6.5 + double batchVar = 0.0; + for(int i = 0; i < 3; i++) + { + for(int j = 0; j < 4; j++) + { + dy.setHostValue(static_cast(i * 4 + j + 1) / 10.0, 0, i, j); + auto xVal = static_cast(i * 4 + j + 1); + x.setHostValue(xVal, 0, i, j); + batchVar += (xVal - 6.5) * (xVal - 6.5); + } + } + mean.setHostValue(6.5, 0); + rstd.setHostValue(1.0 / std::sqrt(batchVar + LAYERNORM_DEFAULT_EPSILON), 0); + + // Batch 1: dy values 1.3..2.4, x values 13..24, mean = (13+14+...+24)/12 = 222/12 = 18.5 + batchVar = 0.0; + for(int i = 0; i < 2; i++) + { + for(int j = 0; j < 3; j++) + { + dy.setHostValue(static_cast(i * 4 + j + 13) / 10.0, 1, i, j); + auto xVal = static_cast(i * 4 + j + 13); + x.setHostValue(xVal, 1, i, j); + batchVar += (xVal - 18.5) * (xVal - 18.5); + } + } + mean.setHostValue(18.5, 1); + rstd.setHostValue(1.0 / std::sqrt(batchVar + LAYERNORM_DEFAULT_EPSILON), 1); + + scale.fillWithValue(1.0); + + CpuFpReferenceLayernorm::bprop( + dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 2); + + auto tolerance = layernorm::getTolerance(); + + for(size_t i = 0; i < 2; ++i) + { + const double batchMean = mean.getHostValue(i); + const double batchRstd = rstd.getHostValue(i); + double sumDyScaleX = 0.0; + double sumDyScale = 0.0; + for(size_t j = 0; j < 3; ++j) + { + for(size_t k = 0; k < 4; ++k) + { + sumDyScaleX += dy.getHostValue(i, j, k) * scale.getHostValue(j, k) + * x.getHostValue(i, j, k); + sumDyScale += dy.getHostValue(i, j, k) * scale.getHostValue(j, k); + } + } + const double a + = batchRstd * batchRstd * batchRstd * (sumDyScaleX - sumDyScale * batchMean) / 12.0; + const double b = batchRstd * sumDyScale / 12.0 - a * batchMean; + for(size_t j = 0; j < 3; ++j) + { + for(size_t k = 0; k < 4; ++k) + { + const double outDx = batchRstd * dy.getHostValue(i, j, k) * scale.getHostValue(j, k) + - a * x.getHostValue(i, j, k) - b; + EXPECT_NEAR(outDx, dx.getHostValue(i, j, k), tolerance); + } + } + } + for(size_t k = 0; k < 4; ++k) + { + for(size_t j = 0; j < 3; ++j) + { + double outDscale = 0.0; + double outDbias = 0.0; + for(size_t i = 0; i < 2; ++i) + { + outDscale += dy.getHostValue(i, j, k) + * (x.getHostValue(i, j, k) - mean.getHostValue(i)) + * rstd.getHostValue(i); + outDbias += dy.getHostValue(i, j, k); + } + EXPECT_NEAR(outDscale, dscale.getHostValue(j, k), tolerance); + EXPECT_NEAR(outDbias, dbias.getHostValue(j, k), tolerance); + } + } +} + +// ============================================================================ +// Corner case: "diagonal-like" pattern — one hot-per-row +// ============================================================================ + +TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropOneHotRows) +{ + // Shape [3, 3], each row is a one-hot vector + // Row 0: [1, 0, 0], mean=1/3, var=(4/9+1/9+1/9)/3=2/9 + // Row 1: [0, 1, 0], same statistics by symmetry + // Row 2: [0, 0, 1], same statistics by symmetry + // + // All rows should produce the same set of output values (permuted) + + Tensor dy({3, 3}); + Tensor x({3, 3}); + Tensor scale({3}); + Tensor mean({3}); + Tensor rstd({3}); + Tensor dx({3, 3}); + Tensor dscale({3}); + Tensor dbias({3}); + + dy.fillWithValue(0.0); + dy.setHostValue(0.1, 0, 0); + dy.setHostValue(0.1, 1, 1); + dy.setHostValue(0.1, 2, 2); + + x.fillWithValue(0.0); + x.setHostValue(1.0, 0, 0); + x.setHostValue(1.0, 1, 1); + x.setHostValue(1.0, 2, 2); + + scale.fillWithValue(1.0); + mean.fillWithValue(1.0 / 3.0); + rstd.fillWithValue(1.0 / std::sqrt(2.0 / 9.0 + LAYERNORM_DEFAULT_EPSILON)); + + CpuFpReferenceLayernorm::bprop( + dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + + auto tolerance = layernorm::getTolerance(); + + // The "hot" position output should be identical across rows + // dx[0,0] should equal dx[1,1] and dx[2,2] + EXPECT_NEAR(dx.getHostValue(0, 0), dx.getHostValue(1, 1), tolerance); + EXPECT_NEAR(dx.getHostValue(1, 1), dx.getHostValue(2, 2), tolerance); + + // The "cold" position output should be identical across rows + EXPECT_NEAR(dx.getHostValue(0, 1), dx.getHostValue(0, 2), tolerance); + EXPECT_NEAR(dx.getHostValue(0, 1), dx.getHostValue(1, 0), tolerance); +} + +// ============================================================================ +// Verify per-feature scale and bias +// ============================================================================ + +TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropPerFeatureScale) +{ + // Verify that different scale per feature is applied correctly + // dy = [[0.1, 0.2, 0.3, 0.4]] + // x = [[1, 2, 3, 4]] + // scale = [1, 2, 3, 4] + + Tensor dy({1, 4}); + Tensor x({1, 4}); + Tensor scale({4}); + Tensor mean({1}); + Tensor rstd({1}); + Tensor dx({1, 4}); + Tensor dscale({4}); + Tensor dbias({4}); + + dy.setHostValue(0.1, 0, 0); + dy.setHostValue(0.2, 0, 1); + dy.setHostValue(0.3, 0, 2); + dy.setHostValue(0.4, 0, 3); + + x.setHostValue(1.0, 0, 0); + x.setHostValue(2.0, 0, 1); + x.setHostValue(3.0, 0, 2); + x.setHostValue(4.0, 0, 3); + + scale.setHostValue(1.0, 0); + scale.setHostValue(2.0, 1); + scale.setHostValue(3.0, 2); + scale.setHostValue(4.0, 3); + + // mean=2.5, var=1.25, rstd=1/sqrt(1.25+1e-5) + mean.setHostValue(2.5, 0); + rstd.setHostValue(1.0 / std::sqrt(1.25 + LAYERNORM_DEFAULT_EPSILON), 0); + + CpuFpReferenceLayernorm::bprop( + dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + + auto tolerance = layernorm::getTolerance(); + + // a = rstd³ * (sum(dy[i] * scale[i] * x[i]) - sum(dy[i] * scale[i]) * mean) + // b = rstd * sum(dy[i] * scale[i]) - a * mean + // dx[i] = rstd * dy[i] * scale[i] - a * x[i] - b + // dscale[i] = dy[i] * (x[i] - mean) * rstd + // dbias[i] = dy[i] + const double batchRstd = rstd.getHostValue(0); + double sumDyScaleX = 0.0; + double sumDyScale = 0.0; + for(size_t k = 0; k < 4; ++k) + { + sumDyScaleX += dy.getHostValue(0, k) * scale.getHostValue(k) * x.getHostValue(0, k); + sumDyScale += dy.getHostValue(0, k) * scale.getHostValue(k); + } + const double a = batchRstd * batchRstd * batchRstd + * (sumDyScaleX - sumDyScale * mean.getHostValue(0)) / 4.0; + const double b = batchRstd * sumDyScale / 4.0 - a * mean.getHostValue(0); + for(size_t k = 0; k < 4; ++k) + { + const double outDx = batchRstd * dy.getHostValue(0, k) * scale.getHostValue(k) + - a * x.getHostValue(0, k) - b; + EXPECT_NEAR(outDx, dx.getHostValue(0, k), tolerance); + } + for(size_t k = 0; k < 4; ++k) + { + const double outDscale = dy.getHostValue(0, k) + * (x.getHostValue(0, k) - mean.getHostValue(0)) + * rstd.getHostValue(0); + const double outDbias = dy.getHostValue(0, k); + EXPECT_NEAR(outDscale, dscale.getHostValue(k), tolerance); + EXPECT_NEAR(outDbias, dbias.getHostValue(k), tolerance); + } +} + +// ============================================================================ +// Error handling: invalid dimensions +// ============================================================================ + +TEST(TestCpuFpReferenceLayernormBackwardFp32, BpropThrowsOnInvalidNormalizedDimCount) +{ + const Tensor dy({2, 4}); + const Tensor x({2, 4}); + const Tensor scale({4}); + const Tensor mean({2}); + const Tensor rstd({2}); + Tensor dx({2, 4}); + Tensor dscale({4}); + Tensor dbias({4}); + + // normalizedDimCount = 0 is invalid + EXPECT_THROW(CpuFpReferenceLayernorm::bprop( + dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 0), + std::runtime_error); + + // normalizedDimCount > ndim is invalid + EXPECT_THROW(CpuFpReferenceLayernorm::bprop( + dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 3), + std::runtime_error); +} + +// ============================================================================ +// Error handling: scalar (0D) tensor +// ============================================================================ + +TEST(TestCpuFpReferenceLayernormBackwardFp32, BpropThrowsOnScalarTensor) +{ + const Tensor dy({}); + const Tensor x({}); + const Tensor scale({}); + const Tensor mean({}); + const Tensor rstd({}); + Tensor dx({}); + Tensor dscale({}); + Tensor dbias({}); + + EXPECT_THROW(CpuFpReferenceLayernorm::bprop( + dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1), + std::runtime_error); +} + +// ============================================================================ +// Dimensionality coverage: 1D tensor +// ============================================================================ + +TEST(TestCpuFpReferenceLayernormBackwardFp64, Bprop1D) +{ + // Shape [5] — normalize entire 1D tensor (single group, no batch dim) + // dy = [-1, -2, -3, -4, -5] + // x = [2, 4, 6, 8, 10] + // mean = 6, var = (16+4+0+4+16)/5 = 8 + // rstd = 1/sqrt(8 + 1e-5) + + Tensor dy({5}); + Tensor x({5}); + Tensor scale({5}); + Tensor mean({1}); + Tensor rstd({1}); + Tensor dx({5}); + Tensor dscale({5}); + Tensor dbias({5}); + + dy.setHostValue(-1, 0); + dy.setHostValue(-2, 1); + dy.setHostValue(-3, 2); + dy.setHostValue(-4, 3); + dy.setHostValue(-5, 4); + + x.setHostValue(2.0, 0); + x.setHostValue(4.0, 1); + x.setHostValue(6.0, 2); + x.setHostValue(8.0, 3); + x.setHostValue(10.0, 4); + + scale.fillWithValue(1.0); + mean.fillWithValue(6.0); + rstd.fillWithValue(1.0 / std::sqrt(8.0 + LAYERNORM_DEFAULT_EPSILON)); + + CpuFpReferenceLayernorm::bprop( + dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + + auto tolerance = layernorm::getTolerance(); + + const double batchRstd = rstd.getHostValue(0); + double sumDyScaleX = 0.0; + double sumDyScale = 0.0; + for(size_t k = 0; k < 5; ++k) + { + sumDyScaleX += dy.getHostValue(k) * scale.getHostValue(k) * x.getHostValue(k); + sumDyScale += dy.getHostValue(k) * scale.getHostValue(k); + } + const double a = batchRstd * batchRstd * batchRstd + * (sumDyScaleX - sumDyScale * mean.getHostValue(0)) / 5.0; + const double b = batchRstd * sumDyScale / 5.0 - a * mean.getHostValue(0); + for(size_t k = 0; k < 5; ++k) + { + const double outDx + = batchRstd * dy.getHostValue(k) * scale.getHostValue(k) - a * x.getHostValue(k) - b; + EXPECT_NEAR(outDx, dx.getHostValue(k), tolerance); + } + for(size_t k = 0; k < 5; ++k) + { + const double outDscale = dy.getHostValue(k) * (x.getHostValue(k) - mean.getHostValue(0)) + * rstd.getHostValue(0); + const double outDbias = dy.getHostValue(k); + EXPECT_NEAR(outDscale, dscale.getHostValue(k), tolerance); + EXPECT_NEAR(outDbias, dbias.getHostValue(k), tolerance); + } +} + +// ============================================================================ +// Dimensionality coverage: 4D tensor +// ============================================================================ + +TEST(TestCpuFpReferenceLayernormBackwardFp64, Bprop4DNormalizeLast1) +{ + // Shape [2, 3, 2, 4], normalizedDimCount=1: normalize over last dim (4) + // Batch dims = [2, 3, 2], 12 independent groups of 4 elements each + + Tensor dy({2, 3, 2, 4}); + Tensor x({2, 3, 2, 4}); + Tensor scale({4}); + Tensor mean({2, 3, 2}); + Tensor rstd({2, 3, 2}); + Tensor dx({2, 3, 2, 4}); + Tensor dscale({4}); + Tensor dbias({4}); + + // Fill with sequential values so each group has a known pattern + double val = 1.0; + for(int b = 0; b < 2; b++) + { + for(int c = 0; c < 3; c++) + { + for(int h = 0; h < 2; h++) + { + for(int w = 0; w < 4; w++) + { + dy.setHostValue(-val / 10.0, b, c, h, w); + x.setHostValue(val, b, c, h, w); + val += 1.0; + } + } + } + } + + scale.fillWithValue(1.0); + mean.fillWithValue(2.5); + rstd.fillWithValue(1.0 / std::sqrt(1.25 + LAYERNORM_DEFAULT_EPSILON)); + + CpuFpReferenceLayernorm::bprop( + dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + + auto tolerance = layernorm::getTolerance(); + + for(size_t i = 0; i < 2; ++i) + { + for(size_t j = 0; j < 3; ++j) + { + for(size_t k = 0; k < 2; ++k) + { + const double batchRstd = rstd.getHostValue(i, j, k); + double sumDyScaleX = 0.0; + double sumDyScale = 0.0; + for(size_t l = 0; l < 4; ++l) + { + sumDyScaleX += dy.getHostValue(i, j, k, l) * scale.getHostValue(l) + * x.getHostValue(i, j, k, l); + sumDyScale += dy.getHostValue(i, j, k, l) * scale.getHostValue(l); + } + const double a = batchRstd * batchRstd * batchRstd + * (sumDyScaleX - sumDyScale * mean.getHostValue(i, j, k)) / 4.0; + const double b = batchRstd * sumDyScale / 4.0 - a * mean.getHostValue(i, j, k); + for(size_t l = 0; l < 4; ++l) + { + const double outDx + = batchRstd * dy.getHostValue(i, j, k, l) * scale.getHostValue(l) + - a * x.getHostValue(i, j, k, l) - b; + EXPECT_NEAR(outDx, dx.getHostValue(i, j, k, l), tolerance); + } + } + } + } + for(size_t l = 0; l < 4; ++l) + { + double outDscale = 0.0; + double outDbias = 0.0; + for(size_t i = 0; i < 2; ++i) + { + for(size_t j = 0; j < 3; ++j) + { + for(size_t k = 0; k < 2; ++k) + { + outDscale += dy.getHostValue(i, j, k, l) + * (x.getHostValue(i, j, k, l) - mean.getHostValue(i, j, k)) + * rstd.getHostValue(i, j, k); + outDbias += dy.getHostValue(i, j, k, l); + } + } + } + EXPECT_NEAR(outDscale, dscale.getHostValue(l), tolerance); + EXPECT_NEAR(outDbias, dbias.getHostValue(l), tolerance); + } +} + +TEST(TestCpuFpReferenceLayernormBackwardFp64, Bprop4DNormalizeLast3) +{ + // Shape [2, 3, 2, 4], normalizedDimCount=3: normalize over last 3 dims (3*2*4=24) + // Batch dims = [2], 2 independent groups of 24 elements each + + Tensor dy({2, 3, 2, 4}); + Tensor x({2, 3, 2, 4}); + Tensor scale({3, 2, 4}); + Tensor mean({2}); + Tensor rstd({2}); + Tensor dx({2, 3, 2, 4}); + Tensor dscale({3, 2, 4}); + Tensor dbias({3, 2, 4}); + + // Batch 0: values 1..24, Batch 1: values 25..48 + double val = 1.0; + for(int b = 0; b < 2; b++) + { + for(int c = 0; c < 3; c++) + { + for(int h = 0; h < 2; h++) + { + for(int w = 0; w < 4; w++) + { + dy.setHostValue(-val / 10.0, b, c, h, w); + x.setHostValue(val, b, c, h, w); + val += 1.0; + } + } + } + } + + scale.fillWithValue(1.0); + mean.fillWithValue(12.5); + rstd.fillWithValue(1.0 / std::sqrt(47.91666666667 + LAYERNORM_DEFAULT_EPSILON)); + + CpuFpReferenceLayernorm::bprop( + dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 3); + + auto tolerance = layernorm::getTolerance(); + + for(size_t i = 0; i < 2; ++i) + { + const double batchRstd = rstd.getHostValue(i); + double sumDyScaleX = 0.0; + double sumDyScale = 0.0; + for(size_t j = 0; j < 3; ++j) + { + for(size_t k = 0; k < 2; ++k) + { + for(size_t l = 0; l < 4; ++l) + { + sumDyScaleX += dy.getHostValue(i, j, k, l) * scale.getHostValue(j, k, l) + * x.getHostValue(i, j, k, l); + sumDyScale += dy.getHostValue(i, j, k, l) * scale.getHostValue(j, k, l); + } + } + } + const double a = batchRstd * batchRstd * batchRstd + * (sumDyScaleX - sumDyScale * mean.getHostValue(i)) / 24.0; + const double b = batchRstd * sumDyScale / 24.0 - a * mean.getHostValue(i); + for(size_t j = 0; j < 3; ++j) + { + for(size_t k = 0; k < 2; ++k) + { + for(size_t l = 0; l < 4; ++l) + { + const double outDx + = batchRstd * dy.getHostValue(i, j, k, l) * scale.getHostValue(j, k, l) + - a * x.getHostValue(i, j, k, l) - b; + EXPECT_NEAR(outDx, dx.getHostValue(i, j, k, l), tolerance); + } + } + } + } + for(size_t j = 0; j < 3; ++j) + { + for(size_t k = 0; k < 2; ++k) + { + for(size_t l = 0; l < 4; ++l) + { + double outDscale = 0.0; + double outDbias = 0.0; + for(size_t i = 0; i < 2; ++i) + { + outDscale += dy.getHostValue(i, j, k, l) + * (x.getHostValue(i, j, k, l) - mean.getHostValue(i)) + * rstd.getHostValue(i); + outDbias += dy.getHostValue(i, j, k, l); + } + EXPECT_NEAR(outDscale, dscale.getHostValue(j, k, l), tolerance); + EXPECT_NEAR(outDbias, dbias.getHostValue(j, k, l), tolerance); + } + } + } +} + +// ============================================================================ +// Full-tensor normalization (normalizedDimCount == ndim) +// ============================================================================ + +TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropFullTensorNormalization) +{ + // Shape [2, 3], normalizedDimCount=2 means normalize entire tensor per "batch" + // But there are no batch dims here, so entire tensor is one group + // x = [[-1, 2, -3], [4, -5, 6]] + // x = [[1, 2, 3], [4, 5, 6]] + // mean = 3.5, var = 17.5/6 = 2.9166... + + Tensor dy({2, 3}); + Tensor x({2, 3}); + Tensor scale({2, 3}); + Tensor mean({1}); + Tensor rstd({1}); + Tensor dx({2, 3}); + Tensor dscale({2, 3}); + Tensor dbias({2, 3}); + + dy.setHostValue(-1.0, 0, 0); + dy.setHostValue(2.0, 0, 1); + dy.setHostValue(-3.0, 0, 2); + dy.setHostValue(4.0, 1, 0); + dy.setHostValue(-5.0, 1, 1); + dy.setHostValue(6.0, 1, 2); + + x.setHostValue(1.0, 0, 0); + x.setHostValue(2.0, 0, 1); + x.setHostValue(3.0, 0, 2); + x.setHostValue(4.0, 1, 0); + x.setHostValue(5.0, 1, 1); + x.setHostValue(6.0, 1, 2); + + scale.fillWithValue(1.0); + mean.fillWithValue(3.5); + rstd.fillWithValue(1.0 / std::sqrt(2.916666667 + LAYERNORM_DEFAULT_EPSILON)); + + CpuFpReferenceLayernorm::bprop( + dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 2); + + auto tolerance = layernorm::getTolerance(); + + const double batchRstd = rstd.getHostValue(0); + double sumDyScaleX = 0.0; + double sumDyScale = 0.0; + for(size_t i = 0; i < 2; ++i) + { + for(size_t j = 0; j < 3; ++j) + { + sumDyScaleX += dy.getHostValue(i, j) * scale.getHostValue(i, j) * x.getHostValue(i, j); + sumDyScale += dy.getHostValue(i, j) * scale.getHostValue(i, j); + } + } + const double a = batchRstd * batchRstd * batchRstd + * (sumDyScaleX - sumDyScale * mean.getHostValue(0)) / 6.0; + const double b = batchRstd * sumDyScale / 6.0 - a * mean.getHostValue(0); + for(size_t i = 0; i < 2; ++i) + { + for(size_t j = 0; j < 3; ++j) + { + const double outDx = batchRstd * dy.getHostValue(i, j) * scale.getHostValue(i, j) + - a * x.getHostValue(i, j) - b; + EXPECT_NEAR(outDx, dx.getHostValue(i, j), tolerance); + } + } + for(size_t i = 0; i < 2; ++i) + { + for(size_t j = 0; j < 3; ++j) + { + double outDscale = 0.0; + double outDbias = 0.0; + outDscale += dy.getHostValue(i, j) * (x.getHostValue(i, j) - mean.getHostValue(0)) + * rstd.getHostValue(0); + outDbias += dy.getHostValue(i, j); + EXPECT_NEAR(outDscale, dscale.getHostValue(i, j), tolerance); + EXPECT_NEAR(outDbias, dbias.getHostValue(i, j), tolerance); + } + } +} + +// ============================================================================ +// Dimensionality coverage: 5D tensor +// ============================================================================ + +TEST(TestCpuFpReferenceLayernormBackwardFp64, Bprop5DNormalizeLast2) +{ + // Shape [2, 2, 3, 4, 5], normalizedDimCount=2: normalize over last 2 dims (4*5=20) + // Batch dims = [2, 2, 3], 12 independent groups of 20 elements each + + Tensor dy({2, 2, 3, 4, 5}); + Tensor x({2, 2, 3, 4, 5}); + Tensor scale({4, 5}); + Tensor mean({2, 2, 3}); + Tensor rstd({2, 2, 3}); + Tensor dx({2, 2, 3, 4, 5}); + Tensor dscale({4, 5}); + Tensor dbias({4, 5}); + + dy.fillWithRandomValues(-5.0, 5.0, 123); + x.fillWithRandomValues(-5.0, 5.0, 123); + scale.fillWithValue(1.0); + for(size_t i = 0; i < 2; ++i) + { + for(size_t j = 0; j < 2; ++j) + { + for(size_t k = 0; k < 3; ++k) + { + double batchMean = 0.0; + for(size_t l = 0; l < 4; ++l) + { + for(size_t m = 0; m < 5; ++m) + { + batchMean += x.getHostValue(i, j, k, l, m); + } + } + batchMean /= 12.0; + mean.setHostValue(batchMean, i, j, k); + double batchVar = 0.0; + for(size_t l = 0; l < 4; ++l) + { + for(size_t m = 0; m < 5; ++m) + { + const double diff = x.getHostValue(i, j, k, l, m) - batchMean; + batchVar += diff * diff; + } + } + batchVar /= 12.0; + rstd.setHostValue(1.0 / std::sqrt(batchVar + LAYERNORM_DEFAULT_EPSILON), i, j, k); + } + } + } + + CpuFpReferenceLayernorm::bprop( + dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 2); + + auto tolerance = layernorm::getTolerance(); + + for(size_t i = 0; i < 2; ++i) + { + for(size_t j = 0; j < 2; ++j) + { + for(size_t k = 0; k < 3; ++k) + { + const double batchRstd = rstd.getHostValue(i, j, k); + double sumDyScaleX = 0.0; + double sumDyScale = 0.0; + for(size_t l = 0; l < 4; ++l) + { + for(size_t m = 0; m < 5; ++m) + { + sumDyScaleX += dy.getHostValue(i, j, k, l, m) * scale.getHostValue(l, m) + * x.getHostValue(i, j, k, l, m); + sumDyScale += dy.getHostValue(i, j, k, l, m) * scale.getHostValue(l, m); + } + } + const double a = batchRstd * batchRstd * batchRstd + * (sumDyScaleX - sumDyScale * mean.getHostValue(i, j, k)) / 20.0; + const double b = batchRstd * sumDyScale / 20.0 - a * mean.getHostValue(i, j, k); + for(size_t l = 0; l < 4; ++l) + { + for(size_t m = 0; m < 5; ++m) + { + const double outDx + = batchRstd * dy.getHostValue(i, j, k, l, m) * scale.getHostValue(l, m) + - a * x.getHostValue(i, j, k, l, m) - b; + EXPECT_NEAR(outDx, dx.getHostValue(i, j, k, l, m), tolerance); + } + } + } + } + } + for(size_t l = 0; l < 4; ++l) + { + for(size_t m = 0; m < 5; ++m) + { + double outDscale = 0.0; + double outDbias = 0.0; + for(size_t i = 0; i < 2; ++i) + { + for(size_t j = 0; j < 2; ++j) + { + for(size_t k = 0; k < 3; ++k) + { + outDscale += dy.getHostValue(i, j, k, l, m) + * (x.getHostValue(i, j, k, l, m) - mean.getHostValue(i, j, k)) + * rstd.getHostValue(i, j, k); + outDbias += dy.getHostValue(i, j, k, l, m); + } + } + } + EXPECT_NEAR(outDscale, dscale.getHostValue(l, m), tolerance); + EXPECT_NEAR(outDbias, dbias.getHostValue(l, m), tolerance); + } + } +} diff --git a/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/LayernormGraphUtils.hpp b/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/LayernormGraphUtils.hpp index fe5e5bf3d39f..e05482d5479b 100644 --- a/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/LayernormGraphUtils.hpp +++ b/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/LayernormGraphUtils.hpp @@ -159,4 +159,153 @@ inline std::shared_ptr return graph; } + +inline std::shared_ptr + buildLayernormBpropGraph(hipdnn_flatbuffers_sdk::data_objects::DataType inputDataType, + hipdnn_flatbuffers_sdk::data_objects::DataType scaleBiasDataType, + hipdnn_flatbuffers_sdk::data_objects::DataType meanInvVarianceDataType, + hipdnn_flatbuffers_sdk::data_objects::DataType computeDataType, + const std::vector& dims, + const int64_t normalizedDimCount, + const hipdnn_data_sdk::utilities::TensorLayout& layout, + bool meanInvVar = false, + bool onePadded = false) +{ + auto graph = std::make_shared(); + graph->set_name("LayernormBpropTest"); + + auto strides = hipdnn_data_sdk::utilities::generateStrides(dims, layout.strideOrder); + + // Scale/bias shape = normalized dims (last normalizedDimCount dims for NCHW) + std::vector normalizedDims; + std::vector statDims; + if(onePadded) + { + normalizedDims = std::vector(dims.size(), 1); + statDims = std::vector(dims.size(), 1); + for(size_t i = 0; i < dims.size(); ++i) + { + if(static_cast(i) < static_cast(dims.size()) - normalizedDimCount) + { + statDims[i] = dims[i]; + } + else + { + normalizedDims[i] = dims[i]; + } + } + } + else + { + normalizedDims = std::vector( + dims.begin() + + std::max(static_cast(dims.size()) - normalizedDimCount, int64_t{0}), + dims.end()); + statDims = std::vector( + dims.begin(), + dims.begin() + + std::max(static_cast(dims.size()) - normalizedDimCount, int64_t{0})); + } + auto normalizedStrides = hipdnn_data_sdk::utilities::generateStrides(normalizedDims); + auto statStrides = hipdnn_data_sdk::utilities::generateStrides(statDims); + + int64_t uid = 1; + auto dyAttr = hipdnn_frontend::graph::makeTensorAttributes( + "dy", hipdnn_test_sdk::utilities::sdkToFrontendDataType(inputDataType), dims, strides); + dyAttr.set_uid(uid++); + auto dyTensorAttr + = std::make_shared(std::move(dyAttr)); + + auto xAttr = hipdnn_frontend::graph::makeTensorAttributes( + "x", hipdnn_test_sdk::utilities::sdkToFrontendDataType(inputDataType), dims, strides); + xAttr.set_uid(uid++); + auto xTensorAttr = std::make_shared(std::move(xAttr)); + + auto scaleAttr = hipdnn_frontend::graph::makeTensorAttributes( + "scale", + hipdnn_test_sdk::utilities::sdkToFrontendDataType(scaleBiasDataType), + normalizedDims, + normalizedStrides); + scaleAttr.set_uid(uid++); + auto scaleTensorAttr + = std::make_shared(std::move(scaleAttr)); + + auto epsilonTensor = std::make_shared(); + epsilonTensor->set_uid(uid++) + .set_name("EpsilonTensor") + .set_data_type(hipdnn_frontend::DataType::DOUBLE) + .set_dim({1}) + .set_stride({1}) + .set_value(hipdnn_data_sdk::utilities::LAYERNORM_DEFAULT_EPSILON); + + hipdnn_frontend::graph::LayernormBackwardAttributes lnAttrs; + lnAttrs.set_name("layernorm_bprop"); + lnAttrs.set_epsilon(epsilonTensor); + lnAttrs.set_compute_data_type( + hipdnn_test_sdk::utilities::sdkToFrontendDataType(computeDataType)); + + if(meanInvVar) + { + auto meanAttr = hipdnn_frontend::graph::makeTensorAttributes( + "mean", + hipdnn_test_sdk::utilities::sdkToFrontendDataType(meanInvVarianceDataType), + statDims, + statStrides); + meanAttr.set_uid(uid++); + auto meanTensorAttr + = std::make_shared(std::move(meanAttr)); + lnAttrs.set_mean(std::move(meanTensorAttr)); + + auto invVarianceAttr = hipdnn_frontend::graph::makeTensorAttributes( + "inv_variance", + hipdnn_test_sdk::utilities::sdkToFrontendDataType(meanInvVarianceDataType), + statDims, + statStrides); + invVarianceAttr.set_uid(uid++); + auto invVarianceTensorAttr = std::make_shared( + std::move(invVarianceAttr)); + lnAttrs.set_inv_variance(std::move(invVarianceTensorAttr)); + } + + auto outputTensorsAttr + = graph->layernorm_backward(dyTensorAttr, xTensorAttr, scaleTensorAttr, lnAttrs); + + auto& dxTensorAttr = outputTensorsAttr[0]; + if(!dxTensorAttr->has_uid()) + { + dxTensorAttr->set_uid(uid++); + } + dxTensorAttr->set_name("DX"); + dxTensorAttr->set_data_type(hipdnn_test_sdk::utilities::sdkToFrontendDataType(inputDataType)); + dxTensorAttr->set_dim(dims); + dxTensorAttr->set_stride(strides); + dxTensorAttr->set_is_virtual(false); + + auto& dscaleTensorAttr = outputTensorsAttr[1]; + if(!dscaleTensorAttr->has_uid()) + { + dscaleTensorAttr->set_uid(uid++); + } + dscaleTensorAttr->set_name("DSCALE"); + dscaleTensorAttr->set_data_type( + hipdnn_test_sdk::utilities::sdkToFrontendDataType(inputDataType)); + dscaleTensorAttr->set_dim(normalizedDims); + dscaleTensorAttr->set_stride(normalizedStrides); + dscaleTensorAttr->set_is_virtual(false); + + auto& dbiasTensorAttr = outputTensorsAttr[2]; + if(!dbiasTensorAttr->has_uid()) + { + dbiasTensorAttr->set_uid(uid++); + } + dbiasTensorAttr->set_name("DBIAS"); + dbiasTensorAttr->set_data_type( + hipdnn_test_sdk::utilities::sdkToFrontendDataType(inputDataType)); + dbiasTensorAttr->set_dim(normalizedDims); + dbiasTensorAttr->set_stride(normalizedStrides); + dbiasTensorAttr->set_is_virtual(false); + + return graph; +} + } diff --git a/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/LayernormTensorBundles.hpp b/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/LayernormTensorBundles.hpp index 22d11cf04ebb..74d6b75bb5f8 100644 --- a/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/LayernormTensorBundles.hpp +++ b/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/LayernormTensorBundles.hpp @@ -30,4 +30,31 @@ struct LayernormFpropTensorBundle : public hipdnn_test_sdk::utilities::GraphTens } }; +struct LayernormBpropTensorBundle : public hipdnn_test_sdk::utilities::GraphTensorBundle +{ + LayernormBpropTensorBundle( + const hipdnn_flatbuffers_sdk::flatbuffer_utilities::INodeWrapper& node, + const std::unordered_map& + tensorMap, + unsigned int seed) + : hipdnn_test_sdk::utilities::GraphTensorBundle(tensorMap) + { + const auto& attributes = node.attributesAs< + hipdnn_flatbuffers_sdk::data_objects::LayernormBackwardAttributes>(); + + randomizeTensor(attributes.dy_tensor_uid(), 0.0f, 1.0f, seed); + randomizeTensor(attributes.x_tensor_uid(), 0.0f, 1.0f, seed); + randomizeTensor(attributes.scale_tensor_uid(), 0.0f, 1.0f, seed); + if(attributes.mean_tensor_uid().has_value()) + { + randomizeTensor(attributes.mean_tensor_uid().value(), 0.0f, 1.0f, seed); + } + if(attributes.inv_variance_tensor_uid().has_value()) + { + randomizeTensor(attributes.inv_variance_tensor_uid().value(), 0.0f, 1.0f, seed); + } + } +}; + } diff --git a/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestLayernormBpropPlan.cpp b/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestLayernormBpropPlan.cpp new file mode 100644 index 000000000000..699ae8a54939 --- /dev/null +++ b/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestLayernormBpropPlan.cpp @@ -0,0 +1,453 @@ +// Copyright © Advanced Micro Devices, Inc., or its affiliates. +// SPDX-License-Identifier: MIT + +#include + +#include "LayernormGraphUtils.hpp" +#include "LayernormTensorBundles.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace hipdnn_test_sdk::utilities; +using namespace hipdnn_test_sdk::detail; +using namespace hipdnn_flatbuffers_sdk::data_objects; +using namespace hipdnn_data_sdk::utilities; +using namespace hipdnn_flatbuffers_sdk::flatbuffer_utilities; +using namespace ::testing; +using namespace hipdnn_sdk_test_utils; + +class TestLayernormBpropPlan : public ::testing::Test +{ +}; + +TEST_F(TestLayernormBpropPlan, ExecutePlan) +{ + auto tolerance = layernorm::getTolerance(); + const std::vector dims = {6, 3, 32, 32}; + const int64_t normalizedDimCount = 3; + const unsigned int seed = getGlobalTestSeed(); + auto graph = buildLayernormBpropGraph(DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + dims, + normalizedDimCount, + TensorLayout::NHWC, + true); + auto [serializedGraph, serErr] = graph->to_binary(); + ASSERT_TRUE(serErr.is_good()) << serErr.get_message(); + const GraphWrapper graphWrapper(serializedGraph.data(), serializedGraph.size()); + const INodeWrapper& node = graphWrapper.getNodeWrapper(0); + LayernormBpropTensorBundle planTensorBundle(node, graphWrapper.getTensorMap(), seed); + LayernormBpropTensorBundle directTensorBundle(node, graphWrapper.getTensorMap(), seed); + + const auto& attributes + = node.attributesAs(); + + EXPECT_TRUE(attributes.mean_tensor_uid().has_value()); + EXPECT_TRUE(attributes.inv_variance_tensor_uid().has_value()); + + const auto& tensorMap = graphWrapper.getTensorMap(); + LayernormBpropParams params(*tensorMap.at(attributes.dy_tensor_uid()), + *tensorMap.at(attributes.x_tensor_uid()), + *tensorMap.at(attributes.scale_tensor_uid()), + *tensorMap.at(attributes.mean_tensor_uid().value()), + *tensorMap.at(attributes.inv_variance_tensor_uid().value()), + *tensorMap.at(attributes.dx_tensor_uid()), + *tensorMap.at(attributes.dscale_tensor_uid()), + *tensorMap.at(attributes.dbias_tensor_uid()), + normalizedDimCount, + attributes.epsilon_tensor_uid().has_value() + ? tensorMap.at(attributes.epsilon_tensor_uid().value()) + : nullptr); + + const std::unordered_map variantPack = planTensorBundle.toHostVariantPack(); + + auto shallowDyTensor = createShallowTensor( + params.dyTensor, directTensorBundle.getTensor(attributes.dy_tensor_uid()).rawHostData()); + auto shallowXTensor = createShallowTensor( + params.xTensor, directTensorBundle.getTensor(attributes.x_tensor_uid()).rawHostData()); + auto shallowScaleTensor = createShallowTensor( + params.scaleTensor, + directTensorBundle.getTensor(attributes.scale_tensor_uid()).rawHostData()); + auto shallowMeanTensor = createShallowTensor( + params.meanTensor, + directTensorBundle.getTensor(attributes.mean_tensor_uid().value()).rawHostData()); + auto shallowInvVarianceTensor = createShallowTensor( + params.invVarianceTensor, + directTensorBundle.getTensor(attributes.inv_variance_tensor_uid().value()).rawHostData()); + auto shallowDxTensor = createShallowTensor( + params.dxTensor, directTensorBundle.getTensor(attributes.dx_tensor_uid()).rawHostData()); + auto shallowDscaleTensor = createShallowTensor( + params.dscaleTensor, + directTensorBundle.getTensor(attributes.dscale_tensor_uid()).rawHostData()); + auto shallowDbiasTensor = createShallowTensor( + params.dbiasTensor, + directTensorBundle.getTensor(attributes.dbias_tensor_uid()).rawHostData()); + + CpuFpReferenceLayernorm::bprop(*shallowDyTensor, + *shallowXTensor, + *shallowScaleTensor, + *shallowMeanTensor, + *shallowInvVarianceTensor, + *shallowDxTensor, + *shallowDscaleTensor, + *shallowDbiasTensor, + hipdnn_data_sdk::utilities::LAYERNORM_DEFAULT_EPSILON, + normalizedDimCount); + + LayernormBpropPlan bpropPlan(std::move(params)); + bpropPlan.execute(variantPack); + + const CpuFpReferenceValidation cpuRefOutputValidation(tolerance, tolerance); + EXPECT_TRUE( + cpuRefOutputValidation.allClose(directTensorBundle.getTensor(attributes.dx_tensor_uid()), + planTensorBundle.getTensor(attributes.dx_tensor_uid()))); + EXPECT_TRUE(cpuRefOutputValidation.allClose( + directTensorBundle.getTensor(attributes.dscale_tensor_uid()), + planTensorBundle.getTensor(attributes.dscale_tensor_uid()))); + EXPECT_TRUE( + cpuRefOutputValidation.allClose(directTensorBundle.getTensor(attributes.dbias_tensor_uid()), + planTensorBundle.getTensor(attributes.dbias_tensor_uid()))); +} + +TEST_F(TestLayernormBpropPlan, ExecutePlanOnePaddedNormalizedDimCount2) +{ + auto tolerance = layernorm::getTolerance(); + const std::vector dims = {6, 3, 32, 32}; + const int64_t normalizedDimCount = 2; + const unsigned int seed = getGlobalTestSeed(); + auto graph = buildLayernormBpropGraph(DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + dims, + normalizedDimCount, + TensorLayout::NHWC, + true, + true); + auto [serializedGraph, serErr] = graph->to_binary(); + ASSERT_TRUE(serErr.is_good()) << serErr.get_message(); + const GraphWrapper graphWrapper(serializedGraph.data(), serializedGraph.size()); + const INodeWrapper& node = graphWrapper.getNodeWrapper(0); + LayernormBpropTensorBundle planTensorBundle(node, graphWrapper.getTensorMap(), seed); + LayernormBpropTensorBundle directTensorBundle(node, graphWrapper.getTensorMap(), seed); + + const auto& attributes + = node.attributesAs(); + + EXPECT_TRUE(attributes.mean_tensor_uid().has_value()); + EXPECT_TRUE(attributes.inv_variance_tensor_uid().has_value()); + + const auto& tensorMap = graphWrapper.getTensorMap(); + LayernormBpropParams params(*tensorMap.at(attributes.dy_tensor_uid()), + *tensorMap.at(attributes.x_tensor_uid()), + *tensorMap.at(attributes.scale_tensor_uid()), + *tensorMap.at(attributes.mean_tensor_uid().value()), + *tensorMap.at(attributes.inv_variance_tensor_uid().value()), + *tensorMap.at(attributes.dx_tensor_uid()), + *tensorMap.at(attributes.dscale_tensor_uid()), + *tensorMap.at(attributes.dbias_tensor_uid()), + normalizedDimCount, + attributes.epsilon_tensor_uid().has_value() + ? tensorMap.at(attributes.epsilon_tensor_uid().value()) + : nullptr); + + const std::unordered_map variantPack = planTensorBundle.toHostVariantPack(); + + auto shallowDyTensor = createShallowTensor( + params.dyTensor, directTensorBundle.getTensor(attributes.dy_tensor_uid()).rawHostData()); + auto shallowXTensor = createShallowTensor( + params.xTensor, directTensorBundle.getTensor(attributes.x_tensor_uid()).rawHostData()); + auto shallowScaleTensor = createShallowTensor( + params.scaleTensor, + directTensorBundle.getTensor(attributes.scale_tensor_uid()).rawHostData()); + auto shallowMeanTensor = createShallowTensor( + params.meanTensor, + directTensorBundle.getTensor(attributes.mean_tensor_uid().value()).rawHostData()); + auto shallowInvVarianceTensor = createShallowTensor( + params.invVarianceTensor, + directTensorBundle.getTensor(attributes.inv_variance_tensor_uid().value()).rawHostData()); + auto shallowDxTensor = createShallowTensor( + params.dxTensor, directTensorBundle.getTensor(attributes.dx_tensor_uid()).rawHostData()); + auto shallowDscaleTensor = createShallowTensor( + params.dscaleTensor, + directTensorBundle.getTensor(attributes.dscale_tensor_uid()).rawHostData()); + auto shallowDbiasTensor = createShallowTensor( + params.dbiasTensor, + directTensorBundle.getTensor(attributes.dbias_tensor_uid()).rawHostData()); + + CpuFpReferenceLayernorm::bprop(*shallowDyTensor, + *shallowXTensor, + *shallowScaleTensor, + *shallowMeanTensor, + *shallowInvVarianceTensor, + *shallowDxTensor, + *shallowDscaleTensor, + *shallowDbiasTensor, + hipdnn_data_sdk::utilities::LAYERNORM_DEFAULT_EPSILON, + normalizedDimCount); + + LayernormBpropPlan bpropPlan(std::move(params)); + bpropPlan.execute(variantPack); + + const CpuFpReferenceValidation cpuRefOutputValidation(tolerance, tolerance); + EXPECT_TRUE( + cpuRefOutputValidation.allClose(directTensorBundle.getTensor(attributes.dx_tensor_uid()), + planTensorBundle.getTensor(attributes.dx_tensor_uid()))); + EXPECT_TRUE(cpuRefOutputValidation.allClose( + directTensorBundle.getTensor(attributes.dscale_tensor_uid()), + planTensorBundle.getTensor(attributes.dscale_tensor_uid()))); + EXPECT_TRUE( + cpuRefOutputValidation.allClose(directTensorBundle.getTensor(attributes.dbias_tensor_uid()), + planTensorBundle.getTensor(attributes.dbias_tensor_uid()))); +} + +TEST_F(TestLayernormBpropPlan, ExecutePlanTrainingPhase) +{ + auto tolerance = layernorm::getTolerance(); + const std::vector dims = {6, 3, 32, 32}; + const int64_t normalizedDimCount = 3; + const unsigned int seed = getGlobalTestSeed(); + auto graph = buildLayernormBpropGraph(DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + dims, + normalizedDimCount, + TensorLayout::NHWC, + true); + auto [serializedGraph, serErr] = graph->to_binary(); + ASSERT_TRUE(serErr.is_good()) << serErr.get_message(); + const GraphWrapper graphWrapper(serializedGraph.data(), serializedGraph.size()); + const INodeWrapper& node = graphWrapper.getNodeWrapper(0); + LayernormBpropTensorBundle planTensorBundle(node, graphWrapper.getTensorMap(), seed); + LayernormBpropTensorBundle directTensorBundle(node, graphWrapper.getTensorMap(), seed); + + const auto& attributes + = node.attributesAs(); + const auto& tensorMap = graphWrapper.getTensorMap(); + + EXPECT_TRUE(attributes.mean_tensor_uid().has_value()); + EXPECT_TRUE(attributes.inv_variance_tensor_uid().has_value()); + + LayernormBpropParams params(*tensorMap.at(attributes.dy_tensor_uid()), + *tensorMap.at(attributes.x_tensor_uid()), + *tensorMap.at(attributes.scale_tensor_uid()), + *tensorMap.at(attributes.mean_tensor_uid().value()), + *tensorMap.at(attributes.inv_variance_tensor_uid().value()), + *tensorMap.at(attributes.dx_tensor_uid()), + *tensorMap.at(attributes.dscale_tensor_uid()), + *tensorMap.at(attributes.dbias_tensor_uid()), + normalizedDimCount, + attributes.epsilon_tensor_uid().has_value() + ? tensorMap.at(attributes.epsilon_tensor_uid().value()) + : nullptr); + + const std::unordered_map variantPack = planTensorBundle.toHostVariantPack(); + + auto shallowDyTensor = createShallowTensor( + params.dyTensor, directTensorBundle.getTensor(attributes.dy_tensor_uid()).rawHostData()); + auto shallowXTensor = createShallowTensor( + params.xTensor, directTensorBundle.getTensor(attributes.x_tensor_uid()).rawHostData()); + auto shallowScaleTensor = createShallowTensor( + params.scaleTensor, + directTensorBundle.getTensor(attributes.scale_tensor_uid()).rawHostData()); + auto shallowMeanTensor = createShallowTensor( + params.meanTensor, + directTensorBundle.getTensor(attributes.mean_tensor_uid().value()).rawHostData()); + auto shallowInvVarianceTensor = createShallowTensor( + params.invVarianceTensor, + directTensorBundle.getTensor(attributes.inv_variance_tensor_uid().value()).rawHostData()); + auto shallowDxTensor = createShallowTensor( + params.dxTensor, directTensorBundle.getTensor(attributes.dx_tensor_uid()).rawHostData()); + auto shallowDscaleTensor = createShallowTensor( + params.dscaleTensor, + directTensorBundle.getTensor(attributes.dscale_tensor_uid()).rawHostData()); + auto shallowDbiasTensor = createShallowTensor( + params.dbiasTensor, + directTensorBundle.getTensor(attributes.dbias_tensor_uid()).rawHostData()); + + CpuFpReferenceLayernorm::bprop(*shallowDyTensor, + *shallowXTensor, + *shallowScaleTensor, + *shallowMeanTensor, + *shallowInvVarianceTensor, + *shallowDxTensor, + *shallowDscaleTensor, + *shallowDbiasTensor, + hipdnn_data_sdk::utilities::LAYERNORM_DEFAULT_EPSILON, + normalizedDimCount); + + LayernormBpropPlan bpropPlan(std::move(params)); + bpropPlan.execute(variantPack); + + const CpuFpReferenceValidation cpuRefOutputValidation(tolerance, tolerance); + EXPECT_TRUE( + cpuRefOutputValidation.allClose(directTensorBundle.getTensor(attributes.dx_tensor_uid()), + planTensorBundle.getTensor(attributes.dx_tensor_uid()))); + EXPECT_TRUE(cpuRefOutputValidation.allClose( + directTensorBundle.getTensor(attributes.dscale_tensor_uid()), + planTensorBundle.getTensor(attributes.dscale_tensor_uid()))); + EXPECT_TRUE( + cpuRefOutputValidation.allClose(directTensorBundle.getTensor(attributes.dbias_tensor_uid()), + planTensorBundle.getTensor(attributes.dbias_tensor_uid()))); + + if(attributes.mean_tensor_uid().has_value()) + { + EXPECT_TRUE(cpuRefOutputValidation.allClose( + directTensorBundle.getTensor(attributes.mean_tensor_uid().value()), + planTensorBundle.getTensor(attributes.mean_tensor_uid().value()))); + } + + if(attributes.inv_variance_tensor_uid().has_value()) + { + EXPECT_TRUE(cpuRefOutputValidation.allClose( + directTensorBundle.getTensor(attributes.inv_variance_tensor_uid().value()), + planTensorBundle.getTensor(attributes.inv_variance_tensor_uid().value()))); + } +} + +TEST(TestLayernormBpropPlanBuilder, PlanConstruction) +{ + const std::vector dims = {1, 1, 1, 1}; + const int64_t normalizedDimCount = 3; + auto graph = buildLayernormBpropGraph(DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + dims, + normalizedDimCount, + TensorLayout::NHWC, + true); + auto [serializedGraph, serErr] = graph->to_binary(); + ASSERT_TRUE(serErr.is_good()) << serErr.get_message(); + const GraphWrapper graphWrapper(serializedGraph.data(), serializedGraph.size()); + + const LayernormBpropPlanBuilder + patient; + + auto builtPlan = patient.buildNodePlan(graphWrapper, graphWrapper.getNode(0)); + + const bool result + = dynamic_cast*>(builtPlan.get()) + != nullptr; + EXPECT_TRUE(result); +} + +TEST(TestLayernormBpropPlanBuilder, IsApplicable) +{ + const std::vector dims = {1, 1, 1, 1}; + const int64_t normalizedDimCount = 3; + auto graph = buildLayernormBpropGraph(DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + dims, + normalizedDimCount, + TensorLayout::NHWC, + true); + auto [serializedGraph, serErr] = graph->to_binary(); + ASSERT_TRUE(serErr.is_good()) << serErr.get_message(); + const GraphWrapper graphWrapper(serializedGraph.data(), serializedGraph.size()); + + const LayernormBpropPlanBuilder + floatPlanBuilder; + + EXPECT_TRUE( + floatPlanBuilder.isApplicable(graphWrapper.getNode(0), graphWrapper.getTensorMap())); + + const LayernormBpropPlanBuilder + badTypesPlanBuilder; + EXPECT_FALSE( + badTypesPlanBuilder.isApplicable(graphWrapper.getNode(0), graphWrapper.getTensorMap())); + + auto tensorMapCopy = graphWrapper.getTensorMap(); + tensorMapCopy.erase(5); + EXPECT_FALSE(floatPlanBuilder.isApplicable(graphWrapper.getNode(0), tensorMapCopy)); +} + +TEST(TestLayernormBpropPlanBuilder, PlanConstructionTrainingPhase) +{ + const std::vector dims = {1, 1, 1, 1}; + const int64_t normalizedDimCount = 3; + auto graph = buildLayernormBpropGraph(DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + dims, + normalizedDimCount, + TensorLayout::NHWC, + true); + auto [serializedGraph, serErr] = graph->to_binary(); + ASSERT_TRUE(serErr.is_good()) << serErr.get_message(); + const GraphWrapper graphWrapper(serializedGraph.data(), serializedGraph.size()); + + const LayernormBpropPlanBuilder + patient; + + auto builtPlan = patient.buildNodePlan(graphWrapper, graphWrapper.getNode(0)); + + const bool result + = dynamic_cast*>(builtPlan.get()) + != nullptr; + EXPECT_TRUE(result); +} + +TEST(TestLayernormBpropPlanBuilder, IsApplicableTrainingPhase) +{ + const std::vector dims = {1, 1, 1, 1}; + const int64_t normalizedDimCount = 3; + auto graph = buildLayernormBpropGraph(DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + dims, + normalizedDimCount, + TensorLayout::NHWC, + true); + auto [serializedGraph, serErr] = graph->to_binary(); + ASSERT_TRUE(serErr.is_good()) << serErr.get_message(); + const GraphWrapper graphWrapper(serializedGraph.data(), serializedGraph.size()); + + const LayernormBpropPlanBuilder + floatPlanBuilder; + + EXPECT_TRUE( + floatPlanBuilder.isApplicable(graphWrapper.getNode(0), graphWrapper.getTensorMap())); + + const LayernormBpropPlanBuilder + badMeanTypePlanBuilder; + EXPECT_FALSE( + badMeanTypePlanBuilder.isApplicable(graphWrapper.getNode(0), graphWrapper.getTensorMap())); +} diff --git a/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestLayernormBpropSignatureKey.cpp b/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestLayernormBpropSignatureKey.cpp new file mode 100644 index 000000000000..63d6b4a3ab48 --- /dev/null +++ b/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestLayernormBpropSignatureKey.cpp @@ -0,0 +1,121 @@ +// Copyright © Advanced Micro Devices, Inc., or its affiliates. +// SPDX-License-Identifier: MIT + +#include + +#include "LayernormGraphUtils.hpp" +#include +#include + +using namespace hipdnn_test_sdk::utilities; +using namespace hipdnn_test_sdk::detail; +using namespace hipdnn_flatbuffers_sdk::data_objects; +using namespace hipdnn_flatbuffers_sdk::utilities; +using namespace hipdnn_sdk_test_utils; + +TEST(TestLayernormBpropSignatureKey, EqualityOperator) +{ + const LayernormBpropSignatureKey key1{ + DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT}; + const LayernormBpropSignatureKey key2{ + DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT}; + EXPECT_TRUE(key1 == key2); + + const LayernormBpropSignatureKey key3{ + DataType::HALF, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::HALF}; + const LayernormBpropSignatureKey key4{ + DataType::HALF, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::HALF}; + EXPECT_TRUE(key3 == key4); + + const LayernormBpropSignatureKey key5{ + DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT}; + const LayernormBpropSignatureKey key6{ + DataType::HALF, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::HALF}; + EXPECT_FALSE(key5 == key6); + + const LayernormBpropSignatureKey key7{ + DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT}; + const LayernormBpropSignatureKey key8{ + DataType::FLOAT, DataType::HALF, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT}; + EXPECT_FALSE(key7 == key8); + + const LayernormBpropSignatureKey key9{ + DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT}; + const LayernormBpropSignatureKey key10{ + DataType::FLOAT, DataType::FLOAT, DataType::DOUBLE, DataType::FLOAT, DataType::FLOAT}; + EXPECT_FALSE(key9 == key10); + + const LayernormBpropSignatureKey key11{ + DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT}; + const LayernormBpropSignatureKey key12{ + DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::DOUBLE, DataType::FLOAT}; + EXPECT_FALSE(key11 == key12); +} + +TEST(TestLayernormBpropSignatureKey, HashFunction) +{ + const LayernormBpropSignatureKey key1{ + DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT}; + const LayernormBpropSignatureKey key2{ + DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT}; + + EXPECT_EQ(key1.hashSelf(), key2.hashSelf()); + + const LayernormBpropSignatureKey key3{ + DataType::HALF, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::HALF}; + const LayernormBpropSignatureKey key4{ + DataType::FLOAT, DataType::HALF, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT}; + const LayernormBpropSignatureKey key5{ + DataType::FLOAT, DataType::FLOAT, DataType::HALF, DataType::FLOAT, DataType::FLOAT}; + const LayernormBpropSignatureKey key6{ + DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::HALF, DataType::FLOAT}; + const LayernormBpropSignatureKey key7{ + DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::HALF}; + + auto hash3 = key3.hashSelf(); + auto hash4 = key4.hashSelf(); + auto hash5 = key5.hashSelf(); + auto hash6 = key6.hashSelf(); + auto hash7 = key7.hashSelf(); + + EXPECT_TRUE(hash3 != hash4 && hash3 != hash5 && hash4 != hash5 && hash3 != hash6 + && hash4 != hash6 && hash5 != hash6 && hash3 != hash7 && hash4 != hash7 + && hash5 != hash7 && hash6 != hash7); +} + +TEST(TestLayernormBpropSignatureKey, Copy) +{ + const LayernormBpropSignatureKey original{ + DataType::FLOAT, DataType::HALF, DataType::DOUBLE, DataType::FLOAT, DataType::BFLOAT16}; + const LayernormBpropSignatureKey copied{original}; + + EXPECT_TRUE(original == copied); + EXPECT_EQ(copied.dyDataType, DataType::FLOAT); + EXPECT_EQ(copied.scaleBiasDataType, DataType::HALF); + EXPECT_EQ(copied.meanInvVarianceDataType, DataType::DOUBLE); + EXPECT_EQ(copied.outputDataType, DataType::FLOAT); + EXPECT_EQ(copied.computeDataType, DataType::BFLOAT16); +} + +TEST(TestLayernormBpropSignatureKey, CreateFromNodeAndTensorMap) +{ + const LayernormBpropSignatureKey expectedKey{ + DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT}; + const std::vector dims = {1, 1, 1, 1}; + const int64_t normalizedDimCount = 3; + auto graph = buildLayernormBpropGraph(DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + dims, + normalizedDimCount, + hipdnn_data_sdk::utilities::TensorLayout::NHWC); + auto [serializedGraph, serErr] = graph->to_binary(); + ASSERT_TRUE(serErr.is_good()) << serErr.get_message(); + auto graphWrap = hipdnn_flatbuffers_sdk::flatbuffer_utilities::GraphWrapper( + serializedGraph.data(), serializedGraph.size()); + + const LayernormBpropSignatureKey keyFromNode(graphWrap.getNode(0), graphWrap.getTensorMap()); + + EXPECT_TRUE(keyFromNode == expectedKey); +} diff --git a/projects/hipdnn/tests/frontend/CMakeLists.txt b/projects/hipdnn/tests/frontend/CMakeLists.txt index 7ce31a2d05a8..65580feffea1 100644 --- a/projects/hipdnn/tests/frontend/CMakeLists.txt +++ b/projects/hipdnn/tests/frontend/CMakeLists.txt @@ -46,9 +46,10 @@ add_executable( IntegrationLayerNormDescriptorLifting.cpp IntegrationLayerNormDescriptorLowering.cpp IntegrationLayernormForward.cpp - IntegrationMalformedVersionPlugin.cpp + IntegrationLayernormBackward.cpp IntegrationLayernormBackwardDescriptorLowering.cpp IntegrationLayernormBackwardDescriptorLifting.cpp + IntegrationMalformedVersionPlugin.cpp IntegrationMatmul.cpp IntegrationMatmulDescriptorLifting.cpp IntegrationMatmulDescriptorLowering.cpp diff --git a/projects/hipdnn/tests/frontend/IntegrationLayernormBackward.cpp b/projects/hipdnn/tests/frontend/IntegrationLayernormBackward.cpp new file mode 100644 index 000000000000..673f60ef8cca --- /dev/null +++ b/projects/hipdnn/tests/frontend/IntegrationLayernormBackward.cpp @@ -0,0 +1,411 @@ +// Copyright © Advanced Micro Devices, Inc., or its affiliates. +// SPDX-License-Identifier: MIT + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace hipdnn_frontend; +using namespace hipdnn_frontend::graph; +using namespace hipdnn_data_sdk::utilities; + +namespace +{ + +enum class FailurePoint +{ + NONE, // No failure expected + VALIDATE, // Expect failure at validate + CREATE_EXECUTION_PLAN, // Expect failure at create execution plan + EXECUTE // Expect failure at execute +}; + +struct IntegrationTestCase +{ + std::string pluginPath; + std::string description; + std::string graphName; + FailurePoint expectedFailure; + bool useManualUids; + bool useMeanInvVar; + + friend std::ostream& operator<<(std::ostream& os, const IntegrationTestCase& tc) + { + os << "LayernormBackwardTestCase{" << "plugin_path: " << tc.pluginPath + << ", description: " << tc.description << ", graph_name: " << tc.graphName + << ", expected_failure: "; + + switch(tc.expectedFailure) + { + case FailurePoint::NONE: + os << "NONE"; + break; + case FailurePoint::VALIDATE: + os << "VALIDATE"; + break; + case FailurePoint::CREATE_EXECUTION_PLAN: + os << "CREATE_EXECUTION_PLAN"; + break; + case FailurePoint::EXECUTE: + os << "EXECUTE"; + break; + default: + os << "UNKNOWN"; + break; + } + + os << ", use_manual_uids: " << (tc.useManualUids ? "true" : "false") + << ", use_mean_inv_var: " << (tc.useMeanInvVar ? "true" : "false") << "}"; + + return os; + } +}; + +class IntegrationLayernormBackwardFp32 : public ::testing::TestWithParam +{ +protected: + template + struct SimpleLayernormTensorBundle + { + SimpleLayernormTensorBundle(const std::vector& dims) + : normalizedDims(dims.begin() + 1, dims.end()) + , statsDims(makeStatsDims(dims)) + , dyTensor(Tensor(dims)) + , xTensor(Tensor(dims)) + , scaleTensor(Tensor(normalizedDims)) + , epsilonTensor(Tensor({1})) + , meanTensor(Tensor(statsDims)) + , invVarianceTensor(Tensor(statsDims)) + , dxTensor(Tensor(dims)) + , dscaleTensor(Tensor(dims)) + , dbiasTensor(Tensor(dims)) + { + dyTensor.fillWithValue(static_cast(1.0f)); + xTensor.fillWithValue(static_cast(1.0f)); + scaleTensor.fillWithValue(static_cast(1.0f)); + epsilonTensor.fillWithValue(static_cast(1e-5f)); + meanTensor.fillWithValue(static_cast(0.0f)); + invVarianceTensor.fillWithValue(static_cast(0.0f)); + dxTensor.fillWithValue(static_cast(0.0f)); + dscaleTensor.fillWithValue(static_cast(0.0f)); + dbiasTensor.fillWithValue(static_cast(0.0f)); + } + + std::vector normalizedDims; + std::vector statsDims; + Tensor dyTensor; + Tensor xTensor; + Tensor scaleTensor; + Tensor epsilonTensor; + Tensor meanTensor; + Tensor invVarianceTensor; + Tensor dxTensor; + Tensor dscaleTensor; + Tensor dbiasTensor; + + private: + static std::vector makeStatsDims(const std::vector& dims) + { + std::vector result(dims.size(), 1); + result[0] = dims[0]; + return result; + } + }; + + struct LayernormTestTensors + { + std::shared_ptr dy; + std::shared_ptr x; + std::shared_ptr scale; + std::shared_ptr epsilon; + std::shared_ptr mean; + std::shared_ptr invVariance; + std::shared_ptr dx; + std::shared_ptr dscale; + std::shared_ptr dbias; + }; + + void SetUp() override + { + SKIP_IF_NO_DEVICES(); + + ASSERT_EQ(hipInit(0), hipSuccess); + int deviceId = 0; + ASSERT_EQ(hipGetDevice(&deviceId), hipSuccess); + } + + void TearDown() override + { + if(_handle != nullptr) + { + ASSERT_EQ(hipdnnDestroy(_handle), HIPDNN_STATUS_SUCCESS); + } + } + + static hipdnnHandle_t setupEnvironmentWithPlugin(const std::string& pluginPath) + { + const std::array paths = {pluginPath.c_str()}; + EXPECT_EQ(hipdnnSetEnginePluginPaths_ext( + paths.size(), paths.data(), HIPDNN_PLUGIN_LOADING_ABSOLUTE), + HIPDNN_STATUS_SUCCESS); + + hipdnnHandle_t handle = nullptr; + EXPECT_EQ(hipdnnCreate(&handle), HIPDNN_STATUS_SUCCESS); + + return handle; + } + + static std::pair, LayernormTestTensors> createLayernormTestGraphWithUids( + const std::string& graphName, + const SimpleLayernormTensorBundle& tensorBundle, + bool useManualUids, + bool useMeanInvVar) + { + auto graph = std::make_shared(); + graph->set_name(graphName) + .set_io_data_type(DataType::FLOAT) + .set_intermediate_data_type(DataType::FLOAT) + .set_compute_data_type(DataType::FLOAT); + + int64_t uid = 1; + LayernormTestTensors tensors; + + auto dyAttr = makeTensorAttributes("DY", DataType::FLOAT, tensorBundle.dyTensor); + if(useManualUids) + { + dyAttr.set_uid(uid++); + } + tensors.dy = std::make_shared(std::move(dyAttr)); + + auto xAttr = makeTensorAttributes("X", DataType::FLOAT, tensorBundle.xTensor); + if(useManualUids) + { + xAttr.set_uid(uid++); + } + tensors.x = std::make_shared(std::move(xAttr)); + + auto scaleAttr = makeTensorAttributes("scale", DataType::FLOAT, tensorBundle.scaleTensor); + if(useManualUids) + { + scaleAttr.set_uid(uid++); + } + tensors.scale = std::make_shared(std::move(scaleAttr)); + + auto epsilonAttr = makeTensorAttributes("epsilon", 1e-5f); + tensors.epsilon = std::make_shared(std::move(epsilonAttr)); + if(useManualUids) + { + tensors.epsilon->set_uid(uid++); + } + + LayernormBackwardAttributes lnAttrs; + lnAttrs.set_name("layernorm_bwd"); + lnAttrs.set_epsilon(tensors.epsilon); + lnAttrs.set_compute_data_type(DataType::FLOAT); + + if(useMeanInvVar) + { + auto meanAttr = makeTensorAttributes("mean", DataType::FLOAT, tensorBundle.meanTensor); + if(useManualUids) + { + meanAttr.set_uid(uid++); + } + tensors.mean = std::make_shared(std::move(meanAttr)); + lnAttrs.set_mean(std::move(tensors.mean)); + + auto invVarianceAttr = makeTensorAttributes( + "inv_variance", DataType::FLOAT, tensorBundle.invVarianceTensor); + if(useManualUids) + { + invVarianceAttr.set_uid(uid++); + } + tensors.invVariance = std::make_shared(std::move(invVarianceAttr)); + lnAttrs.set_inv_variance(std::move(tensors.invVariance)); + } + + auto outputTensors + = graph->layernorm_backward(tensors.dy, tensors.x, tensors.scale, lnAttrs); + + tensors.dx = outputTensors[0]; + tensors.dscale = outputTensors[1]; + tensors.dbias = outputTensors[2]; + + if(useManualUids) + { + tensors.dx->set_uid(uid++); + tensors.dscale->set_uid(uid++); + tensors.dbias->set_uid(uid++); + } + tensors.dx->set_data_type(DataType::FLOAT); + tensors.dscale->set_data_type(DataType::FLOAT); + tensors.dbias->set_data_type(DataType::FLOAT); + + return {graph, tensors}; + } + + static std::unordered_map + createVariantPack(const LayernormTestTensors& tensors, + SimpleLayernormTensorBundle& tensorBundle) + { + std::unordered_map variantPack; + variantPack[tensors.dy->get_uid()] = tensorBundle.dyTensor.memory().deviceData(); + variantPack[tensors.x->get_uid()] = tensorBundle.xTensor.memory().deviceData(); + variantPack[tensors.scale->get_uid()] = tensorBundle.scaleTensor.memory().deviceData(); + variantPack[tensors.epsilon->get_uid()] = tensorBundle.epsilonTensor.memory().deviceData(); + variantPack[tensors.dy->get_uid()] = tensorBundle.dyTensor.memory().deviceData(); + variantPack[tensors.dscale->get_uid()] = tensorBundle.dscaleTensor.memory().deviceData(); + variantPack[tensors.dbias->get_uid()] = tensorBundle.dbiasTensor.memory().deviceData(); + + if(tensors.mean != nullptr) + { + variantPack[tensors.mean->get_uid()] = tensorBundle.meanTensor.memory().deviceData(); + } + if(tensors.invVariance != nullptr) + { + variantPack[tensors.invVariance->get_uid()] + = tensorBundle.invVarianceTensor.memory().deviceData(); + } + + return variantPack; + } + + static void runGraphPipeline(const std::shared_ptr& graph, + hipdnnHandle_t handle, + const LayernormTestTensors& tensors, + SimpleLayernormTensorBundle& tensorBundle, + FailurePoint expectedFailure = FailurePoint::NONE) + { + auto result = graph->validate(); + if(expectedFailure == FailurePoint::VALIDATE) + { + ASSERT_NE(result.code, ErrorCode::OK) << "validate should fail"; + return; + } + ASSERT_EQ(result.code, ErrorCode::OK) << result.err_msg; + + result = graph->build_operation_graph(handle); + ASSERT_EQ(result.code, ErrorCode::OK) << result.err_msg; + + result = graph->create_execution_plans(); + if(expectedFailure == FailurePoint::CREATE_EXECUTION_PLAN) + { + ASSERT_NE(result.code, ErrorCode::OK) << "create_execution_plans should fail"; + return; + } + ASSERT_EQ(result.code, ErrorCode::OK) << result.err_msg; + + result = graph->check_support(); + ASSERT_EQ(result.code, ErrorCode::OK) << result.err_msg; + + result = graph->build_plans(); + ASSERT_EQ(result.code, ErrorCode::OK) << result.err_msg; + + ASSERT_TRUE(tensors.dy->has_uid()); + ASSERT_TRUE(tensors.x->has_uid()); + ASSERT_TRUE(tensors.scale->has_uid()); + ASSERT_TRUE(tensors.epsilon->has_uid()); + ASSERT_TRUE(tensors.dx->has_uid()); + ASSERT_TRUE(tensors.dscale->has_uid()); + ASSERT_TRUE(tensors.dbias->has_uid()); + if(tensors.mean) + { + ASSERT_TRUE(tensors.mean->has_uid()); + } + if(tensors.invVariance) + { + ASSERT_TRUE(tensors.invVariance->has_uid()); + } + + auto variantPack = createVariantPack(tensors, tensorBundle); + + result = graph->execute(handle, variantPack, nullptr); + if(expectedFailure == FailurePoint::EXECUTE) + { + ASSERT_NE(result.code, ErrorCode::OK) << "Execute should fail"; + } + else + { + ASSERT_EQ(result.code, ErrorCode::OK) << result.err_msg; + } + } + + void runTest() + { + const auto& testCase = GetParam(); + + _handle = setupEnvironmentWithPlugin(testCase.pluginPath); + + const std::vector dims = {2, 3, 14, 14}; + SimpleLayernormTensorBundle tensorBundle(dims); + + auto [graph, tensors] = createLayernormTestGraphWithUids( + testCase.graphName, tensorBundle, testCase.useManualUids, testCase.useMeanInvVar); + + runGraphPipeline(graph, _handle, tensors, tensorBundle, testCase.expectedFailure); + } + +private: + hipdnnHandle_t _handle = nullptr; +}; + +} // namespace + +INSTANTIATE_TEST_SUITE_P( + , + IntegrationLayernormBackwardFp32, + ::testing::Values( + IntegrationTestCase{hipdnn_tests::plugin_constants::testGoodPluginPath(), + "ManualUids", + "DefaultPluginLayernormTest", + FailurePoint::NONE, + true, + false}, + IntegrationTestCase{hipdnn_tests::plugin_constants::testGoodPluginPath(), + "AutoUids", + "DefaultPluginLayernormTestAutoUID", + FailurePoint::NONE, + false, + false}, + IntegrationTestCase{hipdnn_tests::plugin_constants::testGoodPluginPath(), + "ManualUidsWithMeanInvVar", + "MeanInvVarPluginLayernormTest", + FailurePoint::NONE, + true, + true}, + IntegrationTestCase{hipdnn_tests::plugin_constants::testGoodPluginPath(), + "AutoUidsWithMeanInvVar", + "MeanInvVarPluginLayernormTestAutoUID", + FailurePoint::NONE, + false, + true}, + IntegrationTestCase{hipdnn_tests::plugin_constants::testExecuteFailsPluginPath(), + "ExecuteFailsPlugin", + "ExecuteFailsPluginLayernormTest", + FailurePoint::EXECUTE, + true, + true}, + IntegrationTestCase{hipdnn_tests::plugin_constants::testNoApplicableEnginesAPluginPath(), + "NoApplicableEnginesPlugin", + "NoEnginesPluginLayernormTest", + FailurePoint::CREATE_EXECUTION_PLAN, + true, + true}), + [](const ::testing::TestParamInfo& info) { + std::string name = info.param.description; + std::transform(name.cbegin(), name.cend(), name.begin(), [](char c) { + return std::isalnum(c) ? c : '_'; + }); + return name; + }); + +TEST_P(IntegrationLayernormBackwardFp32, ExecutePluginPipeline) +{ + runTest(); +} From de8418fcdb93c9a7a791f6b8534b1791f4ecd61d Mon Sep 17 00:00:00 2001 From: Brent Maas Date: Mon, 1 Jun 2026 14:55:54 +0000 Subject: [PATCH 3/7] Support for absent mean/rstd for layernorm bwd reference --- .../utilities/CpuFpReferenceLayernorm.hpp | 118 +++++++--- .../detail/LayernormBpropPlan.hpp | 110 ++++++---- .../TestCpuFpReferenceLayernormBackward.cpp | 203 ++++++++++++++++-- .../TestLayernormBpropPlan.cpp | 122 +++++++++-- 4 files changed, 438 insertions(+), 115 deletions(-) diff --git a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/CpuFpReferenceLayernorm.hpp b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/CpuFpReferenceLayernorm.hpp index 8a67c8206b1d..8296d7f61bde 100644 --- a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/CpuFpReferenceLayernorm.hpp +++ b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/CpuFpReferenceLayernorm.hpp @@ -222,12 +222,12 @@ class CpuFpReferenceLayernorm static void bprop(const hipdnn_data_sdk::utilities::TensorBase& dy, const hipdnn_data_sdk::utilities::TensorBase& x, const hipdnn_data_sdk::utilities::TensorBase& scale, - const hipdnn_data_sdk::utilities::TensorBase& mean, - const hipdnn_data_sdk::utilities::TensorBase& rstd, hipdnn_data_sdk::utilities::TensorBase& dx, hipdnn_data_sdk::utilities::TensorBase& dscale, hipdnn_data_sdk::utilities::TensorBase& dbias, [[maybe_unused]] const double epsilon, + const hipdnn_data_sdk::utilities::TensorBase* mean, + const hipdnn_data_sdk::utilities::TensorBase* rstd, const int64_t normalizedDimCount) { const auto& dims = dy.dims(); @@ -250,7 +250,13 @@ class CpuFpReferenceLayernorm throw std::runtime_error("Scale, dscale and dbias tensors must have the same rank."); } - if(mean.dims().size() != rstd.dims().size()) + if((mean == nullptr) != (rstd == nullptr)) + { + throw std::runtime_error( + "Layernorm backward requires both mean and rstd to be provided, or neither."); + } + + if(mean != nullptr && mean->dims().size() != rstd->dims().size()) { throw std::runtime_error("Mean and rstd tensors must have the same rank."); } @@ -265,43 +271,45 @@ class CpuFpReferenceLayernorm } // Split dimensions into batch dims and normalized dims + auto normalizedDims = scale.dims(); + const int64_t normalizedDimsSize = std::accumulate( + normalizedDims.begin(), normalizedDims.end(), int64_t{1}, std::multiplies{}); std::vector batchDims; - int64_t batchDimsSize = 1; - std::vector normalizedDims; - int64_t normalizedDimsSize = 1; - if(scale.dims().size() == dims.size() && mean.dims().size() == dims.size()) // Pad with ones + if(mean != nullptr) + { + batchDims = mean->dims(); + } + else if(dims.size() == normalizedDims.size()) { batchDims = std::vector(dims.size(), 1); - normalizedDims = std::vector(dims.size(), 1); for(size_t i = 0; i < dims.size(); ++i) { - if(static_cast(i) < ndim - normalizedDimCount) + if(dims[i] != normalizedDims[i]) { batchDims[i] = dims[i]; - batchDimsSize *= dims[i]; - } - else - { - normalizedDims[i] = dims[i]; - normalizedDimsSize *= dims[i]; } } } - else // Don't pad with ones + else { - batchDims - = std::vector(dims.begin(), dims.begin() + (ndim - normalizedDimCount)); - for(auto d : batchDims) + batchDims = std::vector(static_cast(ndim - normalizedDimCount), 1); + for(size_t i = 0; i < static_cast(ndim - normalizedDimCount); ++i) { - batchDimsSize *= d; - } - normalizedDims - = std::vector(dims.begin() + (ndim - normalizedDimCount), dims.end()); - for(auto d : normalizedDims) - { - normalizedDimsSize *= d; + batchDims[i] = dims[i]; } } + auto strideOrder = hipdnn_data_sdk::utilities::extractStrideOrder(x.strides()); + auto batchStrides = hipdnn_data_sdk::utilities::generateStrides(batchDims, strideOrder); + const int64_t batchDimsSize = std::accumulate( + batchDims.begin(), batchDims.end(), int64_t{1}, std::multiplies{}); + + std::vector tmpMean; + std::vector tmpRstd; + if(mean == nullptr || rstd == nullptr) + { + tmpMean = std::vector(static_cast(batchDimsSize)); + tmpRstd = std::vector(static_cast(batchDimsSize)); + } // If batchDims is empty (entire tensor is normalized), use a single scalar iteration if(batchDims.empty()) @@ -324,8 +332,44 @@ class CpuFpReferenceLayernorm sumDyScaleX += dyVal * scaleVal * xVal; sumDyScale += dyVal * scaleVal; }); - auto meanVal = static_cast(mean.getHostValue(batchIndices)); - auto rstdVal = static_cast(rstd.getHostValue(batchIndices)); + + ComputeDataType meanVal; + ComputeDataType rstdVal; + if(mean == nullptr || rstd == nullptr) + { + int64_t count = 0; + meanVal = static_cast(0.0); + auto m2 = static_cast(0.0); + + hipdnn_data_sdk::utilities::iterateAlongDimensions( + normalizedDims, [&](const std::vector& normIndices) { + auto fullIndices + = buildFullIndices(batchIndices, normIndices, ndim, normalizedDimCount); + auto xVal = static_cast(x.getHostValue(fullIndices)); + + count++; + auto delta = xVal - meanVal; + meanVal += delta / static_cast(count); + auto delta2 = xVal - meanVal; + m2 += delta * delta2; + }); + + auto batchVariance = m2 / static_cast(count); + rstdVal = static_cast(1.0) + / hipdnn_data_sdk::types::sqrt(batchVariance + + static_cast(epsilon)); + + auto idx = static_cast(std::inner_product( + batchIndices.begin(), batchIndices.end(), batchStrides.begin(), int64_t{0})); + tmpMean[idx] = meanVal; + tmpRstd[idx] = rstdVal; + } + else + { + meanVal = static_cast(mean->getHostValue(batchIndices)); + rstdVal = static_cast(rstd->getHostValue(batchIndices)); + } + auto a = rstdVal * rstdVal * rstdVal * (sumDyScaleX - sumDyScale * meanVal) / static_cast(normalizedDimsSize); auto b = rstdVal * sumDyScale / static_cast(normalizedDimsSize) @@ -352,8 +396,22 @@ class CpuFpReferenceLayernorm = buildFullIndices(batchIndices, normIndices, ndim, normalizedDimCount); auto dyVal = static_cast(dy.getHostValue(fullIndices)); auto xVal = static_cast(x.getHostValue(fullIndices)); - auto meanVal = static_cast(mean.getHostValue(batchIndices)); - auto rstdVal = static_cast(rstd.getHostValue(batchIndices)); + ComputeDataType meanVal; + ComputeDataType rstdVal; + if(mean == nullptr || rstd == nullptr) + { + auto idx = static_cast(std::inner_product(batchIndices.begin(), + batchIndices.end(), + batchStrides.begin(), + int64_t{0})); + meanVal = tmpMean[idx]; + rstdVal = tmpRstd[idx]; + } + else + { + meanVal = static_cast(mean->getHostValue(batchIndices)); + rstdVal = static_cast(rstd->getHostValue(batchIndices)); + } dscaleVal += dyVal * (xVal - meanVal) * rstdVal; dbiasVal += dyVal; }); diff --git a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormBpropPlan.hpp b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormBpropPlan.hpp index 3ebbe8b287c4..ebfcaaad71f3 100644 --- a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormBpropPlan.hpp +++ b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormBpropPlan.hpp @@ -26,22 +26,27 @@ struct LayernormBpropParams const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes& dyAttributes, const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes& xAttributes, const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes& scaleAttributes, - const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes& meanAttributes, - const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes& invVarianceAttributes, const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes& dxAttributes, const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes& dscaleAttributes, const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes& dbiasAttributes, const int64_t normalizedDimCount, + const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes* meanAttributes = nullptr, + const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes* invVarianceAttributes + = nullptr, const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes* epsilonAttributes = nullptr) : dyTensor(unpackTensorAttributes(dyAttributes)) , xTensor(unpackTensorAttributes(xAttributes)) , scaleTensor(unpackTensorAttributes(scaleAttributes)) - , meanTensor(unpackTensorAttributes(meanAttributes)) - , invVarianceTensor(unpackTensorAttributes(invVarianceAttributes)) , dxTensor(unpackTensorAttributes(dxAttributes)) , dscaleTensor(unpackTensorAttributes(dscaleAttributes)) , dbiasTensor(unpackTensorAttributes(dbiasAttributes)) , normalizedDimCount(normalizedDimCount) + , meanTensor(meanAttributes != nullptr + ? std::make_optional(unpackTensorAttributes(*meanAttributes)) + : std::nullopt) + , invVarianceTensor(invVarianceAttributes != nullptr + ? std::make_optional(unpackTensorAttributes(*invVarianceAttributes)) + : std::nullopt) , epsilonTensor(epsilonAttributes != nullptr ? std::make_optional(unpackTensorAttributes(*epsilonAttributes)) : std::nullopt) @@ -51,12 +56,12 @@ struct LayernormBpropParams hipdnn_flatbuffers_sdk::data_objects::TensorAttributesT dyTensor; hipdnn_flatbuffers_sdk::data_objects::TensorAttributesT xTensor; hipdnn_flatbuffers_sdk::data_objects::TensorAttributesT scaleTensor; - hipdnn_flatbuffers_sdk::data_objects::TensorAttributesT meanTensor; - hipdnn_flatbuffers_sdk::data_objects::TensorAttributesT invVarianceTensor; hipdnn_flatbuffers_sdk::data_objects::TensorAttributesT dxTensor; hipdnn_flatbuffers_sdk::data_objects::TensorAttributesT dscaleTensor; hipdnn_flatbuffers_sdk::data_objects::TensorAttributesT dbiasTensor; int64_t normalizedDimCount; + std::optional meanTensor; + std::optional invVarianceTensor; std::optional epsilonTensor; }; @@ -89,11 +94,20 @@ class LayernormBpropPlan : public IGraphNodePlanExecutor auto shallowScaleTensor = createShallowTensor( _params.scaleTensor, variantPack.at(_params.scaleTensor.uid)); - auto shallowMeanTensor = createShallowTensor( - _params.meanTensor, variantPack.at(_params.meanTensor.uid)); + std::unique_ptr> + shallowMeanTensor; + std::unique_ptr> + shallowInvVarianceTensor; - auto shallowInvVarianceTensor = createShallowTensor( - _params.invVarianceTensor, variantPack.at(_params.invVarianceTensor.uid)); + if(_params.meanTensor.has_value() && _params.invVarianceTensor.has_value()) + { + shallowMeanTensor = createShallowTensor( + _params.meanTensor.value(), variantPack.at(_params.meanTensor.value().uid)); + + shallowInvVarianceTensor = createShallowTensor( + _params.invVarianceTensor.value(), + variantPack.at(_params.invVarianceTensor.value().uid)); + } auto shallowDxTensor = createShallowTensor( _params.dxTensor, variantPack.at(_params.dxTensor.uid)); @@ -115,12 +129,12 @@ class LayernormBpropPlan : public IGraphNodePlanExecutor utilities::CpuFpReferenceLayernorm::bprop(*shallowDyTensor, *shallowXTensor, *shallowScaleTensor, - *shallowMeanTensor, - *shallowInvVarianceTensor, *shallowDxTensor, *shallowDscaleTensor, *shallowDbiasTensor, epsilon, + shallowMeanTensor.get(), + shallowInvVarianceTensor.get(), _params.normalizedDimCount); } @@ -163,16 +177,6 @@ class LayernormBpropPlanBuilder : public IGraphNodePlanBuilder CHECK_TENSOR_EXISTS(tensorMap, nodeAttributes->dy_tensor_uid()); CHECK_TENSOR_EXISTS(tensorMap, nodeAttributes->x_tensor_uid()); CHECK_TENSOR_EXISTS(tensorMap, nodeAttributes->scale_tensor_uid()); - if(!nodeAttributes->mean_tensor_uid().has_value()) - { - return false; - } - CHECK_TENSOR_EXISTS(tensorMap, nodeAttributes->mean_tensor_uid().value()); - if(!nodeAttributes->inv_variance_tensor_uid().has_value()) - { - return false; - } - CHECK_TENSOR_EXISTS(tensorMap, nodeAttributes->inv_variance_tensor_uid().value()); CHECK_TENSOR_EXISTS(tensorMap, nodeAttributes->dx_tensor_uid()); CHECK_TENSOR_EXISTS(tensorMap, nodeAttributes->dscale_tensor_uid()); CHECK_TENSOR_EXISTS(tensorMap, nodeAttributes->dbias_tensor_uid()); @@ -180,15 +184,27 @@ class LayernormBpropPlanBuilder : public IGraphNodePlanBuilder CHECK_TENSOR_TYPE(tensorMap, nodeAttributes->dy_tensor_uid(), DyDataTypeEnum); CHECK_TENSOR_TYPE(tensorMap, nodeAttributes->x_tensor_uid(), DyDataTypeEnum); CHECK_TENSOR_TYPE(tensorMap, nodeAttributes->scale_tensor_uid(), ScaleBiasDataTypeEnum); - CHECK_TENSOR_TYPE( - tensorMap, nodeAttributes->mean_tensor_uid().value(), MeanInvVarianceDataTypeEnum); - CHECK_TENSOR_TYPE(tensorMap, - nodeAttributes->inv_variance_tensor_uid().value(), - MeanInvVarianceDataTypeEnum); CHECK_TENSOR_TYPE(tensorMap, nodeAttributes->dx_tensor_uid(), OutputDataTypeEnum); CHECK_TENSOR_TYPE(tensorMap, nodeAttributes->dscale_tensor_uid(), OutputDataTypeEnum); CHECK_TENSOR_TYPE(tensorMap, nodeAttributes->dbias_tensor_uid(), OutputDataTypeEnum); + const bool hasMean = nodeAttributes->mean_tensor_uid().has_value(); + const bool hasInvVariance = nodeAttributes->inv_variance_tensor_uid().has_value(); + if(hasMean != hasInvVariance) + { + return false; + } + + if(hasMean) + { + CHECK_OPTIONAL_TENSOR_EXISTS(tensorMap, nodeAttributes->mean_tensor_uid()); + CHECK_OPTIONAL_TENSOR_TYPE( + tensorMap, nodeAttributes->mean_tensor_uid(), MeanInvVarianceDataTypeEnum); + CHECK_OPTIONAL_TENSOR_EXISTS(tensorMap, nodeAttributes->inv_variance_tensor_uid()); + CHECK_OPTIONAL_TENSOR_TYPE( + tensorMap, nodeAttributes->inv_variance_tensor_uid(), MeanInvVarianceDataTypeEnum); + } + // Optional epsilon tensor if(nodeAttributes->epsilon_tensor_uid().has_value()) { @@ -207,17 +223,20 @@ class LayernormBpropPlanBuilder : public IGraphNodePlanBuilder { throw std::runtime_error("Node attributes are not of type LayernormBackwardAttributes"); } - if(!nodeAttributes->mean_tensor_uid().has_value()) - { - throw std::runtime_error("Node attributes are missing mean tensor UID"); - } - if(!nodeAttributes->inv_variance_tensor_uid().has_value()) - { - throw std::runtime_error("Node attributes are missing inv variance tensor UID"); - } const auto& tensorMap = graph.getTensorMap(); + const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes* meanAttr = nullptr; + const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes* invVarAttr = nullptr; + + const bool hasMean = nodeAttributes->mean_tensor_uid().has_value(); + const bool hasInvVariance = nodeAttributes->inv_variance_tensor_uid().has_value(); + if(hasMean && hasInvVariance) + { + meanAttr = tensorMap.at(nodeAttributes->mean_tensor_uid().value()); + invVarAttr = tensorMap.at(nodeAttributes->inv_variance_tensor_uid().value()); + } + const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes* epsilon = nullptr; if(nodeAttributes->epsilon_tensor_uid().has_value()) @@ -225,17 +244,16 @@ class LayernormBpropPlanBuilder : public IGraphNodePlanBuilder epsilon = tensorMap.at(nodeAttributes->epsilon_tensor_uid().value()); } - LayernormBpropParams params( - *tensorMap.at(nodeAttributes->dy_tensor_uid()), - *tensorMap.at(nodeAttributes->x_tensor_uid()), - *tensorMap.at(nodeAttributes->scale_tensor_uid()), - *tensorMap.at(nodeAttributes->mean_tensor_uid().value()), - *tensorMap.at(nodeAttributes->inv_variance_tensor_uid().value()), - *tensorMap.at(nodeAttributes->dx_tensor_uid()), - *tensorMap.at(nodeAttributes->dscale_tensor_uid()), - *tensorMap.at(nodeAttributes->dbias_tensor_uid()), - nodeAttributes->normalized_dim_count(), - epsilon); + LayernormBpropParams params(*tensorMap.at(nodeAttributes->dy_tensor_uid()), + *tensorMap.at(nodeAttributes->x_tensor_uid()), + *tensorMap.at(nodeAttributes->scale_tensor_uid()), + *tensorMap.at(nodeAttributes->dx_tensor_uid()), + *tensorMap.at(nodeAttributes->dscale_tensor_uid()), + *tensorMap.at(nodeAttributes->dbias_tensor_uid()), + nodeAttributes->normalized_dim_count(), + meanAttr, + invVarAttr, + epsilon); return std::make_unique dy({2, 4}); + Tensor x({2, 4}); + Tensor scale({4}); + Tensor dx({2, 4}); + Tensor dscale({4}); + Tensor dbias({4}); + + dy.setHostValue(-1.0, 0, 0); + dy.setHostValue(2.0, 0, 1); + dy.setHostValue(-3.0, 0, 2); + dy.setHostValue(4.0, 0, 3); + dy.setHostValue(2.0, 1, 0); + dy.setHostValue(-4.0, 1, 1); + dy.setHostValue(6.0, 1, 2); + dy.setHostValue(-8.0, 1, 3); + + x.setHostValue(1.0, 0, 0); + x.setHostValue(2.0, 0, 1); + x.setHostValue(3.0, 0, 2); + x.setHostValue(4.0, 0, 3); + x.setHostValue(2.0, 1, 0); + x.setHostValue(4.0, 1, 1); + x.setHostValue(6.0, 1, 2); + x.setHostValue(8.0, 1, 3); + + scale.fillWithValue(2.0); + + CpuFpReferenceLayernorm::bprop( + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, nullptr, nullptr, 1); + + auto tolerance = layernorm::getTolerance(); + + // Sample 0 outputs + EXPECT_NEAR(dx.getHostValue(0, 0), -2.1465994991753945e-05, tolerance); + EXPECT_NEAR(dx.getHostValue(0, 1), 3.577687297918808, tolerance); + EXPECT_NEAR(dx.getHostValue(0, 2), -7.15538175116928, tolerance); + EXPECT_NEAR(dx.getHostValue(0, 3), 3.5777159192454633, tolerance); + + // Sample 1 outputs + EXPECT_NEAR(dx.getHostValue(1, 0), 5.366547046303793e-06, tolerance); + EXPECT_NEAR(dx.getHostValue(1, 1), -3.5777033974472507, tolerance); + EXPECT_NEAR(dx.getHostValue(1, 2), 7.155408583743517, tolerance); + EXPECT_NEAR(dx.getHostValue(1, 3), -3.577710552843312, tolerance); + + // dscale + EXPECT_NEAR(dscale.getHostValue(0), -1.3416434697532726, tolerance); + EXPECT_NEAR(dscale.getHostValue(1), 0.8944289798355152, tolerance); + EXPECT_NEAR(dscale.getHostValue(2), 1.3416434697532726, tolerance); + EXPECT_NEAR(dscale.getHostValue(3), -5.366573879013091, tolerance); + + // dbias + EXPECT_NEAR(dbias.getHostValue(0), 1.0, tolerance); + EXPECT_NEAR(dbias.getHostValue(1), -2.0, tolerance); + EXPECT_NEAR(dbias.getHostValue(2), 3.0, tolerance); + EXPECT_NEAR(dbias.getHostValue(3), -4.0, tolerance); +} + +TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropSanityValidationWithOptional2D) +{ + // Input shape: [2, 4] — normalize over last dim (4 features per sample) + // dy = [[-1, 2, -3, 4], + // [2, -4, 6, -8]] + // x = [[1, 2, 3, 4], + // [2, 4, 6, 8]] + // + // Sample 0: mean=2.5, var=1.25, rstd=1/sqrt(1.25+1e-5)=0.894423613312618 + // a = 1.7888329159619083 + // b = -3.5776586765921525 + // dx = [-4.7998926705705713e-05, 3.577687297918808, -7.15538175116928, 3.5777159192454633] + // + // Sample 1: mean=5.0, var=5.0, rstd=1/sqrt(5.0+1e-5)=0.4472131482870333 + // a = -0.8944245077250512 + // b = 3.5776962420511893 + // dx = [2.3999731674440028e-05, -3.5777033974472507, 7.155408583743517, -3.577710552843312] + // + // dscale = [-1.341640786446209, 0.8944271909641394, 1.341640786446209, -5.366563145784836] + // dbias = [1, -2, 3, -4] + Tensor dy({2, 4}); Tensor x({2, 4}); Tensor scale({4}); @@ -76,7 +154,7 @@ TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropSanityValidation2D) scale.fillWithValue(2.0); CpuFpReferenceLayernorm::bprop( - dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, &mean, &rstd, 1); auto tolerance = layernorm::getTolerance(); @@ -123,6 +201,89 @@ TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropSanityValidation3D) // dscale = sum(dy * (x - mean) * rstd) // dbias = sum(dy) + Tensor dy({1, 2, 3}); + Tensor x({1, 2, 3}); + Tensor scale({2, 3}); + Tensor dx({1, 2, 3}); + Tensor dscale({2, 3}); + Tensor dbias({2, 3}); + + dy.setHostValue(-1.0, 0, 0, 0); + dy.setHostValue(2.0, 0, 0, 1); + dy.setHostValue(-3.0, 0, 0, 2); + dy.setHostValue(4.0, 0, 1, 0); + dy.setHostValue(-5.0, 0, 1, 1); + dy.setHostValue(6.0, 0, 1, 2); + + x.setHostValue(1.0, 0, 0, 0); + x.setHostValue(2.0, 0, 0, 1); + x.setHostValue(3.0, 0, 0, 2); + x.setHostValue(4.0, 0, 1, 0); + x.setHostValue(5.0, 0, 1, 1); + x.setHostValue(6.0, 0, 1, 2); + + scale.fillWithValue(1.0); + + CpuFpReferenceLayernorm::bprop( + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, nullptr, nullptr, 2); + + Tensor dxRef({1, 2, 3}); + Tensor dscaleRef({2, 3}); + Tensor dbiasRef({2, 3}); + + dxRef.setHostValue(-3.0113333097103734e-06, 0, 0, 0); + dxRef.setHostValue(1.4052918891730595, 0, 0, 1); + dxRef.setHostValue(-1.8737255302307223, 0, 0, 2); + dxRef.setHostValue(1.8737255302307223, 0, 1, 0); + dxRef.setHostValue(-3.7474480491281357, 0, 1, 1); + dxRef.setHostValue(2.342159171288385, 0, 1, 2); + + dscaleRef.setHostValue(1.4638475999719223, 0, 0); + dscaleRef.setHostValue(-1.7566171199663065, 0, 1); + dscaleRef.setHostValue(0.8783085599831533, 0, 2); + dscaleRef.setHostValue(1.1710780799775378, 1, 0); + dscaleRef.setHostValue(-4.391542799915767, 1, 1); + dscaleRef.setHostValue(8.783085599831534, 1, 2); + + dbiasRef.setHostValue(-1.0, 0, 0); + dbiasRef.setHostValue(2.0, 0, 1); + dbiasRef.setHostValue(-3.0, 0, 2); + dbiasRef.setHostValue(4.0, 1, 0); + dbiasRef.setHostValue(-5.0, 1, 1); + dbiasRef.setHostValue(6.0, 1, 2); + + auto tolerance = layernorm::getTolerance(); + + // Verify each output element: y = (x - mean) * rstd + for(int i = 0; i < 2; i++) + { + for(int j = 0; j < 3; j++) + { + EXPECT_NEAR(dx.getHostValue(0, i, j), dxRef.getHostValue(0, i, j), tolerance); + EXPECT_NEAR(dscale.getHostValue(i, j), dscaleRef.getHostValue(i, j), tolerance); + EXPECT_NEAR(dbias.getHostValue(i, j), dbiasRef.getHostValue(i, j), tolerance); + } + } +} + +TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropSanityValidationWithOptional3D) +{ + // Input shape: [1, 2, 3] — normalize over last 2 dims (2x3 = 6 features) + // dy = [[[-1, 2, -3], [4, -5, 6]]] + // x = [[[1, 2, 3], [4, 5, 6]]] + // + // mean = (1+2+3+4+5+6)/6 = 3.5 + // var = ((1-3.5)^2 + (2-3.5)^2 + (3-3.5)^2 + (4-3.5)^2 + (5-3.5)^2 + (6-3.5)^2) / 6 + // = (6.25 + 2.25 + 0.25 + 0.25 + 2.25 + 6.25) / 6 = 17.5 / 6 = 2.9166666666666665 + // rstd = 1/sqrt(2.9166666666666665 + 1e-5) = 0.5855390399887689 + // + // With scale=1, bias=0 (identity affine): + // a = rstd³ * (sum(dy * scale * x) - sum(dy * scale) * mean) / 6 + // b = rstd * sum(dy * scale) / 6 - a * mean + // dx = rstd * dy * scale - a * x - b + // dscale = sum(dy * (x - mean) * rstd) + // dbias = sum(dy) + Tensor dy({1, 2, 3}); Tensor x({1, 2, 3}); Tensor scale({2, 3}); @@ -152,7 +313,7 @@ TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropSanityValidation3D) rstd.setHostValue(0.5855390399887689, 0); CpuFpReferenceLayernorm::bprop( - dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 2); + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, &mean, &rstd, 2); Tensor dxRef({1, 2, 3}); Tensor dscaleRef({2, 3}); @@ -226,7 +387,7 @@ TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropAllZeros) } CpuFpReferenceLayernorm::bprop( - dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, &mean, &rstd, 1); auto tolerance = layernorm::getTolerance(); @@ -270,7 +431,7 @@ TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropAllOnes) rstd.fillWithValue(1.0 / std::sqrt(LAYERNORM_DEFAULT_EPSILON)); CpuFpReferenceLayernorm::bprop( - dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, &mean, &rstd, 1); auto tolerance = layernorm::getTolerance(); @@ -314,7 +475,7 @@ TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropConstantInput) rstd.fillWithValue(1.0 / std::sqrt(LAYERNORM_DEFAULT_EPSILON)); CpuFpReferenceLayernorm::bprop( - dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, &mean, &rstd, 1); auto tolerance = layernorm::getTolerance(); @@ -369,7 +530,7 @@ TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropSingleFeature) rstd.fillWithValue(1.0 / std::sqrt(LAYERNORM_DEFAULT_EPSILON)); CpuFpReferenceLayernorm::bprop( - dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, &mean, &rstd, 1); auto tolerance = layernorm::getTolerance(); @@ -424,7 +585,7 @@ TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropNegativeAndMixedValues) rstd.fillWithValue(1.0 / std::sqrt(5.0 + LAYERNORM_DEFAULT_EPSILON)); CpuFpReferenceLayernorm::bprop( - dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, &mean, &rstd, 1); auto tolerance = layernorm::getTolerance(); @@ -502,7 +663,7 @@ TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropLargeValueNumericalStability) rstd.fillWithValue(1.0 / std::sqrt(1.25 + LAYERNORM_DEFAULT_EPSILON)); CpuFpReferenceLayernorm::bprop( - dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, &mean, &rstd, 1); auto tolerance = layernorm::getTolerance(); @@ -568,7 +729,7 @@ TEST(TestCpuFpReferenceLayernormBackwardFp32, BpropTypicalTransformerShape) } CpuFpReferenceLayernorm::bprop( - dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, &mean, &rstd, 1); auto tolerance = layernorm::getTolerance(); @@ -660,7 +821,7 @@ TYPED_TEST(CpuFpReferenceLayernormBpropTyped, BpropRunsWithoutError) // Should not throw CpuFpReferenceLayernorm::bprop( - dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, &mean, &rstd, 1); auto tolerance = layernorm::getTolerance(); @@ -730,7 +891,7 @@ TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropNormalizeLastTwoDims) scale.fillWithValue(1.0); CpuFpReferenceLayernorm::bprop( - dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 2); + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, &mean, &rstd, 2); auto tolerance = layernorm::getTolerance(); @@ -818,7 +979,7 @@ TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropOneHotRows) rstd.fillWithValue(1.0 / std::sqrt(2.0 / 9.0 + LAYERNORM_DEFAULT_EPSILON)); CpuFpReferenceLayernorm::bprop( - dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, &mean, &rstd, 1); auto tolerance = layernorm::getTolerance(); @@ -872,7 +1033,7 @@ TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropPerFeatureScale) rstd.setHostValue(1.0 / std::sqrt(1.25 + LAYERNORM_DEFAULT_EPSILON), 0); CpuFpReferenceLayernorm::bprop( - dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, &mean, &rstd, 1); auto tolerance = layernorm::getTolerance(); @@ -926,12 +1087,12 @@ TEST(TestCpuFpReferenceLayernormBackwardFp32, BpropThrowsOnInvalidNormalizedDimC // normalizedDimCount = 0 is invalid EXPECT_THROW(CpuFpReferenceLayernorm::bprop( - dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 0), + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, &mean, &rstd, 0), std::runtime_error); // normalizedDimCount > ndim is invalid EXPECT_THROW(CpuFpReferenceLayernorm::bprop( - dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 3), + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, &mean, &rstd, 3), std::runtime_error); } @@ -951,7 +1112,7 @@ TEST(TestCpuFpReferenceLayernormBackwardFp32, BpropThrowsOnScalarTensor) Tensor dbias({}); EXPECT_THROW(CpuFpReferenceLayernorm::bprop( - dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1), + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, &mean, &rstd, 1), std::runtime_error); } @@ -993,7 +1154,7 @@ TEST(TestCpuFpReferenceLayernormBackwardFp64, Bprop1D) rstd.fillWithValue(1.0 / std::sqrt(8.0 + LAYERNORM_DEFAULT_EPSILON)); CpuFpReferenceLayernorm::bprop( - dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, &mean, &rstd, 1); auto tolerance = layernorm::getTolerance(); @@ -1065,7 +1226,7 @@ TEST(TestCpuFpReferenceLayernormBackwardFp64, Bprop4DNormalizeLast1) rstd.fillWithValue(1.0 / std::sqrt(1.25 + LAYERNORM_DEFAULT_EPSILON)); CpuFpReferenceLayernorm::bprop( - dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 1); + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, &mean, &rstd, 1); auto tolerance = layernorm::getTolerance(); @@ -1156,7 +1317,7 @@ TEST(TestCpuFpReferenceLayernormBackwardFp64, Bprop4DNormalizeLast3) rstd.fillWithValue(1.0 / std::sqrt(47.91666666667 + LAYERNORM_DEFAULT_EPSILON)); CpuFpReferenceLayernorm::bprop( - dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 3); + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, &mean, &rstd, 3); auto tolerance = layernorm::getTolerance(); @@ -1256,7 +1417,7 @@ TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropFullTensorNormalization) rstd.fillWithValue(1.0 / std::sqrt(2.916666667 + LAYERNORM_DEFAULT_EPSILON)); CpuFpReferenceLayernorm::bprop( - dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 2); + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, &mean, &rstd, 2); auto tolerance = layernorm::getTolerance(); @@ -1351,7 +1512,7 @@ TEST(TestCpuFpReferenceLayernormBackwardFp64, Bprop5DNormalizeLast2) } CpuFpReferenceLayernorm::bprop( - dy, x, scale, mean, rstd, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, 2); + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, &mean, &rstd, 2); auto tolerance = layernorm::getTolerance(); diff --git a/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestLayernormBpropPlan.cpp b/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestLayernormBpropPlan.cpp index 699ae8a54939..4b26723ef7bb 100644 --- a/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestLayernormBpropPlan.cpp +++ b/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestLayernormBpropPlan.cpp @@ -57,12 +57,12 @@ TEST_F(TestLayernormBpropPlan, ExecutePlan) LayernormBpropParams params(*tensorMap.at(attributes.dy_tensor_uid()), *tensorMap.at(attributes.x_tensor_uid()), *tensorMap.at(attributes.scale_tensor_uid()), - *tensorMap.at(attributes.mean_tensor_uid().value()), - *tensorMap.at(attributes.inv_variance_tensor_uid().value()), *tensorMap.at(attributes.dx_tensor_uid()), *tensorMap.at(attributes.dscale_tensor_uid()), *tensorMap.at(attributes.dbias_tensor_uid()), normalizedDimCount, + tensorMap.at(attributes.mean_tensor_uid().value()), + tensorMap.at(attributes.inv_variance_tensor_uid().value()), attributes.epsilon_tensor_uid().has_value() ? tensorMap.at(attributes.epsilon_tensor_uid().value()) : nullptr); @@ -77,10 +77,10 @@ TEST_F(TestLayernormBpropPlan, ExecutePlan) params.scaleTensor, directTensorBundle.getTensor(attributes.scale_tensor_uid()).rawHostData()); auto shallowMeanTensor = createShallowTensor( - params.meanTensor, + params.meanTensor.value(), directTensorBundle.getTensor(attributes.mean_tensor_uid().value()).rawHostData()); auto shallowInvVarianceTensor = createShallowTensor( - params.invVarianceTensor, + params.invVarianceTensor.value(), directTensorBundle.getTensor(attributes.inv_variance_tensor_uid().value()).rawHostData()); auto shallowDxTensor = createShallowTensor( params.dxTensor, directTensorBundle.getTensor(attributes.dx_tensor_uid()).rawHostData()); @@ -94,12 +94,12 @@ TEST_F(TestLayernormBpropPlan, ExecutePlan) CpuFpReferenceLayernorm::bprop(*shallowDyTensor, *shallowXTensor, *shallowScaleTensor, - *shallowMeanTensor, - *shallowInvVarianceTensor, *shallowDxTensor, *shallowDscaleTensor, *shallowDbiasTensor, hipdnn_data_sdk::utilities::LAYERNORM_DEFAULT_EPSILON, + shallowMeanTensor.get(), + shallowInvVarianceTensor.get(), normalizedDimCount); LayernormBpropPlan bpropPlan(std::move(params)); @@ -117,6 +117,92 @@ TEST_F(TestLayernormBpropPlan, ExecutePlan) planTensorBundle.getTensor(attributes.dbias_tensor_uid()))); } +TEST_F(TestLayernormBpropPlan, ExecutePlanWithoutOptionals) +{ + auto tolerance = layernorm::getTolerance(); + const std::vector dims = {6, 3, 32, 32}; + const int64_t normalizedDimCount = 3; + const unsigned int seed = getGlobalTestSeed(); + auto graph = buildLayernormBpropGraph(DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + dims, + normalizedDimCount, + TensorLayout::NHWC, + false); + auto [serializedGraph, serErr] = graph->to_binary(); + ASSERT_TRUE(serErr.is_good()) << serErr.get_message(); + const GraphWrapper graphWrapper(serializedGraph.data(), serializedGraph.size()); + const INodeWrapper& node = graphWrapper.getNodeWrapper(0); + LayernormBpropTensorBundle planTensorBundle(node, graphWrapper.getTensorMap(), seed); + LayernormBpropTensorBundle directTensorBundle(node, graphWrapper.getTensorMap(), seed); + + const auto& attributes + = node.attributesAs(); + + EXPECT_FALSE(attributes.mean_tensor_uid().has_value()); + EXPECT_FALSE(attributes.inv_variance_tensor_uid().has_value()); + + const auto& tensorMap = graphWrapper.getTensorMap(); + LayernormBpropParams params(*tensorMap.at(attributes.dy_tensor_uid()), + *tensorMap.at(attributes.x_tensor_uid()), + *tensorMap.at(attributes.scale_tensor_uid()), + *tensorMap.at(attributes.dx_tensor_uid()), + *tensorMap.at(attributes.dscale_tensor_uid()), + *tensorMap.at(attributes.dbias_tensor_uid()), + normalizedDimCount, + nullptr, + nullptr, + attributes.epsilon_tensor_uid().has_value() + ? tensorMap.at(attributes.epsilon_tensor_uid().value()) + : nullptr); + + const std::unordered_map variantPack = planTensorBundle.toHostVariantPack(); + + auto shallowDyTensor = createShallowTensor( + params.dyTensor, directTensorBundle.getTensor(attributes.dy_tensor_uid()).rawHostData()); + auto shallowXTensor = createShallowTensor( + params.xTensor, directTensorBundle.getTensor(attributes.x_tensor_uid()).rawHostData()); + auto shallowScaleTensor = createShallowTensor( + params.scaleTensor, + directTensorBundle.getTensor(attributes.scale_tensor_uid()).rawHostData()); + auto shallowDxTensor = createShallowTensor( + params.dxTensor, directTensorBundle.getTensor(attributes.dx_tensor_uid()).rawHostData()); + auto shallowDscaleTensor = createShallowTensor( + params.dscaleTensor, + directTensorBundle.getTensor(attributes.dscale_tensor_uid()).rawHostData()); + auto shallowDbiasTensor = createShallowTensor( + params.dbiasTensor, + directTensorBundle.getTensor(attributes.dbias_tensor_uid()).rawHostData()); + + CpuFpReferenceLayernorm::bprop( + *shallowDyTensor, + *shallowXTensor, + *shallowScaleTensor, + *shallowDxTensor, + *shallowDscaleTensor, + *shallowDbiasTensor, + hipdnn_data_sdk::utilities::LAYERNORM_DEFAULT_EPSILON, + static_cast*>(nullptr), + static_cast*>(nullptr), + normalizedDimCount); + + LayernormBpropPlan bpropPlan(std::move(params)); + bpropPlan.execute(variantPack); + + const CpuFpReferenceValidation cpuRefOutputValidation(tolerance, tolerance); + EXPECT_TRUE( + cpuRefOutputValidation.allClose(directTensorBundle.getTensor(attributes.dx_tensor_uid()), + planTensorBundle.getTensor(attributes.dx_tensor_uid()))); + EXPECT_TRUE(cpuRefOutputValidation.allClose( + directTensorBundle.getTensor(attributes.dscale_tensor_uid()), + planTensorBundle.getTensor(attributes.dscale_tensor_uid()))); + EXPECT_TRUE( + cpuRefOutputValidation.allClose(directTensorBundle.getTensor(attributes.dbias_tensor_uid()), + planTensorBundle.getTensor(attributes.dbias_tensor_uid()))); +} + TEST_F(TestLayernormBpropPlan, ExecutePlanOnePaddedNormalizedDimCount2) { auto tolerance = layernorm::getTolerance(); @@ -149,12 +235,12 @@ TEST_F(TestLayernormBpropPlan, ExecutePlanOnePaddedNormalizedDimCount2) LayernormBpropParams params(*tensorMap.at(attributes.dy_tensor_uid()), *tensorMap.at(attributes.x_tensor_uid()), *tensorMap.at(attributes.scale_tensor_uid()), - *tensorMap.at(attributes.mean_tensor_uid().value()), - *tensorMap.at(attributes.inv_variance_tensor_uid().value()), *tensorMap.at(attributes.dx_tensor_uid()), *tensorMap.at(attributes.dscale_tensor_uid()), *tensorMap.at(attributes.dbias_tensor_uid()), normalizedDimCount, + tensorMap.at(attributes.mean_tensor_uid().value()), + tensorMap.at(attributes.inv_variance_tensor_uid().value()), attributes.epsilon_tensor_uid().has_value() ? tensorMap.at(attributes.epsilon_tensor_uid().value()) : nullptr); @@ -169,10 +255,10 @@ TEST_F(TestLayernormBpropPlan, ExecutePlanOnePaddedNormalizedDimCount2) params.scaleTensor, directTensorBundle.getTensor(attributes.scale_tensor_uid()).rawHostData()); auto shallowMeanTensor = createShallowTensor( - params.meanTensor, + params.meanTensor.value(), directTensorBundle.getTensor(attributes.mean_tensor_uid().value()).rawHostData()); auto shallowInvVarianceTensor = createShallowTensor( - params.invVarianceTensor, + params.invVarianceTensor.value(), directTensorBundle.getTensor(attributes.inv_variance_tensor_uid().value()).rawHostData()); auto shallowDxTensor = createShallowTensor( params.dxTensor, directTensorBundle.getTensor(attributes.dx_tensor_uid()).rawHostData()); @@ -186,12 +272,12 @@ TEST_F(TestLayernormBpropPlan, ExecutePlanOnePaddedNormalizedDimCount2) CpuFpReferenceLayernorm::bprop(*shallowDyTensor, *shallowXTensor, *shallowScaleTensor, - *shallowMeanTensor, - *shallowInvVarianceTensor, *shallowDxTensor, *shallowDscaleTensor, *shallowDbiasTensor, hipdnn_data_sdk::utilities::LAYERNORM_DEFAULT_EPSILON, + shallowMeanTensor.get(), + shallowInvVarianceTensor.get(), normalizedDimCount); LayernormBpropPlan bpropPlan(std::move(params)); @@ -240,12 +326,12 @@ TEST_F(TestLayernormBpropPlan, ExecutePlanTrainingPhase) LayernormBpropParams params(*tensorMap.at(attributes.dy_tensor_uid()), *tensorMap.at(attributes.x_tensor_uid()), *tensorMap.at(attributes.scale_tensor_uid()), - *tensorMap.at(attributes.mean_tensor_uid().value()), - *tensorMap.at(attributes.inv_variance_tensor_uid().value()), *tensorMap.at(attributes.dx_tensor_uid()), *tensorMap.at(attributes.dscale_tensor_uid()), *tensorMap.at(attributes.dbias_tensor_uid()), normalizedDimCount, + tensorMap.at(attributes.mean_tensor_uid().value()), + tensorMap.at(attributes.inv_variance_tensor_uid().value()), attributes.epsilon_tensor_uid().has_value() ? tensorMap.at(attributes.epsilon_tensor_uid().value()) : nullptr); @@ -260,10 +346,10 @@ TEST_F(TestLayernormBpropPlan, ExecutePlanTrainingPhase) params.scaleTensor, directTensorBundle.getTensor(attributes.scale_tensor_uid()).rawHostData()); auto shallowMeanTensor = createShallowTensor( - params.meanTensor, + params.meanTensor.value(), directTensorBundle.getTensor(attributes.mean_tensor_uid().value()).rawHostData()); auto shallowInvVarianceTensor = createShallowTensor( - params.invVarianceTensor, + params.invVarianceTensor.value(), directTensorBundle.getTensor(attributes.inv_variance_tensor_uid().value()).rawHostData()); auto shallowDxTensor = createShallowTensor( params.dxTensor, directTensorBundle.getTensor(attributes.dx_tensor_uid()).rawHostData()); @@ -277,12 +363,12 @@ TEST_F(TestLayernormBpropPlan, ExecutePlanTrainingPhase) CpuFpReferenceLayernorm::bprop(*shallowDyTensor, *shallowXTensor, *shallowScaleTensor, - *shallowMeanTensor, - *shallowInvVarianceTensor, *shallowDxTensor, *shallowDscaleTensor, *shallowDbiasTensor, hipdnn_data_sdk::utilities::LAYERNORM_DEFAULT_EPSILON, + shallowMeanTensor.get(), + shallowInvVarianceTensor.get(), normalizedDimCount); LayernormBpropPlan bpropPlan(std::move(params)); From 19a66e9aeed2adff83895426209f891ed1199414 Mon Sep 17 00:00:00 2001 From: Brent Maas Date: Fri, 5 Jun 2026 09:59:52 +0000 Subject: [PATCH 4/7] Additional tests and remove some unnecessary checks --- .../include/HipdnnBackendDescriptorType.h | 5 +- .../tests/TestBackendEnumStringUtils.cpp | 28 ++++ .../node/LayernormBackwardNode.hpp | 89 +--------- .../tests/TestLayernormBackwardAttributes.cpp | 95 +++++++++-- .../tests/TestLayernormBackwardNode.cpp | 48 +++++- .../utilities/CpuFpReferenceLayernorm.hpp | 19 +-- .../detail/LayernormBpropSignatureKey.hpp | 11 +- .../detail/LayernormFpropSignatureKey.hpp | 11 +- .../TestCpuFpReferenceLayernormBackward.cpp | 154 ++++++++++++++++++ .../TestCpuReferenceGraphExecutor.cpp | 49 ++++++ .../TestLayernormBpropPlan.cpp | 139 ++++++++++++++++ .../TestLayernormBpropSignatureKey.cpp | 143 ++++++++++++++++ 12 files changed, 666 insertions(+), 125 deletions(-) diff --git a/projects/hipdnn/backend/include/HipdnnBackendDescriptorType.h b/projects/hipdnn/backend/include/HipdnnBackendDescriptorType.h index cbd555653453..7b444b8a49da 100644 --- a/projects/hipdnn/backend/include/HipdnnBackendDescriptorType.h +++ b/projects/hipdnn/backend/include/HipdnnBackendDescriptorType.h @@ -316,7 +316,10 @@ typedef enum /** * @brief Layernorm backward operation descriptor * - * Represents a backward layer normalization operation with output gradient (DY), input (X), scale, mean, inverse variance, input gradient (DX), scale gradient and mean gradient tensors, a backward layernorm operator, and a compute data type + * Represents a backward layer normalization operation with output + * gradient (DY), input (X), scale, mean, inverse variance, input + * gradient (DX), scale gradient and mean gradient tensors, a backward + * layernorm operator, and a compute data type */ HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR = 35, diff --git a/projects/hipdnn/backend/tests/TestBackendEnumStringUtils.cpp b/projects/hipdnn/backend/tests/TestBackendEnumStringUtils.cpp index 5295070cc939..71afb5557a33 100644 --- a/projects/hipdnn/backend/tests/TestBackendEnumStringUtils.cpp +++ b/projects/hipdnn/backend/tests/TestBackendEnumStringUtils.cpp @@ -68,6 +68,9 @@ TEST(TestBackendEnumStringUtils, GetBackendDescriptorTypeName) EXPECT_STREQ( hipdnnGetBackendDescriptorTypeName(HIPDNN_BACKEND_OPERATION_LAYERNORM_DESCRIPTOR_EXT), "HIPDNN_BACKEND_OPERATION_LAYERNORM_DESCRIPTOR_EXT"); + EXPECT_STREQ( + hipdnnGetBackendDescriptorTypeName(HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR), + "HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR_EXT"); EXPECT_STREQ(hipdnnGetBackendDescriptorTypeName( HIPDNN_BACKEND_OPERATION_BLOCK_SCALE_QUANTIZE_DESCRIPTOR), "HIPDNN_BACKEND_OPERATION_BLOCK_SCALE_QUANTIZE_DESCRIPTOR"); @@ -715,6 +718,31 @@ TEST(TestBackendEnumStringUtils, GetBackendAttributeName) EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_RMSNORM_BACKWARD_COMP_TYPE_EXT), "HIPDNN_ATTR_RMSNORM_BACKWARD_COMP_TYPE_EXT"); + // Layernorm backward operation attributes + EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY), + "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY"); + EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X), + "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X"); + EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE), + "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE"); + EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN), + "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN"); + EXPECT_STREQ( + hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE), + "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE"); + EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON), + "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON"); + EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX), + "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX"); + EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE), + "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE"); + EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS), + "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS"); + EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT), + "HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT"); + EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT), + "HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT"); + // Operation extension attributes EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_NAME_EXT), "HIPDNN_ATTR_OPERATION_NAME_EXT"); diff --git a/projects/hipdnn/frontend/include/hipdnn_frontend/node/LayernormBackwardNode.hpp b/projects/hipdnn/frontend/include/hipdnn_frontend/node/LayernormBackwardNode.hpp index 370f9d555f59..27241ead7e34 100644 --- a/projects/hipdnn/frontend/include/hipdnn_frontend/node/LayernormBackwardNode.hpp +++ b/projects/hipdnn/frontend/include/hipdnn_frontend/node/LayernormBackwardNode.hpp @@ -93,6 +93,7 @@ class LayernormBackwardNode : public BaseNodeget_dim().empty()) { attributes.get_dx()->set_dim(attributes.get_x()->get_dim()); @@ -102,6 +103,7 @@ class LayernormBackwardNode : public BaseNodeset_stride(attributes.get_x()->get_stride()); } + // Infer dscale shape and strides if not set if(attributes.get_dscale()->get_dim().empty()) { attributes.get_dscale()->set_dim(attributes.get_scale()->get_dim()); @@ -111,6 +113,7 @@ class LayernormBackwardNode : public BaseNodeset_stride(attributes.get_scale()->get_stride()); } + // Infer dbias shape and strides if not set if(attributes.get_dbias()->get_dim().empty()) { attributes.get_dbias()->set_dim(attributes.get_scale()->get_dim()); @@ -120,92 +123,6 @@ class LayernormBackwardNode : public BaseNodeset_stride(attributes.get_scale()->get_stride()); } - auto dxTensor = attributes.get_dx(); - auto dscaleTensor = attributes.get_dscale(); - auto dbiasTensor = attributes.get_dbias(); - auto dyTensor = attributes.get_dy(); - auto scaleTensor = attributes.get_scale(); - - // Infer output strides if not set - if(dxTensor->get_stride().empty()) - { - auto& dyStrides = dyTensor->get_stride(); - auto& dxDims = dxTensor->get_dim(); - - HIPDNN_RETURN_IF_TRUE( - dyStrides.empty(), - ErrorCode::ATTRIBUTE_NOT_SET, - "LayernormBackwardNode: Cannot infer output strides - missing input strides"); - - HIPDNN_RETURN_IF_TRUE( - dxDims.empty(), - ErrorCode::ATTRIBUTE_NOT_SET, - "LayernormBackwardNode: Cannot infer output strides - missing output dimensions"); - - HIPDNN_RETURN_IF_NE(dyStrides.size(), - dxDims.size(), - ErrorCode::ATTRIBUTE_NOT_SET, - "LayernormBackwardNode: Stride dimension mismatch between input " - "and output tensors"); - - auto strideOrder = hipdnn_data_sdk::utilities::extractStrideOrder(dyStrides); - auto dxStrides = hipdnn_data_sdk::utilities::generateStrides(dxDims, strideOrder); - dxTensor->set_stride(dxStrides); - } - - if(dscaleTensor->get_stride().empty()) - { - auto& scaleStrides = scaleTensor->get_stride(); - auto& dscaleDims = dscaleTensor->get_dim(); - - HIPDNN_RETURN_IF_TRUE(scaleStrides.empty(), - ErrorCode::ATTRIBUTE_NOT_SET, - "LayernormBackwardNode: Cannot infer gradient scale strides - " - "missing scale strides"); - - HIPDNN_RETURN_IF_TRUE(dscaleDims.empty(), - ErrorCode::ATTRIBUTE_NOT_SET, - "LayernormBackwardNode: Cannot infer gradient scale strides - " - "missing gradient scale dimensions"); - - HIPDNN_RETURN_IF_NE(scaleStrides.size(), - dscaleDims.size(), - ErrorCode::ATTRIBUTE_NOT_SET, - "LayernormBackwardNode: Stride dimension mismatch between scale " - "and gradient scale tensors"); - - auto strideOrder = hipdnn_data_sdk::utilities::extractStrideOrder(scaleStrides); - auto dscaleStrides - = hipdnn_data_sdk::utilities::generateStrides(dscaleDims, strideOrder); - dscaleTensor->set_stride(dscaleStrides); - } - - if(dbiasTensor->get_stride().empty()) - { - auto& scaleStrides = scaleTensor->get_stride(); - auto& dbiasDims = dbiasTensor->get_dim(); - - HIPDNN_RETURN_IF_TRUE(scaleStrides.empty(), - ErrorCode::ATTRIBUTE_NOT_SET, - "LayernormBackwardNode: Cannot infer gradient bias strides - " - "missing scale strides"); - - HIPDNN_RETURN_IF_TRUE(dbiasDims.empty(), - ErrorCode::ATTRIBUTE_NOT_SET, - "LayernormBackwardNode: Cannot infer gradient bias strides - " - "missing gradient bias dimensions"); - - HIPDNN_RETURN_IF_NE(scaleStrides.size(), - dbiasDims.size(), - ErrorCode::ATTRIBUTE_NOT_SET, - "LayernormBackwardNode: Stride dimension mismatch between scale " - "and gradient bias tensors"); - - auto strideOrder = hipdnn_data_sdk::utilities::extractStrideOrder(scaleStrides); - auto dbiasStrides = hipdnn_data_sdk::utilities::generateStrides(dbiasDims, strideOrder); - dbiasTensor->set_stride(dbiasStrides); - } - // Infer normalized dimension count if not available if(attributes.get_normalized_dim_count() <= 0) { diff --git a/projects/hipdnn/frontend/tests/TestLayernormBackwardAttributes.cpp b/projects/hipdnn/frontend/tests/TestLayernormBackwardAttributes.cpp index 6aa6bab45fdc..de86a76c78f4 100644 --- a/projects/hipdnn/frontend/tests/TestLayernormBackwardAttributes.cpp +++ b/projects/hipdnn/frontend/tests/TestLayernormBackwardAttributes.cpp @@ -44,6 +44,7 @@ TEST(TestLayernormBackwardAttributes, CreateLayernormBackwardAttributes) attrs.set_dbias(dbiasTensor); // Set data fields + attrs.set_normalized_dim_count(2); // Verify tensor getters EXPECT_NE(attrs.get_dy(), nullptr); @@ -66,6 +67,7 @@ TEST(TestLayernormBackwardAttributes, CreateLayernormBackwardAttributes) EXPECT_EQ(attrs.get_dbias()->get_uid(), 18); // Verify data field getters + EXPECT_EQ(attrs.get_normalized_dim_count(), 2); } TEST(TestLayernormBackwardAttributes, DefaultValues) @@ -82,8 +84,6 @@ TEST(TestLayernormBackwardAttributes, DefaultValues) EXPECT_EQ(attrs.get_dx(), nullptr); EXPECT_EQ(attrs.get_dscale(), nullptr); EXPECT_EQ(attrs.get_dbias(), nullptr); - - // Vector fields should be empty by default } TEST(TestLayernormBackwardAttributes, SetDyMove) @@ -101,9 +101,10 @@ TEST(TestLayernormBackwardAttributes, SetDyMove) // After move, original should be nullptr EXPECT_EQ(dyTensor, nullptr); - // The moved tensor should be accessible through the getter + // The moved tensor should be accessible through the getter and should have the correct name auto retrievedTensor = attrs.get_dy(); EXPECT_EQ(retrievedTensor.get(), rawPtr); + EXPECT_EQ(retrievedTensor->get_name(), "MovedDyTensor"); } TEST(TestLayernormBackwardAttributes, SetXMove) @@ -121,9 +122,10 @@ TEST(TestLayernormBackwardAttributes, SetXMove) // After move, original should be nullptr EXPECT_EQ(xTensor, nullptr); - // The moved tensor should be accessible through the getter + // The moved tensor should be accessible through the getter and should have the correct name auto retrievedTensor = attrs.get_x(); EXPECT_EQ(retrievedTensor.get(), rawPtr); + EXPECT_EQ(retrievedTensor->get_name(), "MovedXTensor"); } TEST(TestLayernormBackwardAttributes, SetScaleMove) @@ -143,9 +145,10 @@ TEST(TestLayernormBackwardAttributes, SetScaleMove) // After move, original should be nullptr EXPECT_EQ(scaleTensor, nullptr); - // The moved tensor should be accessible through the getter + // The moved tensor should be accessible through the getter and should have the correct name auto retrievedTensor = attrs.get_scale(); EXPECT_EQ(retrievedTensor.get(), rawPtr); + EXPECT_EQ(retrievedTensor->get_name(), "MovedScaleTensor"); } TEST(TestLayernormBackwardAttributes, SetMeanMove) @@ -165,9 +168,10 @@ TEST(TestLayernormBackwardAttributes, SetMeanMove) // After move, original should be nullptr EXPECT_EQ(meanTensor, nullptr); - // The moved tensor should be accessible through the getter + // The moved tensor should be accessible through the getter and should have the correct name auto retrievedTensor = attrs.get_mean(); EXPECT_EQ(retrievedTensor.get(), rawPtr); + EXPECT_EQ(retrievedTensor->get_name(), "MovedMeanTensor"); } TEST(TestLayernormBackwardAttributes, SetInvVarianceMove) @@ -187,9 +191,74 @@ TEST(TestLayernormBackwardAttributes, SetInvVarianceMove) // After move, original should be nullptr EXPECT_EQ(invVarianceTensor, nullptr); - // The moved tensor should be accessible through the getter + // The moved tensor should be accessible through the getter and should have the correct name auto retrievedTensor = attrs.get_inv_variance(); EXPECT_EQ(retrievedTensor.get(), rawPtr); + EXPECT_EQ(retrievedTensor->get_name(), "MovedInvVarianceTensor"); +} + +TEST(TestLayernormBackwardAttributes, SetMeanAndInvVarianceMove) +{ + LayernormBackwardAttributes attrs; + + auto meanTensor = std::make_shared(); + meanTensor->set_uid(13) + .set_name("MovedMeanTensor") + .set_data_type(hipdnn_frontend::DataType::FLOAT); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_uid(14) + .set_name("MovedInvVarianceTensor") + .set_data_type(hipdnn_frontend::DataType::FLOAT); + + // Store the raw pointer before moving + auto rawMeanPtr = meanTensor.get(); + auto rawInvVariancePtr = invVarianceTensor.get(); + + attrs.set_saved_mean_and_inv_variance(std::move(meanTensor), std::move(invVarianceTensor)); + + // After move, original should be nullptr + EXPECT_EQ(meanTensor, nullptr); + EXPECT_EQ(invVarianceTensor, nullptr); + + // The moved tensor should be accessible through the getter and should have the correct name + auto retrievedMeanTensor = attrs.get_mean(); + EXPECT_EQ(retrievedMeanTensor.get(), rawMeanPtr); + EXPECT_EQ(retrievedMeanTensor->get_name(), "MovedMeanTensor"); + auto retrievedInvVarianceTensor = attrs.get_inv_variance(); + EXPECT_EQ(retrievedInvVarianceTensor.get(), rawInvVariancePtr); + EXPECT_EQ(retrievedInvVarianceTensor->get_name(), "MovedInvVarianceTensor"); +} + +TEST(TestLayernormBackwardAttributes, SetMeanAndInvVarianceCopy) +{ + LayernormBackwardAttributes attrs; + + auto meanTensor = std::make_shared(); + meanTensor->set_uid(13) + .set_name("CopiedMeanTensor") + .set_data_type(hipdnn_frontend::DataType::FLOAT); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_uid(14) + .set_name("CopiedInvVarianceTensor") + .set_data_type(hipdnn_frontend::DataType::FLOAT); + + // Store the raw pointer before moving + auto rawMeanPtr = meanTensor.get(); + auto rawInvVariancePtr = invVarianceTensor.get(); + + attrs.set_saved_mean_and_inv_variance(meanTensor, invVarianceTensor); + + // After copy, original should still exist + EXPECT_NE(meanTensor, nullptr); + EXPECT_NE(invVarianceTensor, nullptr); + + // The copied tensor should be accessible through the getter and should have the correct name + auto retrievedMeanTensor = attrs.get_mean(); + EXPECT_EQ(retrievedMeanTensor.get(), rawMeanPtr); + EXPECT_EQ(retrievedMeanTensor->get_name(), "CopiedMeanTensor"); + auto retrievedInvVarianceTensor = attrs.get_inv_variance(); + EXPECT_EQ(retrievedInvVarianceTensor.get(), rawInvVariancePtr); + EXPECT_EQ(retrievedInvVarianceTensor->get_name(), "CopiedInvVarianceTensor"); } TEST(TestLayernormBackwardAttributes, SetEpsilonMove) @@ -209,9 +278,10 @@ TEST(TestLayernormBackwardAttributes, SetEpsilonMove) // After move, original should be nullptr EXPECT_EQ(epsilonTensor, nullptr); - // The moved tensor should be accessible through the getter + // The moved tensor should be accessible through the getter and should have the correct name auto retrievedTensor = attrs.get_epsilon(); EXPECT_EQ(retrievedTensor.get(), rawPtr); + EXPECT_EQ(retrievedTensor->get_name(), "MovedEpsilonTensor"); } TEST(TestLayernormBackwardAttributes, SetDxMove) @@ -229,9 +299,10 @@ TEST(TestLayernormBackwardAttributes, SetDxMove) // After move, original should be nullptr EXPECT_EQ(dxTensor, nullptr); - // The moved tensor should be accessible through the getter + // The moved tensor should be accessible through the getter and should have the correct name auto retrievedTensor = attrs.get_dx(); EXPECT_EQ(retrievedTensor.get(), rawPtr); + EXPECT_EQ(retrievedTensor->get_name(), "MovedDxTensor"); } TEST(TestLayernormBackwardAttributes, SetDscaleMove) @@ -251,9 +322,10 @@ TEST(TestLayernormBackwardAttributes, SetDscaleMove) // After move, original should be nullptr EXPECT_EQ(dscaleTensor, nullptr); - // The moved tensor should be accessible through the getter + // The moved tensor should be accessible through the getter and should have the correct name auto retrievedTensor = attrs.get_dscale(); EXPECT_EQ(retrievedTensor.get(), rawPtr); + EXPECT_EQ(retrievedTensor->get_name(), "MovedDscaleTensor"); } TEST(TestLayernormBackwardAttributes, SetDbiasMove) @@ -273,9 +345,10 @@ TEST(TestLayernormBackwardAttributes, SetDbiasMove) // After move, original should be nullptr EXPECT_EQ(dbiasTensor, nullptr); - // The moved tensor should be accessible through the getter + // The moved tensor should be accessible through the getter and should have the correct name auto retrievedTensor = attrs.get_dbias(); EXPECT_EQ(retrievedTensor.get(), rawPtr); + EXPECT_EQ(retrievedTensor->get_name(), "MovedDbiasTensor"); } TEST(TestLayernormBackwardAttributes, SetTensorsConstRef) diff --git a/projects/hipdnn/frontend/tests/TestLayernormBackwardNode.cpp b/projects/hipdnn/frontend/tests/TestLayernormBackwardNode.cpp index e1d523ce6a53..a14367788d74 100644 --- a/projects/hipdnn/frontend/tests/TestLayernormBackwardNode.cpp +++ b/projects/hipdnn/frontend/tests/TestLayernormBackwardNode.cpp @@ -49,6 +49,32 @@ LayernormBackwardAttributes createValidAttributes() return attrs; } +LayernormBackwardAttributes createMinimalAttributes() +{ + LayernormBackwardAttributes attrs; + + auto dyTensor = std::make_shared(); + dyTensor->set_dim({16, 64, 32, 32}); + dyTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dy(dyTensor); + auto xTensor = std::make_shared(); + xTensor->set_dim({16, 64, 32, 32}); + xTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_x(xTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_dim({1, 64, 32, 32}); + scaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_scale(scaleTensor); + auto dxTensor = std::make_shared(); + attrs.set_dx(dxTensor); + auto dscaleTensor = std::make_shared(); + attrs.set_dscale(dscaleTensor); + auto dbiasTensor = std::make_shared(); + attrs.set_dbias(dbiasTensor); + + return attrs; +} + } // namespace // --- GetNodeType --- @@ -300,8 +326,28 @@ TEST(TestLayernormBackwardNode, InferPropertiesNode) LayernormBackwardNode node(std::move(attrs), graphAttributes); auto error = node.infer_properties_node(); - // Stub implementation: verify the method can be called without error EXPECT_EQ(error.code, error_code_t::OK) << error.err_msg; + EXPECT_EQ(node.attributes.get_normalized_dim_count(), 3); +} + +// --- InferMinimalPropertiesNode --- +TEST(TestLayernormBackwardNode, InferMinimalPropertiesNode) +{ + auto attrs = createMinimalAttributes(); + + const GraphAttributes graphAttributes; + LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.infer_properties_node(); + EXPECT_EQ(error.code, error_code_t::OK) << error.err_msg; + EXPECT_EQ(node.attributes.get_dx()->get_dim(), node.attributes.get_x()->get_dim()); + EXPECT_EQ(node.attributes.get_dx()->get_stride(), node.attributes.get_x()->get_stride()); + EXPECT_EQ(node.attributes.get_dscale()->get_dim(), node.attributes.get_scale()->get_dim()); + EXPECT_EQ(node.attributes.get_dscale()->get_stride(), + node.attributes.get_scale()->get_stride()); + EXPECT_EQ(node.attributes.get_dbias()->get_dim(), node.attributes.get_scale()->get_dim()); + EXPECT_EQ(node.attributes.get_dbias()->get_stride(), node.attributes.get_scale()->get_stride()); + EXPECT_EQ(node.attributes.get_normalized_dim_count(), 3); } // --- GatherHipdnnTensors --- diff --git a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/CpuFpReferenceLayernorm.hpp b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/CpuFpReferenceLayernorm.hpp index 8296d7f61bde..d47edf401d96 100644 --- a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/CpuFpReferenceLayernorm.hpp +++ b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/CpuFpReferenceLayernorm.hpp @@ -244,10 +244,10 @@ class CpuFpReferenceLayernorm "normalizedDimCount must be between 1 and the number of tensor dimensions."); } - if(scale.dims().size() != dscale.dims().size() - || scale.dims().size() != dbias.dims().size()) + if(scale.dims() != dscale.dims() || scale.dims() != dbias.dims()) { - throw std::runtime_error("Scale, dscale and dbias tensors must have the same rank."); + throw std::runtime_error( + "Scale, dscale and dbias tensors must have the same dimensions."); } if((mean == nullptr) != (rstd == nullptr)) @@ -256,18 +256,9 @@ class CpuFpReferenceLayernorm "Layernorm backward requires both mean and rstd to be provided, or neither."); } - if(mean != nullptr && mean->dims().size() != rstd->dims().size()) - { - throw std::runtime_error("Mean and rstd tensors must have the same rank."); - } - - for(auto d : dims) + if(mean != nullptr && mean->dims() != rstd->dims()) { - if(d <= 0) - { - throw std::runtime_error( - "Dimensions must all be positive (no zero-size dimensions)."); - } + throw std::runtime_error("Mean and rstd tensors must have the same dimensions."); } // Split dimensions into batch dims and normalized dims diff --git a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormBpropSignatureKey.hpp b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormBpropSignatureKey.hpp index 036de947c252..98ea4233822b 100644 --- a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormBpropSignatureKey.hpp +++ b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormBpropSignatureKey.hpp @@ -87,12 +87,11 @@ struct LayernormBpropSignatureKey constexpr std::size_t hashSelf() const { - return static_cast(static_cast(nodeType)) - ^ (static_cast(static_cast(dyDataType)) << 4) - ^ (static_cast(static_cast(scaleBiasDataType)) << 8) - ^ (static_cast(static_cast(meanInvVarianceDataType)) << 12) - ^ (static_cast(static_cast(outputDataType)) << 16) - ^ (static_cast(static_cast(computeDataType)) << 20); + return static_cast(nodeType) ^ (static_cast(dyDataType) << 4) + ^ (static_cast(scaleBiasDataType) << 8) + ^ (static_cast(meanInvVarianceDataType) << 12) + ^ (static_cast(outputDataType) << 16) + ^ (static_cast(computeDataType) << 20); } bool operator==(const LayernormBpropSignatureKey& other) const noexcept diff --git a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormFpropSignatureKey.hpp b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormFpropSignatureKey.hpp index bd2ba550ef64..0aadc4370ae7 100644 --- a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormFpropSignatureKey.hpp +++ b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormFpropSignatureKey.hpp @@ -85,12 +85,11 @@ struct LayernormFpropSignatureKey constexpr std::size_t hashSelf() const { - return static_cast(static_cast(nodeType)) - ^ (static_cast(static_cast(xDataType)) << 4) - ^ (static_cast(static_cast(scaleBiasDataType)) << 8) - ^ (static_cast(static_cast(meanInvVarianceDataType)) << 12) - ^ (static_cast(static_cast(outputDataType)) << 16) - ^ (static_cast(static_cast(computeDataType)) << 20); + return static_cast(nodeType) ^ (static_cast(xDataType) << 4) + ^ (static_cast(scaleBiasDataType) << 8) + ^ (static_cast(meanInvVarianceDataType) << 12) + ^ (static_cast(outputDataType) << 16) + ^ (static_cast(computeDataType) << 20); } bool operator==(const LayernormFpropSignatureKey& other) const noexcept diff --git a/projects/hipdnn/test_sdk/tests/utilities/TestCpuFpReferenceLayernormBackward.cpp b/projects/hipdnn/test_sdk/tests/utilities/TestCpuFpReferenceLayernormBackward.cpp index 5da68d8773c7..c988fd9df7c1 100644 --- a/projects/hipdnn/test_sdk/tests/utilities/TestCpuFpReferenceLayernormBackward.cpp +++ b/projects/hipdnn/test_sdk/tests/utilities/TestCpuFpReferenceLayernormBackward.cpp @@ -354,6 +354,89 @@ TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropSanityValidationWithOptional3 } } +TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropSanityValidationEntire3D) +{ + // Input shape: [1, 2, 3] — normalize over last 2 dims (2x3 = 6 features) + // dy = [[[-1, 2, -3], [4, -5, 6]]] + // x = [[[1, 2, 3], [4, 5, 6]]] + // + // mean = (1+2+3+4+5+6)/6 = 3.5 + // var = ((1-3.5)^2 + (2-3.5)^2 + (3-3.5)^2 + (4-3.5)^2 + (5-3.5)^2 + (6-3.5)^2) / 6 + // = (6.25 + 2.25 + 0.25 + 0.25 + 2.25 + 6.25) / 6 = 17.5 / 6 = 2.9166666666666665 + // rstd = 1/sqrt(2.9166666666666665 + 1e-5) = 0.5855390399887689 + // + // With scale=1, bias=0 (identity affine): + // a = rstd³ * (sum(dy * scale * x) - sum(dy * scale) * mean) / 6 + // b = rstd * sum(dy * scale) / 6 - a * mean + // dx = rstd * dy * scale - a * x - b + // dscale = sum(dy * (x - mean) * rstd) + // dbias = sum(dy) + + Tensor dy({1, 2, 3}); + Tensor x({1, 2, 3}); + Tensor scale({1, 2, 3}); + Tensor dx({1, 2, 3}); + Tensor dscale({1, 2, 3}); + Tensor dbias({1, 2, 3}); + + dy.setHostValue(-1.0, 0, 0, 0); + dy.setHostValue(2.0, 0, 0, 1); + dy.setHostValue(-3.0, 0, 0, 2); + dy.setHostValue(4.0, 0, 1, 0); + dy.setHostValue(-5.0, 0, 1, 1); + dy.setHostValue(6.0, 0, 1, 2); + + x.setHostValue(1.0, 0, 0, 0); + x.setHostValue(2.0, 0, 0, 1); + x.setHostValue(3.0, 0, 0, 2); + x.setHostValue(4.0, 0, 1, 0); + x.setHostValue(5.0, 0, 1, 1); + x.setHostValue(6.0, 0, 1, 2); + + scale.fillWithValue(1.0); + + CpuFpReferenceLayernorm::bprop( + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, nullptr, nullptr, 3); + + Tensor dxRef({1, 2, 3}); + Tensor dscaleRef({1, 2, 3}); + Tensor dbiasRef({1, 2, 3}); + + dxRef.setHostValue(-3.0113333097103734e-06, 0, 0, 0); + dxRef.setHostValue(1.4052918891730595, 0, 0, 1); + dxRef.setHostValue(-1.8737255302307223, 0, 0, 2); + dxRef.setHostValue(1.8737255302307223, 0, 1, 0); + dxRef.setHostValue(-3.7474480491281357, 0, 1, 1); + dxRef.setHostValue(2.342159171288385, 0, 1, 2); + + dscaleRef.setHostValue(1.4638475999719223, 0, 0, 0); + dscaleRef.setHostValue(-1.7566171199663065, 0, 0, 1); + dscaleRef.setHostValue(0.8783085599831533, 0, 0, 2); + dscaleRef.setHostValue(1.1710780799775378, 0, 1, 0); + dscaleRef.setHostValue(-4.391542799915767, 0, 1, 1); + dscaleRef.setHostValue(8.783085599831534, 0, 1, 2); + + dbiasRef.setHostValue(-1.0, 0, 0, 0); + dbiasRef.setHostValue(2.0, 0, 0, 1); + dbiasRef.setHostValue(-3.0, 0, 0, 2); + dbiasRef.setHostValue(4.0, 0, 1, 0); + dbiasRef.setHostValue(-5.0, 0, 1, 1); + dbiasRef.setHostValue(6.0, 0, 1, 2); + + auto tolerance = layernorm::getTolerance(); + + // Verify each output element: y = (x - mean) * rstd + for(int i = 0; i < 2; i++) + { + for(int j = 0; j < 3; j++) + { + EXPECT_NEAR(dx.getHostValue(0, i, j), dxRef.getHostValue(0, i, j), tolerance); + EXPECT_NEAR(dscale.getHostValue(0, i, j), dscaleRef.getHostValue(0, i, j), tolerance); + EXPECT_NEAR(dbias.getHostValue(0, i, j), dbiasRef.getHostValue(0, i, j), tolerance); + } + } +} + // ============================================================================ // Corner case: all zeros input // ============================================================================ @@ -1074,6 +1157,50 @@ TEST(TestCpuFpReferenceLayernormBackwardFp64, BpropPerFeatureScale) // Error handling: invalid dimensions // ============================================================================ +TEST(TestCpuFpReferenceLayernormBackwardFp32, BpropThrowsOnInvalidDimensions) +{ + const Tensor dy({2, 4}); + const Tensor x({2, 4}); + const Tensor scale({4}); + const Tensor mean({2}); + const Tensor rstd({2}); + Tensor dx({2, 4}); + Tensor dscale({4}); + Tensor dbias({4}); + Tensor invalidDscale({2}); + Tensor invalidDbias({2}); + const Tensor invalidMean({4}); + + // scale and dscale must have the same dimensions + EXPECT_THROW( + CpuFpReferenceLayernorm::bprop( + dy, x, scale, dx, invalidDscale, dbias, LAYERNORM_DEFAULT_EPSILON, &mean, &rstd, 1), + std::runtime_error); + + // bias and dbias must have the same dimensions + EXPECT_THROW( + CpuFpReferenceLayernorm::bprop( + dy, x, scale, dx, dscale, invalidDbias, LAYERNORM_DEFAULT_EPSILON, &mean, &rstd, 1), + std::runtime_error); + + // mean and rstd must have the same dimensions + EXPECT_THROW(CpuFpReferenceLayernorm::bprop(dy, + x, + scale, + dx, + dscale, + invalidDbias, + LAYERNORM_DEFAULT_EPSILON, + &invalidMean, + &rstd, + 1), + std::runtime_error); +} + +// ============================================================================ +// Error handling: invalid normalized dimension count +// ============================================================================ + TEST(TestCpuFpReferenceLayernormBackwardFp32, BpropThrowsOnInvalidNormalizedDimCount) { const Tensor dy({2, 4}); @@ -1096,6 +1223,33 @@ TEST(TestCpuFpReferenceLayernormBackwardFp32, BpropThrowsOnInvalidNormalizedDimC std::runtime_error); } +// ============================================================================ +// Error handling: invalid mean or rstd nullptr +// ============================================================================ + +TEST(TestCpuFpReferenceLayernormBackwardFp32, BpropThrowsOnOnlyOneNullptrMeanOrRstd) +{ + const Tensor dy({2, 4}); + const Tensor x({2, 4}); + const Tensor scale({4}); + const Tensor mean({2}); + const Tensor rstd({2}); + Tensor dx({2, 4}); + Tensor dscale({4}); + Tensor dbias({4}); + const hipdnn_data_sdk::utilities::TensorBase* nullTensor = nullptr; + + // mean and rstd must both be specified or both be nullptr + EXPECT_THROW( + CpuFpReferenceLayernorm::bprop( + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, nullTensor, &rstd, 1), + std::runtime_error); + EXPECT_THROW( + CpuFpReferenceLayernorm::bprop( + dy, x, scale, dx, dscale, dbias, LAYERNORM_DEFAULT_EPSILON, &mean, nullTensor, 1), + std::runtime_error); +} + // ============================================================================ // Error handling: scalar (0D) tensor // ============================================================================ diff --git a/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestCpuReferenceGraphExecutor.cpp b/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestCpuReferenceGraphExecutor.cpp index 5f99760c10c0..47d762a7cbff 100644 --- a/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestCpuReferenceGraphExecutor.cpp +++ b/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestCpuReferenceGraphExecutor.cpp @@ -304,6 +304,39 @@ class TestCpuReferenceGraphExecutor serializedGraph.data(), serializedGraph.size(), variantPack); } + static void runLayernormBackwardTest( + hipdnn_flatbuffers_sdk::data_objects::DataType inputDataType, + hipdnn_flatbuffers_sdk::data_objects::DataType scaleBiasDataType, + hipdnn_flatbuffers_sdk::data_objects::DataType meanInvVarianceDataType, + hipdnn_flatbuffers_sdk::data_objects::DataType computeDataType) + { + const unsigned int seed = getGlobalTestSeed(); + const std::vector dims = {1, 3, 14, 14}; + + auto graph = buildLayernormBpropGraph(inputDataType, + scaleBiasDataType, + meanInvVarianceDataType, + computeDataType, + dims, + 2, // normalize over last 2 dims + TensorLayout::NCHW); + + auto result = graph->validate(); + ASSERT_EQ(result.code, hipdnn_frontend::ErrorCode::OK) << result.err_msg; + + auto [serializedGraph, serErr] = graph->to_binary(); + ASSERT_TRUE(serErr.is_good()) << serErr.get_message(); + const GraphWrapper graphWrapper(serializedGraph.data(), serializedGraph.size()); + + LayernormBpropTensorBundle tensorBundle( + graphWrapper.getNodeWrapper(0), graphWrapper.getTensorMap(), seed); + + auto variantPack = tensorBundle.toHostVariantPack(); + + CpuReferenceGraphExecutor().execute( + serializedGraph.data(), serializedGraph.size(), variantPack); + } + static void runRMSNormTest(hipdnn_flatbuffers_sdk::data_objects::DataType inputDataType, hipdnn_flatbuffers_sdk::data_objects::DataType scaleDataType, hipdnn_flatbuffers_sdk::data_objects::DataType computeDataType) @@ -630,6 +663,22 @@ TEST(TestCpuReferenceGraphExecutor, LayernormAllBFloat16) DataType::BFLOAT16, DataType::BFLOAT16, DataType::BFLOAT16, DataType::BFLOAT16); } +TEST(TestCpuReferenceGraphExecutor, LayernormBackwardAllFloats) +{ + TestCpuReferenceGraphExecutor::runLayernormBackwardTest( + DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT); +} +TEST(TestCpuReferenceGraphExecutor, LayernormBackwardAllHalfs) +{ + TestCpuReferenceGraphExecutor::runLayernormBackwardTest( + DataType::HALF, DataType::HALF, DataType::HALF, DataType::HALF); +} +TEST(TestCpuReferenceGraphExecutor, LayernormBackwardAllBFloat16) +{ + TestCpuReferenceGraphExecutor::runLayernormBackwardTest( + DataType::BFLOAT16, DataType::BFLOAT16, DataType::BFLOAT16, DataType::BFLOAT16); +} + TEST(TestCpuReferenceGraphExecutor, RMSNormAllFloats) { TestCpuReferenceGraphExecutor::runRMSNormTest( diff --git a/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestLayernormBpropPlan.cpp b/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestLayernormBpropPlan.cpp index 4b26723ef7bb..78920ebe5f52 100644 --- a/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestLayernormBpropPlan.cpp +++ b/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestLayernormBpropPlan.cpp @@ -400,6 +400,87 @@ TEST_F(TestLayernormBpropPlan, ExecutePlanTrainingPhase) } } +TEST_F(TestLayernormBpropPlan, ExecutePlanGetOutputTensorIds) +{ + const std::vector dims = {6, 3, 32, 32}; + const int64_t normalizedDimCount = 3; + const unsigned int seed = getGlobalTestSeed(); + auto graph = buildLayernormBpropGraph(DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + dims, + normalizedDimCount, + TensorLayout::NHWC, + true); + auto [serializedGraph, serErr] = graph->to_binary(); + ASSERT_TRUE(serErr.is_good()) << serErr.get_message(); + const GraphWrapper graphWrapper(serializedGraph.data(), serializedGraph.size()); + const INodeWrapper& node = graphWrapper.getNodeWrapper(0); + LayernormBpropTensorBundle planTensorBundle(node, graphWrapper.getTensorMap(), seed); + LayernormBpropTensorBundle directTensorBundle(node, graphWrapper.getTensorMap(), seed); + + const auto& attributes + = node.attributesAs(); + const auto& tensorMap = graphWrapper.getTensorMap(); + + EXPECT_TRUE(attributes.mean_tensor_uid().has_value()); + EXPECT_TRUE(attributes.inv_variance_tensor_uid().has_value()); + + LayernormBpropParams params(*tensorMap.at(attributes.dy_tensor_uid()), + *tensorMap.at(attributes.x_tensor_uid()), + *tensorMap.at(attributes.scale_tensor_uid()), + *tensorMap.at(attributes.dx_tensor_uid()), + *tensorMap.at(attributes.dscale_tensor_uid()), + *tensorMap.at(attributes.dbias_tensor_uid()), + normalizedDimCount, + tensorMap.at(attributes.mean_tensor_uid().value()), + tensorMap.at(attributes.inv_variance_tensor_uid().value()), + attributes.epsilon_tensor_uid().has_value() + ? tensorMap.at(attributes.epsilon_tensor_uid().value()) + : nullptr); + + const std::unordered_map variantPack = planTensorBundle.toHostVariantPack(); + + auto shallowDyTensor = createShallowTensor( + params.dyTensor, directTensorBundle.getTensor(attributes.dy_tensor_uid()).rawHostData()); + auto shallowXTensor = createShallowTensor( + params.xTensor, directTensorBundle.getTensor(attributes.x_tensor_uid()).rawHostData()); + auto shallowScaleTensor = createShallowTensor( + params.scaleTensor, + directTensorBundle.getTensor(attributes.scale_tensor_uid()).rawHostData()); + auto shallowMeanTensor = createShallowTensor( + params.meanTensor.value(), + directTensorBundle.getTensor(attributes.mean_tensor_uid().value()).rawHostData()); + auto shallowInvVarianceTensor = createShallowTensor( + params.invVarianceTensor.value(), + directTensorBundle.getTensor(attributes.inv_variance_tensor_uid().value()).rawHostData()); + auto shallowDxTensor = createShallowTensor( + params.dxTensor, directTensorBundle.getTensor(attributes.dx_tensor_uid()).rawHostData()); + auto shallowDscaleTensor = createShallowTensor( + params.dscaleTensor, + directTensorBundle.getTensor(attributes.dscale_tensor_uid()).rawHostData()); + auto shallowDbiasTensor = createShallowTensor( + params.dbiasTensor, + directTensorBundle.getTensor(attributes.dbias_tensor_uid()).rawHostData()); + + CpuFpReferenceLayernorm::bprop(*shallowDyTensor, + *shallowXTensor, + *shallowScaleTensor, + *shallowDxTensor, + *shallowDscaleTensor, + *shallowDbiasTensor, + hipdnn_data_sdk::utilities::LAYERNORM_DEFAULT_EPSILON, + shallowMeanTensor.get(), + shallowInvVarianceTensor.get(), + normalizedDimCount); + + const std::vector expectedIds + = {params.dxTensor.uid, params.dscaleTensor.uid, params.dbiasTensor.uid}; + const LayernormBpropPlan bpropPlan(std::move(params)); + EXPECT_EQ(bpropPlan.getOutputTensorIds(), expectedIds); +} + TEST(TestLayernormBpropPlanBuilder, PlanConstruction) { const std::vector dims = {1, 1, 1, 1}; @@ -537,3 +618,61 @@ TEST(TestLayernormBpropPlanBuilder, IsApplicableTrainingPhase) EXPECT_FALSE( badMeanTypePlanBuilder.isApplicable(graphWrapper.getNode(0), graphWrapper.getTensorMap())); } + +TEST(TestLayernormBpropPlanBuilder, IsApplicableInvalidComputeData) +{ + const std::vector dims = {1, 1, 1, 1}; + const int64_t normalizedDimCount = 3; + auto graph = buildLayernormBpropGraph(DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + dims, + normalizedDimCount, + TensorLayout::NHWC, + true); + auto [serializedGraph, serErr] = graph->to_binary(); + ASSERT_TRUE(serErr.is_good()) << serErr.get_message(); + const GraphWrapper graphWrapper(serializedGraph.data(), serializedGraph.size()); + + const LayernormBpropPlanBuilder + floatPlanBuilder; + + EXPECT_FALSE( + floatPlanBuilder.isApplicable(graphWrapper.getNode(0), graphWrapper.getTensorMap())); +} + +TEST(TestLayernormBpropPlanBuilder, IsApplicableAndBuildNodePlanInvalidAttributes) +{ + const std::vector dims = {1, 1, 1, 1}; + const int64_t normalizedDimCount = 3; + // Fprop instead of bprop to get wrong attributes + auto graph = buildLayernormFpropGraph(DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + dims, + normalizedDimCount, + TensorLayout::NHWC, + true); + auto [serializedGraph, serErr] = graph->to_binary(); + ASSERT_TRUE(serErr.is_good()) << serErr.get_message(); + const GraphWrapper graphWrapper(serializedGraph.data(), serializedGraph.size()); + + const LayernormBpropPlanBuilder + floatPlanBuilder; + + EXPECT_FALSE( + floatPlanBuilder.isApplicable(graphWrapper.getNode(0), graphWrapper.getTensorMap())); + + EXPECT_THROW(floatPlanBuilder.buildNodePlan(graphWrapper, graphWrapper.getNode(0)), + std::runtime_error); +} diff --git a/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestLayernormBpropSignatureKey.cpp b/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestLayernormBpropSignatureKey.cpp index 63d6b4a3ab48..ed4d30355b49 100644 --- a/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestLayernormBpropSignatureKey.cpp +++ b/projects/hipdnn/test_sdk/tests/utilities/cpu_graph_executor/TestLayernormBpropSignatureKey.cpp @@ -119,3 +119,146 @@ TEST(TestLayernormBpropSignatureKey, CreateFromNodeAndTensorMap) EXPECT_TRUE(keyFromNode == expectedKey); } + +TEST(TestLayernormBpropSignatureKey, CreateFromNodeAndTensorMapWithOptionals) +{ + const LayernormBpropSignatureKey expectedKey{ + DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, DataType::FLOAT}; + const std::vector dims = {1, 1, 1, 1}; + const int64_t normalizedDimCount = 3; + auto graph = buildLayernormBpropGraph(DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + dims, + normalizedDimCount, + hipdnn_data_sdk::utilities::TensorLayout::NHWC, + true); + auto [serializedGraph, serErr] = graph->to_binary(); + ASSERT_TRUE(serErr.is_good()) << serErr.get_message(); + auto graphWrap = hipdnn_flatbuffers_sdk::flatbuffer_utilities::GraphWrapper( + serializedGraph.data(), serializedGraph.size()); + + const LayernormBpropSignatureKey keyFromNode(graphWrap.getNode(0), graphWrap.getTensorMap()); + + EXPECT_TRUE(keyFromNode == expectedKey); +} + +TEST(TestLayernormBpropSignatureKey, ThrowForInvalidNode) +{ + const std::vector dims = {1, 1, 1, 1}; + const int64_t normalizedDimCount = 3; + // Fprop instead of Bprop to trigger an exception + auto graph = buildLayernormFpropGraph(DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + dims, + normalizedDimCount, + hipdnn_data_sdk::utilities::TensorLayout::NHWC); + auto [serializedGraph, serErr] = graph->to_binary(); + ASSERT_TRUE(serErr.is_good()) << serErr.get_message(); + auto graphWrap = hipdnn_flatbuffers_sdk::flatbuffer_utilities::GraphWrapper( + serializedGraph.data(), serializedGraph.size()); + + EXPECT_THROW(LayernormBpropSignatureKey(graphWrap.getNode(0), graphWrap.getTensorMap()), + std::runtime_error); +} + +TEST(TestLayernormBpropSignatureKey, ThrowForMissingDy) +{ + const std::vector dims = {1, 1, 1, 1}; + const int64_t normalizedDimCount = 3; + auto graph = buildLayernormBpropGraph(DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + dims, + normalizedDimCount, + hipdnn_data_sdk::utilities::TensorLayout::NHWC); + auto [serializedGraph, serErr] = graph->to_binary(); + ASSERT_TRUE(serErr.is_good()) << serErr.get_message(); + auto graphWrap = hipdnn_flatbuffers_sdk::flatbuffer_utilities::GraphWrapper( + serializedGraph.data(), serializedGraph.size()); + auto* nodeAttributes + = const_cast( + graphWrap.getNode(0).attributes_as_LayernormBackwardAttributes()); + ASSERT_TRUE(nodeAttributes != nullptr); + auto& tensorMap = const_cast< + std::unordered_map&>( + graphWrap.getTensorMap()); + tensorMap[nodeAttributes->dy_tensor_uid()] = nullptr; + + ASSERT_THROW(LayernormBpropSignatureKey(graphWrap.getNode(0), graphWrap.getTensorMap()), + std::runtime_error); +} + +TEST(TestLayernormBpropSignatureKey, ThrowForMissingX) +{ + const std::vector dims = {1, 1, 1, 1}; + const int64_t normalizedDimCount = 3; + auto graph = buildLayernormBpropGraph(DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + dims, + normalizedDimCount, + hipdnn_data_sdk::utilities::TensorLayout::NHWC); + auto [serializedGraph, serErr] = graph->to_binary(); + ASSERT_TRUE(serErr.is_good()) << serErr.get_message(); + auto graphWrap = hipdnn_flatbuffers_sdk::flatbuffer_utilities::GraphWrapper( + serializedGraph.data(), serializedGraph.size()); + auto* nodeAttributes + = const_cast( + graphWrap.getNode(0).attributes_as_LayernormBackwardAttributes()); + ASSERT_TRUE(nodeAttributes != nullptr); + auto& tensorMap = const_cast< + std::unordered_map&>( + graphWrap.getTensorMap()); + tensorMap[nodeAttributes->x_tensor_uid()] = nullptr; + + ASSERT_THROW(LayernormBpropSignatureKey(graphWrap.getNode(0), graphWrap.getTensorMap()), + std::runtime_error); +} + +TEST(TestLayernormBpropSignatureKey, ThrowForMissingDx) +{ + const std::vector dims = {1, 1, 1, 1}; + const int64_t normalizedDimCount = 3; + auto graph = buildLayernormBpropGraph(DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + DataType::FLOAT, + dims, + normalizedDimCount, + hipdnn_data_sdk::utilities::TensorLayout::NHWC); + auto [serializedGraph, serErr] = graph->to_binary(); + ASSERT_TRUE(serErr.is_good()) << serErr.get_message(); + auto graphWrap = hipdnn_flatbuffers_sdk::flatbuffer_utilities::GraphWrapper( + serializedGraph.data(), serializedGraph.size()); + auto* nodeAttributes + = const_cast( + graphWrap.getNode(0).attributes_as_LayernormBackwardAttributes()); + ASSERT_TRUE(nodeAttributes != nullptr); + auto& tensorMap = const_cast< + std::unordered_map&>( + graphWrap.getTensorMap()); + tensorMap[nodeAttributes->dx_tensor_uid()] = nullptr; + + ASSERT_THROW(LayernormBpropSignatureKey(graphWrap.getNode(0), graphWrap.getTensorMap()), + std::runtime_error); +} + +TEST(TestLayernormBpropSignatureKey, StreamOperator) +{ + const LayernormBpropSignatureKey key{ + DataType::FLOAT, DataType::HALF, DataType::BFLOAT16, DataType::DOUBLE, DataType::INT32}; + std::stringstream stream; + stream << key; + ASSERT_EQ(stream.str(), + "Layernorm(dyX=FLOAT, scale=HALF, meanInvVar=BFLOAT16, dxDscaleDbias=DOUBLE, " + "compute=INT32)"); +} From ddb96b76861ed65d0e265b48bc74d9f7a9310a54 Mon Sep 17 00:00:00 2001 From: Brent Maas Date: Fri, 5 Jun 2026 16:25:13 +0000 Subject: [PATCH 5/7] Fix layernorm backward descriptor name --- .../hipdnn/backend/include/HipdnnBackendAttributeName.h | 2 +- .../hipdnn/backend/include/HipdnnBackendDescriptorType.h | 2 +- projects/hipdnn/backend/src/BackendEnumStringUtils.hpp | 2 +- .../hipdnn/backend/src/descriptors/DescriptorFactory.cpp | 2 +- .../descriptors/LayernormBackwardOperationDescriptor.cpp | 2 +- .../hipdnn/backend/tests/TestBackendEnumStringUtils.cpp | 6 +++--- .../TestLayernormBackwardOperationDescriptor.cpp | 2 +- .../descriptors/TestLayernormBackwardOperationFromNode.cpp | 2 +- .../hipdnn_frontend/detail/LayernormBackwardPacker.hpp | 3 ++- .../DescriptorGenerator/configs/layernorm_backward.yaml | 2 +- 10 files changed, 13 insertions(+), 12 deletions(-) diff --git a/projects/hipdnn/backend/include/HipdnnBackendAttributeName.h b/projects/hipdnn/backend/include/HipdnnBackendAttributeName.h index 4d79a4506338..5a89c98aabc9 100644 --- a/projects/hipdnn/backend/include/HipdnnBackendAttributeName.h +++ b/projects/hipdnn/backend/include/HipdnnBackendAttributeName.h @@ -1261,7 +1261,7 @@ typedef enum /** * @name Layernorm Backward Operation Attributes (3600-3699) - * Attributes for HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR + * Attributes for HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR_EXT * @{ */ diff --git a/projects/hipdnn/backend/include/HipdnnBackendDescriptorType.h b/projects/hipdnn/backend/include/HipdnnBackendDescriptorType.h index 7b444b8a49da..c0e2195304db 100644 --- a/projects/hipdnn/backend/include/HipdnnBackendDescriptorType.h +++ b/projects/hipdnn/backend/include/HipdnnBackendDescriptorType.h @@ -321,6 +321,6 @@ typedef enum * gradient (DX), scale gradient and mean gradient tensors, a backward * layernorm operator, and a compute data type */ - HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR = 35, + HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR_EXT = 35, } hipdnnBackendDescriptorType_t; diff --git a/projects/hipdnn/backend/src/BackendEnumStringUtils.hpp b/projects/hipdnn/backend/src/BackendEnumStringUtils.hpp index 2e585bbfd931..a3aba4bac63c 100644 --- a/projects/hipdnn/backend/src/BackendEnumStringUtils.hpp +++ b/projects/hipdnn/backend/src/BackendEnumStringUtils.hpp @@ -255,7 +255,7 @@ inline const char* hipdnnGetBackendDescriptorTypeName(hipdnnBackendDescriptorTyp return "HIPDNN_BACKEND_OPERATION_SDPA_FWD_DESCRIPTOR"; case HIPDNN_BACKEND_OPERATION_LAYERNORM_DESCRIPTOR_EXT: return "HIPDNN_BACKEND_OPERATION_LAYERNORM_DESCRIPTOR_EXT"; - case HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR: + case HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR_EXT: return "HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR_EXT"; case HIPDNN_BACKEND_OPERATION_BLOCK_SCALE_QUANTIZE_DESCRIPTOR: return "HIPDNN_BACKEND_OPERATION_BLOCK_SCALE_QUANTIZE_DESCRIPTOR"; diff --git a/projects/hipdnn/backend/src/descriptors/DescriptorFactory.cpp b/projects/hipdnn/backend/src/descriptors/DescriptorFactory.cpp index b5d50aeb510f..b179bb8f24d6 100644 --- a/projects/hipdnn/backend/src/descriptors/DescriptorFactory.cpp +++ b/projects/hipdnn/backend/src/descriptors/DescriptorFactory.cpp @@ -118,7 +118,7 @@ void DescriptorFactory::create(hipdnnBackendDescriptorType_t descriptorType, case HIPDNN_BACKEND_OPERATION_LAYERNORM_DESCRIPTOR_EXT: privateDesc = std::make_shared(); break; - case HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR: + case HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR_EXT: privateDesc = std::make_shared(); break; case HIPDNN_BACKEND_OPERATION_BATCHNORM_DESCRIPTOR_EXT: diff --git a/projects/hipdnn/backend/src/descriptors/LayernormBackwardOperationDescriptor.cpp b/projects/hipdnn/backend/src/descriptors/LayernormBackwardOperationDescriptor.cpp index 17f108c48df4..2d8dcc54b2b4 100644 --- a/projects/hipdnn/backend/src/descriptors/LayernormBackwardOperationDescriptor.cpp +++ b/projects/hipdnn/backend/src/descriptors/LayernormBackwardOperationDescriptor.cpp @@ -324,7 +324,7 @@ std::unique_ptr hipdnnBackendDescriptorType_t LayernormBackwardOperationDescriptor::getStaticType() { - return HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR; + return HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR_EXT; } std::string LayernormBackwardOperationDescriptor::toString() const diff --git a/projects/hipdnn/backend/tests/TestBackendEnumStringUtils.cpp b/projects/hipdnn/backend/tests/TestBackendEnumStringUtils.cpp index 71afb5557a33..f35efd60c92d 100644 --- a/projects/hipdnn/backend/tests/TestBackendEnumStringUtils.cpp +++ b/projects/hipdnn/backend/tests/TestBackendEnumStringUtils.cpp @@ -68,9 +68,9 @@ TEST(TestBackendEnumStringUtils, GetBackendDescriptorTypeName) EXPECT_STREQ( hipdnnGetBackendDescriptorTypeName(HIPDNN_BACKEND_OPERATION_LAYERNORM_DESCRIPTOR_EXT), "HIPDNN_BACKEND_OPERATION_LAYERNORM_DESCRIPTOR_EXT"); - EXPECT_STREQ( - hipdnnGetBackendDescriptorTypeName(HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR), - "HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR_EXT"); + EXPECT_STREQ(hipdnnGetBackendDescriptorTypeName( + HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR_EXT), + "HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR_EXT"); EXPECT_STREQ(hipdnnGetBackendDescriptorTypeName( HIPDNN_BACKEND_OPERATION_BLOCK_SCALE_QUANTIZE_DESCRIPTOR), "HIPDNN_BACKEND_OPERATION_BLOCK_SCALE_QUANTIZE_DESCRIPTOR"); diff --git a/projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationDescriptor.cpp b/projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationDescriptor.cpp index a48fa9eb21f7..89fd190bff6c 100644 --- a/projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationDescriptor.cpp +++ b/projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationDescriptor.cpp @@ -165,7 +165,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, CreateDescriptor) auto desc = getDescriptor(); ASSERT_NE(desc, nullptr); ASSERT_FALSE(desc->isFinalized()); - ASSERT_EQ(desc->getType(), HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR); + ASSERT_EQ(desc->getType(), HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR_EXT); } TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeWithRequiredAttributes) diff --git a/projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationFromNode.cpp b/projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationFromNode.cpp index 57f5af7306f1..edc7f27ba121 100644 --- a/projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationFromNode.cpp +++ b/projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationFromNode.cpp @@ -142,7 +142,7 @@ TEST_F(TestLayernormBackwardOperationFromNode, CreatesValidFinalizedDescriptor) ASSERT_NE(desc, nullptr); ASSERT_TRUE(desc->isFinalized()); - ASSERT_EQ(desc->getType(), HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR); + ASSERT_EQ(desc->getType(), HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR_EXT); EXPECT_EQ(desc->getData().dy_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DY_UID); } diff --git a/projects/hipdnn/frontend/include/hipdnn_frontend/detail/LayernormBackwardPacker.hpp b/projects/hipdnn/frontend/include/hipdnn_frontend/detail/LayernormBackwardPacker.hpp index cc20a124503e..2f2830a6caa1 100644 --- a/projects/hipdnn/frontend/include/hipdnn_frontend/detail/LayernormBackwardPacker.hpp +++ b/projects/hipdnn/frontend/include/hipdnn_frontend/detail/LayernormBackwardPacker.hpp @@ -17,7 +17,8 @@ inline Error createLayernormBackwardOperation( std::vector& operations) { // Create operation descriptor - ScopedHipdnnBackendDescriptor opDesc(HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR); + ScopedHipdnnBackendDescriptor opDesc( + HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR_EXT); if(!opDesc.valid()) { return {ErrorCode::HIPDNN_BACKEND_ERROR, diff --git a/projects/hipdnn/tools/DescriptorGenerator/configs/layernorm_backward.yaml b/projects/hipdnn/tools/DescriptorGenerator/configs/layernorm_backward.yaml index ea27a829c878..ac640dedb88e 100644 --- a/projects/hipdnn/tools/DescriptorGenerator/configs/layernorm_backward.yaml +++ b/projects/hipdnn/tools/DescriptorGenerator/configs/layernorm_backward.yaml @@ -5,7 +5,7 @@ operation: fbs_generated_header: "layernorm_backward_attributes_generated.h" descriptor_type: - enum_name: "HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR" + enum_name: "HIPDNN_BACKEND_OPERATION_LAYERNORM_BACKWARD_DESCRIPTOR_EXT" operation_attr_prefix: "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD" From 53fb6128e49026157522eb67ab976511e6baffad Mon Sep 17 00:00:00 2001 From: Brent Maas Date: Wed, 1 Jul 2026 13:54:59 +0000 Subject: [PATCH 6/7] Various fixes, checks and changes to address review comments --- .../include/HipdnnBackendAttributeName.h | 20 +- .../backend/src/BackendEnumStringUtils.hpp | 42 +- .../LayernormBackwardOperationDescriptor.cpp | 44 +- .../tests/TestBackendEnumStringUtils.cpp | 41 +- .../TestGraphDescriptorLayernormBackward.cpp | 36 +- ...stLayernormBackwardOperationDescriptor.cpp | 462 ++++++++--- ...TestLayernormBackwardOperationFromNode.cpp | 18 +- .../include/hipdnn_data_sdk/types/Int32.hpp | 5 + .../detail/LayernormBackwardPacker.hpp | 38 +- .../detail/LayernormBackwardUnpacker.hpp | 38 +- .../node/LayernormBackwardNode.hpp | 115 ++- .../tests/TestLayernormBackwardNode.cpp | 740 +++++++++++++++++- .../constants/LayernormBackwardConstants.hpp | 20 +- .../detail/LayernormBpropPlan.hpp | 28 +- .../frontend/IntegrationLayernormBackward.cpp | 10 +- ...tionLayernormBackwardDescriptorLifting.cpp | 64 +- ...ionLayernormBackwardDescriptorLowering.cpp | 15 + 17 files changed, 1474 insertions(+), 262 deletions(-) diff --git a/projects/hipdnn/backend/include/HipdnnBackendAttributeName.h b/projects/hipdnn/backend/include/HipdnnBackendAttributeName.h index 5a89c98aabc9..bad4830f5c7e 100644 --- a/projects/hipdnn/backend/include/HipdnnBackendAttributeName.h +++ b/projects/hipdnn/backend/include/HipdnnBackendAttributeName.h @@ -1266,34 +1266,34 @@ typedef enum */ /** @brief Output gradient tensor for backward layernorm */ - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY = 3600, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT = 3600, /** @brief Input tensor for backward layernorm */ - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X = 3601, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT = 3601, /** @brief Scale tensor for backward layernorm */ - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE = 3602, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT = 3602, /** @brief Mean tensor for backward layernorm */ - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN = 3603, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN_EXT = 3603, /** @brief Inverse variance tensor for backward layernorm */ - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE = 3604, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE_EXT = 3604, /** @brief Epsilon tensor for backward layernorm */ - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON = 3605, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT = 3605, /** @brief Input gradient tensor for backward layernorm */ - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX = 3606, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT = 3606, /** @brief Scale gradient tensor for backward layernorm */ - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE = 3607, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT = 3607, /** @brief Bias gradient tensor for backward layernorm */ - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS = 3608, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT = 3608, /** @brief Number of normalized dimensions for backward layernorm */ - HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT = 3609, + HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT_EXT = 3609, /** @brief Compute type for backward layernorm */ HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT = 3610, diff --git a/projects/hipdnn/backend/src/BackendEnumStringUtils.hpp b/projects/hipdnn/backend/src/BackendEnumStringUtils.hpp index a3aba4bac63c..7727a3ce4dac 100644 --- a/projects/hipdnn/backend/src/BackendEnumStringUtils.hpp +++ b/projects/hipdnn/backend/src/BackendEnumStringUtils.hpp @@ -901,26 +901,26 @@ inline const char* hipdnnGetAttributeNameString(hipdnnBackendAttributeName_t att return "HIPDNN_ATTR_RMSNORM_BACKWARD_COMP_TYPE_EXT"; // Layernorm backward operation attributes - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY: - return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY"; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X: - return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X"; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE: - return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE"; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN: - return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN"; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE: - return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE"; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON: - return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON"; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX: - return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX"; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE: - return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE"; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS: - return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS"; - case HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT: - return "HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT"; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT: + return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT"; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT: + return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT"; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT: + return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT"; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN_EXT: + return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN_EXT"; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE_EXT: + return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE_EXT"; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT: + return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT"; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT: + return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT"; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT: + return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT"; + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT: + return "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT"; + case HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT_EXT: + return "HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT_EXT"; case HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT: return "HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT"; @@ -979,6 +979,8 @@ inline const char* hipdnnGetOperationTypeString(hipdnnOperationType_ext_t type) return "HIPDNN_OPERATION_TYPE_CUSTOM_OP_EXT"; case HIPDNN_OPERATION_TYPE_LAYERNORM_EXT: return "HIPDNN_OPERATION_TYPE_LAYERNORM_EXT"; + case HIPDNN_OPERATION_TYPE_LAYERNORM_BACKWARD_EXT: + return "HIPDNN_OPERATION_TYPE_LAYERNORM_BACKWARD_EXT"; case HIPDNN_OPERATION_TYPE_MATMUL_EXT: return "HIPDNN_OPERATION_TYPE_MATMUL_EXT"; case HIPDNN_OPERATION_TYPE_NOT_SET_EXT: diff --git a/projects/hipdnn/backend/src/descriptors/LayernormBackwardOperationDescriptor.cpp b/projects/hipdnn/backend/src/descriptors/LayernormBackwardOperationDescriptor.cpp index 2d8dcc54b2b4..8ca45e4596fa 100644 --- a/projects/hipdnn/backend/src/descriptors/LayernormBackwardOperationDescriptor.cpp +++ b/projects/hipdnn/backend/src/descriptors/LayernormBackwardOperationDescriptor.cpp @@ -31,6 +31,10 @@ void LayernormBackwardOperationDescriptor::finalize() THROW_IF_NULL(_dbiasDesc, HIPDNN_STATUS_BAD_PARAM, "LayernormBackwardOperationDescriptor::finalize() failed: DBIAS tensor not set"); + THROW_IF_TRUE(_meanDesc == nullptr ^ _invVarianceDesc == nullptr, + HIPDNN_STATUS_BAD_PARAM, + "LayernormBackwardDescriptor::finalize() failed: MEAN and INV_VARIANCE should " + "both be set or both not be set"); THROW_IF_TRUE(_computeDataType == hipdnn_flatbuffers_sdk::data_objects::DataType::UNSET, HIPDNN_STATUS_BAD_PARAM, "LayernormBackwardOperationDescriptor::finalize() failed: compute data type not " @@ -55,7 +59,7 @@ void LayernormBackwardOperationDescriptor::setAttribute(hipdnnBackendAttributeNa switch(attributeName) { - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY: + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT: setTensorDescriptor(_dyDesc, _data.dy_tensor_uid, attributeType, @@ -63,7 +67,7 @@ void LayernormBackwardOperationDescriptor::setAttribute(hipdnnBackendAttributeNa arrayOfElements, "LayernormBackwardOperationDescriptor::setAttribute()"); break; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X: + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT: setTensorDescriptor(_xDesc, _data.x_tensor_uid, attributeType, @@ -71,7 +75,7 @@ void LayernormBackwardOperationDescriptor::setAttribute(hipdnnBackendAttributeNa arrayOfElements, "LayernormBackwardOperationDescriptor::setAttribute()"); break; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE: + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT: setTensorDescriptor(_scaleDesc, _data.scale_tensor_uid, attributeType, @@ -79,7 +83,7 @@ void LayernormBackwardOperationDescriptor::setAttribute(hipdnnBackendAttributeNa arrayOfElements, "LayernormBackwardOperationDescriptor::setAttribute()"); break; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN: + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN_EXT: setOptionalTensorDescriptor(_meanDesc, _data.mean_tensor_uid, attributeType, @@ -87,7 +91,7 @@ void LayernormBackwardOperationDescriptor::setAttribute(hipdnnBackendAttributeNa arrayOfElements, "LayernormBackwardOperationDescriptor::setAttribute()"); break; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE: + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE_EXT: setOptionalTensorDescriptor(_invVarianceDesc, _data.inv_variance_tensor_uid, attributeType, @@ -95,7 +99,7 @@ void LayernormBackwardOperationDescriptor::setAttribute(hipdnnBackendAttributeNa arrayOfElements, "LayernormBackwardOperationDescriptor::setAttribute()"); break; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON: + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT: setOptionalTensorDescriptor(_epsilonDesc, _data.epsilon_tensor_uid, attributeType, @@ -103,7 +107,7 @@ void LayernormBackwardOperationDescriptor::setAttribute(hipdnnBackendAttributeNa arrayOfElements, "LayernormBackwardOperationDescriptor::setAttribute()"); break; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX: + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT: setTensorDescriptor(_dxDesc, _data.dx_tensor_uid, attributeType, @@ -111,7 +115,7 @@ void LayernormBackwardOperationDescriptor::setAttribute(hipdnnBackendAttributeNa arrayOfElements, "LayernormBackwardOperationDescriptor::setAttribute()"); break; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE: + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT: setTensorDescriptor(_dscaleDesc, _data.dscale_tensor_uid, attributeType, @@ -119,7 +123,7 @@ void LayernormBackwardOperationDescriptor::setAttribute(hipdnnBackendAttributeNa arrayOfElements, "LayernormBackwardOperationDescriptor::setAttribute()"); break; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS: + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT: setTensorDescriptor(_dbiasDesc, _data.dbias_tensor_uid, attributeType, @@ -127,7 +131,7 @@ void LayernormBackwardOperationDescriptor::setAttribute(hipdnnBackendAttributeNa arrayOfElements, "LayernormBackwardOperationDescriptor::setAttribute()"); break; - case HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT: + case HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT_EXT: setScalar(_data.normalized_dim_count, HIPDNN_TYPE_INT64, attributeType, @@ -173,7 +177,7 @@ void LayernormBackwardOperationDescriptor::getAttribute(hipdnnBackendAttributeNa switch(attributeName) { - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY: + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT: getTensorDescriptor(_dyDesc, attributeType, requestedElementCount, @@ -181,7 +185,7 @@ void LayernormBackwardOperationDescriptor::getAttribute(hipdnnBackendAttributeNa arrayOfElements, "LayernormBackwardOperationDescriptor::getAttribute()"); break; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X: + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT: getTensorDescriptor(_xDesc, attributeType, requestedElementCount, @@ -189,7 +193,7 @@ void LayernormBackwardOperationDescriptor::getAttribute(hipdnnBackendAttributeNa arrayOfElements, "LayernormBackwardOperationDescriptor::getAttribute()"); break; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE: + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT: getTensorDescriptor(_scaleDesc, attributeType, requestedElementCount, @@ -197,7 +201,7 @@ void LayernormBackwardOperationDescriptor::getAttribute(hipdnnBackendAttributeNa arrayOfElements, "LayernormBackwardOperationDescriptor::getAttribute()"); break; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN: + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN_EXT: getOptionalTensorDescriptor(_meanDesc, attributeType, requestedElementCount, @@ -205,7 +209,7 @@ void LayernormBackwardOperationDescriptor::getAttribute(hipdnnBackendAttributeNa arrayOfElements, "LayernormBackwardOperationDescriptor::getAttribute()"); break; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE: + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE_EXT: getOptionalTensorDescriptor(_invVarianceDesc, attributeType, requestedElementCount, @@ -213,7 +217,7 @@ void LayernormBackwardOperationDescriptor::getAttribute(hipdnnBackendAttributeNa arrayOfElements, "LayernormBackwardOperationDescriptor::getAttribute()"); break; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON: + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT: getOptionalTensorDescriptor(_epsilonDesc, attributeType, requestedElementCount, @@ -221,7 +225,7 @@ void LayernormBackwardOperationDescriptor::getAttribute(hipdnnBackendAttributeNa arrayOfElements, "LayernormBackwardOperationDescriptor::getAttribute()"); break; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX: + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT: getTensorDescriptor(_dxDesc, attributeType, requestedElementCount, @@ -229,7 +233,7 @@ void LayernormBackwardOperationDescriptor::getAttribute(hipdnnBackendAttributeNa arrayOfElements, "LayernormBackwardOperationDescriptor::getAttribute()"); break; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE: + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT: getTensorDescriptor(_dscaleDesc, attributeType, requestedElementCount, @@ -237,7 +241,7 @@ void LayernormBackwardOperationDescriptor::getAttribute(hipdnnBackendAttributeNa arrayOfElements, "LayernormBackwardOperationDescriptor::getAttribute()"); break; - case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS: + case HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT: getTensorDescriptor(_dbiasDesc, attributeType, requestedElementCount, @@ -245,7 +249,7 @@ void LayernormBackwardOperationDescriptor::getAttribute(hipdnnBackendAttributeNa arrayOfElements, "LayernormBackwardOperationDescriptor::getAttribute()"); break; - case HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT: + case HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT_EXT: getScalar(_data.normalized_dim_count, HIPDNN_TYPE_INT64, attributeType, diff --git a/projects/hipdnn/backend/tests/TestBackendEnumStringUtils.cpp b/projects/hipdnn/backend/tests/TestBackendEnumStringUtils.cpp index f35efd60c92d..8ad7740cf1b8 100644 --- a/projects/hipdnn/backend/tests/TestBackendEnumStringUtils.cpp +++ b/projects/hipdnn/backend/tests/TestBackendEnumStringUtils.cpp @@ -719,27 +719,28 @@ TEST(TestBackendEnumStringUtils, GetBackendAttributeName) "HIPDNN_ATTR_RMSNORM_BACKWARD_COMP_TYPE_EXT"); // Layernorm backward operation attributes - EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY), - "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY"); - EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X), - "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X"); - EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE), - "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE"); - EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN), - "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN"); + EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT), + "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT"); + EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT), + "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT"); + EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT), + "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT"); + EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN_EXT), + "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN_EXT"); EXPECT_STREQ( - hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE), - "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE"); - EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON), - "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON"); - EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX), - "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX"); - EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE), - "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE"); - EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS), - "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS"); - EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT), - "HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT"); + hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE_EXT), + "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE_EXT"); + EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT), + "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT"); + EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT), + "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT"); + EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT), + "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT"); + EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT), + "HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT"); + EXPECT_STREQ( + hipdnnGetAttributeNameString(HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT_EXT), + "HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT_EXT"); EXPECT_STREQ(hipdnnGetAttributeNameString(HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT), "HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT"); diff --git a/projects/hipdnn/backend/tests/descriptors/TestGraphDescriptorLayernormBackward.cpp b/projects/hipdnn/backend/tests/descriptors/TestGraphDescriptorLayernormBackward.cpp index 518a9692b0ad..f53ea3ccbe06 100644 --- a/projects/hipdnn/backend/tests/descriptors/TestGraphDescriptorLayernormBackward.cpp +++ b/projects/hipdnn/backend/tests/descriptors/TestGraphDescriptorLayernormBackward.cpp @@ -47,39 +47,39 @@ inline std::unique_ptr auto wrapper = createDescriptor(); auto desc = wrapper->asDescriptor(); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, static_cast(&dyDesc)); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, static_cast(&xDesc)); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, static_cast(&scaleDesc)); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, static_cast(&meanDesc)); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, static_cast(&invVarianceDesc)); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, static_cast(&epsilonDesc)); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, static_cast(&dxDesc)); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, static_cast(&dscaleDesc)); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, static_cast(&dbiasDesc)); @@ -414,47 +414,47 @@ TEST_F(TestGraphDescriptorLayernormBackward, LayernormBackwardAttributesPreserve auto opDesc = wrapper->asDescriptor(); HipdnnBackendDescriptor* dyPtr = dyDesc.get(); - opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, static_cast(&dyPtr)); HipdnnBackendDescriptor* xPtr = xDesc.get(); - opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, + opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, static_cast(&xPtr)); HipdnnBackendDescriptor* scalePtr = scaleDesc.get(); - opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, static_cast(&scalePtr)); HipdnnBackendDescriptor* meanPtr = meanDesc.get(); - opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, static_cast(&meanPtr)); HipdnnBackendDescriptor* invVariancePtr = invVarianceDesc.get(); - opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, static_cast(&invVariancePtr)); HipdnnBackendDescriptor* epsilonPtr = epsilonDesc.get(); - opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, static_cast(&epsilonPtr)); HipdnnBackendDescriptor* dxPtr = dxDesc.get(); - opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, + opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, static_cast(&dxPtr)); HipdnnBackendDescriptor* dscalePtr = dscaleDesc.get(); - opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, static_cast(&dscalePtr)); HipdnnBackendDescriptor* dbiasPtr = dbiasDesc.get(); - opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + opDesc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, static_cast(&dbiasPtr)); diff --git a/projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationDescriptor.cpp b/projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationDescriptor.cpp index 89fd190bff6c..87653b9c78db 100644 --- a/projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationDescriptor.cpp +++ b/projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationDescriptor.cpp @@ -37,40 +37,46 @@ class TestLayernormBackwardOperationDescriptor : public ::testing::Test void setTensors() const { auto desc = getDescriptor(); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dyDesc); - desc->setAttribute( - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_xDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_xDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_scaleDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_meanDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_invVarianceDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_epsilonDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dxDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dscaleDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dbiasDesc); + desc->setAttribute(HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT_EXT, + HIPDNN_TYPE_INT64, + 1, + &_normalizedDimCount); } void setLayernormBackwardParams() const @@ -104,6 +110,7 @@ class TestLayernormBackwardOperationDescriptor : public ::testing::Test std::unique_ptr _dxDesc = nullptr; std::unique_ptr _dscaleDesc = nullptr; std::unique_ptr _dbiasDesc = nullptr; + int64_t _normalizedDimCount = -1; std::unique_ptr _unfinalizedTensor = nullptr; void SetUp() override @@ -137,6 +144,7 @@ class TestLayernormBackwardOperationDescriptor : public ::testing::Test _dbiasDesc = createFinalizedTensor(K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID, toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_DIMS), toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_STRIDES)); + _normalizedDimCount = K_LAYERNORMBACKWARD_NORMALIZED_DIM_COUNT; _unfinalizedTensor = createDescriptor(); } @@ -152,6 +160,7 @@ class TestLayernormBackwardOperationDescriptor : public ::testing::Test _dxDesc.reset(); _dscaleDesc.reset(); _dbiasDesc.reset(); + _normalizedDimCount = -1; _unfinalizedTensor.reset(); } }; @@ -179,33 +188,42 @@ TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeFailsWithoutDyTensor) { auto desc = getDescriptor(); desc->setAttribute( - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_xDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_xDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_scaleDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_meanDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_invVarianceDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_epsilonDesc); - desc->setAttribute( - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dxDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dxDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dscaleDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dbiasDesc); + desc->setAttribute(HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT_EXT, + HIPDNN_TYPE_INT64, + 1, + &_normalizedDimCount); + auto computeType = HIPDNN_DATA_FLOAT; + getDescriptor()->setAttribute( + HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, HIPDNN_TYPE_DATA_TYPE, 1, &computeType); setLayernormBackwardParams(); ASSERT_THROW_HIPDNN_STATUS(desc->finalize(), HIPDNN_STATUS_BAD_PARAM); @@ -214,34 +232,45 @@ TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeFailsWithoutDyTensor) TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeFailsWithoutXTensor) { auto desc = getDescriptor(); - desc->setAttribute( - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dyDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dyDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_scaleDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_meanDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_invVarianceDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_epsilonDesc); - desc->setAttribute( - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dxDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dxDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dscaleDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dbiasDesc); + desc->setAttribute(HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT_EXT, + HIPDNN_TYPE_INT64, + 1, + &_normalizedDimCount); + auto computeType = HIPDNN_DATA_FLOAT; + getDescriptor()->setAttribute( + HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, HIPDNN_TYPE_DATA_TYPE, 1, &computeType); setLayernormBackwardParams(); ASSERT_THROW_HIPDNN_STATUS(desc->finalize(), HIPDNN_STATUS_BAD_PARAM); @@ -250,32 +279,43 @@ TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeFailsWithoutXTensor) TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeFailsWithoutScaleTensor) { auto desc = getDescriptor(); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dyDesc); desc->setAttribute( - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dyDesc); - desc->setAttribute( - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_xDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_xDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_meanDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_invVarianceDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_epsilonDesc); - desc->setAttribute( - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dxDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dxDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dscaleDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dbiasDesc); + desc->setAttribute(HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT_EXT, + HIPDNN_TYPE_INT64, + 1, + &_normalizedDimCount); + auto computeType = HIPDNN_DATA_FLOAT; + getDescriptor()->setAttribute( + HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, HIPDNN_TYPE_DATA_TYPE, 1, &computeType); setLayernormBackwardParams(); ASSERT_THROW_HIPDNN_STATUS(desc->finalize(), HIPDNN_STATUS_BAD_PARAM); @@ -284,34 +324,43 @@ TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeFailsWithoutScaleTensor TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeFailsWithoutDxTensor) { auto desc = getDescriptor(); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dyDesc); desc->setAttribute( - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dyDesc); - desc->setAttribute( - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_xDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_xDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_scaleDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_meanDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_invVarianceDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_epsilonDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dscaleDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dbiasDesc); + desc->setAttribute(HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT_EXT, + HIPDNN_TYPE_INT64, + 1, + &_normalizedDimCount); + auto computeType = HIPDNN_DATA_FLOAT; + getDescriptor()->setAttribute( + HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, HIPDNN_TYPE_DATA_TYPE, 1, &computeType); setLayernormBackwardParams(); ASSERT_THROW_HIPDNN_STATUS(desc->finalize(), HIPDNN_STATUS_BAD_PARAM); @@ -320,32 +369,43 @@ TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeFailsWithoutDxTensor) TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeFailsWithoutDscaleTensor) { auto desc = getDescriptor(); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dyDesc); desc->setAttribute( - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dyDesc); - desc->setAttribute( - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_xDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_xDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_scaleDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_meanDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_invVarianceDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_epsilonDesc); - desc->setAttribute( - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dxDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dxDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dbiasDesc); + desc->setAttribute(HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT_EXT, + HIPDNN_TYPE_INT64, + 1, + &_normalizedDimCount); + auto computeType = HIPDNN_DATA_FLOAT; + getDescriptor()->setAttribute( + HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, HIPDNN_TYPE_DATA_TYPE, 1, &computeType); setLayernormBackwardParams(); ASSERT_THROW_HIPDNN_STATUS(desc->finalize(), HIPDNN_STATUS_BAD_PARAM); @@ -354,32 +414,217 @@ TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeFailsWithoutDscaleTenso TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeFailsWithoutDbiasTensor) { auto desc = getDescriptor(); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dyDesc); desc->setAttribute( - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dyDesc); + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_xDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_scaleDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_meanDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_invVarianceDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_epsilonDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dxDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dscaleDesc); + desc->setAttribute(HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT_EXT, + HIPDNN_TYPE_INT64, + 1, + &_normalizedDimCount); + auto computeType = HIPDNN_DATA_FLOAT; + getDescriptor()->setAttribute( + HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, HIPDNN_TYPE_DATA_TYPE, 1, &computeType); + setLayernormBackwardParams(); + + ASSERT_THROW_HIPDNN_STATUS(desc->finalize(), HIPDNN_STATUS_BAD_PARAM); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeFailsWithoutInvVarianceTensor) +{ + auto desc = getDescriptor(); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dyDesc); desc->setAttribute( - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_xDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_xDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_scaleDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_meanDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_epsilonDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dxDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dscaleDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dbiasDesc); + desc->setAttribute(HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT_EXT, + HIPDNN_TYPE_INT64, + 1, + &_normalizedDimCount); + auto computeType = HIPDNN_DATA_FLOAT; + getDescriptor()->setAttribute( + HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, HIPDNN_TYPE_DATA_TYPE, 1, &computeType); + setLayernormBackwardParams(); + + ASSERT_THROW_HIPDNN_STATUS(desc->finalize(), HIPDNN_STATUS_BAD_PARAM); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeFailsWithoutMeanTensor) +{ + auto desc = getDescriptor(); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dyDesc); + desc->setAttribute( + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_xDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_scaleDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_invVarianceDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_epsilonDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dxDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dscaleDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dbiasDesc); + desc->setAttribute(HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT_EXT, + HIPDNN_TYPE_INT64, + 1, + &_normalizedDimCount); + auto computeType = HIPDNN_DATA_FLOAT; + getDescriptor()->setAttribute( + HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, HIPDNN_TYPE_DATA_TYPE, 1, &computeType); + setLayernormBackwardParams(); + + ASSERT_THROW_HIPDNN_STATUS(desc->finalize(), HIPDNN_STATUS_BAD_PARAM); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, + FinalizeSucceedsWithoutMeanTensorAndInvVarianceTensor) +{ + auto desc = getDescriptor(); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dyDesc); + desc->setAttribute( + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_xDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_scaleDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_epsilonDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dxDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dscaleDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dbiasDesc); + desc->setAttribute(HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT_EXT, + HIPDNN_TYPE_INT64, + 1, + &_normalizedDimCount); + auto computeType = HIPDNN_DATA_FLOAT; + getDescriptor()->setAttribute( + HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, HIPDNN_TYPE_DATA_TYPE, 1, &computeType); + setLayernormBackwardParams(); + + ASSERT_NO_THROW(desc->finalize()); +} + +TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeFailsWithoutNormalizedDimCount) +{ + auto desc = getDescriptor(); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dyDesc); desc->setAttribute( - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dxDesc); - desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_xDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_scaleDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_meanDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_invVarianceDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_epsilonDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dxDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dscaleDesc); + desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dbiasDesc); setLayernormBackwardParams(); ASSERT_THROW_HIPDNN_STATUS(desc->finalize(), HIPDNN_STATUS_BAD_PARAM); @@ -399,8 +644,10 @@ TEST_F(TestLayernormBackwardOperationDescriptor, FinalizeFailsWithoutComputeType TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorDy) { auto desc = getDescriptor(); - ASSERT_NO_THROW(desc->setAttribute( - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dyDesc)); + ASSERT_NO_THROW(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dyDesc)); // Verify UID extracted via getData() ASSERT_EQ(desc->getData().dy_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DY_UID); @@ -410,8 +657,10 @@ TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorDy) TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorX) { auto desc = getDescriptor(); - ASSERT_NO_THROW(desc->setAttribute( - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_xDesc)); + ASSERT_NO_THROW(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_xDesc)); ASSERT_EQ(desc->getData().x_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_X_UID); ASSERT_NE(desc->getXDesc(), nullptr); @@ -420,7 +669,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorX) TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorScale) { auto desc = getDescriptor(); - ASSERT_NO_THROW(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + ASSERT_NO_THROW(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_scaleDesc)); @@ -432,7 +681,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorScale) TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorMean) { auto desc = getDescriptor(); - ASSERT_NO_THROW(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + ASSERT_NO_THROW(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_meanDesc)); @@ -444,7 +693,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorMean) TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorInvVariance) { auto desc = getDescriptor(); - ASSERT_NO_THROW(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + ASSERT_NO_THROW(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_invVarianceDesc)); @@ -456,7 +705,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorInvVariance) TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorEpsilon) { auto desc = getDescriptor(); - ASSERT_NO_THROW(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + ASSERT_NO_THROW(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_epsilonDesc)); @@ -468,8 +717,10 @@ TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorEpsilon) TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorDx) { auto desc = getDescriptor(); - ASSERT_NO_THROW(desc->setAttribute( - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dxDesc)); + ASSERT_NO_THROW(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT, + HIPDNN_TYPE_BACKEND_DESCRIPTOR, + 1, + &_dxDesc)); ASSERT_EQ(desc->getData().dx_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DX_UID); ASSERT_NE(desc->getDxDesc(), nullptr); @@ -478,7 +729,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorDx) TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorDscale) { auto desc = getDescriptor(); - ASSERT_NO_THROW(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + ASSERT_NO_THROW(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dscaleDesc)); @@ -490,7 +741,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorDscale) TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorDbias) { auto desc = getDescriptor(); - ASSERT_NO_THROW(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + ASSERT_NO_THROW(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dbiasDesc)); @@ -499,10 +750,21 @@ TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorDescriptorDbias) ASSERT_NE(desc->getDbiasDesc(), nullptr); } +TEST_F(TestLayernormBackwardOperationDescriptor, SetNormalizedDimCount) +{ + auto desc = getDescriptor(); + desc->setAttribute(HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT_EXT, + HIPDNN_TYPE_INT64, + 1, + &K_LAYERNORMBACKWARD_NORMALIZED_DIM_COUNT); + + ASSERT_EQ(desc->getData().normalized_dim_count, K_LAYERNORMBACKWARD_NORMALIZED_DIM_COUNT); +} + TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorFailsNotFinalized) { auto desc = getDescriptor(); - ASSERT_THROW_HIPDNN_STATUS(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + ASSERT_THROW_HIPDNN_STATUS(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_unfinalizedTensor), @@ -514,14 +776,14 @@ TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorFailsWrongType) auto desc = getDescriptor(); ASSERT_THROW_HIPDNN_STATUS( desc->setAttribute( - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, HIPDNN_TYPE_INT64, 1, &_dyDesc), + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, HIPDNN_TYPE_INT64, 1, &_dyDesc), HIPDNN_STATUS_BAD_PARAM); } TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorFailsWrongElementCount) { auto desc = getDescriptor(); - ASSERT_THROW_HIPDNN_STATUS(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + ASSERT_THROW_HIPDNN_STATUS(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 2, &_dyDesc), @@ -531,7 +793,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorFailsWrongElementCount TEST_F(TestLayernormBackwardOperationDescriptor, SetTensorFailsNullPointer) { auto desc = getDescriptor(); - ASSERT_THROW_HIPDNN_STATUS(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + ASSERT_THROW_HIPDNN_STATUS(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, nullptr), @@ -573,7 +835,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, SetAttributeFailsAfterFinalize) makeFinalized(); auto desc = getDescriptor(); - ASSERT_THROW_HIPDNN_STATUS(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + ASSERT_THROW_HIPDNN_STATUS(desc->setAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &_dyDesc), @@ -601,7 +863,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeTensorDescriptor) HipdnnBackendDescriptor* retrievedDy = nullptr; int64_t elementCount = 0; - ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &elementCount, @@ -647,7 +909,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeFailsBeforeFinalize setRequiredAttributes(); HipdnnBackendDescriptor* dummy = nullptr; - ASSERT_THROW_HIPDNN_STATUS(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + ASSERT_THROW_HIPDNN_STATUS(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, nullptr, @@ -660,7 +922,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeFailsNullPointer) makeFinalized(); auto desc = getDescriptor(); - ASSERT_THROW_HIPDNN_STATUS(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + ASSERT_THROW_HIPDNN_STATUS(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, nullptr, @@ -689,7 +951,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeTensorDyQueryReturn auto desc = getDescriptor(); int64_t elementCount = 0; - ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 0, &elementCount, @@ -703,7 +965,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeTensorXQueryReturns auto desc = getDescriptor(); int64_t elementCount = 0; - ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, + ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 0, &elementCount, @@ -717,7 +979,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeTensorScaleQueryRet auto desc = getDescriptor(); int64_t elementCount = 0; - ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 0, &elementCount, @@ -731,7 +993,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeTensorMeanQueryRetu auto desc = getDescriptor(); int64_t elementCount = 0; - ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 0, &elementCount, @@ -745,7 +1007,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeTensorInvVarianceQu auto desc = getDescriptor(); int64_t elementCount = 0; - ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 0, &elementCount, @@ -759,7 +1021,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeTensorEpsilonQueryR auto desc = getDescriptor(); int64_t elementCount = 0; - ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 0, &elementCount, @@ -773,7 +1035,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeTensorDxQueryReturn auto desc = getDescriptor(); int64_t elementCount = 0; - ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, + ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 0, &elementCount, @@ -787,7 +1049,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeTensorDscaleQueryRe auto desc = getDescriptor(); int64_t elementCount = 0; - ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 0, &elementCount, @@ -801,7 +1063,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeTensorDbiasQueryRet auto desc = getDescriptor(); int64_t elementCount = 0; - ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + ASSERT_NO_THROW(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 0, &elementCount, @@ -828,7 +1090,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, GetAttributeTensorQueryFailsNul makeFinalized(); auto desc = getDescriptor(); - ASSERT_THROW_HIPDNN_STATUS(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + ASSERT_THROW_HIPDNN_STATUS(desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 0, nullptr, @@ -855,6 +1117,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, FinalizePreservesTensorReferenc ASSERT_NE(desc->getDxDesc(), nullptr); ASSERT_NE(desc->getDscaleDesc(), nullptr); ASSERT_NE(desc->getDbiasDesc(), nullptr); + ASSERT_NE(desc->getData().normalized_dim_count, -1); // Verify UIDs match ASSERT_EQ(desc->getDyDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DY_UID); @@ -867,6 +1130,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, FinalizePreservesTensorReferenc ASSERT_EQ(desc->getDxDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DX_UID); ASSERT_EQ(desc->getDscaleDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID); ASSERT_EQ(desc->getDbiasDesc()->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID); + ASSERT_EQ(desc->getData().normalized_dim_count, K_LAYERNORMBACKWARD_NORMALIZED_DIM_COUNT); } // ============================================================================= @@ -899,6 +1163,9 @@ TEST_F(TestLayernormBackwardOperationDescriptor, ToStringContainsExpectedInfo) std::string::npos); ASSERT_NE(str.find("dbias_uid=" + std::to_string(K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID)), std::string::npos); + ASSERT_NE(str.find("normalized_dim_count=" + + std::to_string(K_LAYERNORMBACKWARD_NORMALIZED_DIM_COUNT)), + std::string::npos); ASSERT_NE(str.find("compute_data_type="), std::string::npos); } @@ -922,6 +1189,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, GetTensorDescriptorsReturnsAllT ASSERT_EQ(tensors[6]->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DX_UID); ASSERT_EQ(tensors[7]->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID); ASSERT_EQ(tensors[8]->getData().uid, K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID); + ASSERT_EQ(desc->getData().normalized_dim_count, K_LAYERNORMBACKWARD_NORMALIZED_DIM_COUNT); } TEST_F(TestLayernormBackwardOperationDescriptor, BuildNodeProducesCorrectNodeT) @@ -950,6 +1218,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, BuildNodeProducesCorrectNodeT) ASSERT_EQ(attrs->dx_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DX_UID); ASSERT_EQ(attrs->dscale_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID); ASSERT_EQ(attrs->dbias_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID); + ASSERT_EQ(attrs->normalized_dim_count, K_LAYERNORMBACKWARD_NORMALIZED_DIM_COUNT); } TEST_F(TestLayernormBackwardOperationDescriptor, BuildNodeWithHalfComputeType) @@ -985,6 +1254,7 @@ TEST_F(TestLayernormBackwardOperationDescriptor, EXPECT_EQ(tensors[6], desc->getDxDesc()); EXPECT_EQ(tensors[7], desc->getDscaleDesc()); EXPECT_EQ(tensors[8], desc->getDbiasDesc()); + EXPECT_EQ(desc->getData().normalized_dim_count, K_LAYERNORMBACKWARD_NORMALIZED_DIM_COUNT); } TEST_F(TestLayernormBackwardOperationDescriptor, TryAsInterfaceReturnsValidGraphOp) diff --git a/projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationFromNode.cpp b/projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationFromNode.cpp index edc7f27ba121..02a8a4d2c898 100644 --- a/projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationFromNode.cpp +++ b/projects/hipdnn/backend/tests/descriptors/TestLayernormBackwardOperationFromNode.cpp @@ -466,7 +466,7 @@ TEST_F(TestLayernormBackwardOperationFromNode, GetAttributeWorksAfterFromNode) // Verify dy tensor hipdnn_backend::ScopedDescriptor dyScoped; int64_t dyCount = 0; - desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &dyCount, @@ -482,7 +482,7 @@ TEST_F(TestLayernormBackwardOperationFromNode, GetAttributeWorksAfterFromNode) // Verify x tensor hipdnn_backend::ScopedDescriptor xScoped; int64_t xCount = 0; - desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, + desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &xCount, @@ -498,7 +498,7 @@ TEST_F(TestLayernormBackwardOperationFromNode, GetAttributeWorksAfterFromNode) // Verify scale tensor hipdnn_backend::ScopedDescriptor scaleScoped; int64_t scaleCount = 0; - desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &scaleCount, @@ -514,7 +514,7 @@ TEST_F(TestLayernormBackwardOperationFromNode, GetAttributeWorksAfterFromNode) // Verify dx tensor hipdnn_backend::ScopedDescriptor dxScoped; int64_t dxCount = 0; - desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, + desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &dxCount, @@ -530,7 +530,7 @@ TEST_F(TestLayernormBackwardOperationFromNode, GetAttributeWorksAfterFromNode) // Verify dscale tensor hipdnn_backend::ScopedDescriptor dscaleScoped; int64_t dscaleCount = 0; - desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &dscaleCount, @@ -546,7 +546,7 @@ TEST_F(TestLayernormBackwardOperationFromNode, GetAttributeWorksAfterFromNode) // Verify dbias tensor hipdnn_backend::ScopedDescriptor dbiasScoped; int64_t dbiasCount = 0; - desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &dbiasCount, @@ -562,7 +562,7 @@ TEST_F(TestLayernormBackwardOperationFromNode, GetAttributeWorksAfterFromNode) // Verify mean tensor (optional) hipdnn_backend::ScopedDescriptor meanScoped; int64_t meanCount = 0; - desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &meanCount, @@ -578,7 +578,7 @@ TEST_F(TestLayernormBackwardOperationFromNode, GetAttributeWorksAfterFromNode) // Verify inv_variance tensor (optional) hipdnn_backend::ScopedDescriptor invVarianceScoped; int64_t invVarianceCount = 0; - desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &invVarianceCount, @@ -594,7 +594,7 @@ TEST_F(TestLayernormBackwardOperationFromNode, GetAttributeWorksAfterFromNode) // Verify epsilon tensor (optional) hipdnn_backend::ScopedDescriptor epsilonScoped; int64_t epsilonCount = 0; - desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + desc->getAttribute(HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT, HIPDNN_TYPE_BACKEND_DESCRIPTOR, 1, &epsilonCount, diff --git a/projects/hipdnn/data_sdk/include/hipdnn_data_sdk/types/Int32.hpp b/projects/hipdnn/data_sdk/include/hipdnn_data_sdk/types/Int32.hpp index 6760aa45669d..bd2c80e81d16 100644 --- a/projects/hipdnn/data_sdk/include/hipdnn_data_sdk/types/Int32.hpp +++ b/projects/hipdnn/data_sdk/include/hipdnn_data_sdk/types/Int32.hpp @@ -38,4 +38,9 @@ inline int32_t min(int32_t a, int32_t b) return std::min(a, b); } +inline int32_t sqrt(int32_t x) +{ + return static_cast(std::sqrt(x)); +} + } // namespace hipdnn_data_sdk::types diff --git a/projects/hipdnn/frontend/include/hipdnn_frontend/detail/LayernormBackwardPacker.hpp b/projects/hipdnn/frontend/include/hipdnn_frontend/detail/LayernormBackwardPacker.hpp index 2f2830a6caa1..64b308876903 100644 --- a/projects/hipdnn/frontend/include/hipdnn_frontend/detail/LayernormBackwardPacker.hpp +++ b/projects/hipdnn/frontend/include/hipdnn_frontend/detail/LayernormBackwardPacker.hpp @@ -27,59 +27,61 @@ inline Error createLayernormBackwardOperation( // Create tensor descriptors (if needed) and set them on the operation HIPDNN_CHECK_ERROR(ensureAndSetTensorRef(opDesc.get(), - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, attributes.get_dy(), tensorDescs, "layernormbackward DY")); HIPDNN_CHECK_ERROR(ensureAndSetTensorRef(opDesc.get(), - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT, attributes.get_x(), tensorDescs, "layernormbackward X")); HIPDNN_CHECK_ERROR(ensureAndSetTensorRef(opDesc.get(), - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT, attributes.get_scale(), tensorDescs, "layernormbackward SCALE")); - HIPDNN_CHECK_ERROR(ensureAndSetOptionalTensorRef(opDesc.get(), - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, - attributes.get_mean(), - tensorDescs, - "layernormbackward MEAN")); HIPDNN_CHECK_ERROR( ensureAndSetOptionalTensorRef(opDesc.get(), - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN_EXT, + attributes.get_mean(), + tensorDescs, + "layernormbackward MEAN")); + HIPDNN_CHECK_ERROR( + ensureAndSetOptionalTensorRef(opDesc.get(), + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE_EXT, attributes.get_inv_variance(), tensorDescs, "layernormbackward INV_VARIANCE")); HIPDNN_CHECK_ERROR( ensureAndSetOptionalTensorRef(opDesc.get(), - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT, attributes.get_epsilon(), tensorDescs, "layernormbackward EPSILON")); HIPDNN_CHECK_ERROR(ensureAndSetTensorRef(opDesc.get(), - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT, attributes.get_dx(), tensorDescs, "layernormbackward DX")); HIPDNN_CHECK_ERROR(ensureAndSetTensorRef(opDesc.get(), - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT, attributes.get_dscale(), tensorDescs, "layernormbackward DSCALE")); HIPDNN_CHECK_ERROR(ensureAndSetTensorRef(opDesc.get(), - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT, attributes.get_dbias(), tensorDescs, "layernormbackward DBIAS")); // Set layernormbackward parameters - HIPDNN_CHECK_ERROR(setDescriptorAttrScalar(opDesc.get(), - HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT, - HIPDNN_TYPE_INT64, - attributes.get_normalized_dim_count(), - "layernormbackward normalized_dim_count")); + HIPDNN_CHECK_ERROR( + setDescriptorAttrScalar(opDesc.get(), + HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT_EXT, + HIPDNN_TYPE_INT64, + attributes.get_normalized_dim_count(), + "layernormbackward normalized_dim_count")); HIPDNN_CHECK_ERROR(setDescriptorAttrDataType(opDesc.get(), HIPDNN_ATTR_LAYERNORM_BACKWARD_COMP_TYPE_EXT, diff --git a/projects/hipdnn/frontend/include/hipdnn_frontend/detail/LayernormBackwardUnpacker.hpp b/projects/hipdnn/frontend/include/hipdnn_frontend/detail/LayernormBackwardUnpacker.hpp index 2d1f1068899f..8249e02d9b09 100644 --- a/projects/hipdnn/frontend/include/hipdnn_frontend/detail/LayernormBackwardUnpacker.hpp +++ b/projects/hipdnn/frontend/include/hipdnn_frontend/detail/LayernormBackwardUnpacker.hpp @@ -20,7 +20,7 @@ namespace hipdnn_frontend::detail // Unpack dy tensor std::shared_ptr dyTensor; HIPDNN_CHECK_ERROR(unpackAndRegisterTensor(opDesc, - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DY_EXT, tensorMap, dyTensor, "layernormbackward DY tensor")); @@ -29,7 +29,7 @@ namespace hipdnn_frontend::detail // Unpack x tensor std::shared_ptr xTensor; HIPDNN_CHECK_ERROR(unpackAndRegisterTensor(opDesc, - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_X_EXT, tensorMap, xTensor, "layernormbackward X tensor")); @@ -38,7 +38,7 @@ namespace hipdnn_frontend::detail // Unpack scale tensor std::shared_ptr scaleTensor; HIPDNN_CHECK_ERROR(unpackAndRegisterTensor(opDesc, - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_SCALE_EXT, tensorMap, scaleTensor, "layernormbackward SCALE tensor")); @@ -47,7 +47,7 @@ namespace hipdnn_frontend::detail // Unpack mean tensor std::shared_ptr meanTensor; HIPDNN_CHECK_ERROR(unpackOptionalTensor(opDesc, - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_MEAN_EXT, tensorMap, meanTensor, "layernormbackward MEAN tensor")); @@ -58,11 +58,12 @@ namespace hipdnn_frontend::detail // Unpack inv_variance tensor std::shared_ptr invVarianceTensor; - HIPDNN_CHECK_ERROR(unpackOptionalTensor(opDesc, - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE, - tensorMap, - invVarianceTensor, - "layernormbackward INV_VARIANCE tensor")); + HIPDNN_CHECK_ERROR( + unpackOptionalTensor(opDesc, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_INV_VARIANCE_EXT, + tensorMap, + invVarianceTensor, + "layernormbackward INV_VARIANCE tensor")); if(invVarianceTensor) { attributes.set_inv_variance(invVarianceTensor); @@ -71,7 +72,7 @@ namespace hipdnn_frontend::detail // Unpack epsilon tensor std::shared_ptr epsilonTensor; HIPDNN_CHECK_ERROR(unpackOptionalTensor(opDesc, - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_EPSILON_EXT, tensorMap, epsilonTensor, "layernormbackward EPSILON tensor")); @@ -83,7 +84,7 @@ namespace hipdnn_frontend::detail // Unpack dx tensor std::shared_ptr dxTensor; HIPDNN_CHECK_ERROR(unpackAndRegisterTensor(opDesc, - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DX_EXT, tensorMap, dxTensor, "layernormbackward DX tensor")); @@ -92,7 +93,7 @@ namespace hipdnn_frontend::detail // Unpack dscale tensor std::shared_ptr dscaleTensor; HIPDNN_CHECK_ERROR(unpackAndRegisterTensor(opDesc, - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DSCALE_EXT, tensorMap, dscaleTensor, "layernormbackward DSCALE tensor")); @@ -101,7 +102,7 @@ namespace hipdnn_frontend::detail // Unpack dbias tensor std::shared_ptr dbiasTensor; HIPDNN_CHECK_ERROR(unpackAndRegisterTensor(opDesc, - HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS, + HIPDNN_ATTR_OPERATION_LAYERNORM_BACKWARD_DBIAS_EXT, tensorMap, dbiasTensor, "layernormbackward DBIAS tensor")); @@ -109,11 +110,12 @@ namespace hipdnn_frontend::detail // Unpack normalized_dim_count int64_t normalizedDimCount = 0; - HIPDNN_CHECK_ERROR(getDescriptorAttrScalar(opDesc, - HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT, - HIPDNN_TYPE_INT64, - normalizedDimCount, - "layernormbackward normalized_dim_count")); + HIPDNN_CHECK_ERROR( + getDescriptorAttrScalar(opDesc, + HIPDNN_ATTR_LAYERNORM_BACKWARD_NORMALIZED_DIM_COUNT_EXT, + HIPDNN_TYPE_INT64, + normalizedDimCount, + "layernormbackward normalized_dim_count")); attributes.set_normalized_dim_count(normalizedDimCount); // Unpack compute data type diff --git a/projects/hipdnn/frontend/include/hipdnn_frontend/node/LayernormBackwardNode.hpp b/projects/hipdnn/frontend/include/hipdnn_frontend/node/LayernormBackwardNode.hpp index 27241ead7e34..a2c97e0eb91b 100644 --- a/projects/hipdnn/frontend/include/hipdnn_frontend/node/LayernormBackwardNode.hpp +++ b/projects/hipdnn/frontend/include/hipdnn_frontend/node/LayernormBackwardNode.hpp @@ -49,6 +49,12 @@ class LayernormBackwardNode : public BaseNodeget_dim().empty() + || attributes.get_dy()->get_dim() == attributes.get_x()->get_dim(), + ErrorCode::INVALID_VALUE, + "LayernormBackwardNode requires equal shapes for dy (input) and x (input)"); + HIPDNN_RETURN_IF_FALSE( + attributes.get_dx()->get_dim().empty() + || attributes.get_dx()->get_dim() == attributes.get_x()->get_dim(), + ErrorCode::INVALID_VALUE, + "LayernormBackwardNode requires equal shapes for dx (output) and x (input)"); + HIPDNN_RETURN_IF_FALSE( + attributes.get_mean() == nullptr || attributes.get_inv_variance() == nullptr + || attributes.get_mean()->get_dim() == attributes.get_inv_variance()->get_dim(), + ErrorCode::INVALID_VALUE, + "LayernormBackwardNode requires equal shapes for mean (input) and inv_variance " + "(input)"); + HIPDNN_RETURN_IF_FALSE( + attributes.get_dscale()->get_dim().empty() + || attributes.get_dscale()->get_dim() == attributes.get_scale()->get_dim(), + ErrorCode::INVALID_VALUE, + "LayernormBackwardNode requires equal shapes for dscale (output) and scale (input)"); + HIPDNN_RETURN_IF_FALSE( + attributes.get_dbias()->get_dim().empty() + || attributes.get_dbias()->get_dim() == attributes.get_scale()->get_dim(), + ErrorCode::INVALID_VALUE, + "LayernormBackwardNode requires equal shapes for dbias (output) and scale (input)"); + if(attributes.normalized_dim_count != 0) + { + HIPDNN_RETURN_IF_FALSE( + attributes.get_normalized_dim_count() >= 0, + ErrorCode::INVALID_VALUE, + "LayernormBackwardNode requires a positive normalized_dim_count"); + HIPDNN_RETURN_IF_FALSE(attributes.get_normalized_dim_count() <= static_cast( + attributes.get_x()->get_dim().size()), + ErrorCode::INVALID_VALUE, + "LayernormBackwardNode requires that normalized_dim_count is " + "less than or equal to the number of dimensions of x (input)"); + auto batchDimCount = static_cast(attributes.get_x()->get_dim().size()) + - attributes.get_normalized_dim_count(); + auto scaleOnePadded + = attributes.get_scale()->get_dim().size() == attributes.get_x()->get_dim().size(); + if(scaleOnePadded) + { + for(size_t i = 0; i < static_cast(batchDimCount); ++i) + { + HIPDNN_RETURN_IF_FALSE( + attributes.get_scale()->get_dim()[i] == 1, + ErrorCode::INVALID_VALUE, + "LayernormBackwardNode requires that one-padded scale (input) conforms to " + "the specified normalized_dim_count"); + } + } + else + { + HIPDNN_RETURN_IF_FALSE( + static_cast(attributes.get_scale()->get_dim().size()) + == attributes.get_normalized_dim_count(), + ErrorCode::INVALID_VALUE, + "LayernormBackwardNode requires that scale (input) conforms to the specified " + "normalized_dim_count"); + } + if(attributes.get_mean() != nullptr) + { + auto meanOnePadded = attributes.get_mean()->get_dim().size() + == attributes.get_x()->get_dim().size(); + HIPDNN_RETURN_IF_TRUE( + meanOnePadded ^ scaleOnePadded, + ErrorCode::INVALID_VALUE, + "LayernormBackwardNode requires that both mean (input) and scale (input) are " + "one-padded or that both mean (input) and scale (input) are not one-padded"); + if(meanOnePadded) + { + for(auto i = static_cast(batchDimCount); + i < attributes.get_x()->get_dim().size(); + ++i) + { + HIPDNN_RETURN_IF_FALSE( + attributes.get_mean()->get_dim()[i] == 1, + ErrorCode::INVALID_VALUE, + "LayernormBackwardNode requires that one-padded mean (input) conforms " + "to the specified normalized_dim_count"); + } + } + else + { + HIPDNN_RETURN_IF_TRUE( + attributes.get_mean() != nullptr + && static_cast(attributes.get_mean()->get_dim().size()) + != batchDimCount, + ErrorCode::INVALID_VALUE, + "LayernormBackwardNode requires that mean (input) conforms to the " + "specified normalized_dim_count"); + } + } + } + return {}; } @@ -93,7 +196,17 @@ class LayernormBackwardNode : public BaseNodeget_dim().empty()) + { + attributes.get_dy()->set_dim(attributes.get_x()->get_dim()); + } + if(attributes.get_dy()->get_stride().empty()) + { + attributes.get_dy()->set_stride(attributes.get_x()->get_stride()); + } + + // Infer dx shape and strides if not set if(attributes.get_dx()->get_dim().empty()) { attributes.get_dx()->set_dim(attributes.get_x()->get_dim()); diff --git a/projects/hipdnn/frontend/tests/TestLayernormBackwardNode.cpp b/projects/hipdnn/frontend/tests/TestLayernormBackwardNode.cpp index a14367788d74..a95de7565f67 100644 --- a/projects/hipdnn/frontend/tests/TestLayernormBackwardNode.cpp +++ b/projects/hipdnn/frontend/tests/TestLayernormBackwardNode.cpp @@ -114,6 +114,14 @@ TEST(TestLayernormBackwardNode, PreValidateNodeMissingDyTensor) scaleTensor->set_dim({1, 64, 32, 32}); scaleTensor->set_stride({65536, 1024, 32, 1}); attrs.set_scale(scaleTensor); + auto meanTensor = std::make_shared(); + meanTensor->set_dim({16, 1, 1, 1}); + meanTensor->set_stride({1, 1, 1, 1}); + attrs.set_mean(meanTensor); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_dim({16, 1, 1, 1}); + invVarianceTensor->set_stride({1, 1, 1, 1}); + attrs.set_inv_variance(invVarianceTensor); auto dxTensor = std::make_shared(); dxTensor->set_dim({16, 64, 32, 32}); dxTensor->set_stride({65536, 1024, 32, 1}); @@ -126,6 +134,7 @@ TEST(TestLayernormBackwardNode, PreValidateNodeMissingDyTensor) dbiasTensor->set_dim({1, 64, 32, 32}); dbiasTensor->set_stride({65536, 1024, 32, 1}); attrs.set_dbias(dbiasTensor); + attrs.set_normalized_dim_count(3); // dy tensor is missing const GraphAttributes graphAttributes; @@ -148,6 +157,14 @@ TEST(TestLayernormBackwardNode, PreValidateNodeMissingXTensor) scaleTensor->set_dim({1, 64, 32, 32}); scaleTensor->set_stride({65536, 1024, 32, 1}); attrs.set_scale(scaleTensor); + auto meanTensor = std::make_shared(); + meanTensor->set_dim({16, 1, 1, 1}); + meanTensor->set_stride({1, 1, 1, 1}); + attrs.set_mean(meanTensor); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_dim({16, 1, 1, 1}); + invVarianceTensor->set_stride({1, 1, 1, 1}); + attrs.set_inv_variance(invVarianceTensor); auto dxTensor = std::make_shared(); dxTensor->set_dim({16, 64, 32, 32}); dxTensor->set_stride({65536, 1024, 32, 1}); @@ -160,6 +177,7 @@ TEST(TestLayernormBackwardNode, PreValidateNodeMissingXTensor) dbiasTensor->set_dim({1, 64, 32, 32}); dbiasTensor->set_stride({65536, 1024, 32, 1}); attrs.set_dbias(dbiasTensor); + attrs.set_normalized_dim_count(3); // x tensor is missing const GraphAttributes graphAttributes; @@ -182,6 +200,14 @@ TEST(TestLayernormBackwardNode, PreValidateNodeMissingScaleTensor) xTensor->set_dim({16, 64, 32, 32}); xTensor->set_stride({65536, 1024, 32, 1}); attrs.set_x(xTensor); + auto meanTensor = std::make_shared(); + meanTensor->set_dim({16, 1, 1, 1}); + meanTensor->set_stride({1, 1, 1, 1}); + attrs.set_mean(meanTensor); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_dim({16, 1, 1, 1}); + invVarianceTensor->set_stride({1, 1, 1, 1}); + attrs.set_inv_variance(invVarianceTensor); auto dxTensor = std::make_shared(); dxTensor->set_dim({16, 64, 32, 32}); dxTensor->set_stride({65536, 1024, 32, 1}); @@ -194,6 +220,7 @@ TEST(TestLayernormBackwardNode, PreValidateNodeMissingScaleTensor) dbiasTensor->set_dim({1, 64, 32, 32}); dbiasTensor->set_stride({65536, 1024, 32, 1}); attrs.set_dbias(dbiasTensor); + attrs.set_normalized_dim_count(3); // scale tensor is missing const GraphAttributes graphAttributes; @@ -203,6 +230,128 @@ TEST(TestLayernormBackwardNode, PreValidateNodeMissingScaleTensor) EXPECT_EQ(error.code, error_code_t::ATTRIBUTE_NOT_SET); } +TEST(TestLayernormBackwardNode, PreValidateNodeMissingMeanTensor) +{ + LayernormBackwardAttributes attrs; + + // Set all required tensors except mean + auto dyTensor = std::make_shared(); + dyTensor->set_dim({16, 64, 32, 32}); + dyTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dy(dyTensor); + auto xTensor = std::make_shared(); + xTensor->set_dim({16, 64, 32, 32}); + xTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_x(xTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_dim({1, 64, 32, 32}); + scaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_scale(scaleTensor); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_dim({16, 1, 1, 1}); + invVarianceTensor->set_stride({1, 1, 1, 1}); + attrs.set_inv_variance(invVarianceTensor); + auto dxTensor = std::make_shared(); + dxTensor->set_dim({16, 64, 32, 32}); + dxTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dx(dxTensor); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_dim({1, 64, 32, 32}); + dscaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dscale(dscaleTensor); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_dim({1, 64, 32, 32}); + dbiasTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dbias(dbiasTensor); + attrs.set_normalized_dim_count(3); + + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.pre_validate_node(); + EXPECT_EQ(error.code, error_code_t::ATTRIBUTE_NOT_SET); +} + +TEST(TestLayernormBackwardNode, PreValidateNodeMissingInverseVarianceTensor) +{ + LayernormBackwardAttributes attrs; + + // Set all required tensors except inverse variance + auto dyTensor = std::make_shared(); + dyTensor->set_dim({16, 64, 32, 32}); + dyTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dy(dyTensor); + auto xTensor = std::make_shared(); + xTensor->set_dim({16, 64, 32, 32}); + xTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_x(xTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_dim({1, 64, 32, 32}); + scaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_scale(scaleTensor); + auto meanTensor = std::make_shared(); + meanTensor->set_dim({16, 1, 1, 1}); + meanTensor->set_stride({1, 1, 1, 1}); + attrs.set_mean(meanTensor); + auto dxTensor = std::make_shared(); + dxTensor->set_dim({16, 64, 32, 32}); + dxTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dx(dxTensor); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_dim({1, 64, 32, 32}); + dscaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dscale(dscaleTensor); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_dim({1, 64, 32, 32}); + dbiasTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dbias(dbiasTensor); + attrs.set_normalized_dim_count(3); + + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.pre_validate_node(); + EXPECT_EQ(error.code, error_code_t::ATTRIBUTE_NOT_SET); +} + +TEST(TestLayernormBackwardNode, PreValidateNodeMissingMeanAndInverseVarianceTensor) +{ + LayernormBackwardAttributes attrs; + + // Set all required tensors except inverse variance + auto dyTensor = std::make_shared(); + dyTensor->set_dim({16, 64, 32, 32}); + dyTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dy(dyTensor); + auto xTensor = std::make_shared(); + xTensor->set_dim({16, 64, 32, 32}); + xTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_x(xTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_dim({1, 64, 32, 32}); + scaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_scale(scaleTensor); + auto dxTensor = std::make_shared(); + dxTensor->set_dim({16, 64, 32, 32}); + dxTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dx(dxTensor); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_dim({1, 64, 32, 32}); + dscaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dscale(dscaleTensor); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_dim({1, 64, 32, 32}); + dbiasTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dbias(dbiasTensor); + attrs.set_normalized_dim_count(3); + + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.pre_validate_node(); + EXPECT_EQ(error.code, error_code_t::OK); +} + TEST(TestLayernormBackwardNode, PreValidateNodeMissingDxTensor) { LayernormBackwardAttributes attrs; @@ -220,6 +369,14 @@ TEST(TestLayernormBackwardNode, PreValidateNodeMissingDxTensor) scaleTensor->set_dim({1, 64, 32, 32}); scaleTensor->set_stride({65536, 1024, 32, 1}); attrs.set_scale(scaleTensor); + auto meanTensor = std::make_shared(); + meanTensor->set_dim({16, 1, 1, 1}); + meanTensor->set_stride({1, 1, 1, 1}); + attrs.set_mean(meanTensor); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_dim({16, 1, 1, 1}); + invVarianceTensor->set_stride({1, 1, 1, 1}); + attrs.set_inv_variance(invVarianceTensor); auto dscaleTensor = std::make_shared(); dscaleTensor->set_dim({1, 64, 32, 32}); dscaleTensor->set_stride({65536, 1024, 32, 1}); @@ -228,6 +385,7 @@ TEST(TestLayernormBackwardNode, PreValidateNodeMissingDxTensor) dbiasTensor->set_dim({1, 64, 32, 32}); dbiasTensor->set_stride({65536, 1024, 32, 1}); attrs.set_dbias(dbiasTensor); + attrs.set_normalized_dim_count(3); // dx tensor is missing const GraphAttributes graphAttributes; @@ -254,6 +412,14 @@ TEST(TestLayernormBackwardNode, PreValidateNodeMissingDscaleTensor) scaleTensor->set_dim({1, 64, 32, 32}); scaleTensor->set_stride({65536, 1024, 32, 1}); attrs.set_scale(scaleTensor); + auto meanTensor = std::make_shared(); + meanTensor->set_dim({16, 1, 1, 1}); + meanTensor->set_stride({1, 1, 1, 1}); + attrs.set_mean(meanTensor); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_dim({16, 1, 1, 1}); + invVarianceTensor->set_stride({1, 1, 1, 1}); + attrs.set_inv_variance(invVarianceTensor); auto dxTensor = std::make_shared(); dxTensor->set_dim({16, 64, 32, 32}); dxTensor->set_stride({65536, 1024, 32, 1}); @@ -262,6 +428,7 @@ TEST(TestLayernormBackwardNode, PreValidateNodeMissingDscaleTensor) dbiasTensor->set_dim({1, 64, 32, 32}); dbiasTensor->set_stride({65536, 1024, 32, 1}); attrs.set_dbias(dbiasTensor); + attrs.set_normalized_dim_count(3); // dscale tensor is missing const GraphAttributes graphAttributes; @@ -288,6 +455,14 @@ TEST(TestLayernormBackwardNode, PreValidateNodeMissingDbiasTensor) scaleTensor->set_dim({1, 64, 32, 32}); scaleTensor->set_stride({65536, 1024, 32, 1}); attrs.set_scale(scaleTensor); + auto meanTensor = std::make_shared(); + meanTensor->set_dim({16, 1, 1, 1}); + meanTensor->set_stride({1, 1, 1, 1}); + attrs.set_mean(meanTensor); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_dim({16, 1, 1, 1}); + invVarianceTensor->set_stride({1, 1, 1, 1}); + attrs.set_inv_variance(invVarianceTensor); auto dxTensor = std::make_shared(); dxTensor->set_dim({16, 64, 32, 32}); dxTensor->set_stride({65536, 1024, 32, 1}); @@ -296,6 +471,7 @@ TEST(TestLayernormBackwardNode, PreValidateNodeMissingDbiasTensor) dscaleTensor->set_dim({1, 64, 32, 32}); dscaleTensor->set_stride({65536, 1024, 32, 1}); attrs.set_dscale(dscaleTensor); + attrs.set_normalized_dim_count(3); // dbias tensor is missing const GraphAttributes graphAttributes; @@ -316,6 +492,560 @@ TEST(TestLayernormBackwardNode, PreValidateNodeAllValuesSet) EXPECT_EQ(error.code, error_code_t::OK) << error.err_msg; } +// --- PreValidateNode: mismatched tensors --- + +TEST(TestLayernormBackwardNode, PreValidateNodeMismatchingDyTensor) +{ + LayernormBackwardAttributes attrs; + + // Mismatch between dy and x + auto dyTensor = std::make_shared(); + dyTensor->set_dim({32, 64, 32, 32}); + dyTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dy(dyTensor); + auto xTensor = std::make_shared(); + xTensor->set_dim({16, 64, 32, 32}); + xTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_x(xTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_dim({1, 64, 32, 32}); + scaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_scale(scaleTensor); + auto meanTensor = std::make_shared(); + meanTensor->set_dim({16, 1, 1, 1}); + meanTensor->set_stride({1, 1, 1, 1}); + attrs.set_mean(meanTensor); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_dim({16, 1, 1, 1}); + invVarianceTensor->set_stride({1, 1, 1, 1}); + attrs.set_inv_variance(invVarianceTensor); + auto dxTensor = std::make_shared(); + dxTensor->set_dim({16, 64, 32, 32}); + dxTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dx(dxTensor); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_dim({1, 64, 32, 32}); + dscaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dscale(dscaleTensor); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_dim({1, 64, 32, 32}); + dbiasTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dbias(dbiasTensor); + attrs.set_normalized_dim_count(3); + + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.pre_validate_node(); + EXPECT_EQ(error.code, error_code_t::INVALID_VALUE); +} + +TEST(TestLayernormBackwardNode, PreValidateNodeMismatchingDxTensor) +{ + LayernormBackwardAttributes attrs; + + // Mismatch between dx and x + auto dyTensor = std::make_shared(); + dyTensor->set_dim({16, 64, 32, 32}); + dyTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dy(dyTensor); + auto xTensor = std::make_shared(); + xTensor->set_dim({16, 64, 32, 32}); + xTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_x(xTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_dim({1, 64, 32, 32}); + scaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_scale(scaleTensor); + auto meanTensor = std::make_shared(); + meanTensor->set_dim({16, 1, 1, 1}); + meanTensor->set_stride({1, 1, 1, 1}); + attrs.set_mean(meanTensor); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_dim({16, 1, 1, 1}); + invVarianceTensor->set_stride({1, 1, 1, 1}); + attrs.set_inv_variance(invVarianceTensor); + auto dxTensor = std::make_shared(); + dxTensor->set_dim({32, 64, 32, 32}); + dxTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dx(dxTensor); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_dim({1, 64, 32, 32}); + dscaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dscale(dscaleTensor); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_dim({1, 64, 32, 32}); + dbiasTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dbias(dbiasTensor); + attrs.set_normalized_dim_count(3); + + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.pre_validate_node(); + EXPECT_EQ(error.code, error_code_t::INVALID_VALUE); +} + +TEST(TestLayernormBackwardNode, PreValidateNodeMismatchingMeanAndInverseVarianceTensor) +{ + LayernormBackwardAttributes attrs; + + // Mismatch between mean and inverse variance + auto dyTensor = std::make_shared(); + dyTensor->set_dim({16, 64, 32, 32}); + dyTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dy(dyTensor); + auto xTensor = std::make_shared(); + xTensor->set_dim({16, 64, 32, 32}); + xTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_x(xTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_dim({1, 64, 32, 32}); + scaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_scale(scaleTensor); + auto meanTensor = std::make_shared(); + meanTensor->set_dim({16, 1, 1, 1}); + meanTensor->set_stride({1, 1, 1, 1}); + attrs.set_mean(meanTensor); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_dim({32, 1, 1, 1}); + invVarianceTensor->set_stride({1, 1, 1, 1}); + attrs.set_inv_variance(invVarianceTensor); + auto dxTensor = std::make_shared(); + dxTensor->set_dim({16, 64, 32, 32}); + dxTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dx(dxTensor); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_dim({1, 64, 32, 32}); + dscaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dscale(dscaleTensor); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_dim({1, 64, 32, 32}); + dbiasTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dbias(dbiasTensor); + attrs.set_normalized_dim_count(3); + + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.pre_validate_node(); + EXPECT_EQ(error.code, error_code_t::INVALID_VALUE); +} + +TEST(TestLayernormBackwardNode, PreValidateNodeMismatchingDscaleTensor) +{ + LayernormBackwardAttributes attrs; + + // Mismatch between dscale and scale + auto dyTensor = std::make_shared(); + dyTensor->set_dim({16, 64, 32, 32}); + dyTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dy(dyTensor); + auto xTensor = std::make_shared(); + xTensor->set_dim({16, 64, 32, 32}); + xTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_x(xTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_dim({1, 64, 32, 32}); + scaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_scale(scaleTensor); + auto meanTensor = std::make_shared(); + meanTensor->set_dim({16, 1, 1, 1}); + meanTensor->set_stride({1, 1, 1, 1}); + attrs.set_mean(meanTensor); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_dim({16, 1, 1, 1}); + invVarianceTensor->set_stride({1, 1, 1, 1}); + attrs.set_inv_variance(invVarianceTensor); + auto dxTensor = std::make_shared(); + dxTensor->set_dim({16, 64, 32, 32}); + dxTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dx(dxTensor); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_dim({1, 64, 64, 32}); + dscaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dscale(dscaleTensor); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_dim({1, 64, 32, 32}); + dbiasTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dbias(dbiasTensor); + attrs.set_normalized_dim_count(3); + + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.pre_validate_node(); + EXPECT_EQ(error.code, error_code_t::INVALID_VALUE); +} + +TEST(TestLayernormBackwardNode, PreValidateNodeMismatchingDbiasTensor) +{ + LayernormBackwardAttributes attrs; + + // Mismatch between dy and x + auto dyTensor = std::make_shared(); + dyTensor->set_dim({16, 64, 32, 32}); + dyTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dy(dyTensor); + auto xTensor = std::make_shared(); + xTensor->set_dim({16, 64, 32, 32}); + xTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_x(xTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_dim({1, 64, 32, 32}); + scaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_scale(scaleTensor); + auto meanTensor = std::make_shared(); + meanTensor->set_dim({16, 1, 1, 1}); + meanTensor->set_stride({1, 1, 1, 1}); + attrs.set_mean(meanTensor); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_dim({16, 1, 1, 1}); + invVarianceTensor->set_stride({1, 1, 1, 1}); + attrs.set_inv_variance(invVarianceTensor); + auto dxTensor = std::make_shared(); + dxTensor->set_dim({16, 64, 32, 32}); + dxTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dx(dxTensor); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_dim({1, 64, 32, 32}); + dscaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dscale(dscaleTensor); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_dim({1, 64, 64, 32}); + dbiasTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dbias(dbiasTensor); + attrs.set_normalized_dim_count(3); + + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.pre_validate_node(); + EXPECT_EQ(error.code, error_code_t::INVALID_VALUE); +} + +TEST(TestLayernormBackwardNode, PreValidateNodeNegativeNormalizedDimCount) +{ + LayernormBackwardAttributes attrs; + + // Mismatch between dy and x + auto dyTensor = std::make_shared(); + dyTensor->set_dim({16, 64, 32, 32}); + dyTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dy(dyTensor); + auto xTensor = std::make_shared(); + xTensor->set_dim({16, 64, 32, 32}); + xTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_x(xTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_dim({1, 64, 32, 32}); + scaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_scale(scaleTensor); + auto meanTensor = std::make_shared(); + meanTensor->set_dim({16, 1, 1, 1}); + meanTensor->set_stride({1, 1, 1, 1}); + attrs.set_mean(meanTensor); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_dim({16, 1, 1, 1}); + invVarianceTensor->set_stride({1, 1, 1, 1}); + attrs.set_inv_variance(invVarianceTensor); + auto dxTensor = std::make_shared(); + dxTensor->set_dim({16, 64, 32, 32}); + dxTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dx(dxTensor); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_dim({1, 64, 32, 32}); + dscaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dscale(dscaleTensor); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_dim({1, 64, 32, 32}); + dbiasTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dbias(dbiasTensor); + attrs.set_normalized_dim_count(-1); + + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.pre_validate_node(); + EXPECT_EQ(error.code, error_code_t::INVALID_VALUE); +} + +TEST(TestLayernormBackwardNode, PreValidateNodeTooLargeNormalizedDimCount) +{ + LayernormBackwardAttributes attrs; + + // Mismatch between dy and x + auto dyTensor = std::make_shared(); + dyTensor->set_dim({16, 64, 32, 32}); + dyTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dy(dyTensor); + auto xTensor = std::make_shared(); + xTensor->set_dim({16, 64, 32, 32}); + xTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_x(xTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_dim({1, 64, 32, 32}); + scaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_scale(scaleTensor); + auto meanTensor = std::make_shared(); + meanTensor->set_dim({16, 1, 1, 1}); + meanTensor->set_stride({1, 1, 1, 1}); + attrs.set_mean(meanTensor); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_dim({16, 1, 1, 1}); + invVarianceTensor->set_stride({1, 1, 1, 1}); + attrs.set_inv_variance(invVarianceTensor); + auto dxTensor = std::make_shared(); + dxTensor->set_dim({16, 64, 32, 32}); + dxTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dx(dxTensor); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_dim({1, 64, 32, 32}); + dscaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dscale(dscaleTensor); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_dim({1, 64, 32, 32}); + dbiasTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dbias(dbiasTensor); + attrs.set_normalized_dim_count(5); + + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.pre_validate_node(); + EXPECT_EQ(error.code, error_code_t::INVALID_VALUE); +} + +TEST(TestLayernormBackwardNode, PreValidateNodeMismatchingOnePadded) +{ + LayernormBackwardAttributes attrs; + + // Mismatch between dy and x + auto dyTensor = std::make_shared(); + dyTensor->set_dim({16, 64, 32, 32}); + dyTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dy(dyTensor); + auto xTensor = std::make_shared(); + xTensor->set_dim({16, 64, 32, 32}); + xTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_x(xTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_dim({1, 64, 32, 32}); + scaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_scale(scaleTensor); + auto meanTensor = std::make_shared(); + meanTensor->set_dim({16}); + meanTensor->set_stride({1}); + attrs.set_mean(meanTensor); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_dim({16}); + invVarianceTensor->set_stride({1}); + attrs.set_inv_variance(invVarianceTensor); + auto dxTensor = std::make_shared(); + dxTensor->set_dim({16, 64, 32, 32}); + dxTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dx(dxTensor); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_dim({1, 64, 32, 32}); + dscaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dscale(dscaleTensor); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_dim({1, 64, 32, 32}); + dbiasTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dbias(dbiasTensor); + attrs.set_normalized_dim_count(3); + + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.pre_validate_node(); + EXPECT_EQ(error.code, error_code_t::INVALID_VALUE); +} + +TEST(TestLayernormBackwardNode, PreValidateNodeInvalidOnePaddedBatchDims) +{ + LayernormBackwardAttributes attrs; + + // Mismatch between dy and x + auto dyTensor = std::make_shared(); + dyTensor->set_dim({16, 64, 32, 32}); + dyTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dy(dyTensor); + auto xTensor = std::make_shared(); + xTensor->set_dim({16, 64, 32, 32}); + xTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_x(xTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_dim({1, 64, 32, 32}); + scaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_scale(scaleTensor); + auto meanTensor = std::make_shared(); + meanTensor->set_dim({16, 1, 1, 32}); + meanTensor->set_stride({32, 32, 32, 1}); + attrs.set_mean(meanTensor); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_dim({16, 1, 1, 32}); + invVarianceTensor->set_stride({32, 32, 32, 1}); + attrs.set_inv_variance(invVarianceTensor); + auto dxTensor = std::make_shared(); + dxTensor->set_dim({16, 64, 32, 32}); + dxTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dx(dxTensor); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_dim({1, 64, 32, 32}); + dscaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dscale(dscaleTensor); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_dim({1, 64, 32, 32}); + dbiasTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dbias(dbiasTensor); + attrs.set_normalized_dim_count(3); + + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.pre_validate_node(); + EXPECT_EQ(error.code, error_code_t::INVALID_VALUE); +} + +TEST(TestLayernormBackwardNode, PreValidateNodeInvalidOnePaddedNormalizedDims) +{ + LayernormBackwardAttributes attrs; + + // Mismatch between dy and x + auto dyTensor = std::make_shared(); + dyTensor->set_dim({16, 64, 32, 32}); + dyTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dy(dyTensor); + auto xTensor = std::make_shared(); + xTensor->set_dim({16, 64, 32, 32}); + xTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_x(xTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_dim({16, 64, 32, 32}); + scaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_scale(scaleTensor); + auto meanTensor = std::make_shared(); + meanTensor->set_dim({16, 1, 1, 1}); + meanTensor->set_stride({1, 1, 1, 1}); + attrs.set_mean(meanTensor); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_dim({16, 1, 1, 1}); + invVarianceTensor->set_stride({1, 1, 1, 1}); + attrs.set_inv_variance(invVarianceTensor); + auto dxTensor = std::make_shared(); + dxTensor->set_dim({16, 64, 32, 32}); + dxTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dx(dxTensor); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_dim({16, 64, 32, 32}); + dscaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dscale(dscaleTensor); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_dim({16, 64, 32, 32}); + dbiasTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dbias(dbiasTensor); + attrs.set_normalized_dim_count(3); + + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.pre_validate_node(); + EXPECT_EQ(error.code, error_code_t::INVALID_VALUE); +} + +TEST(TestLayernormBackwardNode, PreValidateNodeInvalidOnePaddedBatchDimsLength) +{ + LayernormBackwardAttributes attrs; + + // Mismatch between dy and x + auto dyTensor = std::make_shared(); + dyTensor->set_dim({16, 64, 32, 32}); + dyTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dy(dyTensor); + auto xTensor = std::make_shared(); + xTensor->set_dim({16, 64, 32, 32}); + xTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_x(xTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_dim({1, 64, 32, 32}); + scaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_scale(scaleTensor); + auto meanTensor = std::make_shared(); + meanTensor->set_dim({16, 1, 1}); + meanTensor->set_stride({1, 1, 1}); + attrs.set_mean(meanTensor); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_dim({16, 1, 1}); + invVarianceTensor->set_stride({1, 1, 1}); + attrs.set_inv_variance(invVarianceTensor); + auto dxTensor = std::make_shared(); + dxTensor->set_dim({16, 64, 32, 32}); + dxTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dx(dxTensor); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_dim({1, 64, 32, 32}); + dscaleTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dscale(dscaleTensor); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_dim({1, 64, 32, 32}); + dbiasTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dbias(dbiasTensor); + attrs.set_normalized_dim_count(3); + + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.pre_validate_node(); + EXPECT_EQ(error.code, error_code_t::INVALID_VALUE); +} + +TEST(TestLayernormBackwardNode, PreValidateNodeInvalidOnePaddedNormalizedDimsLength) +{ + LayernormBackwardAttributes attrs; + + // Mismatch between dy and x + auto dyTensor = std::make_shared(); + dyTensor->set_dim({16, 64, 32, 32}); + dyTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dy(dyTensor); + auto xTensor = std::make_shared(); + xTensor->set_dim({16, 64, 32, 32}); + xTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_x(xTensor); + auto scaleTensor = std::make_shared(); + scaleTensor->set_dim({64, 32, 32}); + scaleTensor->set_stride({1024, 32, 1}); + attrs.set_scale(scaleTensor); + auto meanTensor = std::make_shared(); + meanTensor->set_dim({16, 1, 1, 1}); + meanTensor->set_stride({1, 1, 1, 1}); + attrs.set_mean(meanTensor); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_dim({16, 1, 1, 1}); + invVarianceTensor->set_stride({1, 1, 1, 1}); + attrs.set_inv_variance(invVarianceTensor); + auto dxTensor = std::make_shared(); + dxTensor->set_dim({16, 64, 32, 32}); + dxTensor->set_stride({65536, 1024, 32, 1}); + attrs.set_dx(dxTensor); + auto dscaleTensor = std::make_shared(); + dscaleTensor->set_dim({64, 32, 32}); + dscaleTensor->set_stride({1024, 32, 1}); + attrs.set_dscale(dscaleTensor); + auto dbiasTensor = std::make_shared(); + dbiasTensor->set_dim({64, 32, 32}); + dbiasTensor->set_stride({1024, 32, 1}); + attrs.set_dbias(dbiasTensor); + attrs.set_normalized_dim_count(3); + + const GraphAttributes graphAttributes; + const LayernormBackwardNode node(std::move(attrs), graphAttributes); + + auto error = node.pre_validate_node(); + EXPECT_EQ(error.code, error_code_t::INVALID_VALUE); +} + // --- InferPropertiesNode --- TEST(TestLayernormBackwardNode, InferPropertiesNode) @@ -365,6 +1095,12 @@ TEST(TestLayernormBackwardNode, GatherHipdnnTensor) auto scaleTensor = std::make_shared(); scaleTensor->set_uid(12).set_name("ScaleTensor"); attrs.set_scale(scaleTensor); + auto meanTensor = std::make_shared(); + meanTensor->set_uid(13).set_name("MeanTensor"); + attrs.set_mean(meanTensor); + auto invVarianceTensor = std::make_shared(); + invVarianceTensor->set_uid(14).set_name("InvVarianceTensor"); + attrs.set_inv_variance(invVarianceTensor); auto dxTensor = std::make_shared(); dxTensor->set_uid(16).set_name("DxTensor"); attrs.set_dx(dxTensor); @@ -385,8 +1121,10 @@ TEST(TestLayernormBackwardNode, GatherHipdnnTensor) EXPECT_TRUE(allTensors.find(dyTensor) != allTensors.end()); EXPECT_TRUE(allTensors.find(xTensor) != allTensors.end()); EXPECT_TRUE(allTensors.find(scaleTensor) != allTensors.end()); + EXPECT_TRUE(allTensors.find(meanTensor) != allTensors.end()); + EXPECT_TRUE(allTensors.find(invVarianceTensor) != allTensors.end()); EXPECT_TRUE(allTensors.find(dxTensor) != allTensors.end()); EXPECT_TRUE(allTensors.find(dscaleTensor) != allTensors.end()); EXPECT_TRUE(allTensors.find(dbiasTensor) != allTensors.end()); - EXPECT_EQ(allTensors.size(), 6u); + EXPECT_EQ(allTensors.size(), 8u); } diff --git a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/constants/LayernormBackwardConstants.hpp b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/constants/LayernormBackwardConstants.hpp index f8b3ba026d4e..0a8782d46d45 100644 --- a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/constants/LayernormBackwardConstants.hpp +++ b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/constants/LayernormBackwardConstants.hpp @@ -12,40 +12,42 @@ namespace hipdnn_tests::constants // Standard LayernormBackward constants for testing get/set of valid operations. // These represent "any valid layernormbackward" — specific values are not significant. -constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_DY_UID = 10; +constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_DY_UID = 3610; constexpr std::array K_LAYERNORMBACKWARD_TENSOR_DY_DIMS = {16, 64, 32, 32}; constexpr std::array K_LAYERNORMBACKWARD_TENSOR_DY_STRIDES = {65536, 1024, 32, 1}; -constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_X_UID = 11; +constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_X_UID = 3611; constexpr std::array K_LAYERNORMBACKWARD_TENSOR_X_DIMS = {16, 64, 32, 32}; constexpr std::array K_LAYERNORMBACKWARD_TENSOR_X_STRIDES = {65536, 1024, 32, 1}; -constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_SCALE_UID = 12; +constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_SCALE_UID = 3612; constexpr std::array K_LAYERNORMBACKWARD_TENSOR_SCALE_DIMS = {1, 64, 32, 32}; constexpr std::array K_LAYERNORMBACKWARD_TENSOR_SCALE_STRIDES = {65536, 1024, 32, 1}; -constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_MEAN_UID = 13; +constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_MEAN_UID = 3613; constexpr std::array K_LAYERNORMBACKWARD_TENSOR_MEAN_DIMS = {16, 1, 1, 1}; constexpr std::array K_LAYERNORMBACKWARD_TENSOR_MEAN_STRIDES = {1, 1, 1, 1}; -constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID = 14; +constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID = 3614; constexpr std::array K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_DIMS = {16, 1, 1, 1}; constexpr std::array K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_STRIDES = {1, 1, 1, 1}; -constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID = 15; +constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_EPSILON_UID = 3615; constexpr std::array K_LAYERNORMBACKWARD_TENSOR_EPSILON_DIMS = {1}; constexpr std::array K_LAYERNORMBACKWARD_TENSOR_EPSILON_STRIDES = {1}; -constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_DX_UID = 16; +constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_DX_UID = 3616; constexpr std::array K_LAYERNORMBACKWARD_TENSOR_DX_DIMS = {16, 64, 32, 32}; constexpr std::array K_LAYERNORMBACKWARD_TENSOR_DX_STRIDES = {65536, 1024, 32, 1}; -constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID = 17; +constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID = 3617; constexpr std::array K_LAYERNORMBACKWARD_TENSOR_DSCALE_DIMS = {1, 64, 32, 32}; constexpr std::array K_LAYERNORMBACKWARD_TENSOR_DSCALE_STRIDES = {65536, 1024, 32, 1}; -constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID = 18; +constexpr int64_t K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID = 3618; constexpr std::array K_LAYERNORMBACKWARD_TENSOR_DBIAS_DIMS = {1, 64, 32, 32}; constexpr std::array K_LAYERNORMBACKWARD_TENSOR_DBIAS_STRIDES = {65536, 1024, 32, 1}; +constexpr int64_t K_LAYERNORMBACKWARD_NORMALIZED_DIM_COUNT = 3; + } // namespace hipdnn_tests::constants diff --git a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormBpropPlan.hpp b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormBpropPlan.hpp index ebfcaaad71f3..07066317c45c 100644 --- a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormBpropPlan.hpp +++ b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/cpu_graph_executor/detail/LayernormBpropPlan.hpp @@ -126,16 +126,20 @@ class LayernormBpropPlan : public IGraphNodePlanExecutor _params.epsilonTensor.value(), "Epsilon"); } - utilities::CpuFpReferenceLayernorm::bprop(*shallowDyTensor, - *shallowXTensor, - *shallowScaleTensor, - *shallowDxTensor, - *shallowDscaleTensor, - *shallowDbiasTensor, - epsilon, - shallowMeanTensor.get(), - shallowInvVarianceTensor.get(), - _params.normalizedDimCount); + utilities::CpuFpReferenceLayernorm::bprop(*shallowDyTensor, + *shallowXTensor, + *shallowScaleTensor, + *shallowDxTensor, + *shallowDscaleTensor, + *shallowDbiasTensor, + epsilon, + shallowMeanTensor.get(), + shallowInvVarianceTensor.get(), + _params.normalizedDimCount); } private: @@ -185,8 +189,8 @@ class LayernormBpropPlanBuilder : public IGraphNodePlanBuilder CHECK_TENSOR_TYPE(tensorMap, nodeAttributes->x_tensor_uid(), DyDataTypeEnum); CHECK_TENSOR_TYPE(tensorMap, nodeAttributes->scale_tensor_uid(), ScaleBiasDataTypeEnum); CHECK_TENSOR_TYPE(tensorMap, nodeAttributes->dx_tensor_uid(), OutputDataTypeEnum); - CHECK_TENSOR_TYPE(tensorMap, nodeAttributes->dscale_tensor_uid(), OutputDataTypeEnum); - CHECK_TENSOR_TYPE(tensorMap, nodeAttributes->dbias_tensor_uid(), OutputDataTypeEnum); + CHECK_TENSOR_TYPE(tensorMap, nodeAttributes->dscale_tensor_uid(), ScaleBiasDataTypeEnum); + CHECK_TENSOR_TYPE(tensorMap, nodeAttributes->dbias_tensor_uid(), ScaleBiasDataTypeEnum); const bool hasMean = nodeAttributes->mean_tensor_uid().has_value(); const bool hasInvVariance = nodeAttributes->inv_variance_tensor_uid().has_value(); diff --git a/projects/hipdnn/tests/frontend/IntegrationLayernormBackward.cpp b/projects/hipdnn/tests/frontend/IntegrationLayernormBackward.cpp index 673f60ef8cca..18b410c775cf 100644 --- a/projects/hipdnn/tests/frontend/IntegrationLayernormBackward.cpp +++ b/projects/hipdnn/tests/frontend/IntegrationLayernormBackward.cpp @@ -76,7 +76,7 @@ class IntegrationLayernormBackwardFp32 : public ::testing::TestWithParam& dims) : normalizedDims(dims.begin() + 1, dims.end()) - , statsDims(makeStatsDims(dims)) + , statsDims(dims.begin(), dims.begin() + 1) , dyTensor(Tensor(dims)) , xTensor(Tensor(dims)) , scaleTensor(Tensor(normalizedDims)) @@ -109,14 +109,6 @@ class IntegrationLayernormBackwardFp32 : public ::testing::TestWithParam dxTensor; Tensor dscaleTensor; Tensor dbiasTensor; - - private: - static std::vector makeStatsDims(const std::vector& dims) - { - std::vector result(dims.size(), 1); - result[0] = dims[0]; - return result; - } }; struct LayernormTestTensors diff --git a/projects/hipdnn/tests/frontend/IntegrationLayernormBackwardDescriptorLifting.cpp b/projects/hipdnn/tests/frontend/IntegrationLayernormBackwardDescriptorLifting.cpp index a3b82d392720..e4b03f49f24a 100644 --- a/projects/hipdnn/tests/frontend/IntegrationLayernormBackwardDescriptorLifting.cpp +++ b/projects/hipdnn/tests/frontend/IntegrationLayernormBackwardDescriptorLifting.cpp @@ -89,6 +89,8 @@ class IntegrationLayernormBackwardDescriptorLifting : public IntegrationTestFixt .set_stride(toVec(K_LAYERNORMBACKWARD_TENSOR_EPSILON_STRIDES)); attrs.set_epsilon(std::move(epsilon)); + attrs.set_normalized_dim_count(K_LAYERNORMBACKWARD_NORMALIZED_DIM_COUNT); + auto results = graph->layernorm_backward(dy, x, scale, attrs); results[0]->set_uid(K_LAYERNORMBACKWARD_TENSOR_DX_UID).set_output(true).set_name("dx"); results[1] @@ -153,6 +155,27 @@ TEST_F(IntegrationLayernormBackwardDescriptorLifting, BasicLayernormBackwardRoun toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_STRIDES)); EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_SCALE_UID]->get_data_type(), DataType::FLOAT); + // Verify mean tensor + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_MEAN_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_MEAN_UID]->get_uid(), + K_LAYERNORMBACKWARD_TENSOR_MEAN_UID); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_MEAN_UID]->get_dim(), + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_MEAN_UID]->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_STRIDES)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_MEAN_UID]->get_data_type(), DataType::FLOAT); + + // Verify inverse variance tensor + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID]->get_uid(), + K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID]->get_dim(), + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID]->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_STRIDES)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID]->get_data_type(), + DataType::FLOAT); + // Verify dx tensor ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_DX_UID), 0u); EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DX_UID]->get_uid(), @@ -194,6 +217,10 @@ TEST_F(IntegrationLayernormBackwardDescriptorLifting, BasicLayernormBackwardRoun // Verify operation name EXPECT_EQ(opNode->attributes.get_name(), "test_op"); + + // Verify normalized dimension count + EXPECT_EQ(opNode->attributes.get_normalized_dim_count(), + K_LAYERNORMBACKWARD_NORMALIZED_DIM_COUNT); } // After lifting, verifies tensor objects in the node attributes are the same @@ -224,6 +251,15 @@ TEST_F(IntegrationLayernormBackwardDescriptorLifting, LayernormBackwardTensorSha EXPECT_EQ(opNode->attributes.get_scale()->get_uid(), K_LAYERNORMBACKWARD_TENSOR_SCALE_UID); EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_SCALE_UID].get(), opNode->attributes.get_scale().get()); + // Verify mean tensor sharing + EXPECT_EQ(opNode->attributes.get_mean()->get_uid(), K_LAYERNORMBACKWARD_TENSOR_MEAN_UID); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_MEAN_UID].get(), + opNode->attributes.get_mean().get()); + // Verify inverse variance tensor sharing + EXPECT_EQ(opNode->attributes.get_inv_variance()->get_uid(), + K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID].get(), + opNode->attributes.get_inv_variance().get()); // Verify dx tensor sharing EXPECT_EQ(opNode->attributes.get_dx()->get_uid(), K_LAYERNORMBACKWARD_TENSOR_DX_UID); EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DX_UID].get(), @@ -282,6 +318,16 @@ TEST_F(IntegrationLayernormBackwardDescriptorLifting, LayernormBackwardLiftWitho toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_DIMS)); EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_SCALE_UID]->get_stride(), toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_STRIDES)); + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_MEAN_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_MEAN_UID]->get_dim(), + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_MEAN_UID]->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_STRIDES)); + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID]->get_dim(), + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID]->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_STRIDES)); ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_DX_UID), 0u); EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DX_UID]->get_dim(), toVec(K_LAYERNORMBACKWARD_TENSOR_DX_DIMS)); @@ -297,6 +343,10 @@ TEST_F(IntegrationLayernormBackwardDescriptorLifting, LayernormBackwardLiftWitho toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_DIMS)); EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID]->get_stride(), toVec(K_LAYERNORMBACKWARD_TENSOR_DBIAS_STRIDES)); + + // Verify normalized dimension count + EXPECT_EQ(opNode->attributes.get_normalized_dim_count(), + K_LAYERNORMBACKWARD_NORMALIZED_DIM_COUNT); } // Builds a LayernormBackward graph without calling set_uid() on any tensor, @@ -383,13 +433,17 @@ TEST_F(IntegrationLayernormBackwardDescriptorLifting, AutoAssignedUidsPreservedI nodeUids.insert(opNode->attributes.get_x()->get_uid()); ASSERT_NE(opNode->attributes.get_scale(), nullptr); nodeUids.insert(opNode->attributes.get_scale()->get_uid()); + ASSERT_NE(opNode->attributes.get_mean(), nullptr); + nodeUids.insert(opNode->attributes.get_mean()->get_uid()); + ASSERT_NE(opNode->attributes.get_inv_variance(), nullptr); + nodeUids.insert(opNode->attributes.get_inv_variance()->get_uid()); ASSERT_NE(opNode->attributes.get_dx(), nullptr); nodeUids.insert(opNode->attributes.get_dx()->get_uid()); ASSERT_NE(opNode->attributes.get_dscale(), nullptr); nodeUids.insert(opNode->attributes.get_dscale()->get_uid()); ASSERT_NE(opNode->attributes.get_dbias(), nullptr); nodeUids.insert(opNode->attributes.get_dbias()->get_uid()); - ASSERT_EQ(nodeUids.size(), 6u) + ASSERT_EQ(nodeUids.size(), 8u) << "Node tensor UIDs are not all distinct"; // NOLINT(readability-implicit-bool-conversion) // Verify tensor dims survived the round trip @@ -403,6 +457,14 @@ TEST_F(IntegrationLayernormBackwardDescriptorLifting, AutoAssignedUidsPreservedI toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_DIMS)); EXPECT_EQ(opNode->attributes.get_scale()->get_stride(), toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_STRIDES)); + EXPECT_EQ(opNode->attributes.get_mean()->get_dim(), + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_DIMS)); + EXPECT_EQ(opNode->attributes.get_mean()->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_STRIDES)); + EXPECT_EQ(opNode->attributes.get_inv_variance()->get_dim(), + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_DIMS)); + EXPECT_EQ(opNode->attributes.get_inv_variance()->get_stride(), + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_STRIDES)); EXPECT_EQ(opNode->attributes.get_dx()->get_dim(), toVec(K_LAYERNORMBACKWARD_TENSOR_DX_DIMS)); EXPECT_EQ(opNode->attributes.get_dx()->get_stride(), toVec(K_LAYERNORMBACKWARD_TENSOR_DX_STRIDES)); diff --git a/projects/hipdnn/tests/frontend/IntegrationLayernormBackwardDescriptorLowering.cpp b/projects/hipdnn/tests/frontend/IntegrationLayernormBackwardDescriptorLowering.cpp index 79ba1bb24379..74ae23fe8f1e 100644 --- a/projects/hipdnn/tests/frontend/IntegrationLayernormBackwardDescriptorLowering.cpp +++ b/projects/hipdnn/tests/frontend/IntegrationLayernormBackwardDescriptorLowering.cpp @@ -136,6 +136,19 @@ TEST_F(IntegrationLayernormBackwardDescriptorLowering, LayernormBackwardLowering EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_SCALE_UID]->strides, toVec(K_LAYERNORMBACKWARD_TENSOR_SCALE_STRIDES)); EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_SCALE_UID]->data_type, DataTypeSdk::FLOAT); + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_MEAN_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_MEAN_UID]->dims, + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_MEAN_UID]->strides, + toVec(K_LAYERNORMBACKWARD_TENSOR_MEAN_STRIDES)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_MEAN_UID]->data_type, DataTypeSdk::FLOAT); + ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID), 0u); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID]->dims, + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_DIMS)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID]->strides, + toVec(K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_STRIDES)); + EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID]->data_type, + DataTypeSdk::FLOAT); ASSERT_NE(tensorMap.count(K_LAYERNORMBACKWARD_TENSOR_DX_UID), 0u); EXPECT_EQ(tensorMap[K_LAYERNORMBACKWARD_TENSOR_DX_UID]->dims, toVec(K_LAYERNORMBACKWARD_TENSOR_DX_DIMS)); @@ -167,6 +180,8 @@ TEST_F(IntegrationLayernormBackwardDescriptorLowering, LayernormBackwardLowering EXPECT_EQ(opNode->dy_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DY_UID); EXPECT_EQ(opNode->x_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_X_UID); EXPECT_EQ(opNode->scale_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_SCALE_UID); + EXPECT_EQ(opNode->mean_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_MEAN_UID); + EXPECT_EQ(opNode->inv_variance_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_INV_VARIANCE_UID); EXPECT_EQ(opNode->dx_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DX_UID); EXPECT_EQ(opNode->dscale_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DSCALE_UID); EXPECT_EQ(opNode->dbias_tensor_uid, K_LAYERNORMBACKWARD_TENSOR_DBIAS_UID); From 832ec00b1f3b778f5e0e39e14281740f92e6c9ab Mon Sep 17 00:00:00 2001 From: Brent Maas Date: Wed, 1 Jul 2026 17:09:42 +0000 Subject: [PATCH 7/7] A simpler yet more general reimplementation of buildFullIndices with some extra tests --- .../utilities/CpuFpReferenceLayernorm.hpp | 72 ++++---- .../utilities/TestCpuFpReferenceLayernorm.cpp | 166 ++++++++++++++++++ 2 files changed, 196 insertions(+), 42 deletions(-) diff --git a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/CpuFpReferenceLayernorm.hpp b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/CpuFpReferenceLayernorm.hpp index d47edf401d96..a92dc8ad1688 100644 --- a/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/CpuFpReferenceLayernorm.hpp +++ b/projects/hipdnn/test_sdk/include/hipdnn_test_sdk/utilities/CpuFpReferenceLayernorm.hpp @@ -74,29 +74,29 @@ class CpuFpReferenceLayernorm // Split dimensions into batch dims and normalized dims std::vector batchDims; std::vector normalizedDims; - if((scale != nullptr && scale->dims().size() == dims.size()) - || (bias != nullptr && bias->dims().size() == dims.size())) // Pad with ones + if(mean != nullptr) { - batchDims = std::vector(dims.size(), 1); - normalizedDims = std::vector(dims.size(), 1); - for(size_t i = 0; i < dims.size(); ++i) - { - if(static_cast(i) < ndim - normalizedDimCount) - { - batchDims[i] = dims[i]; - } - else - { - normalizedDims[i] = dims[i]; - } - } + batchDims = std::vector(mean->dims().begin(), mean->dims().end()); } - else // Don't pad with ones + else if(rstd != nullptr) { - batchDims - = std::vector(dims.begin(), dims.begin() + (ndim - normalizedDimCount)); - normalizedDims - = std::vector(dims.begin() + (ndim - normalizedDimCount), dims.end()); + batchDims = std::vector(rstd->dims().begin(), rstd->dims().end()); + } + else + { + batchDims = std::vector(dims.begin(), dims.begin() + ndim - normalizedDimCount); + } + if(scale != nullptr) + { + normalizedDims = std::vector(scale->dims().begin(), scale->dims().end()); + } + else if(bias != nullptr) + { + normalizedDims = std::vector(bias->dims().begin(), bias->dims().end()); + } + else + { + normalizedDims = std::vector(dims.begin() + ndim - normalizedDimCount, dims.end()); } for(auto d : normalizedDims) @@ -107,6 +107,14 @@ class CpuFpReferenceLayernorm "Normalized dimensions must all be positive (no zero-size dimensions)."); } } + for(auto d : batchDims) + { + if(d <= 0) + { + throw std::runtime_error( + "Batch dimensions must all be positive (no zero-size dimensions)."); + } + } auto epsilonCompute = static_cast(epsilon); @@ -436,28 +444,8 @@ class CpuFpReferenceLayernorm auto batchDimCount = ndim - normalizedDimCount; std::vector fullIndices; fullIndices.reserve(static_cast(ndim)); - - if(batchIndices.size() == static_cast(ndim) - && normIndices.size() == static_cast(ndim)) // Padded with 1 - { - fullIndices.insert(fullIndices.end(), - batchIndices.begin(), - batchIndices.begin() + ndim - normalizedDimCount); - fullIndices.insert(fullIndices.end(), - normIndices.begin() + ndim - normalizedDimCount, - normIndices.end()); - } - else // Not padded with 1 - { - // If batchDimCount is 0, the batch iteration was over a padded [1] dim, skip it - if(batchDimCount > 0) - { - fullIndices.insert(fullIndices.end(), batchIndices.begin(), batchIndices.end()); - } - - fullIndices.insert(fullIndices.end(), normIndices.begin(), normIndices.end()); - } - + fullIndices.insert(fullIndices.end(), batchIndices.begin(), batchIndices.begin() + batchDimCount); + fullIndices.insert(fullIndices.end(), normIndices.end() - normalizedDimCount, normIndices.end()); return fullIndices; } }; diff --git a/projects/hipdnn/test_sdk/tests/utilities/TestCpuFpReferenceLayernorm.cpp b/projects/hipdnn/test_sdk/tests/utilities/TestCpuFpReferenceLayernorm.cpp index fb05186a000a..1c2eb08ffe36 100644 --- a/projects/hipdnn/test_sdk/tests/utilities/TestCpuFpReferenceLayernorm.cpp +++ b/projects/hipdnn/test_sdk/tests/utilities/TestCpuFpReferenceLayernorm.cpp @@ -1290,3 +1290,169 @@ TEST(TestCpuFpReferenceLayernormFp64, Fprop5DNormalizeLast2) } } } + +// ============================================================================ +// buildFullIndices extra tests +// ============================================================================ + +TEST(TestCpuFpReferenceLayernormFp64, Fprop5DNormalizeLast2OnePadded) +{ + // Shape [2, 2, 3, 4, 5], normalizedDimCount=2: normalize over last 2 dims (4*5=20) + // Batch dims = [2, 2, 3], 12 independent groups of 20 elements each + + Tensor x({2, 2, 3, 4, 5}); + Tensor y({2, 2, 3, 4, 5}); + Tensor scale({1, 1, 1, 4, 5}); + Tensor bias({1, 1, 1, 4, 5}); + Tensor mean({2, 2, 3, 1, 1}); + Tensor rstd({2, 2, 3, 1, 1}); + + x.fillWithRandomValues(-5.0, 5.0, 123); + scale.fillWithValue(1.0); + bias.fillWithValue(0.0); + + CpuFpReferenceLayernorm::fprop(x, &scale, &bias, y, LAYERNORM_DEFAULT_EPSILON, 2, &mean, &rstd); + + auto tolerance = layernorm::getTolerance(); + + // Verify each of the 12 groups has zero-mean, unit-variance output + for(int b0 = 0; b0 < 2; b0++) + { + for(int b1 = 0; b1 < 2; b1++) + { + for(int b2 = 0; b2 < 3; b2++) + { + double outMean = 0.0; + for(int d3 = 0; d3 < 4; d3++) + { + for(int d4 = 0; d4 < 5; d4++) + { + outMean += y.getHostValue(b0, b1, b2, d3, d4); + } + } + outMean /= 20.0; + EXPECT_NEAR(outMean, 0.0, tolerance); + + double outVar = 0.0; + for(int d3 = 0; d3 < 4; d3++) + { + for(int d4 = 0; d4 < 5; d4++) + { + const double diff = y.getHostValue(b0, b1, b2, d3, d4) - outMean; + outVar += diff * diff; + } + } + outVar /= 20.0; + EXPECT_NEAR(outVar, 1.0, tolerance); + } + } + } +} + +TEST(TestCpuFpReferenceLayernormFp64, Fprop5DNormalizeLast2OnePaddedBatchDims) +{ + // Shape [2, 2, 3, 4, 5], normalizedDimCount=2: normalize over last 2 dims (4*5=20) + // Batch dims = [2, 2, 3], 12 independent groups of 20 elements each + + Tensor x({2, 2, 3, 4, 5}); + Tensor y({2, 2, 3, 4, 5}); + Tensor scale({4, 5}); + Tensor bias({4, 5}); + Tensor mean({2, 2, 3, 1, 1}); + Tensor rstd({2, 2, 3, 1, 1}); + + x.fillWithRandomValues(-5.0, 5.0, 123); + scale.fillWithValue(1.0); + bias.fillWithValue(0.0); + + CpuFpReferenceLayernorm::fprop(x, &scale, &bias, y, LAYERNORM_DEFAULT_EPSILON, 2, &mean, &rstd); + + auto tolerance = layernorm::getTolerance(); + + // Verify each of the 12 groups has zero-mean, unit-variance output + for(int b0 = 0; b0 < 2; b0++) + { + for(int b1 = 0; b1 < 2; b1++) + { + for(int b2 = 0; b2 < 3; b2++) + { + double outMean = 0.0; + for(int d3 = 0; d3 < 4; d3++) + { + for(int d4 = 0; d4 < 5; d4++) + { + outMean += y.getHostValue(b0, b1, b2, d3, d4); + } + } + outMean /= 20.0; + EXPECT_NEAR(outMean, 0.0, tolerance); + + double outVar = 0.0; + for(int d3 = 0; d3 < 4; d3++) + { + for(int d4 = 0; d4 < 5; d4++) + { + const double diff = y.getHostValue(b0, b1, b2, d3, d4) - outMean; + outVar += diff * diff; + } + } + outVar /= 20.0; + EXPECT_NEAR(outVar, 1.0, tolerance); + } + } + } +} + +TEST(TestCpuFpReferenceLayernormFp64, Fprop5DNormalizeLast2OnePaddedNormalizedDims) +{ + // Shape [2, 2, 3, 4, 5], normalizedDimCount=2: normalize over last 2 dims (4*5=20) + // Batch dims = [2, 2, 3], 12 independent groups of 20 elements each + + Tensor x({2, 2, 3, 4, 5}); + Tensor y({2, 2, 3, 4, 5}); + Tensor scale({1, 1, 1, 4, 5}); + Tensor bias({1, 1, 1, 4, 5}); + Tensor mean({2, 2, 3}); + Tensor rstd({2, 2, 3}); + + x.fillWithRandomValues(-5.0, 5.0, 123); + scale.fillWithValue(1.0); + bias.fillWithValue(0.0); + + CpuFpReferenceLayernorm::fprop(x, &scale, &bias, y, LAYERNORM_DEFAULT_EPSILON, 2, &mean, &rstd); + + auto tolerance = layernorm::getTolerance(); + + // Verify each of the 12 groups has zero-mean, unit-variance output + for(int b0 = 0; b0 < 2; b0++) + { + for(int b1 = 0; b1 < 2; b1++) + { + for(int b2 = 0; b2 < 3; b2++) + { + double outMean = 0.0; + for(int d3 = 0; d3 < 4; d3++) + { + for(int d4 = 0; d4 < 5; d4++) + { + outMean += y.getHostValue(b0, b1, b2, d3, d4); + } + } + outMean /= 20.0; + EXPECT_NEAR(outMean, 0.0, tolerance); + + double outVar = 0.0; + for(int d3 = 0; d3 < 4; d3++) + { + for(int d4 = 0; d4 < 5; d4++) + { + const double diff = y.getHostValue(b0, b1, b2, d3, d4) - outMean; + outVar += diff * diff; + } + } + outVar /= 20.0; + EXPECT_NEAR(outVar, 1.0, tolerance); + } + } + } +}