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

1952 lines
85 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright © Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#include <cmath>
#include <gtest/gtest.h>
#include <hipdnn_data_sdk/types.hpp>
#include <hipdnn_data_sdk/utilities/Tensor.hpp>
#include <hipdnn_test_sdk/utilities/CpuFpReferenceValidation.hpp>
#include <hipdnn_test_sdk/utilities/DynamicTolerances.hpp>
#include <hipdnn_test_sdk/utilities/FlatbufferGraphTestUtils.hpp>
#include <hipdnn_test_sdk/utilities/pointwise/CpuReferencePointwise.hpp>
#include <hipdnn_test_sdk/utilities/pointwise/PointwiseErrorClassification.hpp>
using namespace hipdnn_test_sdk::utilities;
using namespace hipdnn_data_sdk::utilities;
using namespace hipdnn_flatbuffers_sdk::data_objects;
using namespace hipdnn_data_sdk::types;
using hipdnn_test_sdk::detail::safeTestTypeCast;
namespace
{
// Mathematical constants
constexpr float PI = 3.14159265f;
constexpr float E = 2.71828183f;
constexpr float SQRT_2 = 1.41421356f;
constexpr float SQRT_3 = 1.73205081f;
constexpr float SQRT_5 = 2.23606798f;
constexpr float GOLDEN_RATIO = 1.61803399f; // φ
constexpr float LN_2 = 0.69314718f;
// Common test values
constexpr float TEST_VALUE_1 = 1.0f;
constexpr float TEST_VALUE_2 = 2.0f;
constexpr float TEST_VALUE_3 = 3.0f;
constexpr float TEST_VALUE_5 = 5.0f;
constexpr float TEST_VALUE_2_5 = 2.5f;
constexpr float TEST_VALUE_1_5 = 1.5f;
constexpr float TEST_VALUE_4 = 4.0f;
// Precision test values
constexpr float PRECISION_TEST_A = 0.123456789f;
constexpr float PRECISION_TEST_B = 0.987654321f;
// Broadcasting test values
constexpr float BROADCAST_MULTIPLIER_10 = 10.0f;
} // namespace
template <typename Input1Type,
typename Input2Type = Input1Type,
typename OutputType = Input1Type,
typename ComputeType = double>
class CpuReferencePointwiseFixture : public ::testing::Test
{
protected:
/// Calculates dynamic tolerance based on the operation and input data range.
/// Takes the max of ComputeType-based and float-based tolerance because test
/// expected values are computed from float constants (PI, E, TEST_VALUE_*),
/// so precision is bounded by float even when ComputeType is double.
float getDynamicTolerance(PointwiseMode mode, float scale) const
{
auto errorClass = pointwise::classifyPointwiseOp(mode);
auto effectiveScale = static_cast<double>(pointwise::isBoundedOutput(mode) ? 1.0f : scale);
auto tolerance
= pointwise::calculatePointwiseTolerance<OutputType, Input1Type, ComputeType>(
effectiveScale, errorClass);
auto floatFloor = pointwise::calculatePointwiseTolerance<OutputType, Input1Type, float>(
effectiveScale, errorClass);
return std::max(tolerance, floatFloor);
}
// ======================= BINARY OPERATIONS =======================
void testBinaryAddOperation()
{
Tensor<Input1Type> input1({1, 3, 2, 2});
Tensor<Input2Type> input2({1, 3, 2, 2});
Tensor<OutputType> output({1, 3, 2, 2});
input1.fillWithValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1));
input2.fillWithValue(safeTestTypeCast<Input2Type>(TEST_VALUE_2));
CpuReferencePointwiseImpl<OutputType, Input1Type, Input2Type>::pointwiseCompute(
PointwiseMode::ADD, output, input1, input2);
Tensor<OutputType> expected({1, 3, 2, 2});
expected.fillWithValue(safeTestTypeCast<OutputType>(TEST_VALUE_3));
auto tolerance = getDynamicTolerance(PointwiseMode::ADD, TEST_VALUE_2);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testBinarySubtractOperation()
{
Tensor<Input1Type> input1({1, 3, 2, 2});
Tensor<Input2Type> input2({1, 3, 2, 2});
Tensor<OutputType> output({1, 3, 2, 2});
input1.fillWithValue(safeTestTypeCast<Input1Type>(TEST_VALUE_5));
input2.fillWithValue(safeTestTypeCast<Input2Type>(TEST_VALUE_2));
CpuReferencePointwiseImpl<OutputType, Input1Type, Input2Type>::pointwiseCompute(
PointwiseMode::SUB, output, input1, input2);
Tensor<OutputType> expected({1, 3, 2, 2});
expected.fillWithValue(safeTestTypeCast<OutputType>(TEST_VALUE_3));
auto tolerance = getDynamicTolerance(PointwiseMode::SUB, TEST_VALUE_5);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testBinaryAddOperationSanityValidation()
{
Tensor<Input1Type> input1({1, 1, 2, 2});
Tensor<Input2Type> input2({1, 1, 2, 2});
Tensor<OutputType> output({1, 1, 2, 2});
// Create expected tensor with computed results
Tensor<OutputType> expected({1, 1, 2, 2});
if constexpr(std::is_integral_v<Input1Type> || std::is_integral_v<Input2Type>)
{
input1.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1), 0, 0, 0, 0);
input1.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_2), 0, 0, 0, 1);
input1.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_3), 0, 0, 1, 0);
input1.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_4), 0, 0, 1, 1);
input2.setHostValue(safeTestTypeCast<Input2Type>(-TEST_VALUE_4), 0, 0, 0, 0);
input2.setHostValue(safeTestTypeCast<Input2Type>(-TEST_VALUE_3), 0, 0, 0, 1);
input2.setHostValue(safeTestTypeCast<Input2Type>(-TEST_VALUE_2), 0, 0, 1, 0);
input2.setHostValue(safeTestTypeCast<Input2Type>(-TEST_VALUE_1), 0, 0, 1, 1);
expected.setHostValue(
safeTestTypeCast<OutputType>(TEST_VALUE_1 + (-TEST_VALUE_4)), 0, 0, 0, 0);
expected.setHostValue(
safeTestTypeCast<OutputType>(TEST_VALUE_2 + (-TEST_VALUE_3)), 0, 0, 0, 1);
expected.setHostValue(
safeTestTypeCast<OutputType>(TEST_VALUE_3 + (-TEST_VALUE_2)), 0, 0, 1, 0);
expected.setHostValue(
safeTestTypeCast<OutputType>(TEST_VALUE_4 + (-TEST_VALUE_1)), 0, 0, 1, 1);
}
else
{
input1.setHostValue(safeTestTypeCast<Input1Type>(PI), 0, 0, 0, 0); // π
input1.setHostValue(safeTestTypeCast<Input1Type>(E), 0, 0, 0, 1); // e
input1.setHostValue(safeTestTypeCast<Input1Type>(SQRT_2), 0, 0, 1, 0); // √2
input1.setHostValue(
safeTestTypeCast<Input1Type>(GOLDEN_RATIO), 0, 0, 1, 1); // φ (golden ratio)
input2.setHostValue(safeTestTypeCast<Input2Type>(LN_2), 0, 0, 0, 0); // ln(2)
input2.setHostValue(
safeTestTypeCast<Input2Type>(hipdnn_data_sdk::types::sin(1.0f)), 0, 0, 0, 1);
input2.setHostValue(
safeTestTypeCast<Input2Type>(hipdnn_data_sdk::types::cos(1.0f)), 0, 0, 1, 0);
input2.setHostValue(
safeTestTypeCast<Input2Type>(hipdnn_data_sdk::types::tan(1.0f)), 0, 0, 1, 1);
expected.setHostValue(safeTestTypeCast<OutputType>(PI + LN_2), 0, 0, 0, 0); // π + ln(2)
expected.setHostValue(
safeTestTypeCast<OutputType>(E + hipdnn_data_sdk::types::sin(1.0f)),
0,
0,
0,
1); // e + sin(1)
expected.setHostValue(
safeTestTypeCast<OutputType>(SQRT_2 + hipdnn_data_sdk::types::cos(1.0f)),
0,
0,
1,
0); // √2 + cos(1)
expected.setHostValue(
safeTestTypeCast<OutputType>(GOLDEN_RATIO + hipdnn_data_sdk::types::tan(1.0f)),
0,
0,
1,
1); // φ + tan(1)
}
CpuReferencePointwiseImpl<OutputType, Input1Type, Input2Type>::pointwiseCompute(
PointwiseMode::ADD, output, input1, input2);
auto tolerance = getDynamicTolerance(PointwiseMode::ADD, PI);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testBinarySubtractOperationSanityValidation()
{
Tensor<Input1Type> input1({1, 1, 2, 2});
Tensor<Input2Type> input2({1, 1, 2, 2});
Tensor<OutputType> output({1, 1, 2, 2});
// Create expected tensor with computed results
Tensor<OutputType> expected({1, 1, 2, 2});
if constexpr(std::is_integral_v<Input1Type> || std::is_integral_v<Input2Type>)
{
input1.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1), 0, 0, 0, 0);
input1.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_2), 0, 0, 0, 1);
input1.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_3), 0, 0, 1, 0);
input1.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_4), 0, 0, 1, 1);
input2.setHostValue(safeTestTypeCast<Input2Type>(-TEST_VALUE_4), 0, 0, 0, 0);
input2.setHostValue(safeTestTypeCast<Input2Type>(-TEST_VALUE_3), 0, 0, 0, 1);
input2.setHostValue(safeTestTypeCast<Input2Type>(-TEST_VALUE_2), 0, 0, 1, 0);
input2.setHostValue(safeTestTypeCast<Input2Type>(-TEST_VALUE_1), 0, 0, 1, 1);
expected.setHostValue(
safeTestTypeCast<OutputType>(TEST_VALUE_1 - (-TEST_VALUE_4)), 0, 0, 0, 0);
expected.setHostValue(
safeTestTypeCast<OutputType>(TEST_VALUE_2 - (-TEST_VALUE_3)), 0, 0, 0, 1);
expected.setHostValue(
safeTestTypeCast<OutputType>(TEST_VALUE_3 - (-TEST_VALUE_2)), 0, 0, 1, 0);
expected.setHostValue(
safeTestTypeCast<OutputType>(TEST_VALUE_4 - (-TEST_VALUE_1)), 0, 0, 1, 1);
}
else
{
input1.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_2 * PI), 0, 0, 0, 0); // 2π
input1.setHostValue(safeTestTypeCast<Input1Type>(E * E), 0, 0, 0, 1); // e²
input1.setHostValue(safeTestTypeCast<Input1Type>(SQRT_5), 0, 0, 1, 0); // √5
input1.setHostValue(
safeTestTypeCast<Input1Type>(TEST_VALUE_2), 0, 0, 1, 1); // Simple test value
input2.setHostValue(safeTestTypeCast<Input2Type>(PI / TEST_VALUE_2), 0, 0, 0, 0); // π/2
input2.setHostValue(safeTestTypeCast<Input2Type>(E), 0, 0, 0, 1); // e
input2.setHostValue(safeTestTypeCast<Input2Type>(SQRT_3), 0, 0, 1, 0); // √3
input2.setHostValue(
safeTestTypeCast<Input2Type>(TEST_VALUE_1), 0, 0, 1, 1); // Simple test value
expected.setHostValue(
safeTestTypeCast<OutputType>((TEST_VALUE_2 * PI) - (PI / TEST_VALUE_2)),
0,
0,
0,
0); // 2π - π/2 = 3π/2
expected.setHostValue(safeTestTypeCast<OutputType>((E * E) - E), 0, 0, 0,
1); // e² - e
expected.setHostValue(
safeTestTypeCast<OutputType>(SQRT_5 - SQRT_3), 0, 0, 1, 0); // √5 - √3
expected.setHostValue(
safeTestTypeCast<OutputType>(TEST_VALUE_2 - TEST_VALUE_1), 0, 0, 1, 1); // 2 - 1
}
CpuReferencePointwiseImpl<OutputType, Input1Type, Input2Type>::pointwiseCompute(
PointwiseMode::SUB, output, input1, input2);
auto tolerance = getDynamicTolerance(PointwiseMode::SUB, E * E);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testBinaryAddOperation3D()
{
Tensor<Input1Type> input1({2, 3, 10});
Tensor<Input2Type> input2({2, 3, 10});
Tensor<OutputType> output({2, 3, 10});
input1.fillWithValue(safeTestTypeCast<Input1Type>(TEST_VALUE_2_5));
input2.fillWithValue(safeTestTypeCast<Input2Type>(TEST_VALUE_1_5));
CpuReferencePointwiseImpl<OutputType, Input1Type, Input2Type>::pointwiseCompute(
PointwiseMode::ADD, output, input1, input2);
Tensor<OutputType> expected({2, 3, 10});
expected.fillWithValue(safeTestTypeCast<OutputType>(TEST_VALUE_4));
auto tolerance = getDynamicTolerance(PointwiseMode::ADD, TEST_VALUE_2_5);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testBinarySingleElementTensors()
{
Tensor<Input1Type> input1({1, 1, 1, 1});
Tensor<Input2Type> input2({1, 1, 1, 1});
Tensor<OutputType> output({1, 1, 1, 1});
Tensor<OutputType> expected({1, 1, 1, 1});
if constexpr(std::is_integral_v<Input1Type> || std::is_integral_v<Input2Type>)
{
input1.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_2), 0, 0, 0, 0);
input2.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_5), 0, 0, 0, 0);
expected.setHostValue(
safeTestTypeCast<OutputType>(TEST_VALUE_2 - TEST_VALUE_5), 0, 0, 0, 0);
}
else
{
input1.setHostValue(safeTestTypeCast<Input1Type>(E * E), 0, 0, 0, 0); // e²
input2.setHostValue(safeTestTypeCast<Input2Type>(E), 0, 0, 0, 0); // e
expected.setHostValue(safeTestTypeCast<OutputType>((E * E) - E), 0, 0, 0,
0); // e² - e
}
CpuReferencePointwiseImpl<OutputType, Input1Type, Input2Type>::pointwiseCompute(
PointwiseMode::SUB, output, input1, input2);
auto tolerance = getDynamicTolerance(PointwiseMode::SUB, E * E);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testBinaryNumericalPrecision()
{
Tensor<Input1Type> input1({1, 1, 1, 1});
Tensor<Input2Type> input2({1, 1, 1, 1});
Tensor<OutputType> output({1, 1, 1, 1});
input1.setHostValue(safeTestTypeCast<Input1Type>(PRECISION_TEST_A), 0, 0, 0, 0);
input2.setHostValue(safeTestTypeCast<Input2Type>(PRECISION_TEST_B), 0, 0, 0, 0);
CpuReferencePointwiseImpl<OutputType, Input1Type, Input2Type>::pointwiseCompute(
PointwiseMode::ADD, output, input1, input2);
Tensor<OutputType> expected({1, 1, 1, 1});
expected.setHostValue(
safeTestTypeCast<OutputType>(PRECISION_TEST_A + PRECISION_TEST_B), 0, 0, 0, 0);
auto tolerance = getDynamicTolerance(PointwiseMode::ADD, PRECISION_TEST_B);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testElementwise1D()
{
Tensor<Input1Type> input1({5});
Tensor<Input2Type> input2({5});
Tensor<OutputType> output({5});
for(int i = 0; i < 5; ++i)
{
input1.setHostValue(safeTestTypeCast<Input1Type>(static_cast<float>(i + 1)), i);
input2.setHostValue(safeTestTypeCast<Input2Type>(static_cast<float>(i * 2)), i);
}
CpuReferencePointwiseImpl<OutputType, Input1Type, Input2Type>::pointwiseCompute(
PointwiseMode::ADD, output, input1, input2);
// Create expected tensor: [1,2,3,4,5] + [0,2,4,6,8] = [1,4,7,10,13]
Tensor<OutputType> expected({5});
expected.setHostValue(safeTestTypeCast<OutputType>(static_cast<float>(1)), 0);
expected.setHostValue(safeTestTypeCast<OutputType>(static_cast<float>(4)), 1);
expected.setHostValue(safeTestTypeCast<OutputType>(static_cast<float>(7)), 2);
expected.setHostValue(safeTestTypeCast<OutputType>(static_cast<float>(10)), 3);
expected.setHostValue(safeTestTypeCast<OutputType>(static_cast<float>(13)), 4);
auto tolerance = getDynamicTolerance(PointwiseMode::ADD, 8.0f);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testBroadcast2Dx1D()
{
Tensor<Input1Type> input1({3, 4}); // [M,N] = [3,4]
Tensor<Input2Type> input2({4}); // [N] = [4]
Tensor<OutputType> output({3, 4}); // Output: [3,4]
// Fill input1 with pattern: row*10 + col
for(int m = 0; m < 3; ++m)
{
for(int n = 0; n < 4; ++n)
{
input1.setHostValue(
safeTestTypeCast<Input1Type>(static_cast<float>((m * 10) + n)), m, n);
}
}
// Fill input2 with pattern: [10, 20, 30, 40]
for(int n = 0; n < 4; ++n)
{
input2.setHostValue(safeTestTypeCast<Input2Type>(static_cast<float>((n + 1) * 10)), n);
}
CpuReferencePointwiseImpl<OutputType, Input1Type, Input2Type>::pointwiseCompute(
PointwiseMode::ADD, output, input1, input2);
// Create expected tensor: broadcasting input2[n] to all input1[m,n]
Tensor<OutputType> expected({3, 4});
for(int m = 0; m < 3; ++m)
{
for(int n = 0; n < 4; ++n)
{
auto input1Val = static_cast<float>((m * 10) + n);
auto input2Val = static_cast<float>((n + 1) * 10);
expected.setHostValue(safeTestTypeCast<OutputType>(input1Val + input2Val), m, n);
}
}
auto tolerance = getDynamicTolerance(PointwiseMode::ADD, 40.0f);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testBroadcast3D()
{
Tensor<Input1Type> input1({2, 3, 4}); // [2,3,4]
Tensor<Input2Type> input2({1, 3, 1}); // [1,3,1] - broadcasts to [2,3,4]
Tensor<OutputType> output({2, 3, 4}); // Output: [2,3,4]
input1.fillWithValue(safeTestTypeCast<Input1Type>(TEST_VALUE_5));
input2.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_1), 0, 0, 0); // Channel 0
input2.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_2), 0, 1, 0); // Channel 1
input2.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_3), 0, 2, 0); // Channel 2
CpuReferencePointwiseImpl<OutputType, Input1Type, Input2Type>::pointwiseCompute(
PointwiseMode::SUB, output, input1, input2);
// Create expected tensor: broadcasting subtraction
Tensor<OutputType> expected({2, 3, 4});
for(int n = 0; n < 2; ++n)
{
for(int c = 0; c < 3; ++c)
{
for(int h = 0; h < 4; ++h)
{
const float input1Val = TEST_VALUE_5;
auto input2Val = static_cast<float>(c + 1); // Channel values: 1.0, 2.0, 3.0
expected.setHostValue(
safeTestTypeCast<OutputType>(input1Val - input2Val), n, c, h);
}
}
}
auto tolerance = getDynamicTolerance(PointwiseMode::SUB, TEST_VALUE_5);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testBroadcast3DImplicitLeading()
{
Tensor<Input1Type> input1({2, 3, 4}); // [2,3,4]
Tensor<Input2Type> input2(
{3, 1}); // [3,1] - broadcasts to [2,3,4] (implicit leading dimension)
Tensor<OutputType> output({2, 3, 4}); // Output: [2,3,4]
input1.fillWithValue(safeTestTypeCast<Input1Type>(TEST_VALUE_5));
input2.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_1), 0, 0); // Channel 0
input2.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_2), 1, 0); // Channel 1
input2.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_3), 2, 0); // Channel 2
CpuReferencePointwiseImpl<OutputType, Input1Type, Input2Type>::pointwiseCompute(
PointwiseMode::SUB, output, input1, input2);
// Create expected tensor: broadcasting subtraction with implicit leading dimension
Tensor<OutputType> expected({2, 3, 4});
for(int n = 0; n < 2; ++n)
{
for(int c = 0; c < 3; ++c)
{
for(int h = 0; h < 4; ++h)
{
const float input1Val = TEST_VALUE_5;
auto input2Val = static_cast<float>(c + 1); // Channel values: 1.0, 2.0, 3.0
expected.setHostValue(
safeTestTypeCast<OutputType>(input1Val - input2Val), n, c, h);
}
}
}
auto tolerance = getDynamicTolerance(PointwiseMode::SUB, TEST_VALUE_5);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
// Test case: 4D × 4D: [N,C,H,W] + [1,C,1,1] → broadcast to [N,C,H,W]
void testBroadcast4Dx4D()
{
Tensor<Input1Type> input1({2, 3, 2, 2}); // [N,C,H,W] = [2,3,2,2]
Tensor<Input2Type> input2({1, 3, 1, 1}); // [1,C,1,1] = [1,3,1,1]
Tensor<OutputType> output({2, 3, 2, 2}); // Output: [2,3,2,2]
input1.fillWithValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1));
input2.setHostValue(
safeTestTypeCast<Input2Type>(BROADCAST_MULTIPLIER_10), 0, 0, 0, 0); // Channel 0
input2.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_2 * BROADCAST_MULTIPLIER_10),
0,
1,
0,
0); // Channel 1
input2.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_3 * BROADCAST_MULTIPLIER_10),
0,
2,
0,
0); // Channel 2
CpuReferencePointwiseImpl<OutputType, Input1Type, Input2Type>::pointwiseCompute(
PointwiseMode::ADD, output, input1, input2);
// Create expected tensor: broadcasting addition
Tensor<OutputType> expected({2, 3, 2, 2});
for(int n = 0; n < 2; ++n)
{
for(int c = 0; c < 3; ++c)
{
for(int h = 0; h < 2; ++h)
{
for(int w = 0; w < 2; ++w)
{
const float input1Val = TEST_VALUE_1;
auto input2Val = static_cast<float>(
(static_cast<float>(c) + 1.0f)
* BROADCAST_MULTIPLIER_10); // Channel values: 10.0, 20.0, 30.0
expected.setHostValue(
safeTestTypeCast<OutputType>(input1Val + input2Val), n, c, h, w);
}
}
}
}
auto tolerance = getDynamicTolerance(PointwiseMode::ADD, 30.0f);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
// Test case: Complex N-D broadcasting: [2,1,3,1] + [1,2,1,4] → [2,2,3,4]
void testBroadcastComplexND()
{
Tensor<Input1Type> input1({2, 1, 3, 1});
Tensor<Input2Type> input2({1, 2, 1, 4});
Tensor<OutputType> output({2, 2, 3, 4});
for(int n = 0; n < 2; ++n)
{
for(int h = 0; h < 3; ++h)
{
input1.setHostValue(
safeTestTypeCast<Input1Type>(static_cast<float>((n * 10) + h)), n, 0, h, 0);
}
}
for(int c = 0; c < 2; ++c)
{
for(int w = 0; w < 4; ++w)
{
input2.setHostValue(
safeTestTypeCast<Input2Type>(static_cast<float>((c * 100) + w)), 0, c, 0, w);
}
}
CpuReferencePointwiseImpl<OutputType, Input1Type, Input2Type>::pointwiseCompute(
PointwiseMode::ADD, output, input1, input2);
// Create expected tensor: broadcasting addition
Tensor<OutputType> expected({2, 2, 3, 4});
for(int n = 0; n < 2; ++n)
{
for(int c = 0; c < 2; ++c)
{
for(int h = 0; h < 3; ++h)
{
for(int w = 0; w < 4; ++w)
{
// input1[n,0,h,0] broadcasts to input1[n,c,h,w]
auto input1Val = static_cast<float>((n * 10) + h);
// input2[0,c,0,w] broadcasts to input2[n,c,h,w]
auto input2Val = static_cast<float>((c * 100) + w);
expected.setHostValue(
safeTestTypeCast<OutputType>(input1Val + input2Val), n, c, h, w);
}
}
}
}
auto tolerance = getDynamicTolerance(PointwiseMode::ADD, 103.0f);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testBroadcast5D()
{
const std::vector<int64_t> dims1 = {2, 3, 2, 2, 2}; // [N,C,D,H,W] = [2,3,2,2,2]
const std::vector<int64_t> strides1 = {24, 8, 4, 2, 1}; // Row-major strides for [2,3,2,2,2]
Tensor<Input1Type> input1(dims1, strides1);
const std::vector<int64_t> dims2 = {1, 3, 1, 1, 1}; // [1,C,1,1,1] = [1,3,1,1,1]
const std::vector<int64_t> strides2 = {3, 1, 1, 1, 1}; // Row-major strides for [1,3,1,1,1]
Tensor<Input2Type> input2(dims2, strides2);
const std::vector<int64_t> outputDims = {2, 3, 2, 2, 2}; // Output: [2,3,2,2,2]
const std::vector<int64_t> outputStrides
= {24, 8, 4, 2, 1}; // Row-major strides for [2,3,2,2,2]
Tensor<OutputType> output(outputDims, outputStrides);
input1.fillWithValue(safeTestTypeCast<Input1Type>(TEST_VALUE_2));
// Set channel-specific values in input2
input2.setHostValue(
safeTestTypeCast<Input2Type>(BROADCAST_MULTIPLIER_10), 0, 0, 0, 0, 0); // Channel 0
input2.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_2 * BROADCAST_MULTIPLIER_10),
0,
1,
0,
0,
0); // Channel 1
input2.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_3 * BROADCAST_MULTIPLIER_10),
0,
2,
0,
0,
0); // Channel 2
CpuReferencePointwiseImpl<OutputType, Input1Type, Input2Type>::pointwiseCompute(
PointwiseMode::ADD, output, input1, input2);
// Create expected tensor: broadcasting addition
Tensor<OutputType> expected(outputDims, outputStrides);
for(int n = 0; n < 2; ++n)
{
for(int c = 0; c < 3; ++c)
{
for(int d = 0; d < 2; ++d)
{
for(int h = 0; h < 2; ++h)
{
for(int w = 0; w < 2; ++w)
{
auto input1Val = TEST_VALUE_2;
auto input2Val = static_cast<float>(
(static_cast<float>(c) + 1.0f)
* BROADCAST_MULTIPLIER_10); // Channel values: 10.0, 20.0, 30.0
expected.setHostValue(
safeTestTypeCast<OutputType>(input1Val + input2Val), n, c, d, h, w);
}
}
}
}
}
auto tolerance = getDynamicTolerance(PointwiseMode::ADD, 30.0f);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
// ======================= UNARY OPERATIONS =======================
void testReluForwardOperation()
{
Tensor<Input1Type> input({1, 3, 2, 2});
Tensor<OutputType> output({1, 3, 2, 2});
// Fill with mix of positive and negative values
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1), 0, 0, 0, 0); // 1.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_2), 0, 0, 0, 1); // -2.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_3), 0, 0, 1, 0); // 3.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_1), 0, 0, 1, 1); // -1.0
input.setHostValue(safeTestTypeCast<Input1Type>(0.0f), 0, 1, 0, 0); // 0.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_5), 0, 1, 0, 1); // 5.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_5), 0, 1, 1, 0); // -5.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_2_5), 0, 1, 1, 1); // 2.5
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_1_5), 0, 2, 0, 0); // -1.5
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_4), 0, 2, 0, 1); // 4.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_3), 0, 2, 1, 0); // -3.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1_5), 0, 2, 1, 1); // 1.5
CpuReferencePointwiseImpl<OutputType, Input1Type>::pointwiseCompute(
PointwiseMode::RELU_FWD, output, input);
// Create expected tensor: ReLU(x) = max(0, x)
Tensor<OutputType> expected({1, 3, 2, 2});
expected.setHostValue(
safeTestTypeCast<OutputType>(TEST_VALUE_1), 0, 0, 0, 0); // max(0, 1) = 1
expected.setHostValue(safeTestTypeCast<OutputType>(0.0f), 0, 0, 0, 1); // max(0, -2) = 0
expected.setHostValue(
safeTestTypeCast<OutputType>(TEST_VALUE_3), 0, 0, 1, 0); // max(0, 3) = 3
expected.setHostValue(safeTestTypeCast<OutputType>(0.0f), 0, 0, 1, 1); // max(0, -1) = 0
expected.setHostValue(safeTestTypeCast<OutputType>(0.0f), 0, 1, 0, 0); // max(0, 0) = 0
expected.setHostValue(
safeTestTypeCast<OutputType>(TEST_VALUE_5), 0, 1, 0, 1); // max(0, 5) = 5
expected.setHostValue(safeTestTypeCast<OutputType>(0.0f), 0, 1, 1, 0); // max(0, -5) = 0
expected.setHostValue(
safeTestTypeCast<OutputType>(TEST_VALUE_2_5), 0, 1, 1, 1); // max(0, 2.5) = 2.5
expected.setHostValue(safeTestTypeCast<OutputType>(0.0f), 0, 2, 0, 0); // max(0, -1.5) = 0
expected.setHostValue(
safeTestTypeCast<OutputType>(TEST_VALUE_4), 0, 2, 0, 1); // max(0, 4) = 4
expected.setHostValue(safeTestTypeCast<OutputType>(0.0f), 0, 2, 1, 0); // max(0, -3) = 0
expected.setHostValue(
safeTestTypeCast<OutputType>(TEST_VALUE_1_5), 0, 2, 1, 1); // max(0, 1.5) = 1.5
auto tolerance = getDynamicTolerance(PointwiseMode::RELU_FWD, TEST_VALUE_5);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testReluBackwardOperation()
{
Tensor<Input1Type> input({1, 2, 2, 2}); // Forward input (x)
Tensor<Input2Type> upstreamGrad({1, 2, 2, 2}); // Upstream gradient (dy)
Tensor<OutputType> output({1, 2, 2, 2}); // Downstream gradient (dx)
// Fill input with mix of positive and negative values
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1), 0, 0, 0, 0); // 1.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_2), 0, 0, 0, 1); // -2.0
input.setHostValue(safeTestTypeCast<Input1Type>(0.0f), 0, 0, 1, 0); // 0.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_3), 0, 0, 1, 1); // 3.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_1), 0, 1, 0, 0); // -1.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_2_5), 0, 1, 0, 1); // 2.5
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_5), 0, 1, 1, 0); // -5.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1_5), 0, 1, 1, 1); // 1.5
// Fill upstream gradient with test values
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_2), 0, 0, 0, 0); // 2.0
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_1_5), 0, 0, 0, 1); // 1.5
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_3), 0, 0, 1, 0); // 3.0
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_1), 0, 0, 1, 1); // 1.0
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_2_5), 0, 1, 0, 0); // 2.5
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_4), 0, 1, 0, 1); // 4.0
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_1_5), 0, 1, 1, 0); // 1.5
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_3), 0, 1, 1, 1); // 3.0
// ReLU backward expects dy first, then x.
CpuReferencePointwiseImpl<OutputType, Input2Type, Input1Type>::pointwiseCompute(
PointwiseMode::RELU_BWD, output, upstreamGrad, input);
// Create expected tensor: dx = dy * (x > 0 ? 1 : 0)
Tensor<OutputType> expected({1, 2, 2, 2});
expected.setHostValue(safeTestTypeCast<OutputType>(TEST_VALUE_2 * 1.0f),
0,
0,
0,
0); // dy=2.0, x=1.0>0: dx=2.0*1=2.0
expected.setHostValue(safeTestTypeCast<OutputType>(TEST_VALUE_1_5 * 0.0f),
0,
0,
0,
1); // dy=1.5, x=-2.0<0: dx=1.5*0=0.0
expected.setHostValue(safeTestTypeCast<OutputType>(TEST_VALUE_3 * 0.0f),
0,
0,
1,
0); // dy=3.0, x=0.0<=0: dx=3.0*0=0.0
expected.setHostValue(safeTestTypeCast<OutputType>(TEST_VALUE_1 * 1.0f),
0,
0,
1,
1); // dy=1.0, x=3.0>0: dx=1.0*1=1.0
expected.setHostValue(safeTestTypeCast<OutputType>(TEST_VALUE_2_5 * 0.0f),
0,
1,
0,
0); // dy=2.5, x=-1.0<0: dx=2.5*0=0.0
expected.setHostValue(safeTestTypeCast<OutputType>(TEST_VALUE_4 * 1.0f),
0,
1,
0,
1); // dy=4.0, x=2.5>0: dx=4.0*1=4.0
expected.setHostValue(safeTestTypeCast<OutputType>(TEST_VALUE_1_5 * 0.0f),
0,
1,
1,
0); // dy=1.5, x=-5.0<0: dx=1.5*0=0.0
expected.setHostValue(safeTestTypeCast<OutputType>(TEST_VALUE_3 * 1.0f),
0,
1,
1,
1); // dy=3.0, x=1.5>0: dx=3.0*1=3.0
auto tolerance = getDynamicTolerance(PointwiseMode::RELU_BWD, TEST_VALUE_5);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testParameterizedReluForwardOperation()
{
Tensor<Input1Type> input({1, 2, 2, 2});
Tensor<OutputType> output({1, 2, 2, 2});
// Fill with values that will test all three regions: below lower_clip, between clips, above upper_clip
input.setHostValue(
safeTestTypeCast<Input1Type>(-TEST_VALUE_5), 0, 0, 0, 0); // -5.0 (below lower_clip)
input.setHostValue(
safeTestTypeCast<Input1Type>(-TEST_VALUE_1), 0, 0, 0, 1); // -1.0 (between clips)
input.setHostValue(
safeTestTypeCast<Input1Type>(TEST_VALUE_2), 0, 0, 1, 0); // 2.0 (between clips)
input.setHostValue(
safeTestTypeCast<Input1Type>(TEST_VALUE_5), 0, 0, 1, 1); // 5.0 (above upper_clip)
input.setHostValue(
safeTestTypeCast<Input1Type>(-TEST_VALUE_2), 0, 1, 0, 0); // -2.0 (at lower_clip)
input.setHostValue(safeTestTypeCast<Input1Type>(0.0f), 0, 1, 0, 1); // 0.0 (between clips)
input.setHostValue(
safeTestTypeCast<Input1Type>(TEST_VALUE_4), 0, 1, 1, 0); // 4.0 (at upper_clip)
input.setHostValue(
safeTestTypeCast<Input1Type>(TEST_VALUE_1), 0, 1, 1, 1); // 1.0 (between clips)
// Parameters: lower_clip = -2.0, upper_clip = 4.0, lower_slope = 0.1
const float lowerClip = -TEST_VALUE_2; // -2.0
const float upperClip = TEST_VALUE_4; // 4.0
auto lowerSlope = static_cast<float>(0.1);
CpuReferencePointwiseImpl<OutputType, Input1Type>::pointwiseCompute(
PointwiseMode::RELU_FWD, output, input, lowerClip, upperClip, lowerSlope);
// Create expected tensor: Generalized ReLU with parameters
Tensor<OutputType> expected({1, 2, 2, 2});
// For x < lower_clip: output = lower_clip + lower_slope * (x - lower_clip)
// For lower_clip <= x <= upper_clip: output = x
// For x > upper_clip: output = upper_clip
expected.setHostValue(
safeTestTypeCast<OutputType>(lowerClip + (lowerSlope * (-TEST_VALUE_5 - lowerClip))),
0,
0,
0,
0); // -2 + 0.1*(-5-(-2)) = -2.3
expected.setHostValue(
safeTestTypeCast<OutputType>(-TEST_VALUE_1), 0, 0, 0, 1); // -1.0 (in range)
expected.setHostValue(
safeTestTypeCast<OutputType>(TEST_VALUE_2), 0, 0, 1, 0); // 2.0 (in range)
expected.setHostValue(safeTestTypeCast<OutputType>(upperClip), 0, 0, 1, 1); // 4.0 (clamped)
expected.setHostValue(
safeTestTypeCast<OutputType>(lowerClip), 0, 1, 0, 0); // -2.0 (at boundary)
expected.setHostValue(safeTestTypeCast<OutputType>(0.0f), 0, 1, 0, 1); // 0.0 (in range)
expected.setHostValue(
safeTestTypeCast<OutputType>(upperClip), 0, 1, 1, 0); // 4.0 (at boundary)
expected.setHostValue(
safeTestTypeCast<OutputType>(TEST_VALUE_1), 0, 1, 1, 1); // 1.0 (in range)
auto tolerance = getDynamicTolerance(PointwiseMode::RELU_FWD, TEST_VALUE_5);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testParameterizedReluBackwardOperation()
{
Tensor<Input1Type> input({1, 2, 2, 2}); // Forward input (x)
Tensor<Input2Type> upstreamGrad({1, 2, 2, 2}); // Upstream gradient (dy)
Tensor<OutputType> output({1, 2, 2, 2}); // Downstream gradient (dx)
// Fill forward input with values that will test all three regions: below lower_clip, between clips, above upper_clip
input.setHostValue(
safeTestTypeCast<Input1Type>(-TEST_VALUE_5), 0, 0, 0, 0); // -5.0 (below lower_clip)
input.setHostValue(
safeTestTypeCast<Input1Type>(-TEST_VALUE_1), 0, 0, 0, 1); // -1.0 (between clips)
input.setHostValue(
safeTestTypeCast<Input1Type>(TEST_VALUE_2), 0, 0, 1, 0); // 2.0 (between clips)
input.setHostValue(
safeTestTypeCast<Input1Type>(TEST_VALUE_5), 0, 0, 1, 1); // 5.0 (above upper_clip)
input.setHostValue(
safeTestTypeCast<Input1Type>(-TEST_VALUE_2), 0, 1, 0, 0); // -2.0 (at lower_clip)
input.setHostValue(safeTestTypeCast<Input1Type>(0.0f), 0, 1, 0, 1); // 0.0 (between clips)
input.setHostValue(
safeTestTypeCast<Input1Type>(TEST_VALUE_4), 0, 1, 1, 0); // 4.0 (at upper_clip)
input.setHostValue(
safeTestTypeCast<Input1Type>(TEST_VALUE_1), 0, 1, 1, 1); // 1.0 (between clips)
// Fill upstream gradient with test values
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_2), 0, 0, 0, 0); // 2.0
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_1_5), 0, 0, 0, 1); // 1.5
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_3), 0, 0, 1, 0); // 3.0
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_1), 0, 0, 1, 1); // 1.0
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_2_5), 0, 1, 0, 0); // 2.5
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_4), 0, 1, 0, 1); // 4.0
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_1_5), 0, 1, 1, 0); // 1.5
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_3), 0, 1, 1, 1); // 3.0
// Parameters: lower_clip = -2.0, upper_clip = 4.0, lower_slope = 0.1
const float lowerClip = -TEST_VALUE_2; // -2.0
const float upperClip = TEST_VALUE_4; // 4.0
auto lowerSlope = static_cast<float>(0.1);
// ReLU backward expects dy first, then x.
CpuReferencePointwiseImpl<OutputType, Input2Type, Input1Type>::pointwiseCompute(
PointwiseMode::RELU_BWD, output, upstreamGrad, input, lowerClip, upperClip, lowerSlope);
// Create expected tensor: dx = dy * local_gradient
Tensor<OutputType> expected({1, 2, 2, 2});
for(int n = 0; n < 1; ++n)
{
for(int c = 0; c < 2; ++c)
{
for(int h = 0; h < 2; ++h)
{
for(int w = 0; w < 2; ++w)
{
auto inputVal = static_cast<float>(input.getHostValue(n, c, h, w));
auto upstreamGradVal
= static_cast<float>(upstreamGrad.getHostValue(n, c, h, w));
// For x <= lower_clip: local_gradient = lower_slope
// For x > lower_clip && x <= upper_clip: local_gradient = 1.0
// For x > upper_clip: local_gradient = 0.0
float localGradient;
if(inputVal > lowerClip && inputVal <= upperClip)
{
localGradient = 1.0f;
}
else if(inputVal <= lowerClip)
{
localGradient = lowerSlope;
}
else
{
localGradient = 0.0f;
}
auto downstreamGrad = upstreamGradVal * localGradient;
expected.setHostValue(
safeTestTypeCast<OutputType>(downstreamGrad), n, c, h, w);
}
}
}
}
auto tolerance = getDynamicTolerance(PointwiseMode::RELU_BWD, TEST_VALUE_5);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testSigmoidForwardOperation()
{
Tensor<Input1Type> input({1, 2, 2, 2});
Tensor<OutputType> output({1, 2, 2, 2});
// Fill with values that will test sigmoid function
input.setHostValue(safeTestTypeCast<Input1Type>(0.0f), 0, 0, 0, 0); // 0.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1), 0, 0, 0, 1); // 1.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_1), 0, 0, 1, 0); // -1.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_2), 0, 0, 1, 1); // 2.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_2), 0, 1, 0, 0); // -2.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_5), 0, 1, 0, 1); // 5.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_5), 0, 1, 1, 0); // -5.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1_5), 0, 1, 1, 1); // 1.5
CpuReferencePointwiseImpl<OutputType, Input1Type>::pointwiseCompute(
PointwiseMode::SIGMOID_FWD, output, input);
// Create expected tensor: sigmoid(x) = 1 / (1 + exp(-x))
Tensor<OutputType> expected({1, 2, 2, 2});
expected.setHostValue(
safeTestTypeCast<OutputType>(1.0f / (1.0f + hipdnn_data_sdk::types::exp(-0.0f))),
0,
0,
0,
0); // sigmoid(0) = 0.5
expected.setHostValue(safeTestTypeCast<OutputType>(
1.0f / (1.0f + hipdnn_data_sdk::types::exp(-TEST_VALUE_1))),
0,
0,
0,
1); // sigmoid(1)
expected.setHostValue(
safeTestTypeCast<OutputType>(1.0f / (1.0f + hipdnn_data_sdk::types::exp(TEST_VALUE_1))),
0,
0,
1,
0); // sigmoid(-1)
expected.setHostValue(safeTestTypeCast<OutputType>(
1.0f / (1.0f + hipdnn_data_sdk::types::exp(-TEST_VALUE_2))),
0,
0,
1,
1); // sigmoid(2)
expected.setHostValue(
safeTestTypeCast<OutputType>(1.0f / (1.0f + hipdnn_data_sdk::types::exp(TEST_VALUE_2))),
0,
1,
0,
0); // sigmoid(-2)
expected.setHostValue(safeTestTypeCast<OutputType>(
1.0f / (1.0f + hipdnn_data_sdk::types::exp(-TEST_VALUE_5))),
0,
1,
0,
1); // sigmoid(5)
expected.setHostValue(
safeTestTypeCast<OutputType>(1.0f / (1.0f + hipdnn_data_sdk::types::exp(TEST_VALUE_5))),
0,
1,
1,
0); // sigmoid(-5)
expected.setHostValue(safeTestTypeCast<OutputType>(
1.0f / (1.0f + hipdnn_data_sdk::types::exp(-TEST_VALUE_1_5))),
0,
1,
1,
1); // sigmoid(1.5)
auto tolerance = getDynamicTolerance(PointwiseMode::SIGMOID_FWD, TEST_VALUE_5);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testSigmoidBackwardOperation()
{
Tensor<Input1Type> input({1, 2, 2, 2}); // Forward input (x)
Tensor<Input2Type> upstreamGrad({1, 2, 2, 2}); // Upstream gradient (dy)
Tensor<OutputType> output({1, 2, 2, 2}); // Downstream gradient (dx)
// Fill forward input with values that will test sigmoid backward function
input.setHostValue(safeTestTypeCast<Input1Type>(0.0f), 0, 0, 0, 0); // 0.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1), 0, 0, 0, 1); // 1.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_1), 0, 0, 1, 0); // -1.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_2), 0, 0, 1, 1); // 2.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_2), 0, 1, 0, 0); // -2.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_3), 0, 1, 0, 1); // 3.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_3), 0, 1, 1, 0); // -3.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1_5), 0, 1, 1, 1); // 1.5
// Fill upstream gradient with test values
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_2), 0, 0, 0, 0); // 2.0
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_1_5), 0, 0, 0, 1); // 1.5
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_3), 0, 0, 1, 0); // 3.0
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_1), 0, 0, 1, 1); // 1.0
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_2_5), 0, 1, 0, 0); // 2.5
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_4), 0, 1, 0, 1); // 4.0
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_1_5), 0, 1, 1, 0); // 1.5
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_3), 0, 1, 1, 1); // 3.0
// Backward activations expect dy first, then x.
CpuReferencePointwiseImpl<OutputType, Input2Type, Input1Type>::pointwiseCompute(
PointwiseMode::SIGMOID_BWD, output, upstreamGrad, input);
// Create expected tensor: dx = dy * sigmoid(x) * (1 - sigmoid(x))
Tensor<OutputType> expected({1, 2, 2, 2});
for(int n = 0; n < 1; ++n)
{
for(int c = 0; c < 2; ++c)
{
for(int h = 0; h < 2; ++h)
{
for(int w = 0; w < 2; ++w)
{
auto inputVal = input.getHostValue(n, c, h, w);
auto upstreamGradVal = upstreamGrad.getHostValue(n, c, h, w);
// Compute using the template ComputeType
auto xCompute = static_cast<ComputeType>(inputVal);
auto dyCompute = static_cast<ComputeType>(upstreamGradVal);
auto sigmoid = ComputeType{1}
/ (ComputeType{1} + hipdnn_data_sdk::types::exp(-xCompute));
auto localGradient = sigmoid * (ComputeType{1} - sigmoid);
auto downstreamGrad = dyCompute * localGradient;
expected.setHostValue(
safeTestTypeCast<OutputType>(downstreamGrad), n, c, h, w);
}
}
}
}
auto tolerance = getDynamicTolerance(PointwiseMode::SIGMOID_BWD, TEST_VALUE_4);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testTanhForwardOperation()
{
Tensor<Input1Type> input({1, 2, 2, 2});
Tensor<OutputType> output({1, 2, 2, 2});
// Fill with values that will test tanh function
input.setHostValue(safeTestTypeCast<Input1Type>(0.0f), 0, 0, 0, 0); // 0.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1), 0, 0, 0, 1); // 1.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_1), 0, 0, 1, 0); // -1.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_2), 0, 0, 1, 1); // 2.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_2), 0, 1, 0, 0); // -2.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_3), 0, 1, 0, 1); // 3.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_3), 0, 1, 1, 0); // -3.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1_5), 0, 1, 1, 1); // 1.5
CpuReferencePointwiseImpl<OutputType, Input1Type>::pointwiseCompute(
PointwiseMode::TANH_FWD, output, input);
// Create expected tensor: tanh(x)
Tensor<OutputType> expected({1, 2, 2, 2});
expected.setHostValue(
safeTestTypeCast<OutputType>(std::tanh(0.0f)), 0, 0, 0, 0); // tanh(0) = 0
expected.setHostValue(
safeTestTypeCast<OutputType>(std::tanh(TEST_VALUE_1)), 0, 0, 0, 1); // tanh(1)
expected.setHostValue(
safeTestTypeCast<OutputType>(std::tanh(-TEST_VALUE_1)), 0, 0, 1, 0); // tanh(-1)
expected.setHostValue(
safeTestTypeCast<OutputType>(std::tanh(TEST_VALUE_2)), 0, 0, 1, 1); // tanh(2)
expected.setHostValue(
safeTestTypeCast<OutputType>(std::tanh(-TEST_VALUE_2)), 0, 1, 0, 0); // tanh(-2)
expected.setHostValue(
safeTestTypeCast<OutputType>(std::tanh(TEST_VALUE_3)), 0, 1, 0, 1); // tanh(3)
expected.setHostValue(
safeTestTypeCast<OutputType>(std::tanh(-TEST_VALUE_3)), 0, 1, 1, 0); // tanh(-3)
expected.setHostValue(
safeTestTypeCast<OutputType>(std::tanh(TEST_VALUE_1_5)), 0, 1, 1, 1); // tanh(1.5)
auto tolerance = getDynamicTolerance(PointwiseMode::TANH_FWD, TEST_VALUE_3);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testTanhBackwardOperation()
{
Tensor<Input1Type> input({1, 2, 2, 2}); // Forward input (x)
Tensor<Input2Type> upstreamGrad({1, 2, 2, 2}); // Upstream gradient (dy)
Tensor<OutputType> output({1, 2, 2, 2}); // Downstream gradient (dx)
// Fill forward input with values that will test tanh backward function
input.setHostValue(safeTestTypeCast<Input1Type>(0.0f), 0, 0, 0, 0); // 0.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1), 0, 0, 0, 1); // 1.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_1), 0, 0, 1, 0); // -1.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_2), 0, 0, 1, 1); // 2.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_2), 0, 1, 0, 0); // -2.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_3), 0, 1, 0, 1); // 3.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_3), 0, 1, 1, 0); // -3.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1_5), 0, 1, 1, 1); // 1.5
// Fill upstream gradient with test values
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_2), 0, 0, 0, 0); // 2.0
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_1_5), 0, 0, 0, 1); // 1.5
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_3), 0, 0, 1, 0); // 3.0
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_1), 0, 0, 1, 1); // 1.0
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_2_5), 0, 1, 0, 0); // 2.5
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_4), 0, 1, 0, 1); // 4.0
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_1_5), 0, 1, 1, 0); // 1.5
upstreamGrad.setHostValue(safeTestTypeCast<Input2Type>(TEST_VALUE_3), 0, 1, 1, 1); // 3.0
// Backward activations expect dy first, then x.
CpuReferencePointwiseImpl<OutputType, Input2Type, Input1Type>::pointwiseCompute(
PointwiseMode::TANH_BWD, output, upstreamGrad, input);
// Create expected tensor: dx = dy * (1 - tanh²(x))
// The functor computes in double (default ComputeType), so we must match that
Tensor<OutputType> expected({1, 2, 2, 2});
for(int n = 0; n < 1; ++n)
{
for(int c = 0; c < 2; ++c)
{
for(int h = 0; h < 2; ++h)
{
for(int w = 0; w < 2; ++w)
{
auto inputVal = input.getHostValue(n, c, h, w);
auto upstreamGradVal = upstreamGrad.getHostValue(n, c, h, w);
// Compute using the template ComputeType
auto xCompute = static_cast<ComputeType>(inputVal);
auto dyCompute = static_cast<ComputeType>(upstreamGradVal);
auto tanhVal = std::tanh(xCompute);
auto localGradient = ComputeType{1} - (tanhVal * tanhVal);
auto downstreamGrad = dyCompute * localGradient;
expected.setHostValue(
safeTestTypeCast<OutputType>(downstreamGrad), n, c, h, w);
}
}
}
}
auto tolerance = getDynamicTolerance(PointwiseMode::TANH_BWD, TEST_VALUE_4);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testAbsoluteValueOperation()
{
Tensor<Input1Type> input({1, 2, 2, 2});
Tensor<OutputType> output({1, 2, 2, 2});
// Fill with mix of positive, negative, and zero values
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1), 0, 0, 0, 0); // 1.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_2), 0, 0, 0, 1); // -2.0
input.setHostValue(safeTestTypeCast<Input1Type>(0.0f), 0, 0, 1, 0); // 0.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_3), 0, 0, 1, 1); // -3.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_5), 0, 1, 0, 0); // 5.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_1_5), 0, 1, 0, 1); // -1.5
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_2_5), 0, 1, 1, 0); // 2.5
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_4), 0, 1, 1, 1); // -4.0
CpuReferencePointwiseImpl<OutputType, Input1Type>::pointwiseCompute(
PointwiseMode::ABS, output, input);
// Create expected tensor: abs(x)
Tensor<OutputType> expected({1, 2, 2, 2});
expected.setHostValue(
safeTestTypeCast<OutputType>(hipdnn_data_sdk::types::abs(TEST_VALUE_1)),
0,
0,
0,
0); // |1| = 1
expected.setHostValue(
safeTestTypeCast<OutputType>(hipdnn_data_sdk::types::abs(-TEST_VALUE_2)),
0,
0,
0,
1); // |-2| = 2
expected.setHostValue(
safeTestTypeCast<OutputType>(hipdnn_data_sdk::types::abs(0.0f)), 0, 0, 1, 0); // |0| = 0
expected.setHostValue(
safeTestTypeCast<OutputType>(hipdnn_data_sdk::types::abs(-TEST_VALUE_3)),
0,
0,
1,
1); // |-3| = 3
expected.setHostValue(
safeTestTypeCast<OutputType>(hipdnn_data_sdk::types::abs(TEST_VALUE_5)),
0,
1,
0,
0); // |5| = 5
expected.setHostValue(
safeTestTypeCast<OutputType>(hipdnn_data_sdk::types::abs(-TEST_VALUE_1_5)),
0,
1,
0,
1); // |-1.5| = 1.5
expected.setHostValue(
safeTestTypeCast<OutputType>(hipdnn_data_sdk::types::abs(TEST_VALUE_2_5)),
0,
1,
1,
0); // |2.5| = 2.5
expected.setHostValue(
safeTestTypeCast<OutputType>(hipdnn_data_sdk::types::abs(-TEST_VALUE_4)),
0,
1,
1,
1); // |-4| = 4
auto tolerance = getDynamicTolerance(PointwiseMode::ABS, TEST_VALUE_5);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testNegationOperation()
{
Tensor<Input1Type> input({1, 2, 2, 2});
Tensor<OutputType> output({1, 2, 2, 2});
// Fill with mix of positive, negative, and zero values
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1), 0, 0, 0, 0); // 1.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_2), 0, 0, 0, 1); // -2.0
input.setHostValue(safeTestTypeCast<Input1Type>(0.0f), 0, 0, 1, 0); // 0.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_3), 0, 0, 1, 1); // -3.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_5), 0, 1, 0, 0); // 5.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_1_5), 0, 1, 0, 1); // -1.5
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_2_5), 0, 1, 1, 0); // 2.5
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_4), 0, 1, 1, 1); // -4.0
CpuReferencePointwiseImpl<OutputType, Input1Type>::pointwiseCompute(
PointwiseMode::NEG, output, input);
// Create expected tensor: -x
Tensor<OutputType> expected({1, 2, 2, 2});
expected.setHostValue(safeTestTypeCast<OutputType>(-TEST_VALUE_1), 0, 0, 0, 0); // -1
expected.setHostValue(safeTestTypeCast<OutputType>(TEST_VALUE_2), 0, 0, 0, 1); // -(-2) = 2
expected.setHostValue(safeTestTypeCast<OutputType>(-0.0f), 0, 0, 1, 0); // -0 = 0
expected.setHostValue(safeTestTypeCast<OutputType>(TEST_VALUE_3), 0, 0, 1, 1); // -(-3) = 3
expected.setHostValue(safeTestTypeCast<OutputType>(-TEST_VALUE_5), 0, 1, 0, 0); // -5
expected.setHostValue(
safeTestTypeCast<OutputType>(TEST_VALUE_1_5), 0, 1, 0, 1); // -(-1.5) = 1.5
expected.setHostValue(safeTestTypeCast<OutputType>(-TEST_VALUE_2_5), 0, 1, 1, 0); // -2.5
expected.setHostValue(safeTestTypeCast<OutputType>(TEST_VALUE_4), 0, 1, 1, 1); // -(-4) = 4
auto tolerance = getDynamicTolerance(PointwiseMode::NEG, TEST_VALUE_5);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testGeluForwardOperation()
{
Tensor<Input1Type> input({1, 2, 2, 2});
Tensor<OutputType> output({1, 2, 2, 2});
// Fill with values that will test gelu function
input.setHostValue(safeTestTypeCast<Input1Type>(0.0f), 0, 0, 0, 0); // 0.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1), 0, 0, 0, 1); // 1.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_1), 0, 0, 1, 0); // -1.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_2), 0, 0, 1, 1); // 2.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_2), 0, 1, 0, 0); // -2.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_5), 0, 1, 0, 1); // 5.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_5), 0, 1, 1, 0); // -5.0
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1_5), 0, 1, 1, 1); // 1.5
CpuReferencePointwiseImpl<OutputType, Input1Type>::pointwiseCompute(
PointwiseMode::GELU_FWD, output, input);
// Create expected tensor: gelu_erf(x) = 0.5 * x * (1 + erf(x / sqrt(2)))
auto geluErf = [](float x) {
auto xCompute = static_cast<ComputeType>(safeTestTypeCast<Input1Type>(x));
ComputeType sqrt2 = hipdnn_data_sdk::types::sqrt(ComputeType{2});
return ComputeType{0.5} * xCompute
* (ComputeType{1.0} + hipdnn_data_sdk::types::erf(xCompute / sqrt2));
};
Tensor<OutputType> expected({1, 2, 2, 2});
expected.setHostValue(safeTestTypeCast<OutputType>(geluErf(0.0f)),
0,
0,
0,
0); // gelu_erf(0) = 0.5
expected.setHostValue(safeTestTypeCast<OutputType>(geluErf(TEST_VALUE_1)),
0,
0,
0,
1); // gelu_erf(1)
expected.setHostValue(safeTestTypeCast<OutputType>(geluErf(-TEST_VALUE_1)),
0,
0,
1,
0); // gelu_erf(-1)
expected.setHostValue(safeTestTypeCast<OutputType>(geluErf(TEST_VALUE_2)),
0,
0,
1,
1); // gelu_erf(2)
expected.setHostValue(safeTestTypeCast<OutputType>(geluErf(-TEST_VALUE_2)),
0,
1,
0,
0); // gelu_erf(-2)
expected.setHostValue(safeTestTypeCast<OutputType>(geluErf(TEST_VALUE_5)),
0,
1,
0,
1); // gelu_erf(5)
expected.setHostValue(safeTestTypeCast<OutputType>(geluErf(-TEST_VALUE_5)),
0,
1,
1,
0); // gelu_erf(-5)
expected.setHostValue(safeTestTypeCast<OutputType>(geluErf(TEST_VALUE_1_5)),
0,
1,
1,
1); // gelu_erf(1.5)
auto tolerance = getDynamicTolerance(PointwiseMode::GELU_FWD, TEST_VALUE_5);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testGeluApproxTanhForwardOperation()
{
Tensor<Input1Type> input({1, 2, 2, 2});
Tensor<OutputType> output({1, 2, 2, 2});
input.setHostValue(safeTestTypeCast<Input1Type>(0.0f), 0, 0, 0, 0);
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1), 0, 0, 0, 1);
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_1), 0, 0, 1, 0);
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_2), 0, 0, 1, 1);
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_2), 0, 1, 0, 0);
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_5), 0, 1, 0, 1);
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_5), 0, 1, 1, 0);
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1_5), 0, 1, 1, 1);
CpuReferencePointwiseImpl<OutputType, Input1Type>::pointwiseCompute(
PointwiseMode::GELU_APPROX_TANH_FWD, output, input);
// gelu_tanh(x) ≈ 0.5 * x * (1 + tanh(sqrt(2/π) * (x + 0.044715 * x³)))
auto geluTanh = [](float x) {
auto xCompute = static_cast<ComputeType>(safeTestTypeCast<Input1Type>(x));
const auto sqrt2OverPi = static_cast<ComputeType>(
hipdnn_data_sdk::types::sqrt(ComputeType{2} / static_cast<ComputeType>(PI)));
static const auto s_kCoeff = ComputeType{0.044715};
auto inner = sqrt2OverPi * (xCompute + s_kCoeff * xCompute * xCompute * xCompute);
return ComputeType{0.5} * xCompute
* (ComputeType{1.0} + hipdnn_data_sdk::types::tanh(inner));
};
Tensor<OutputType> expected({1, 2, 2, 2});
expected.setHostValue(safeTestTypeCast<OutputType>(geluTanh(0.0f)), 0, 0, 0, 0);
expected.setHostValue(safeTestTypeCast<OutputType>(geluTanh(TEST_VALUE_1)), 0, 0, 0, 1);
expected.setHostValue(safeTestTypeCast<OutputType>(geluTanh(-TEST_VALUE_1)), 0, 0, 1, 0);
expected.setHostValue(safeTestTypeCast<OutputType>(geluTanh(TEST_VALUE_2)), 0, 0, 1, 1);
expected.setHostValue(safeTestTypeCast<OutputType>(geluTanh(-TEST_VALUE_2)), 0, 1, 0, 0);
expected.setHostValue(safeTestTypeCast<OutputType>(geluTanh(TEST_VALUE_5)), 0, 1, 0, 1);
expected.setHostValue(safeTestTypeCast<OutputType>(geluTanh(-TEST_VALUE_5)), 0, 1, 1, 0);
expected.setHostValue(safeTestTypeCast<OutputType>(geluTanh(TEST_VALUE_1_5)), 0, 1, 1, 1);
auto tolerance = getDynamicTolerance(PointwiseMode::GELU_APPROX_TANH_FWD, TEST_VALUE_5);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testSwishForwardOperation()
{
Tensor<Input1Type> input({1, 2, 2, 2});
Tensor<OutputType> output({1, 2, 2, 2});
input.setHostValue(safeTestTypeCast<Input1Type>(0.0f), 0, 0, 0, 0);
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1), 0, 0, 0, 1);
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_1), 0, 0, 1, 0);
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_2), 0, 0, 1, 1);
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_2), 0, 1, 0, 0);
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_5), 0, 1, 0, 1);
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_5), 0, 1, 1, 0);
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1_5), 0, 1, 1, 1);
CpuReferencePointwiseImpl<OutputType, Input1Type>::pointwiseCompute(
PointwiseMode::SWISH_FWD, output, input);
// swish(x) = x * sigmoid(x) = x / (1 + exp(-x))
auto swish = [](float x) {
auto xCompute = static_cast<ComputeType>(safeTestTypeCast<Input1Type>(x));
auto exp = hipdnn_data_sdk::types::exp(-xCompute);
return xCompute / (ComputeType{1} + exp);
};
Tensor<OutputType> expected({1, 2, 2, 2});
expected.setHostValue(safeTestTypeCast<OutputType>(swish(0.0f)), 0, 0, 0, 0);
expected.setHostValue(safeTestTypeCast<OutputType>(swish(TEST_VALUE_1)), 0, 0, 0, 1);
expected.setHostValue(safeTestTypeCast<OutputType>(swish(-TEST_VALUE_1)), 0, 0, 1, 0);
expected.setHostValue(safeTestTypeCast<OutputType>(swish(TEST_VALUE_2)), 0, 0, 1, 1);
expected.setHostValue(safeTestTypeCast<OutputType>(swish(-TEST_VALUE_2)), 0, 1, 0, 0);
expected.setHostValue(safeTestTypeCast<OutputType>(swish(TEST_VALUE_5)), 0, 1, 0, 1);
expected.setHostValue(safeTestTypeCast<OutputType>(swish(-TEST_VALUE_5)), 0, 1, 1, 0);
expected.setHostValue(safeTestTypeCast<OutputType>(swish(TEST_VALUE_1_5)), 0, 1, 1, 1);
auto tolerance = getDynamicTolerance(PointwiseMode::SWISH_FWD, TEST_VALUE_5);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testUnary1DOperation()
{
Tensor<Input1Type> input({5});
Tensor<OutputType> output({5});
// Fill with simple values for 1D testing
for(int i = 0; i < 5; ++i)
{
input.setHostValue(safeTestTypeCast<Input1Type>(static_cast<float>(i - 2)),
i); // [-2, -1, 0, 1, 2]
}
CpuReferencePointwiseImpl<OutputType, Input1Type>::pointwiseCompute(
PointwiseMode::RELU_FWD, output, input);
// Create expected tensor: ReLU applied to [-2, -1, 0, 1, 2] = [0, 0, 0, 1, 2]
Tensor<OutputType> expected({5});
expected.setHostValue(safeTestTypeCast<OutputType>(0.0f), 0); // max(0, -2) = 0
expected.setHostValue(safeTestTypeCast<OutputType>(0.0f), 1); // max(0, -1) = 0
expected.setHostValue(safeTestTypeCast<OutputType>(0.0f), 2); // max(0, 0) = 0
expected.setHostValue(safeTestTypeCast<OutputType>(1.0f), 3); // max(0, 1) = 1
expected.setHostValue(safeTestTypeCast<OutputType>(2.0f), 4); // max(0, 2) = 2
auto tolerance = getDynamicTolerance(PointwiseMode::RELU_FWD, 2.0f);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testUnary2DOperation()
{
Tensor<Input1Type> input({3, 4});
Tensor<OutputType> output({3, 4});
// Fill with pattern for 2D testing
for(int m = 0; m < 3; ++m)
{
for(int n = 0; n < 4; ++n)
{
input.setHostValue(
safeTestTypeCast<Input1Type>(static_cast<float>((m - 1) + (n - 2))), m, n);
}
}
CpuReferencePointwiseImpl<OutputType, Input1Type>::pointwiseCompute(
PointwiseMode::ABS, output, input);
// Create expected tensor: abs applied to the pattern
Tensor<OutputType> expected({3, 4});
for(int m = 0; m < 3; ++m)
{
for(int n = 0; n < 4; ++n)
{
auto val = static_cast<float>((m - 1) + (n - 2));
expected.setHostValue(
safeTestTypeCast<OutputType>(hipdnn_data_sdk::types::abs(val)), m, n);
}
}
auto tolerance = getDynamicTolerance(PointwiseMode::ABS, 3.0f);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testUnary3DOperation()
{
Tensor<Input1Type> input({2, 3, 4});
Tensor<OutputType> output({2, 3, 4});
input.fillWithValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_2_5));
CpuReferencePointwiseImpl<OutputType, Input1Type>::pointwiseCompute(
PointwiseMode::NEG, output, input);
// Create expected tensor: negation of -2.5 = 2.5
Tensor<OutputType> expected({2, 3, 4});
expected.fillWithValue(safeTestTypeCast<OutputType>(TEST_VALUE_2_5));
auto tolerance = getDynamicTolerance(PointwiseMode::NEG, TEST_VALUE_2_5);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testUnarySingleElementTensor()
{
Tensor<Input1Type> input({1, 1, 1, 1});
Tensor<OutputType> output({1, 1, 1, 1});
input.setHostValue(safeTestTypeCast<Input1Type>(E), 0, 0, 0, 0); // e
CpuReferencePointwiseImpl<OutputType, Input1Type>::pointwiseCompute(
PointwiseMode::TANH_FWD, output, input);
// Create expected tensor: tanh(e)
Tensor<OutputType> expected({1, 1, 1, 1});
expected.setHostValue(safeTestTypeCast<OutputType>(std::tanh(E)), 0, 0, 0, 0);
auto tolerance = getDynamicTolerance(PointwiseMode::TANH_FWD, E);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
void testIdentityOperation()
{
Tensor<Input1Type> input({1, 2, 2, 2});
Tensor<OutputType> output({1, 2, 2, 2});
// Fill with mix of positive, negative, and zero values
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_1), 0, 0, 0, 0); // 1.0
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_2), 0, 0, 0, 1); // -2.0
input.setHostValue(safeTestTypeCast<Input1Type>(0.0f), 0, 0, 1, 0); // 0.0
input.setHostValue(safeTestTypeCast<Input1Type>(PI), 0, 0, 1, 1); // π
input.setHostValue(safeTestTypeCast<Input1Type>(E), 0, 1, 0, 0); // e
input.setHostValue(safeTestTypeCast<Input1Type>(-TEST_VALUE_1_5), 0, 1, 0, 1); // -1.5
input.setHostValue(safeTestTypeCast<Input1Type>(TEST_VALUE_2_5), 0, 1, 1, 0); // 2.5
input.setHostValue(safeTestTypeCast<Input1Type>(SQRT_2), 0, 1, 1, 1); // √2
CpuReferencePointwiseImpl<OutputType, Input1Type>::pointwiseCompute(
PointwiseMode::IDENTITY, output, input);
// Create expected tensor: identity(x) = x
Tensor<OutputType> expected({1, 2, 2, 2});
expected.setHostValue(safeTestTypeCast<OutputType>(TEST_VALUE_1), 0, 0, 0, 0); // 1.0
expected.setHostValue(safeTestTypeCast<OutputType>(-TEST_VALUE_2), 0, 0, 0, 1); // -2.0
expected.setHostValue(safeTestTypeCast<OutputType>(0.0f), 0, 0, 1, 0); // 0.0
expected.setHostValue(safeTestTypeCast<OutputType>(PI), 0, 0, 1, 1); // π
expected.setHostValue(safeTestTypeCast<OutputType>(E), 0, 1, 0, 0); // e
expected.setHostValue(safeTestTypeCast<OutputType>(-TEST_VALUE_1_5), 0, 1, 0, 1); // -1.5
expected.setHostValue(safeTestTypeCast<OutputType>(TEST_VALUE_2_5), 0, 1, 1, 0); // 2.5
expected.setHostValue(safeTestTypeCast<OutputType>(SQRT_2), 0, 1, 1, 1); // √2
auto tolerance = getDynamicTolerance(PointwiseMode::IDENTITY, PI);
auto validator = createAllCloseValidator<OutputType>(tolerance, tolerance);
EXPECT_TRUE(validator->allClose(expected, output));
}
};
using TestTypes = ::testing::Types<float, half, bfloat16, double, int8_t>;
// Empty third argument required for C++17 compatibility with TYPED_TEST_SUITE macro
TYPED_TEST_SUITE(CpuReferencePointwiseFixture, TestTypes, );
// Binary operation tests
TYPED_TEST(CpuReferencePointwiseFixture, BinaryAddOperation)
{
this->testBinaryAddOperation();
}
TYPED_TEST(CpuReferencePointwiseFixture, BinarySubtractOperation)
{
this->testBinarySubtractOperation();
}
TYPED_TEST(CpuReferencePointwiseFixture, BinaryAddOperationSanityValidation)
{
this->testBinaryAddOperationSanityValidation();
}
TYPED_TEST(CpuReferencePointwiseFixture, BinarySubtractOperationSanityValidation)
{
this->testBinarySubtractOperationSanityValidation();
}
TYPED_TEST(CpuReferencePointwiseFixture, BinarySingleElementTensors)
{
this->testBinarySingleElementTensors();
}
TYPED_TEST(CpuReferencePointwiseFixture, BinaryNumericalPrecision)
{
// This test uses fractional precision test values,
// which don't make sense for integer types since they'd be truncated to 0.
if constexpr(std::is_integral_v<TypeParam>)
{
GTEST_SKIP() << "Skipping numerical precision test for integer types";
}
this->testBinaryNumericalPrecision();
}
TYPED_TEST(CpuReferencePointwiseFixture, BinaryElementwise1D)
{
this->testElementwise1D();
}
TYPED_TEST(CpuReferencePointwiseFixture, BinaryBroadcast2Dx1D)
{
this->testBroadcast2Dx1D();
}
TYPED_TEST(CpuReferencePointwiseFixture, BinaryBroadcast3D)
{
this->testBroadcast3D();
}
TYPED_TEST(CpuReferencePointwiseFixture, BinaryBroadcast3DImplicitLeading)
{
this->testBroadcast3DImplicitLeading();
}
TYPED_TEST(CpuReferencePointwiseFixture, BinaryBroadcast4Dx4D)
{
this->testBroadcast4Dx4D();
}
TYPED_TEST(CpuReferencePointwiseFixture, BinaryBroadcastComplexND)
{
this->testBroadcastComplexND();
}
TYPED_TEST(CpuReferencePointwiseFixture, BinaryBroadcast5D)
{
this->testBroadcast5D();
}
// Unary operation tests
TYPED_TEST(CpuReferencePointwiseFixture, UnaryReluForward)
{
this->testReluForwardOperation();
}
TYPED_TEST(CpuReferencePointwiseFixture, UnaryReluBackward)
{
this->testReluBackwardOperation();
}
TYPED_TEST(CpuReferencePointwiseFixture, UnaryParameterizedReluForward)
{
this->testParameterizedReluForwardOperation();
}
TYPED_TEST(CpuReferencePointwiseFixture, UnaryParameterizedReluBackward)
{
this->testParameterizedReluBackwardOperation();
}
TYPED_TEST(CpuReferencePointwiseFixture, UnarySigmoidForward)
{
this->testSigmoidForwardOperation();
}
TYPED_TEST(CpuReferencePointwiseFixture, UnarySigmoidBackward)
{
this->testSigmoidBackwardOperation();
}
TYPED_TEST(CpuReferencePointwiseFixture, UnaryTanhForward)
{
this->testTanhForwardOperation();
}
TYPED_TEST(CpuReferencePointwiseFixture, UnaryTanhBackward)
{
this->testTanhBackwardOperation();
}
TYPED_TEST(CpuReferencePointwiseFixture, UnaryAbsoluteValue)
{
this->testAbsoluteValueOperation();
}
TYPED_TEST(CpuReferencePointwiseFixture, UnaryNegation)
{
this->testNegationOperation();
}
TYPED_TEST(CpuReferencePointwiseFixture, UnaryGeluForward)
{
this->testGeluForwardOperation();
}
TYPED_TEST(CpuReferencePointwiseFixture, UnaryGeluApproxTanhForward)
{
this->testGeluApproxTanhForwardOperation();
}
TYPED_TEST(CpuReferencePointwiseFixture, UnarySwishForward)
{
this->testSwishForwardOperation();
}
TYPED_TEST(CpuReferencePointwiseFixture, Unary1DOperation)
{
this->testUnary1DOperation();
}
TYPED_TEST(CpuReferencePointwiseFixture, Unary2DOperation)
{
this->testUnary2DOperation();
}
TYPED_TEST(CpuReferencePointwiseFixture, Unary3DOperation)
{
this->testUnary3DOperation();
}
TYPED_TEST(CpuReferencePointwiseFixture, UnarySingleElementTensor)
{
this->testUnarySingleElementTensor();
}
TYPED_TEST(CpuReferencePointwiseFixture, UnaryIdentity)
{
this->testIdentityOperation();
}
// Mixed-type binary test instantiations
using TestCpuReferencePointwiseBinaryMixed1Bfp16
= CpuReferencePointwiseFixture<float, half, bfloat16>;
using TestCpuReferencePointwiseBinaryMixed2Bfp16
= CpuReferencePointwiseFixture<half, float, bfloat16>;
using TestCpuReferencePointwiseBinaryMixed1Fp16 = CpuReferencePointwiseFixture<float, float, half>;
using TestCpuReferencePointwiseBinaryMixed2Fp16
= CpuReferencePointwiseFixture<bfloat16, float, half>;
using TestCpuReferencePointwiseBinaryMixed1Fp32
= CpuReferencePointwiseFixture<bfloat16, half, float>;
using TestCpuReferencePointwiseBinaryMixed3Fp16
= CpuReferencePointwiseFixture<float, bfloat16, half>;
using TestCpuReferencePointwiseBinaryMixed2Fp32
= CpuReferencePointwiseFixture<half, bfloat16, float>;
// Test a sample of mixed-type binary operations
TEST_F(TestCpuReferencePointwiseBinaryMixed1Bfp16, BinaryMixedTypeAddOperation)
{
this->testBinaryAddOperation();
}
TEST_F(TestCpuReferencePointwiseBinaryMixed1Bfp16, BinaryMixedTypeSubtractOperation)
{
this->testBinarySubtractOperation();
}
TEST_F(TestCpuReferencePointwiseBinaryMixed2Bfp16, BinaryMixedTypeAddOperation)
{
this->testBinaryAddOperation();
}
TEST_F(TestCpuReferencePointwiseBinaryMixed2Bfp16, BinaryMixedTypeSubtractOperation)
{
this->testBinarySubtractOperation();
}
TEST_F(TestCpuReferencePointwiseBinaryMixed1Fp16, BinaryMixedTypeAddOperation)
{
this->testBinaryAddOperation();
}
TEST_F(TestCpuReferencePointwiseBinaryMixed1Fp16, BinaryMixedTypeSubtractOperation)
{
this->testBinarySubtractOperation();
}
TEST_F(TestCpuReferencePointwiseBinaryMixed2Fp16, BinaryMixedTypeAddOperation)
{
this->testBinaryAddOperation();
}
TEST_F(TestCpuReferencePointwiseBinaryMixed2Fp16, BinaryMixedTypeSubtractOperation)
{
this->testBinarySubtractOperation();
}
TEST_F(TestCpuReferencePointwiseBinaryMixed1Fp32, BinaryMixedTypeAddOperation)
{
this->testBinaryAddOperation();
}
TEST_F(TestCpuReferencePointwiseBinaryMixed1Fp32, BinaryMixedTypeSubtractOperation)
{
this->testBinarySubtractOperation();
}
TEST_F(TestCpuReferencePointwiseBinaryMixed3Fp16, BinaryMixedTypeAddOperation)
{
this->testBinaryAddOperation();
}
TEST_F(TestCpuReferencePointwiseBinaryMixed3Fp16, BinaryMixedTypeSubtractOperation)
{
this->testBinarySubtractOperation();
}
TEST_F(TestCpuReferencePointwiseBinaryMixed2Fp32, BinaryMixedTypeAddOperation)
{
this->testBinaryAddOperation();
}
TEST_F(TestCpuReferencePointwiseBinaryMixed2Fp32, BinaryMixedTypeSubtractOperation)
{
this->testBinarySubtractOperation();
}
// Mixed-type unary test instantiations
using TestCpuReferencePointwiseUnaryMixedFp16 = CpuReferencePointwiseFixture<float, float, half>;
using TestCpuReferencePointwiseUnaryMixed1Fp32 = CpuReferencePointwiseFixture<half, half, float>;
using TestCpuReferencePointwiseUnaryMixed1Bfp16
= CpuReferencePointwiseFixture<float, float, bfloat16>;
using TestCpuReferencePointwiseUnaryMixed2Fp32
= CpuReferencePointwiseFixture<bfloat16, bfloat16, float>;
using TestCpuReferencePointwiseUnaryMixed2Bfp16
= CpuReferencePointwiseFixture<half, half, bfloat16>;
using TestCpuReferencePointwiseUnaryMixed2Fp16
= CpuReferencePointwiseFixture<bfloat16, bfloat16, half>;
// Test a sample of mixed-type unary operations
TEST_F(TestCpuReferencePointwiseUnaryMixedFp16, UnaryMixedTypeReluForward)
{
this->testReluForwardOperation();
}
TEST_F(TestCpuReferencePointwiseUnaryMixedFp16, UnaryMixedTypeSigmoidForward)
{
this->testSigmoidForwardOperation();
}
TEST_F(TestCpuReferencePointwiseUnaryMixedFp16, UnaryMixedTypeGeluForward)
{
this->testGeluForwardOperation();
}
TEST_F(TestCpuReferencePointwiseUnaryMixedFp16, UnaryMixedTypeGeluApproxTanhForward)
{
this->testGeluApproxTanhForwardOperation();
}
TEST_F(TestCpuReferencePointwiseUnaryMixedFp16, UnaryMixedTypeSwishForward)
{
this->testSwishForwardOperation();
}
TEST_F(TestCpuReferencePointwiseUnaryMixed1Fp32, UnaryMixedTypeAbsoluteValue)
{
this->testAbsoluteValueOperation();
}
TEST_F(TestCpuReferencePointwiseUnaryMixed1Fp32, UnaryMixedTypeNegation)
{
this->testNegationOperation();
}
TEST_F(TestCpuReferencePointwiseUnaryMixed1Bfp16, UnaryMixedTypeTanhForward)
{
this->testTanhForwardOperation();
}
TEST_F(TestCpuReferencePointwiseUnaryMixed1Bfp16, UnaryMixedTypeGeluForward)
{
this->testGeluForwardOperation();
}
TEST_F(TestCpuReferencePointwiseUnaryMixed1Bfp16, UnaryMixedTypeGeluApproxTanhForward)
{
this->testGeluApproxTanhForwardOperation();
}
TEST_F(TestCpuReferencePointwiseUnaryMixed1Bfp16, UnaryMixedTypeSwishForward)
{
this->testSwishForwardOperation();
}
TEST_F(TestCpuReferencePointwiseUnaryMixed1Bfp16, UnaryMixedTypeReluBackward)
{
this->testReluBackwardOperation();
}
TEST_F(TestCpuReferencePointwiseUnaryMixed2Fp32, UnaryMixedTypeParameterizedRelu)
{
this->testParameterizedReluForwardOperation();
}
TEST_F(TestCpuReferencePointwiseUnaryMixed2Fp32, UnaryMixedTypeSigmoidBackward)
{
this->testSigmoidBackwardOperation();
}
TEST_F(TestCpuReferencePointwiseUnaryMixed2Bfp16, UnaryMixedTypeTanhBackward)
{
this->testTanhBackwardOperation();
}
TEST_F(TestCpuReferencePointwiseUnaryMixed2Bfp16, UnaryMixedTypeAbsoluteValue)
{
this->testAbsoluteValueOperation();
}
TEST_F(TestCpuReferencePointwiseUnaryMixed2Fp16, UnaryMixedTypeReluForward)
{
this->testReluForwardOperation();
}
TEST_F(TestCpuReferencePointwiseUnaryMixed2Fp16, UnaryMixedTypeParameterizedReluBackward)
{
this->testParameterizedReluBackwardOperation();
}
TEST_F(TestCpuReferencePointwiseUnaryMixed2Fp16, UnaryMixedTypeIdentity)
{
this->testIdentityOperation();
}
TEST_F(TestCpuReferencePointwiseUnaryMixed2Fp32, UnaryMixedTypeGeluForward)
{
this->testGeluForwardOperation();
}
TEST_F(TestCpuReferencePointwiseUnaryMixed2Fp32, UnaryMixedTypeGeluApproxTanhForward)
{
this->testGeluApproxTanhForwardOperation();
}
TEST_F(TestCpuReferencePointwiseUnaryMixed2Fp32, UnaryMixedTypeSwishForward)
{
this->testSwishForwardOperation();
}
// --- Regression tests ---
TEST(TestCpuReferencePointwiseRegressions, ParameterizedReluBwdBfloat16LowerClipEdgeCase)
{
// In bfloat16, a value higher than lowerClip can be equal to lowerClip if lowerClip itself is cast to bfloat16. However, this value should still register as higher than lowerClip.
Tensor<bfloat16> dy({1, 1, 1, 1});
Tensor<bfloat16> y({1, 1, 1, 1});
Tensor<bfloat16> output({1, 1, 1, 1});
const float lowerClip = 0.1f;
const float upperClip = 0.5f;
const float lowerSlope = 0.2f;
// Set y to a value slightly above lowerClip. In bfloat16, this is equal to lowerClip
dy.setHostValue(safeTestTypeCast<bfloat16>(1.0f), 0, 0, 0, 0);
dy.markHostModified();
y.setHostValue(safeTestTypeCast<bfloat16>(0.10009765625f), 0, 0, 0, 0);
y.markHostModified();
EXPECT_NE(lowerClip, safeTestTypeCast<float>(y.getHostValue(0, 0, 0, 0)));
CpuReferencePointwiseImpl<bfloat16, bfloat16, bfloat16>::pointwiseCompute(
PointwiseMode::RELU_BWD, output, dy, y, lowerClip, upperClip, lowerSlope);
output.markDeviceModified();
EXPECT_EQ(output.getHostValue(0, 0, 0, 0), dy.getHostValue(0, 0, 0, 0));
}