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

311 lines
11 KiB
C++

// Copyright Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/generators/catch_generators_adapters.hpp>
#include <catch2/generators/catch_generators_random.hpp>
#include <catch2/generators/catch_generators_range.hpp>
#include <rocRoller/TensorDescriptor.hpp>
#include <rocRoller/Utilities/Random.hpp>
#include <cstddef>
using namespace rocRoller;
TEST_CASE("TensorDescriptor basic functionality", "[utils][tensor-descriptor]")
{
TensorDescriptor t(DataType::Float, {11, 13, 17});
CHECK(t.dimensions() == 3);
CHECK(t.sizes() == std::vector<size_t>({11, 13, 17}));
CHECK(t.strides() == std::vector<size_t>({1, 11, 11 * 13}));
CHECK(t.totalLogicalElements() == 11 * 13 * 17);
CHECK(t.totalAllocatedElements() == 11 * 13 * 17);
CHECK(t.totalAllocatedBytes() == 11 * 13 * 17 * 4);
for(int i = 0; i < 3; i++)
CHECK(t.dimensionPadding(i) == 0);
CHECK(t.index(3, 4, 1) == 3 + 4 * 11 + 11 * 13);
}
TEST_CASE("TensorDescriptor works with overlapping strides", "[utils][tensor-descriptor]")
{
TensorDescriptor t(DataType::Float, {4, 6, 3}, {1, 4, 1});
CHECK(t.dimensions() == 3);
CHECK(t.sizes() == std::vector<size_t>({4, 6, 3}));
CHECK(t.strides() == std::vector<size_t>({1, 4, 1}));
CHECK(t.dimensionPadding(0) == 0);
CHECK(t.dimensionPadding(1) == 0);
CHECK(t.totalLogicalElements() == 4 * 6 * 3);
CHECK(t.totalAllocatedElements() == 4 * 6 + (3 - 1));
CHECK(t.totalAllocatedBytes() == (4 * 6 + (3 - 1)) * 4);
}
TEST_CASE("TensorDescriptor works with padding", "[utils][tensor-descriptor]")
{
TensorDescriptor t(DataType::Float, {11, 13, 17, 4}, {1, 16, 16 * 13, 16 * 13 * 17});
CHECK(t.dimensions() == 4);
CHECK(t.sizes() == std::vector<size_t>({11, 13, 17, 4}));
CHECK(t.strides() == std::vector<size_t>({1, 16, 16 * 13, 16 * 13 * 17}));
CHECK(t.totalLogicalElements() == 11 * 13 * 17 * 4);
CHECK(t.totalAllocatedElements()
== 1 + 1 * (11 - 1) + 16 * (13 - 1) + (16 * 13) * (17 - 1) + (16 * 13 * 17) * (4 - 1));
CHECK(t.dimensionPadding(0) == 0);
CHECK(t.dimensionPadding(1) == 5);
CHECK(t.dimensionPadding(2) == 0);
CHECK(t.dimensionPadding(3) == 0);
CHECK(t.index(3, 4, 1, 2) == 3 + 4 * 16 + 16 * 13 + 16 * 13 * 17 * 2);
}
TEST_CASE("TensorDescriptor works with simplified padding", "[utils][tensor-descriptor]")
{
TensorDescriptor t(DataType::Float, {4, 5}, {1, 8});
CHECK(t.dimensions() == 2);
CHECK(t.sizes() == std::vector<size_t>({4, 5}));
CHECK(t.strides() == std::vector<size_t>({1, 8})); // default 1,4
CHECK(t.dimensionPadding(0) == 0);
CHECK(t.dimensionPadding(1) == 4);
CHECK(t.totalLogicalElements() == 4 * 5);
CHECK(t.totalAllocatedElements() == 4 + 8 * (5 - 1));
CHECK(t.totalAllocatedBytes() == (4 + 8 * (5 - 1)) * 4);
}
TEST_CASE("TensorDescriptor works with zero strides", "[utils][tensor-descriptor]")
{
TensorDescriptor t(DataType::Float,
{4, 5, 6},
{TensorDescriptor::UseDefaultStride, TensorDescriptor::UseDefaultStride, 0});
CHECK(t.dimensions() == 3);
CHECK(t.sizes() == std::vector<size_t>({4, 5, 6}));
CHECK(t.strides() == std::vector<size_t>({1, 4, 0})); // default 1,4
CHECK(t.dimensionPadding(0) == 0);
CHECK(t.dimensionPadding(1) == 0);
CHECK(t.dimensionPadding(2) == -20);
CHECK(t.totalLogicalElements() == 4 * 5 * 6);
CHECK(t.totalAllocatedElements() == 4 * 5);
CHECK(t.totalAllocatedBytes() == 4 * 5 * 4);
}
TEST_CASE("TensorDescriptor::CollapseDims works", "[utils][tensor-descriptor]")
{
TensorDescriptor t(DataType::Float, {11, 13, 17, 4}, {1, 16, 16 * 13, 16 * 13 * 17});
TensorDescriptor u = t;
CHECK_THROWS_AS(u.collapseDims(0, 2), std::runtime_error);
u.collapseDims(1, 3);
CHECK(u.dimensions() == 3);
CHECK(u.sizes() == std::vector<size_t>({11, 13 * 17, 4}));
CHECK(u.strides() == std::vector<size_t>({1, 16, 16 * 13 * 17}));
CHECK(u.totalLogicalElements() == t.totalLogicalElements());
CHECK(u.totalAllocatedElements() == t.totalAllocatedElements());
CHECK(u.totalAllocatedBytes() == t.totalAllocatedBytes());
}
TEST_CASE("TensorDescriptor::CollapseDims works part 2", "[utils][tensor-descriptor]")
{
TensorDescriptor t(DataType::Float, {11, 13, 17, 4});
SECTION("0,2")
{
TensorDescriptor u = t;
u.collapseDims(0, 2);
CHECK(u.dimensions() == 3);
CHECK(u.sizes() == std::vector<size_t>({11 * 13, 17, 4}));
CHECK(u.strides() == std::vector<size_t>({1, 11 * 13, 11 * 13 * 17}));
CHECK(u.totalLogicalElements() == t.totalLogicalElements());
CHECK(u.totalAllocatedElements() == t.totalAllocatedElements());
CHECK(u.totalAllocatedBytes() == t.totalAllocatedBytes());
}
SECTION("0,4")
{
TensorDescriptor u = t;
u.collapseDims(0, 4);
CHECK(u.dimensions() == 1);
CHECK(u.sizes() == std::vector<size_t>({11 * 13 * 17 * 4}));
CHECK(u.strides() == std::vector<size_t>({1}));
CHECK(u.totalLogicalElements() == t.totalLogicalElements());
CHECK(u.totalAllocatedElements() == t.totalAllocatedElements());
CHECK(u.totalAllocatedBytes() == t.totalAllocatedBytes());
}
SECTION("1,4")
{
TensorDescriptor u = t;
u.collapseDims(1, 4);
CHECK(u.dimensions() == 2);
CHECK(u.sizes() == std::vector<size_t>({11, 13 * 17 * 4}));
CHECK(u.strides() == std::vector<size_t>({1, 11}));
CHECK(u.totalLogicalElements() == t.totalLogicalElements());
CHECK(u.totalAllocatedElements() == t.totalAllocatedElements());
CHECK(u.totalAllocatedBytes() == t.totalAllocatedBytes());
}
SECTION("1,3")
{
TensorDescriptor u = t;
u.collapseDims(1, 3);
CHECK(u.dimensions() == 3);
CHECK(u.sizes() == std::vector<size_t>({11, 13 * 17, 4}));
CHECK(u.strides() == std::vector<size_t>({1, 11, 11 * 13 * 17}));
CHECK(u.totalLogicalElements() == t.totalLogicalElements());
CHECK(u.totalAllocatedElements() == t.totalAllocatedElements());
CHECK(u.totalAllocatedBytes() == t.totalAllocatedBytes());
}
}
TEST_CASE("IncrementCoord works for 2 dimensions", "[utils][tensor-descriptor]")
{
std::vector<size_t> dims{2, 4};
std::vector<size_t> lastCoord{1, 3};
std::vector<size_t> coordRef(2);
std::vector<size_t> coordRun(2);
for(coordRef[1] = 0; coordRef[1] < dims[1]; coordRef[1]++)
for(coordRef[0] = 0; coordRef[0] < dims[0]; coordRef[0]++)
{
CHECK(coordRun == coordRef);
bool continueIteration
= IncrementCoord(coordRun.begin(), coordRun.end(), dims.begin(), dims.end());
if(coordRef == lastCoord)
CHECK(continueIteration == false);
else
CHECK(continueIteration == true);
}
coordRef = {0, 0};
CHECK(coordRun == coordRef);
CHECK(IncrementCoord(coordRun.begin(), coordRun.end(), dims.begin(), dims.end()));
}
TEST_CASE("Default strides work for TensorDescriptor.", "[utils][tensor-descriptor]")
{
TensorDescriptor desc(DataType::Float, {4, 5, 6}, {static_cast<size_t>(-1), 5});
CHECK(desc.dimensions() == 3);
CHECK(desc.sizes() == std::vector<size_t>({4, 5, 6}));
CHECK(desc.strides() == std::vector<size_t>({1, 5, 25}));
}
TEST_CASE("Specifying a subset of strides works for TensorDescriptor.",
"[utils][tensor-descriptor]")
{
TensorDescriptor desc(DataType::Float, {4, 5, 6}, {5});
CHECK(desc.dimensions() == 3);
CHECK(desc.sizes() == std::vector<size_t>({4, 5, 6}));
CHECK(desc.strides() == std::vector<size_t>({5, 20, 100}));
}
TEST_CASE("ShuffleDims works in the no-op case", "[utils][tensor-descriptor]")
{
auto dims = GENERATE(Catch::Generators::range(2, 10));
DYNAMIC_SECTION(fmt::format("dims={}", dims))
{
auto seed = GENERATE(Catch::Generators::take(
4, Catch::Generators::random(0, std::numeric_limits<int>::max())));
DYNAMIC_SECTION(fmt::format("seed={}", seed))
{
auto sizes = RandomGenerator(seed).vector<size_t>(dims, 1, 12);
CAPTURE(sizes);
TensorDescriptor desc(DataType::Int32, sizes);
auto input = iota<int>(0, desc.totalAllocatedElements()).to<std::vector>();
auto output = shuffleDims(input, desc, desc);
CHECK(input == output);
}
}
}
TEST_CASE("ShuffleDims is reversible", "[utils][tensor-descriptor]")
{
auto dims = GENERATE(Catch::Generators::range(2, 10));
DYNAMIC_SECTION(fmt::format("dims={}", dims))
{
auto seed = GENERATE(Catch::Generators::take(
4, Catch::Generators::random(0, std::numeric_limits<int>::max())));
DYNAMIC_SECTION(fmt::format("seed={}", seed))
{
auto gen = RandomGenerator(seed);
auto sizes = gen.vector<size_t>(dims, 2, 12);
CAPTURE(sizes);
TensorDescriptor src(DataType::Int32, sizes);
std::vector<size_t> order;
{
auto indices = iota<size_t>(0, dims).to<std::vector>();
while(!indices.empty())
{
auto idx = gen.next<size_t>(0, indices.size() - 1);
REQUIRE(idx < dims);
order.push_back(indices.at(idx));
indices.erase(next(indices.begin(), idx));
}
}
auto dst = TensorDescriptor::ShuffledNoPadding(DataType::Int32, sizes, order);
auto numNonUnitSizes = std::ranges::count_if(sizes, [](auto x) { return x > 1; });
CAPTURE(src.strides());
CAPTURE(dst.strides());
auto input = iota<int>(0, src.totalAllocatedElements()).to<std::vector>();
SECTION("a -> b -> a")
{
auto intermediate = shuffleDims(input, dst, src);
CAPTURE(intermediate);
if(numNonUnitSizes > 1 && src.strides() != dst.strides())
CHECK(input != intermediate);
auto output = shuffleDims(intermediate, src, dst);
REQUIRE(input == output);
}
SECTION("b -> a -> b")
{
auto intermediate = shuffleDims(input, src, dst);
CAPTURE(intermediate);
if(numNonUnitSizes > 1 && src.strides() != dst.strides())
CHECK(input != intermediate);
auto output = shuffleDims(intermediate, dst, src);
REQUIRE(input == output);
}
}
}
}