#include <cute/tensor.hpp>
#include <cstdio>
#include <cstdlib>
using namespace cute;
namespace {
int checks = 0;
void require(bool condition, char const* message)
{
if (!condition) {
std::fprintf(stderr, "FAILED: %s\n", message);
std::exit(1);
}
++checks;
template <class T>
void show(char const* label, T const& value)
std::printf("%s", label);
cute::print(value);
std::printf("\n");
template <class L>
void table(char const* label, L const& layout)
show(label, layout);
for (int m = 0; m < int(size<0>(layout)); ++m) {
for (int n = 0; n < int(size<1>(layout)); ++n) {
std::printf("%4d", int(layout(m,n)));
} // namespace
int main()
auto row = make_layout(Shape<_3,_4>{}, Stride<_4,_1>{});
auto column = make_layout(Shape<_3,_4>{}, Stride<_1,_3>{});
auto padded = make_layout(Shape<_3,_4>{}, Stride<_6,_1>{});
auto broadcast = make_layout(Shape<_3,_4>{}, Stride<_0,_1>{});
table("row: ", row);
table("column: ", column);
table("padded: ", padded);
table("broadcast: ", broadcast);
for (int m = 0; m < 3; ++m) {
for (int n = 0; n < 4; ++n) {
require(int(row(m,n)) == 4*m+n, "row offset");
require(int(column(m,n)) == m+3*n, "column offset");
require(int(padded(m,n)) == 6*m+n, "padded offset");
require(int(broadcast(m,n)) == n, "broadcast alias");
require(int(size(padded)) == 12, "padded size");
require(int(cosize(padded)) == 16, "padded cosize");
require(int(size(broadcast)) == 12, "broadcast size");
require(int(cosize(broadcast)) == 4, "broadcast cosize");
std::printf("row(i):");
for (int i = 0; i < 12; ++i) {
int m = i % 3;
int n = i / 3;
require(int(row(i)) == 4*m+n, "integer coordinate uses shape");
require(int(row(i)) == int(row(m,n)), "integer and tuple coordinate agree");
std::printf(" %d", int(row(i)));
require(int(row(5)) == 9, "row(5) is an input coordinate");
require(row.get_hier_coord(5) == make_coord(1,1), "index-to-coordinate for compact row layout");
require(int(row.get_1d_coord(5)) == 4, "index-to-1D-coordinate for compact row layout");
auto h = make_layout(Shape<_2,Shape<_2,_2>>{},
Stride<_4,Stride<_2,_1>>{});
table("hierarchical: ", h);
require(int(rank(h)) == 2, "hierarchical rank");
require(int(depth(h)) == 2, "hierarchical depth");
require(int(size(h)) == 8, "hierarchical size");
require(int(size<1>(h)) == 4, "nested mode size");
require(int(size<1,0>(h)) == 2, "nested submode size");
show("shape<1>(hierarchical): ", shape<1>(h));
show("layout<1>(hierarchical): ", layout<1>(h));
for (int m = 0; m < 2; ++m) {
for (int j = 0; j < 4; ++j) {
int a = j % 2;
int b = j / 2;
require(int(h(m,j)) == 4*m+2*a+b, "hierarchical 2D formula");
require(int(h(make_coord(m,make_coord(a,b)))) == int(h(m,j)),
"natural coordinate and flat subcoordinate agree");
require(int(h(m+2*j)) == int(h(m,j)), "hierarchical integer coordinate");
auto flat = flatten(h);
auto regrouped = group<1,3>(flat);
show("flatten(h): ", flat);
show("regrouped: ", regrouped);
require(regrouped == h, "regrouping recovers shape and stride");
for (int i = 0; i < 8; ++i) {
require(int(flat(i)) == int(h(i)), "flatten preserves integer-coordinate mapping");
auto left = make_layout(Shape<_2,Shape<_2,_2>>{});
auto right = make_layout(Shape<_2,Shape<_2,_2>>{}, LayoutRight{});
show("hierarchical LayoutLeft: ", left);
show("hierarchical LayoutRight: ", right);
require(left == make_layout(Shape<_2,Shape<_2,_2>>{},
Stride<_1,Stride<_2,_4>>{}), "default left strides");
require(right == h, "right strides across all leaves");
int columns = 4;
auto dynamic = make_layout(make_shape(3,columns), make_stride(columns,1));
auto mixed = make_layout(make_shape(_3{},columns), make_stride(columns,_1{}));
show("static row: ", row);
show("dynamic row: ", dynamic);
show("mixed row: ", mixed);
static_assert(is_static<decltype(row)>::value);
static_assert(!is_static<decltype(dynamic)>::value);
static_assert(!is_static<decltype(mixed)>::value);
require(int(dynamic(i)) == int(row(i)), "dynamic row mapping");
require(int(mixed(i)) == int(row(i)), "mixed row mapping");
int storage[16];
for (int i = 0; i < 16; ++i) {
storage[i] = 100+i;
auto tensor = make_tensor(storage+0, padded);
auto tensor_row = tensor(1,_);
auto tensor_column = tensor(_,2);
auto row_layout = padded(1,_);
auto [row_slice, row_offset] = slice_and_offset(make_coord(1,_), padded);
show("row slice layout: ", row_layout);
show("slice_and_offset layout: ", row_slice);
show("slice_and_offset offset: ", row_offset);
show("tensor row layout: ", tensor_row.layout());
show("tensor column layout: ", tensor_column.layout());
require(tensor_row.data() == storage+6, "row view pointer offset");
require(tensor_column.data() == storage+2, "column view pointer offset");
require(int(row_offset) == 6, "row fixed-coordinate contribution");
require(int(row_layout(0)) == 0, "Layout slice excludes fixed-coordinate offset");
require(int(row_slice(j)) == j, "row slice stride");
require(tensor_row(j) == storage[6+j], "row view values");
require(tensor_column(m) == storage[6*m+2], "column view values");
tensor_row(2) = -7;
require(storage[8] == -7, "view write aliases original storage");
tensor_row(2) = 108;
// A data layout and a thread/value layout answer different questions.
// Here a tiny 2x4 tile is distributed across four logical threads.
auto tile_data = make_layout(Shape<_2,_4>{}, Stride<_4,_1>{});
auto tv = make_layout(Shape<_4,_2>{}, Stride<_1,_4>{});
auto tv_to_address = composition(tile_data,tv);
show("data layout: ", tile_data);
show("thread/value layout: ", tv);
show("thread/value -> data address: ", tv_to_address);
int visits[8]{};
for (int t = 0; t < 4; ++t) {
for (int v = 0; v < 2; ++v) {
int q = int(tv(t,v));
int m = q%2;
int n = q/2;
int address = int(tile_data(m,n));
require(int(tv_to_address(t,v)) == address, "thread/value composition");
require(++visits[address] == 1, "non-overlapping tiny tile ownership");
std::printf("t=%d v=%d -> q=%d -> (%d,%d) -> offset=%d\n",
t,v,q,m,n,address);
require(visits[i] == 1, "complete tiny tile coverage");
// Read actual SM80 MMA metadata on the host; no GPU instruction is run.
auto mma = make_tiled_mma(SM80_16x8x16_F32F16F16F32_TN{});
auto mma_tv = mma.get_layoutA_TV();
auto mma_physical = make_layout(Shape<_16,_16>{}, Stride<_16,_1>{});
show("SM80 A atom layout: ", typename MMA_Traits<SM80_16x8x16_F32F16F16F32_TN>::ALayout{});
show("SM80 A thread/value layout: ", mma_tv);
int mma_visits[256]{};
for (int lane = 0; lane < 32; ++lane) {
for (int v = 0; v < 8; ++v) {
int a = v%2;
int b = (v/2)%2;
int c = v/4;
int q = int(mma_tv(lane,v));
int m = lane/4 + 8*b;
int k = 2*(lane%4) + a + 8*c;
require(q == m+16*k, "MMA A TV logical coordinate");
require(++mma_visits[q] == 1, "unique MMA A ownership");
if (lane < 2) {
std::printf("lane=%d v=%d -> A(%d,%d) q=%d physical_offset=%d\n",
lane,v,m,k,q,int(mma_physical(m,k)));
for (int i = 0; i < 256; ++i) {
require(mma_visits[i] == 1, "complete MMA A coverage");
std::printf("Verified %d checks; 8 tiny-tile values and 256 MMA A values covered.\n", checks);
std::printf("All examples run on the host; no CUDA kernel was launched.\n");