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

504 lines
20 KiB
C++

// Copyright © Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#include <gtest/gtest.h>
#include <cstdint>
#include <memory>
#include <set>
#include <vector>
#include <hipdnn_data_sdk/utilities/Tensor.hpp>
#include <hipdnn_flatbuffers_sdk/data_objects/graph_generated.h>
#include "harness/input-init/SynthesizeInputs.hpp"
#include <hipdnn_test_sdk/utilities/FlatbufferGraphTestUtils.hpp>
#include <hipdnn_test_sdk/utilities/detail/FlatbufferTensorAttributesUtils.hpp>
// NOLINTBEGIN(readability-identifier-naming)
using namespace hipdnn_flatbuffers_sdk::data_objects;
using namespace hipdnn_integration_tests;
namespace
{
const std::vector<int64_t> kDims = {2, 3};
const std::vector<int64_t> kStrides = {3, 1};
InputTensorMap makeTensors(const std::vector<int64_t>& uids)
{
InputTensorMap map;
for(const int64_t uid : uids)
{
map[uid] = std::make_unique<hipdnn_data_sdk::utilities::Tensor<float>>(kDims, kStrides);
map[uid]->fillTensorWithValue(0.f);
}
return map;
}
struct GraphResult
{
flatbuffers::FlatBufferBuilder builder;
const Graph* graph = nullptr;
const Node& node(uint32_t i) const
{
return *graph->nodes()->Get(i);
}
std::vector<int64_t> leafInputUids(const std::set<int64_t>& outputUids) const
{
std::vector<int64_t> uids;
for(const auto* t : *graph->tensors())
{
if(!t->virtual_() && outputUids.count(t->uid()) == 0)
{
uids.push_back(t->uid());
}
}
return uids;
}
};
// ── Conv fwd (single node) ──────────────────────────────────────────────────
GraphResult buildConvFwdGraph()
{
GraphResult r;
auto& b = r.builder;
std::vector<flatbuffers::Offset<TensorAttributes>> tensors;
tensors.push_back(CreateTensorAttributesDirect(b, 1, "x", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(CreateTensorAttributesDirect(b, 2, "w", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(CreateTensorAttributesDirect(b, 3, "y", DataType::FLOAT, &kStrides, &kDims));
auto conv = CreateConvolutionFwdAttributesDirect(b, 1, 2, 3);
std::vector<flatbuffers::Offset<Node>> nodes;
nodes.push_back(CreateNodeDirect(
b, "conv", DataType::FLOAT, NodeAttributes::ConvolutionFwdAttributes, conv.Union()));
auto graph = CreateGraphDirect(
b, "test", DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, &tensors, &nodes);
b.Finish(graph);
r.graph = GetGraph(b.GetBufferPointer());
return r;
}
// ── Conv + bias (2-node fused) ──────────────────────────────────────────────
// conv.y (uid 10) is virtual; bias (uid 4) is leaf
GraphResult buildConvBiasGraph()
{
GraphResult r;
auto& b = r.builder;
std::vector<flatbuffers::Offset<TensorAttributes>> tensors;
tensors.push_back(CreateTensorAttributesDirect(b, 1, "x", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(CreateTensorAttributesDirect(b, 2, "w", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(
CreateTensorAttributesDirect(b, 10, "conv_y", DataType::FLOAT, &kStrides, &kDims, true));
tensors.push_back(
CreateTensorAttributesDirect(b, 4, "bias", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(
CreateTensorAttributesDirect(b, 5, "out", DataType::FLOAT, &kStrides, &kDims));
auto conv = CreateConvolutionFwdAttributesDirect(b, 1, 2, 10);
auto add = CreatePointwiseAttributes(b,
PointwiseMode::ADD,
flatbuffers::nullopt,
flatbuffers::nullopt,
flatbuffers::nullopt,
flatbuffers::nullopt,
10,
4,
flatbuffers::nullopt,
5);
std::vector<flatbuffers::Offset<Node>> nodes;
nodes.push_back(CreateNodeDirect(
b, "conv", DataType::FLOAT, NodeAttributes::ConvolutionFwdAttributes, conv.Union()));
nodes.push_back(CreateNodeDirect(
b, "bias_add", DataType::FLOAT, NodeAttributes::PointwiseAttributes, add.Union()));
auto graph = CreateGraphDirect(
b, "test", DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, &tensors, &nodes);
b.Finish(graph);
r.graph = GetGraph(b.GetBufferPointer());
return r;
}
// ── Conv + bias + relu (3-node fused) ───────────────────────────────────────
// conv.y (uid 10) virtual, bias_add.out (uid 11) virtual, relu.in_0=uid 11, relu.out_0=uid 6
GraphResult buildConvBiasReluGraph()
{
GraphResult r;
auto& b = r.builder;
std::vector<flatbuffers::Offset<TensorAttributes>> tensors;
tensors.push_back(CreateTensorAttributesDirect(b, 1, "x", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(CreateTensorAttributesDirect(b, 2, "w", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(
CreateTensorAttributesDirect(b, 10, "conv_y", DataType::FLOAT, &kStrides, &kDims, true));
tensors.push_back(
CreateTensorAttributesDirect(b, 4, "bias", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(
CreateTensorAttributesDirect(b, 11, "bias_out", DataType::FLOAT, &kStrides, &kDims, true));
tensors.push_back(
CreateTensorAttributesDirect(b, 6, "out", DataType::FLOAT, &kStrides, &kDims));
auto conv = CreateConvolutionFwdAttributesDirect(b, 1, 2, 10);
auto add = CreatePointwiseAttributes(b,
PointwiseMode::ADD,
flatbuffers::nullopt,
flatbuffers::nullopt,
flatbuffers::nullopt,
flatbuffers::nullopt,
10,
4,
flatbuffers::nullopt,
11);
auto relu = CreatePointwiseAttributes(b,
PointwiseMode::RELU_FWD,
flatbuffers::nullopt,
flatbuffers::nullopt,
flatbuffers::nullopt,
flatbuffers::nullopt,
11,
flatbuffers::nullopt,
flatbuffers::nullopt,
6);
std::vector<flatbuffers::Offset<Node>> nodes;
nodes.push_back(CreateNodeDirect(
b, "conv", DataType::FLOAT, NodeAttributes::ConvolutionFwdAttributes, conv.Union()));
nodes.push_back(CreateNodeDirect(
b, "bias_add", DataType::FLOAT, NodeAttributes::PointwiseAttributes, add.Union()));
nodes.push_back(CreateNodeDirect(
b, "relu", DataType::FLOAT, NodeAttributes::PointwiseAttributes, relu.Union()));
auto graph = CreateGraphDirect(
b, "test", DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, &tensors, &nodes);
b.Finish(graph);
r.graph = GetGraph(b.GetBufferPointer());
return r;
}
// ── Batchnorm training with runtime PBV scalars ─────────────────────────────
// uids: x=1, y=2, scale=3, bias=4, epsilon=5, prev_mean=8, prev_variance=9, momentum=10
GraphResult buildBatchnormTrainingRuntimePbvGraph()
{
GraphResult result;
result.builder = hipdnn_test_sdk::utilities::createValidBatchnormFwdTrainingGraph(
kStrides,
kDims,
/*withMeanVariance=*/false,
/*overrideShapeEnabled=*/false,
/*runtimeEpsilon=*/true,
/*withRunningStatsAndMomentum=*/true,
/*runtimeMomentum=*/true);
result.graph = GetGraph(result.builder.GetBufferPointer());
return result;
}
InputTensorMap makeTensorsFromGraph(const GraphResult& gr, const std::vector<int64_t>& uids)
{
std::unordered_map<int64_t, const TensorAttributes*> attributes;
for(const auto* tensor : *gr.graph->tensors())
{
attributes.emplace(tensor->uid(), tensor);
}
InputTensorMap inputs;
for(const int64_t uid : uids)
{
inputs.emplace(uid,
hipdnn_test_sdk::detail::createTensorFromAttribute(*attributes.at(uid)));
}
return inputs;
}
float scalarValue(const InputTensorMap& inputs, int64_t uid)
{
return *static_cast<const float*>(inputs.at(uid)->rawHostData());
}
// ── SDPA forward (no structured optionals) ──────────────────────────────────
GraphResult buildSdpaFwdGraph()
{
GraphResult r;
auto& b = r.builder;
std::vector<flatbuffers::Offset<TensorAttributes>> tensors;
tensors.push_back(CreateTensorAttributesDirect(b, 1, "q", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(CreateTensorAttributesDirect(b, 2, "k", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(CreateTensorAttributesDirect(b, 3, "v", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(CreateTensorAttributesDirect(b, 4, "o", DataType::FLOAT, &kStrides, &kDims));
auto sdpa = CreateSdpaAttributes(b, 1, 2, 3, 4);
std::vector<flatbuffers::Offset<Node>> nodes;
nodes.push_back(CreateNodeDirect(
b, "sdpa_fwd", DataType::FLOAT, NodeAttributes::SdpaAttributes, sdpa.Union()));
auto graph = CreateGraphDirect(
b, "test", DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, &tensors, &nodes);
b.Finish(graph);
r.graph = GetGraph(b.GetBufferPointer());
return r;
}
// ── SDPA forward with structured seq_len_q ──────────────────────────────────
GraphResult buildSdpaFwdWithStructuredGraph()
{
GraphResult r;
auto& b = r.builder;
std::vector<flatbuffers::Offset<TensorAttributes>> tensors;
tensors.push_back(CreateTensorAttributesDirect(b, 1, "q", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(CreateTensorAttributesDirect(b, 2, "k", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(CreateTensorAttributesDirect(b, 3, "v", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(CreateTensorAttributesDirect(b, 4, "o", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(
CreateTensorAttributesDirect(b, 5, "seq_len_q", DataType::FLOAT, &kStrides, &kDims));
auto sdpa = CreateSdpaAttributes(b,
1,
2,
3,
4,
flatbuffers::nullopt, // attn_mask
flatbuffers::nullopt, // scale
5); // seq_len_q
std::vector<flatbuffers::Offset<Node>> nodes;
nodes.push_back(CreateNodeDirect(
b, "sdpa_fwd", DataType::FLOAT, NodeAttributes::SdpaAttributes, sdpa.Union()));
auto graph = CreateGraphDirect(
b, "test", DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, &tensors, &nodes);
b.Finish(graph);
r.graph = GetGraph(b.GetBufferPointer());
return r;
}
// ── SDPA backward standalone ────────────────────────────────────────────────
// O and stats are leaf inputs (not virtual) → DERIVED → refuses
GraphResult buildSdpaBwdStandaloneGraph()
{
GraphResult r;
auto& b = r.builder;
std::vector<flatbuffers::Offset<TensorAttributes>> tensors;
tensors.push_back(CreateTensorAttributesDirect(b, 1, "q", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(CreateTensorAttributesDirect(b, 2, "k", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(CreateTensorAttributesDirect(b, 3, "v", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(CreateTensorAttributesDirect(b, 4, "o", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(CreateTensorAttributesDirect(b, 5, "do", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(
CreateTensorAttributesDirect(b, 6, "stats", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(CreateTensorAttributesDirect(b, 7, "dq", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(CreateTensorAttributesDirect(b, 8, "dk", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(CreateTensorAttributesDirect(b, 9, "dv", DataType::FLOAT, &kStrides, &kDims));
auto bwd = CreateSdpaBackwardAttributes(b, 1, 2, 3, 4, 5, 6, 7, 8, 9);
std::vector<flatbuffers::Offset<Node>> nodes;
nodes.push_back(CreateNodeDirect(
b, "sdpa_bwd", DataType::FLOAT, NodeAttributes::SdpaBackwardAttributes, bwd.Union()));
auto graph = CreateGraphDirect(
b, "test", DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, &tensors, &nodes);
b.Finish(graph);
r.graph = GetGraph(b.GetBufferPointer());
return r;
}
// ── SDPA fwd+bwd fused ─────────────────────────────────────────────────────
// O (uid 10) and stats (uid 11) are virtual inter-node tensors.
// Leaf inputs: Q(1), K(2), V(3) from fwd + dO(5) from bwd.
// Outputs: dQ(7), dK(8), dV(9).
GraphResult buildSdpaFwdBwdFusedGraph()
{
GraphResult r;
auto& b = r.builder;
std::vector<flatbuffers::Offset<TensorAttributes>> tensors;
tensors.push_back(CreateTensorAttributesDirect(b, 1, "q", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(CreateTensorAttributesDirect(b, 2, "k", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(CreateTensorAttributesDirect(b, 3, "v", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(
CreateTensorAttributesDirect(b, 10, "o", DataType::FLOAT, &kStrides, &kDims, true));
tensors.push_back(
CreateTensorAttributesDirect(b, 11, "stats", DataType::FLOAT, &kStrides, &kDims, true));
tensors.push_back(CreateTensorAttributesDirect(b, 5, "do", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(CreateTensorAttributesDirect(b, 7, "dq", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(CreateTensorAttributesDirect(b, 8, "dk", DataType::FLOAT, &kStrides, &kDims));
tensors.push_back(CreateTensorAttributesDirect(b, 9, "dv", DataType::FLOAT, &kStrides, &kDims));
auto fwd = CreateSdpaAttributes(b,
1,
2,
3,
10,
flatbuffers::nullopt,
flatbuffers::nullopt,
flatbuffers::nullopt,
flatbuffers::nullopt,
flatbuffers::nullopt,
flatbuffers::nullopt,
flatbuffers::nullopt,
flatbuffers::nullopt,
flatbuffers::nullopt,
flatbuffers::nullopt,
flatbuffers::nullopt,
flatbuffers::nullopt,
flatbuffers::nullopt,
flatbuffers::nullopt,
flatbuffers::nullopt,
flatbuffers::nullopt,
flatbuffers::nullopt,
flatbuffers::nullopt,
11); // stats_tensor_uid
auto bwd = CreateSdpaBackwardAttributes(b, 1, 2, 3, 10, 5, 11, 7, 8, 9);
std::vector<flatbuffers::Offset<Node>> nodes;
nodes.push_back(CreateNodeDirect(
b, "sdpa_fwd", DataType::FLOAT, NodeAttributes::SdpaAttributes, fwd.Union()));
nodes.push_back(CreateNodeDirect(
b, "sdpa_bwd", DataType::FLOAT, NodeAttributes::SdpaBackwardAttributes, bwd.Union()));
auto graph = CreateGraphDirect(
b, "test", DataType::FLOAT, DataType::FLOAT, DataType::FLOAT, &tensors, &nodes);
b.Finish(graph);
r.graph = GetGraph(b.GetBufferPointer());
return r;
}
SynthesisResult runSynthesis(const GraphResult& gr, const std::set<int64_t>& outputUids)
{
const auto leafUids = gr.leafInputUids(outputUids);
auto inputs = makeTensors(leafUids);
SynthesisConfig config;
synthesizeInputs(*gr.graph, inputs, leafUids, config);
auto missing = config.unfilled(leafUids);
if(!missing.empty())
{
std::string msg = "cannot synthesize:";
for(const int64_t uid : missing)
{
const auto init = config.fill(uid);
const char* kind = init.kind == FillSpec::Kind::STRUCTURED ? "structured" : "derived";
msg += " uid=" + std::to_string(uid) + " (" + kind + ")";
}
return SynthesisResult::unsupported(msg);
}
return SynthesisResult::ok();
}
} // namespace
// ── Test cases ──────────────────────────────────────────────────────────────
TEST(TestSynthesizeInputs, SingleConvFwd)
{
const auto gr = buildConvFwdGraph();
const auto result = runSynthesis(gr, {3});
EXPECT_TRUE(result.filled) << result.reason;
}
TEST(TestSynthesizeInputs, ConvPlusBiasFused)
{
const auto gr = buildConvBiasGraph();
const auto result = runSynthesis(gr, {5});
EXPECT_TRUE(result.filled) << result.reason;
}
TEST(TestSynthesizeInputs, ConvPlusBiasPlusReluFused)
{
const auto gr = buildConvBiasReluGraph();
const auto result = runSynthesis(gr, {6});
EXPECT_TRUE(result.filled) << result.reason;
}
// Unit seam: verify the synthesis policy itself. Epsilon is the fixed-value
// path; momentum is the seeded random path and must reproduce across runs.
TEST(TestSynthesizeInputs, RuntimePbvScalarsUseFixedAndDeterministicRandomFills)
{
const auto graph = buildBatchnormTrainingRuntimePbvGraph();
const std::vector<int64_t> leafUids = {1, 3, 4, 5, 8, 9, 10};
auto firstInputs = makeTensorsFromGraph(graph, leafUids);
SynthesisConfig firstConfig;
const auto firstResult = synthesizeInputs(*graph.graph, firstInputs, leafUids, firstConfig);
ASSERT_TRUE(firstResult.filled) << firstResult.reason;
EXPECT_FLOAT_EQ(scalarValue(firstInputs, 5), 1e-5f);
const float firstMomentum = scalarValue(firstInputs, 10);
EXPECT_GE(firstMomentum, 0.0f);
EXPECT_LE(firstMomentum, 1.0f);
auto secondInputs = makeTensorsFromGraph(graph, leafUids);
SynthesisConfig secondConfig;
const auto secondResult = synthesizeInputs(*graph.graph, secondInputs, leafUids, secondConfig);
ASSERT_TRUE(secondResult.filled) << secondResult.reason;
EXPECT_FLOAT_EQ(scalarValue(secondInputs, 10), firstMomentum);
}
TEST(TestSynthesizeInputs, SdpaFwdNoStructuredOptionals)
{
const auto gr = buildSdpaFwdGraph();
const auto result = runSynthesis(gr, {4});
EXPECT_TRUE(result.filled) << result.reason;
}
TEST(TestSynthesizeInputs, SdpaFwdWithStructuredInputRefuses)
{
const auto gr = buildSdpaFwdWithStructuredGraph();
const auto result = runSynthesis(gr, {4});
EXPECT_FALSE(result.filled);
EXPECT_NE(result.reason.find("uid=5"), std::string::npos);
EXPECT_NE(result.reason.find("structured"), std::string::npos);
}
TEST(TestSynthesizeInputs, SdpaBwdStandaloneRefusesDerived)
{
const auto gr = buildSdpaBwdStandaloneGraph();
const auto result = runSynthesis(gr, {7, 8, 9});
EXPECT_FALSE(result.filled);
EXPECT_NE(result.reason.find("derived"), std::string::npos);
}
TEST(TestSynthesizeInputs, SdpaFwdBwdFusedSucceeds)
{
const auto gr = buildSdpaFwdBwdFusedGraph();
const auto result = runSynthesis(gr, {7, 8, 9});
EXPECT_TRUE(result.filled) << result.reason;
}
// NOLINTEND(readability-identifier-naming)