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

972 lines
36 KiB
C++

// Copyright © Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#include <gtest/gtest.h>
#include <hipdnn_frontend/Error.hpp>
#include <hipdnn_frontend/attributes/RMSNormAttributes.hpp>
#include <hipdnn_frontend/node/RMSNormNode.hpp>
using namespace hipdnn_frontend;
using namespace hipdnn_frontend::graph;
TEST(TestRMSNormNode, RMSNormNodeProperties)
{
RMSNormAttributes rmsnormAttributes;
rmsnormAttributes.set_x(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_scale(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_epsilon(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_forward_phase(NormFwdPhase::INFERENCE);
auto inputTensor = rmsnormAttributes.get_x();
inputTensor->set_uid(1)
.set_name("InputTensor")
.set_data_type(DataType::FLOAT)
.set_dim({1, 2, 3, 4})
.set_stride({5, 6, 7, 8});
auto outputTensor = rmsnormAttributes.get_y();
outputTensor->set_uid(2).set_name("OutputTensor");
auto scaleTensor = rmsnormAttributes.get_scale();
scaleTensor->set_dim({1, 2, 3, 4});
auto epsilonTensor = rmsnormAttributes.get_epsilon();
epsilonTensor->set_dim({1}).set_value(1e-5f);
const GraphAttributes graphAttributes;
RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.infer_properties_node();
EXPECT_EQ(error.code, ErrorCode::OK);
EXPECT_EQ(outputTensor->get_dim(), (std::vector<int64_t>{1, 2, 3, 4}));
EXPECT_EQ(outputTensor->get_stride(), (std::vector<int64_t>{5, 6, 7, 8}));
}
TEST(TestRMSNormNode, PreValidateNode)
{
RMSNormAttributes rmsnormAttributes;
auto xTensor = std::make_shared<TensorAttributes>();
xTensor->set_dim({2, 64, 32, 32}).set_stride({65536, 1024, 32, 1});
rmsnormAttributes.set_x(xTensor);
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
auto scaleTensor = std::make_shared<TensorAttributes>();
scaleTensor->set_dim({1, 64, 32, 32});
rmsnormAttributes.set_scale(scaleTensor);
auto epsilonTensor = std::make_shared<TensorAttributes>();
epsilonTensor->set_dim({1}).set_value(1e-5f);
rmsnormAttributes.set_epsilon(epsilonTensor);
rmsnormAttributes.set_forward_phase(NormFwdPhase::TRAINING);
const GraphAttributes graphAttributes;
const RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.pre_validate_node();
EXPECT_EQ(error.code, ErrorCode::OK);
}
TEST(TestRMSNormNode, PreValidateNodeMissingValues)
{
RMSNormAttributes rmsnormAttributes;
const GraphAttributes graphAttributes;
const RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.pre_validate_node();
EXPECT_EQ(error.code, ErrorCode::ATTRIBUTE_NOT_SET);
rmsnormAttributes = RMSNormAttributes{};
rmsnormAttributes.set_x(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_forward_phase(NormFwdPhase::TRAINING);
auto rmsnormAttributesCopy = rmsnormAttributes;
const RMSNormNode nodeWithX(std::move(rmsnormAttributesCopy), graphAttributes);
error = nodeWithX.pre_validate_node();
EXPECT_EQ(error.code, ErrorCode::ATTRIBUTE_NOT_SET);
rmsnormAttributes.set_scale(std::make_shared<TensorAttributes>());
rmsnormAttributesCopy = rmsnormAttributes;
const RMSNormNode nodeWithScale(std::move(rmsnormAttributesCopy), graphAttributes);
error = nodeWithScale.pre_validate_node();
EXPECT_EQ(error.code, ErrorCode::ATTRIBUTE_NOT_SET);
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
rmsnormAttributesCopy = rmsnormAttributes;
const RMSNormNode nodeWithY(std::move(rmsnormAttributesCopy), graphAttributes);
error = nodeWithY.pre_validate_node();
EXPECT_EQ(error.code, ErrorCode::ATTRIBUTE_NOT_SET);
// Now set epsilon and proper dimensions to make it pass
auto epsilonTensor = std::make_shared<TensorAttributes>();
epsilonTensor->set_dim({1}).set_value(1e-5f);
rmsnormAttributes.set_epsilon(epsilonTensor);
auto xTensor = rmsnormAttributes.get_x();
xTensor->set_dim({2, 64, 32, 32}).set_stride({65536, 1024, 32, 1});
auto scaleTensor = rmsnormAttributes.get_scale();
scaleTensor->set_dim({1, 64, 32, 32});
rmsnormAttributesCopy = rmsnormAttributes;
const RMSNormNode nodeWithAllValues(std::move(rmsnormAttributesCopy), graphAttributes);
error = nodeWithAllValues.pre_validate_node();
EXPECT_EQ(error.code, ErrorCode::OK);
}
TEST(TestRMSNormNode, PreValidateNodeWithBiasNormAxis1)
{
RMSNormAttributes rmsnormAttributes;
auto xTensor = std::make_shared<TensorAttributes>();
xTensor->set_dim({2, 64, 32, 32}).set_stride({65536, 1024, 32, 1});
rmsnormAttributes.set_x(xTensor);
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
auto scaleTensor = std::make_shared<TensorAttributes>();
scaleTensor->set_dim({1, 64, 32, 32});
rmsnormAttributes.set_scale(scaleTensor);
auto biasTensor = std::make_shared<TensorAttributes>();
biasTensor->set_dim({1, 64, 32, 32});
rmsnormAttributes.set_bias(biasTensor);
auto epsilonTensor = std::make_shared<TensorAttributes>();
epsilonTensor->set_dim({1}).set_value(1e-5f);
rmsnormAttributes.set_epsilon(epsilonTensor);
rmsnormAttributes.set_forward_phase(NormFwdPhase::TRAINING);
const GraphAttributes graphAttributes;
const RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.pre_validate_node();
EXPECT_EQ(error.code, ErrorCode::OK);
}
TEST(TestRMSNormNode, PreValidateNodeWithBiasNormAxis2)
{
RMSNormAttributes rmsnormAttributes;
auto xTensor = std::make_shared<TensorAttributes>();
xTensor->set_dim({2, 64, 32, 32}).set_stride({65536, 1024, 32, 1});
rmsnormAttributes.set_x(xTensor);
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
auto scaleTensor = std::make_shared<TensorAttributes>();
scaleTensor->set_dim({1, 1, 32, 32});
rmsnormAttributes.set_scale(scaleTensor);
auto biasTensor = std::make_shared<TensorAttributes>();
biasTensor->set_dim({1, 1, 32, 32});
rmsnormAttributes.set_bias(biasTensor);
auto epsilonTensor = std::make_shared<TensorAttributes>();
epsilonTensor->set_dim({1}).set_value(1e-5f);
rmsnormAttributes.set_epsilon(epsilonTensor);
rmsnormAttributes.set_forward_phase(NormFwdPhase::TRAINING);
const GraphAttributes graphAttributes;
const RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.pre_validate_node();
EXPECT_EQ(error.code, ErrorCode::OK);
}
TEST(TestRMSNormNode, PreValidateNodeWithBiasNormAxis3)
{
RMSNormAttributes rmsnormAttributes;
auto xTensor = std::make_shared<TensorAttributes>();
xTensor->set_dim({2, 64, 32, 32}).set_stride({65536, 1024, 32, 1});
rmsnormAttributes.set_x(xTensor);
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
auto scaleTensor = std::make_shared<TensorAttributes>();
scaleTensor->set_dim({1, 1, 1, 32});
rmsnormAttributes.set_scale(scaleTensor);
auto biasTensor = std::make_shared<TensorAttributes>();
biasTensor->set_dim({1, 1, 1, 32});
rmsnormAttributes.set_bias(biasTensor);
auto epsilonTensor = std::make_shared<TensorAttributes>();
epsilonTensor->set_dim({1}).set_value(1e-5f);
rmsnormAttributes.set_epsilon(epsilonTensor);
rmsnormAttributes.set_forward_phase(NormFwdPhase::TRAINING);
const GraphAttributes graphAttributes;
const RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.pre_validate_node();
EXPECT_EQ(error.code, ErrorCode::OK);
}
TEST(TestRMSNormNode, PreValidateNodeWithBiasSingleElement)
{
RMSNormAttributes rmsnormAttributes;
auto xTensor = std::make_shared<TensorAttributes>();
xTensor->set_dim({2, 1, 1, 1}).set_stride({1, 1, 1, 1});
rmsnormAttributes.set_x(xTensor);
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
auto scaleTensor = std::make_shared<TensorAttributes>();
scaleTensor->set_dim({1, 1, 1, 1});
rmsnormAttributes.set_scale(scaleTensor);
auto biasTensor = std::make_shared<TensorAttributes>();
biasTensor->set_dim({1, 1, 1, 1});
rmsnormAttributes.set_bias(biasTensor);
auto epsilonTensor = std::make_shared<TensorAttributes>();
epsilonTensor->set_dim({1}).set_value(1e-5f);
rmsnormAttributes.set_epsilon(epsilonTensor);
rmsnormAttributes.set_forward_phase(NormFwdPhase::TRAINING);
const GraphAttributes graphAttributes;
const RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.pre_validate_node();
EXPECT_EQ(error.code, ErrorCode::OK);
}
TEST(TestRMSNormNode, PreValidateNodeWithBiasInvalidSingleElement)
{
RMSNormAttributes rmsnormAttributes;
auto xTensor = std::make_shared<TensorAttributes>();
xTensor->set_dim({2, 64, 32, 32}).set_stride({65536, 1024, 32, 1});
rmsnormAttributes.set_x(xTensor);
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
auto scaleTensor = std::make_shared<TensorAttributes>();
scaleTensor->set_dim({1, 1, 1, 1});
rmsnormAttributes.set_scale(scaleTensor);
auto biasTensor = std::make_shared<TensorAttributes>();
biasTensor->set_dim({1, 1, 1, 1});
rmsnormAttributes.set_bias(biasTensor);
auto epsilonTensor = std::make_shared<TensorAttributes>();
epsilonTensor->set_dim({1}).set_value(1e-5f);
rmsnormAttributes.set_epsilon(epsilonTensor);
rmsnormAttributes.set_forward_phase(NormFwdPhase::TRAINING);
const GraphAttributes graphAttributes;
const RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.pre_validate_node();
EXPECT_EQ(error.code, ErrorCode::INVALID_VALUE);
}
TEST(TestRMSNormNode, PreValidateRejectsMismatchedBiasChannelDimensions)
{
RMSNormAttributes rmsnormAttributes;
auto xTensor = std::make_shared<TensorAttributes>();
xTensor->set_dim({2, 64, 32, 32}).set_stride({65536, 1024, 32, 1});
rmsnormAttributes.set_x(xTensor);
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
auto scaleTensor = std::make_shared<TensorAttributes>();
scaleTensor->set_dim({1, 64, 32, 32});
rmsnormAttributes.set_scale(scaleTensor);
auto biasTensor = std::make_shared<TensorAttributes>();
biasTensor->set_dim({1, 128, 32, 32}); // Mismatched channel dimension
rmsnormAttributes.set_bias(biasTensor);
auto epsilonTensor = std::make_shared<TensorAttributes>();
epsilonTensor->set_dim({1}).set_value(1e-5f);
rmsnormAttributes.set_epsilon(epsilonTensor);
rmsnormAttributes.set_forward_phase(NormFwdPhase::TRAINING);
const GraphAttributes graphAttributes;
const RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.pre_validate_node();
EXPECT_EQ(error.code, ErrorCode::INVALID_VALUE);
EXPECT_TRUE(error.get_message().find("Bias tensor") != std::string::npos);
}
TEST(TestRMSNormNode, InferPropertiesNode)
{
RMSNormAttributes rmsnormAttributes;
rmsnormAttributes.set_x(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_scale(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_epsilon(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_forward_phase(NormFwdPhase::INFERENCE);
auto inputTensor = rmsnormAttributes.get_x();
inputTensor->set_uid(1)
.set_name("InputTensor")
.set_data_type(DataType::FLOAT)
.set_dim({1, 2, 3, 4})
.set_stride({5, 6, 7, 8});
auto outputTensor = rmsnormAttributes.get_y();
outputTensor->set_uid(2).set_name("OutputTensor");
auto scaleTensor = rmsnormAttributes.get_scale();
scaleTensor->set_dim({1, 2, 3, 4});
auto epsilonTensor = rmsnormAttributes.get_epsilon();
epsilonTensor->set_dim({1}).set_value(1e-5f);
const GraphAttributes graphAttributes;
RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.infer_properties_node();
EXPECT_EQ(error.code, ErrorCode::OK);
EXPECT_EQ(outputTensor->get_dim(), (std::vector<int64_t>{1, 2, 3, 4}));
EXPECT_EQ(outputTensor->get_stride(), (std::vector<int64_t>{5, 6, 7, 8}));
}
TEST(TestRMSNormNode, InferPropertiesNodeWithInvRms)
{
RMSNormAttributes rmsnormAttributes;
rmsnormAttributes.set_x(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_scale(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_epsilon(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_inv_rms(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_forward_phase(NormFwdPhase::TRAINING);
auto inputTensor = rmsnormAttributes.get_x();
inputTensor->set_uid(1)
.set_name("InputTensor")
.set_data_type(DataType::FLOAT)
.set_dim({2, 64, 8, 8})
.set_stride({4096, 64, 8, 1});
auto outputTensor = rmsnormAttributes.get_y();
outputTensor->set_uid(2).set_name("OutputTensor");
auto scaleTensor = rmsnormAttributes.get_scale();
scaleTensor->set_dim({1, 64, 1, 1});
auto epsilonTensor = rmsnormAttributes.get_epsilon();
epsilonTensor->set_dim({1}).set_value(1e-5f);
auto invRmsTensor = rmsnormAttributes.get_inv_rms();
invRmsTensor->set_uid(5).set_name("InvRmsTensor");
const GraphAttributes graphAttributes;
RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.infer_properties_node();
EXPECT_EQ(error.code, ErrorCode::OK);
// Output should inherit input shape
EXPECT_EQ(outputTensor->get_dim(), (std::vector<int64_t>{2, 64, 8, 8}));
EXPECT_EQ(outputTensor->get_stride(), (std::vector<int64_t>{4096, 64, 8, 1}));
// inv_rms should get norm stats shape [N, 1, H, W]
EXPECT_EQ(invRmsTensor->get_dim(), (std::vector<int64_t>{2, 1, 8, 8}));
}
TEST(TestRMSNormNode, InferPropertiesNodeWithBias)
{
RMSNormAttributes rmsnormAttributes;
rmsnormAttributes.set_x(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_scale(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_epsilon(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_bias(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_forward_phase(NormFwdPhase::INFERENCE);
auto inputTensor = rmsnormAttributes.get_x();
inputTensor->set_uid(1)
.set_name("InputTensor")
.set_data_type(DataType::FLOAT)
.set_dim({2, 64, 8, 8})
.set_stride({4096, 64, 8, 1});
auto outputTensor = rmsnormAttributes.get_y();
outputTensor->set_uid(2).set_name("OutputTensor");
auto scaleTensor = rmsnormAttributes.get_scale();
scaleTensor->set_dim({1, 64, 8, 8});
auto epsilonTensor = rmsnormAttributes.get_epsilon();
epsilonTensor->set_dim({1}).set_value(1e-5f);
auto biasTensor = rmsnormAttributes.get_bias();
biasTensor->set_uid(6).set_name("BiasTensor");
const GraphAttributes graphAttributes;
RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.infer_properties_node();
EXPECT_EQ(error.code, ErrorCode::OK);
// Bias should inherit scale's shape
EXPECT_EQ(biasTensor->get_dim(), (std::vector<int64_t>{1, 64, 8, 8}));
}
TEST(TestRMSNormNode, GatherHipdnnTensors)
{
RMSNormAttributes rmsnormAttributes;
auto xTensor = std::make_shared<TensorAttributes>();
xTensor->set_uid(1).set_name("X");
rmsnormAttributes.set_x(xTensor);
auto scaleTensor = std::make_shared<TensorAttributes>();
scaleTensor->set_uid(2).set_name("Scale");
rmsnormAttributes.set_scale(scaleTensor);
auto epsilonTensor = std::make_shared<TensorAttributes>();
epsilonTensor->set_uid(3).set_name("Epsilon");
rmsnormAttributes.set_epsilon(epsilonTensor);
auto yTensor = std::make_shared<TensorAttributes>();
yTensor->set_uid(4).set_name("Y");
rmsnormAttributes.set_y(yTensor);
const GraphAttributes graphAttributes;
const RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
std::unordered_set<std::shared_ptr<TensorAttributes>> allTensors;
node.gather_hipdnn_tensors(allTensors);
EXPECT_TRUE(allTensors.find(xTensor) != allTensors.end());
EXPECT_TRUE(allTensors.find(scaleTensor) != allTensors.end());
EXPECT_TRUE(allTensors.find(epsilonTensor) != allTensors.end());
EXPECT_TRUE(allTensors.find(yTensor) != allTensors.end());
EXPECT_EQ(allTensors.size(), 4);
}
TEST(TestRMSNormNode, GatherHipdnnTensorsWithInvRmsAndBias)
{
RMSNormAttributes rmsnormAttributes;
auto xTensor = std::make_shared<TensorAttributes>();
xTensor->set_uid(1).set_name("X");
rmsnormAttributes.set_x(xTensor);
auto scaleTensor = std::make_shared<TensorAttributes>();
scaleTensor->set_uid(2).set_name("Scale");
rmsnormAttributes.set_scale(scaleTensor);
auto epsilonTensor = std::make_shared<TensorAttributes>();
epsilonTensor->set_uid(3).set_name("Epsilon");
rmsnormAttributes.set_epsilon(epsilonTensor);
auto yTensor = std::make_shared<TensorAttributes>();
yTensor->set_uid(4).set_name("Y");
rmsnormAttributes.set_y(yTensor);
auto invRmsTensor = std::make_shared<TensorAttributes>();
invRmsTensor->set_uid(5).set_name("InvRms");
rmsnormAttributes.set_inv_rms(invRmsTensor);
auto biasTensor = std::make_shared<TensorAttributes>();
biasTensor->set_uid(6).set_name("Bias");
rmsnormAttributes.set_bias(biasTensor);
const GraphAttributes graphAttributes;
const RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
std::unordered_set<std::shared_ptr<TensorAttributes>> allTensors;
node.gather_hipdnn_tensors(allTensors);
EXPECT_TRUE(allTensors.find(xTensor) != allTensors.end());
EXPECT_TRUE(allTensors.find(scaleTensor) != allTensors.end());
EXPECT_TRUE(allTensors.find(epsilonTensor) != allTensors.end());
EXPECT_TRUE(allTensors.find(yTensor) != allTensors.end());
EXPECT_TRUE(allTensors.find(invRmsTensor) != allTensors.end());
EXPECT_TRUE(allTensors.find(biasTensor) != allTensors.end());
EXPECT_EQ(allTensors.size(), 6);
}
// ============================================================================
// Shape and Dimension Validation Tests
// ============================================================================
TEST(TestRMSNormNode, PreValidateRejectsMismatchedInputOutputShapes)
{
RMSNormAttributes rmsnormAttributes;
auto xTensor = std::make_shared<TensorAttributes>();
xTensor->set_dim({2, 64, 32, 32}).set_stride({65536, 1024, 32, 1});
rmsnormAttributes.set_x(xTensor);
auto yTensor = std::make_shared<TensorAttributes>();
yTensor->set_dim({2, 64, 16, 16}); // Mismatched spatial dimensions
rmsnormAttributes.set_y(yTensor);
auto scaleTensor = std::make_shared<TensorAttributes>();
scaleTensor->set_dim({1, 64, 1, 1});
rmsnormAttributes.set_scale(scaleTensor);
auto epsilonTensor = std::make_shared<TensorAttributes>();
epsilonTensor->set_dim({1}).set_value(1e-5f);
rmsnormAttributes.set_epsilon(epsilonTensor);
const GraphAttributes graphAttributes;
const RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.pre_validate_node();
EXPECT_EQ(error.code, ErrorCode::INVALID_VALUE);
EXPECT_TRUE(error.get_message().find("dimension mismatch") != std::string::npos);
}
TEST(TestRMSNormNode, PreValidateRejectsScaleWithoutTrailingMatch)
{
RMSNormAttributes rmsnormAttributes;
auto xTensor = std::make_shared<TensorAttributes>();
xTensor->set_dim({2, 64, 32, 32}).set_stride({65536, 1024, 32, 1});
rmsnormAttributes.set_x(xTensor);
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
auto scaleTensor = std::make_shared<TensorAttributes>();
scaleTensor->set_dim({1, 64, 1, 1}); // scale[3]=1 vs input[3]=32 — no trailing match
rmsnormAttributes.set_scale(scaleTensor);
auto epsilonTensor = std::make_shared<TensorAttributes>();
epsilonTensor->set_dim({1}).set_value(1e-5f);
rmsnormAttributes.set_epsilon(epsilonTensor);
const GraphAttributes graphAttributes;
const RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.pre_validate_node();
EXPECT_EQ(error.code, ErrorCode::INVALID_VALUE);
const std::string expected = "no trailing dims matching input";
EXPECT_TRUE(error.get_message().find(expected) != std::string::npos);
}
TEST(TestRMSNormNode, PreValidateRejectsScaleWithNonOneInLeadingRegion)
{
RMSNormAttributes rmsnormAttributes;
auto xTensor = std::make_shared<TensorAttributes>();
xTensor->set_dim({2, 64, 32, 32}).set_stride({65536, 1024, 32, 1});
rmsnormAttributes.set_x(xTensor);
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
auto scaleTensor = std::make_shared<TensorAttributes>();
scaleTensor->set_dim({1, 128, 32, 32}); // should be [1, 1, 32, 32] or [1, 64, 32, 32]
rmsnormAttributes.set_scale(scaleTensor);
auto epsilonTensor = std::make_shared<TensorAttributes>();
epsilonTensor->set_dim({1}).set_value(1e-5f);
rmsnormAttributes.set_epsilon(epsilonTensor);
rmsnormAttributes.set_forward_phase(NormFwdPhase::TRAINING);
const GraphAttributes graphAttributes;
const RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.pre_validate_node();
EXPECT_EQ(error.code, ErrorCode::INVALID_VALUE);
EXPECT_TRUE(error.get_message().find("leading region before normalized shape")
!= std::string::npos);
EXPECT_TRUE(error.get_message().find("Scale tensor") != std::string::npos);
}
// ============================================================================
// 5D Tensor (NCDHW) Validation Tests
// ============================================================================
TEST(TestRMSNormNode, PreValidateAcceptsValid5DSpatialDimensions)
{
RMSNormAttributes rmsnormAttributes;
auto xTensor = std::make_shared<TensorAttributes>();
xTensor->set_dim({2, 64, 8, 8, 8}).set_stride({32768, 512, 64, 8, 1});
rmsnormAttributes.set_x(xTensor);
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
auto scaleTensor = std::make_shared<TensorAttributes>();
scaleTensor->set_dim({1, 64, 8, 8, 8}); // 5D, full-shape
rmsnormAttributes.set_scale(scaleTensor);
auto epsilonTensor = std::make_shared<TensorAttributes>();
epsilonTensor->set_dim({1}).set_value(1e-5f);
rmsnormAttributes.set_epsilon(epsilonTensor);
rmsnormAttributes.set_forward_phase(NormFwdPhase::TRAINING);
const GraphAttributes graphAttributes;
const RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.pre_validate_node();
EXPECT_EQ(error.code, ErrorCode::OK);
}
TEST(TestRMSNormNode, PreValidateAcceptsSingleElementSpatialDimensions)
{
RMSNormAttributes rmsnormAttributes;
auto xTensor = std::make_shared<TensorAttributes>();
xTensor->set_dim({1, 256, 1, 1}).set_stride({256, 1, 1, 1});
rmsnormAttributes.set_x(xTensor);
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
auto scaleTensor = std::make_shared<TensorAttributes>();
scaleTensor->set_dim({1, 256, 1, 1});
rmsnormAttributes.set_scale(scaleTensor);
auto epsilonTensor = std::make_shared<TensorAttributes>();
epsilonTensor->set_dim({1}).set_value(1e-5f);
rmsnormAttributes.set_epsilon(epsilonTensor);
rmsnormAttributes.set_forward_phase(NormFwdPhase::INFERENCE);
const GraphAttributes graphAttributes;
const RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.pre_validate_node();
EXPECT_EQ(error.code, ErrorCode::OK);
}
// ============================================================================
// Scale-driven inv_rms Shape Inference Tests
// ============================================================================
TEST(TestRMSNormNode, InferInvRmsShapeFromScale5D)
{
RMSNormAttributes rmsnormAttributes;
rmsnormAttributes.set_x(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_scale(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_epsilon(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_inv_rms(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_forward_phase(NormFwdPhase::TRAINING);
auto inputTensor = rmsnormAttributes.get_x();
inputTensor->set_uid(1)
.set_data_type(DataType::FLOAT)
.set_dim({2, 64, 4, 8, 8})
.set_stride({16384, 256, 64, 8, 1});
rmsnormAttributes.get_y()->set_uid(2);
auto scaleTensor = rmsnormAttributes.get_scale();
scaleTensor->set_dim({1, 1, 4, 8, 8}); // Norm over trailing (D, H, W) in 5D
rmsnormAttributes.get_epsilon()->set_dim({1}).set_value(1e-5f);
auto invRmsTensor = rmsnormAttributes.get_inv_rms();
invRmsTensor->set_uid(5);
const GraphAttributes graphAttributes;
RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.infer_properties_node();
EXPECT_EQ(error.code, ErrorCode::OK);
// inv_rms: where scale is non-1 (D, H, W), collapse to 1 → [N, C, 1, 1, 1]
EXPECT_EQ(invRmsTensor->get_dim(), (std::vector<int64_t>{2, 64, 1, 1, 1}));
}
TEST(TestRMSNormNode, InferInvRmsStridesFromInputLayout)
{
RMSNormAttributes rmsnormAttributes;
rmsnormAttributes.set_x(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_scale(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_epsilon(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_inv_rms(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_forward_phase(NormFwdPhase::TRAINING);
auto inputTensor = rmsnormAttributes.get_x();
inputTensor->set_uid(1)
.set_data_type(DataType::FLOAT)
.set_dim({2, 64, 8, 8})
.set_stride({4096, 64, 8, 1});
rmsnormAttributes.get_y()->set_uid(2);
rmsnormAttributes.get_scale()->set_dim({1, 1, 8, 8});
rmsnormAttributes.get_epsilon()->set_dim({1}).set_value(1e-5f);
auto invRmsTensor = rmsnormAttributes.get_inv_rms();
invRmsTensor->set_uid(5);
const GraphAttributes graphAttributes;
RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.infer_properties_node();
EXPECT_EQ(error.code, ErrorCode::OK);
EXPECT_EQ(invRmsTensor->get_dim(), (std::vector<int64_t>{2, 64, 1, 1}));
EXPECT_FALSE(invRmsTensor->get_stride().empty());
// Row-major strides for [2, 64, 1, 1] derived from input NCHW layout: {64, 1, 1, 1}
EXPECT_EQ(invRmsTensor->get_stride(), (std::vector<int64_t>{64, 1, 1, 1}));
}
TEST(TestRMSNormNode, InferScaleStridesFromInputLayout)
{
RMSNormAttributes rmsnormAttributes;
rmsnormAttributes.set_x(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_scale(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_epsilon(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_forward_phase(NormFwdPhase::INFERENCE);
// Input is NHWC: C is innermost (stride 1), then W, then H, then N.
auto inputTensor = rmsnormAttributes.get_x();
inputTensor->set_uid(1)
.set_data_type(DataType::FLOAT)
.set_dim({2, 64, 8, 8})
.set_stride({4096, 1, 512, 64});
rmsnormAttributes.get_y()->set_uid(2);
// Scale dims set, strides empty — should be inferred from x's NHWC layout.
auto scaleTensor = rmsnormAttributes.get_scale();
scaleTensor->set_dim({1, 1, 8, 8});
rmsnormAttributes.get_epsilon()->set_dim({1}).set_value(1e-5f);
const GraphAttributes graphAttributes;
RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.infer_properties_node();
EXPECT_EQ(error.code, ErrorCode::OK);
// Scale strides follow x's NHWC stride order, not default-packed NCHW.
// For [1, 1, 8, 8] in NHWC: C(stride 1), W(stride 1, since C dim=1),
// H(stride 8 = C*W), N(stride 64 = C*W*H).
EXPECT_FALSE(scaleTensor->get_stride().empty());
EXPECT_EQ(scaleTensor->get_stride(), (std::vector<int64_t>{64, 1, 8, 1}));
}
TEST(TestRMSNormNode, InferInvRmsPreservesUserSetDims)
{
RMSNormAttributes rmsnormAttributes;
rmsnormAttributes.set_x(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_scale(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_epsilon(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_inv_rms(std::make_shared<TensorAttributes>());
rmsnormAttributes.set_forward_phase(NormFwdPhase::TRAINING);
auto inputTensor = rmsnormAttributes.get_x();
inputTensor->set_uid(1)
.set_data_type(DataType::FLOAT)
.set_dim({2, 64, 8, 8})
.set_stride({4096, 64, 8, 1});
rmsnormAttributes.get_y()->set_uid(2);
rmsnormAttributes.get_scale()->set_dim({1, 64, 8, 8});
rmsnormAttributes.get_epsilon()->set_dim({1}).set_value(1e-5f);
// User explicitly sets inv_rms dims — inference should not overwrite
auto invRmsTensor = rmsnormAttributes.get_inv_rms();
invRmsTensor->set_uid(5).set_dim({2, 1, 8, 8}).set_stride({64, 64, 8, 1});
const GraphAttributes graphAttributes;
RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.infer_properties_node();
EXPECT_EQ(error.code, ErrorCode::OK);
EXPECT_EQ(invRmsTensor->get_dim(), (std::vector<int64_t>{2, 1, 8, 8}));
EXPECT_EQ(invRmsTensor->get_stride(), (std::vector<int64_t>{64, 64, 8, 1}));
}
// ============================================================================
// Scale-driven inv_rms Validation Tests
// ============================================================================
TEST(TestRMSNormNode, PreValidateAcceptsCorrectInvRmsShape)
{
RMSNormAttributes rmsnormAttributes;
auto xTensor = std::make_shared<TensorAttributes>();
xTensor->set_dim({2, 64, 32, 32}).set_stride({65536, 1024, 32, 1});
rmsnormAttributes.set_x(xTensor);
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
auto scaleTensor = std::make_shared<TensorAttributes>();
scaleTensor->set_dim({1, 64, 32, 32}); // Full-shape scale
rmsnormAttributes.set_scale(scaleTensor);
// Correct inv_rms shape derived from scale: where scale is non-1 → 1,
// where scale is 1 (only batch here) → keep input dim. Yields [N, 1, 1, 1].
auto invRmsTensor = std::make_shared<TensorAttributes>();
invRmsTensor->set_dim({2, 1, 1, 1});
rmsnormAttributes.set_inv_rms(invRmsTensor);
auto epsilonTensor = std::make_shared<TensorAttributes>();
epsilonTensor->set_dim({1}).set_value(1e-5f);
rmsnormAttributes.set_epsilon(epsilonTensor);
rmsnormAttributes.set_forward_phase(NormFwdPhase::TRAINING);
const GraphAttributes graphAttributes;
const RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.pre_validate_node();
EXPECT_EQ(error.code, ErrorCode::OK);
}
TEST(TestRMSNormNode, PreValidateRejectsInvRmsWithChannelOnlyShape)
{
RMSNormAttributes rmsnormAttributes;
auto xTensor = std::make_shared<TensorAttributes>();
xTensor->set_dim({2, 64, 32, 32}).set_stride({65536, 1024, 32, 1});
rmsnormAttributes.set_x(xTensor);
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
auto scaleTensor = std::make_shared<TensorAttributes>();
scaleTensor->set_dim({1, 64, 32, 32}); // Full-shape scale
rmsnormAttributes.set_scale(scaleTensor);
// Old incorrect shape [1, C, 1, 1] — should be rejected
auto invRmsTensor = std::make_shared<TensorAttributes>();
invRmsTensor->set_dim({1, 64, 1, 1});
rmsnormAttributes.set_inv_rms(invRmsTensor);
auto epsilonTensor = std::make_shared<TensorAttributes>();
epsilonTensor->set_dim({1}).set_value(1e-5f);
rmsnormAttributes.set_epsilon(epsilonTensor);
rmsnormAttributes.set_forward_phase(NormFwdPhase::TRAINING);
const GraphAttributes graphAttributes;
const RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.pre_validate_node();
EXPECT_EQ(error.code, ErrorCode::INVALID_VALUE);
EXPECT_TRUE(error.get_message().find("Inverse RMS tensor") != std::string::npos);
}
TEST(TestRMSNormNode, PreValidateRejectsInvRmsWithMismatchedBatchDim)
{
RMSNormAttributes rmsnormAttributes;
auto xTensor = std::make_shared<TensorAttributes>();
xTensor->set_dim({2, 64, 32, 32}).set_stride({65536, 1024, 32, 1});
rmsnormAttributes.set_x(xTensor);
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
auto scaleTensor = std::make_shared<TensorAttributes>();
scaleTensor->set_dim({1, 64, 32, 32}); // Full-shape scale
rmsnormAttributes.set_scale(scaleTensor);
// Batch dim mismatch: input has N=2, inv_rms has N=4
auto invRmsTensor = std::make_shared<TensorAttributes>();
invRmsTensor->set_dim({4, 1, 1, 1});
rmsnormAttributes.set_inv_rms(invRmsTensor);
auto epsilonTensor = std::make_shared<TensorAttributes>();
epsilonTensor->set_dim({1}).set_value(1e-5f);
rmsnormAttributes.set_epsilon(epsilonTensor);
rmsnormAttributes.set_forward_phase(NormFwdPhase::TRAINING);
const GraphAttributes graphAttributes;
const RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.pre_validate_node();
EXPECT_EQ(error.code, ErrorCode::INVALID_VALUE);
EXPECT_TRUE(error.get_message().find("Inverse RMS tensor") != std::string::npos);
}
TEST(TestRMSNormNode, PreValidateRejectsInvRmsWithWrongRank)
{
RMSNormAttributes rmsnormAttributes;
auto xTensor = std::make_shared<TensorAttributes>();
xTensor->set_dim({2, 64, 32, 32}).set_stride({65536, 1024, 32, 1});
rmsnormAttributes.set_x(xTensor);
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
auto scaleTensor = std::make_shared<TensorAttributes>();
scaleTensor->set_dim({1, 64, 32, 32}); // Full-shape scale
rmsnormAttributes.set_scale(scaleTensor);
// Wrong rank: 5D instead of 4D
auto invRmsTensor = std::make_shared<TensorAttributes>();
invRmsTensor->set_dim({2, 1, 1, 1, 1});
rmsnormAttributes.set_inv_rms(invRmsTensor);
auto epsilonTensor = std::make_shared<TensorAttributes>();
epsilonTensor->set_dim({1}).set_value(1e-5f);
rmsnormAttributes.set_epsilon(epsilonTensor);
rmsnormAttributes.set_forward_phase(NormFwdPhase::TRAINING);
const GraphAttributes graphAttributes;
const RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.pre_validate_node();
EXPECT_EQ(error.code, ErrorCode::INVALID_VALUE);
EXPECT_TRUE(error.get_message().find("same rank") != std::string::npos);
}
TEST(TestRMSNormNode, PreValidateSkipsInvRmsValidationWhenDimsNotSet)
{
RMSNormAttributes rmsnormAttributes;
auto xTensor = std::make_shared<TensorAttributes>();
xTensor->set_dim({2, 64, 32, 32}).set_stride({65536, 1024, 32, 1});
rmsnormAttributes.set_x(xTensor);
rmsnormAttributes.set_y(std::make_shared<TensorAttributes>());
auto scaleTensor = std::make_shared<TensorAttributes>();
scaleTensor->set_dim({1, 64, 32, 32}); // Full-shape scale
rmsnormAttributes.set_scale(scaleTensor);
// inv_rms with no dims set — validation should pass (dims will be inferred)
rmsnormAttributes.set_inv_rms(std::make_shared<TensorAttributes>());
auto epsilonTensor = std::make_shared<TensorAttributes>();
epsilonTensor->set_dim({1}).set_value(1e-5f);
rmsnormAttributes.set_epsilon(epsilonTensor);
rmsnormAttributes.set_forward_phase(NormFwdPhase::TRAINING);
const GraphAttributes graphAttributes;
const RMSNormNode node(std::move(rmsnormAttributes), graphAttributes);
auto error = node.pre_validate_node();
EXPECT_EQ(error.code, ErrorCode::OK);
}
TEST(TestRMSNormNode, GetNodeTypeReturnsRmsNorm)
{
const GraphAttributes graphAttrs;
const RMSNormNode node(RMSNormAttributes{}, graphAttrs);
EXPECT_EQ(node.getNodeType(), NodeType::RMS_NORM);
}