Skip to content
KernelIndex
Search⌘K

submission 873092

Xiao Su · python · License unknown

Use it

Vendorable · source mirrored · license unknownView source →

No package. Vendor the mirrored source: 391 lines, June 9 Researcher Reciprocity License v1.0.

submission_eigh_b200_jacobi32_v6.py
curl "https://kernelindex.com/api/v1/implementations/kernelbot-eigh-873092?include=source"
interfacepython
Compatibility
measured onNVIDIA B200
declared hardwareNVIDIA B200
architecturessm_100
dtypesfp32

Benchmark evidence

1 measurement across 1 GPU, fastest first.

Operation / workload
Hardware
Latency
Rank
Observed
NVIDIA B200
53.8ms
#203 of 286
2026-07-13

Reported · How evidence levels are derived →

Source and license

sourceavailable
revision digestsha256:1e2159553bae322eb55a053056bd7a22c853851cc7af040d3936f7787800e592
license declaredunknown
license concludedunknown
authorsXiao Su
imported2026-08-26

Techniques

Extracted from the mirrored source by pattern, never inferred. Each row cites its line.

shared-memory__shared__ float a0[NN];

Kernel source

submission_eigh_b200_jacobi32_v6.py391 lines
#!POPCORN leaderboard eigh
#!POPCORN gpu B200

"""
GPUMode B200 batched symmetric eigendecomposition scaffold, v6.

n == 32:
    Custom cyclic Jacobi CUDA kernel, one block per matrix.

Other sizes:
    torch.linalg.eigh fallback.

Important build detail:
    The CUDA translation unit contains no PyTorch headers. PyTorch's Python
    3.13 / CUDA 13 runner currently exposes a template-parsing incompatibility
    when NVCC directly parses torch/extension.h, so Tensor handling stays in
    the C++ translation unit and the CUDA unit exposes a plain C launcher.
"""

from __future__ import annotations

import os
import tempfile
from pathlib import Path

import torch
from torch.utils.cpp_extension import load
from task import input_t, output_t


_CPP_SRC = r"""
#include <torch/extension.h>
#include <c10/cuda/CUDAGuard.h>

#include <cstdint>
#include <vector>


extern "C" void launch_jacobi_eigh_32(
    const float* input,
    float* vectors,
    float* values,
    int64_t batch
);


std::vector<torch::Tensor> eigh32_cuda(
    torch::Tensor input
) {
    constexpr int64_t N = 32;

    TORCH_CHECK(input.is_cuda(), "input must be CUDA");
    TORCH_CHECK(
        input.scalar_type() == torch::kFloat32,
        "input must be float32"
    );
    TORCH_CHECK(
        input.dim() == 3 &&
        input.size(1) == N &&
        input.size(2) == N,
        "eigh32_cuda expects [batch, 32, 32]"
    );
    TORCH_CHECK(
        input.is_contiguous(),
        "input must be contiguous"
    );

    c10::cuda::CUDAGuard device_guard(input.device());

    const int64_t batch = input.size(0);

    torch::Tensor vectors = torch::empty_like(input);
    torch::Tensor values = torch::empty(
        {batch, N},
        input.options()
    );

    launch_jacobi_eigh_32(
        input.data_ptr<float>(),
        vectors.data_ptr<float>(),
        values.data_ptr<float>(),
        batch
    );

    return {vectors, values};
}

PYBIND11_MODULE(TORCH_EXTENSION_NAME, module) {
    module.def(
        "eigh32_cuda",
        &eigh32_cuda,
        "Batched 32x32 symmetric Jacobi eigendecomposition"
    );
}

"""


_CUDA_SRC = r"""
#include <cuda_runtime.h>

#include <cmath>
#include <cstdint>


namespace {

constexpr int N = 32;
constexpr int NN = N * N;
constexpr int PAIRS = N / 2;
constexpr int ROUNDS = N - 1;
constexpr int SWEEPS = 14;


__global__ void jacobi_eigh_32_kernel(
    const float* __restrict__ input,
    float* __restrict__ vectors,
    float* __restrict__ values
) {
    const int matrix = static_cast<int>(blockIdx.x);
    const int tid = static_cast<int>(threadIdx.x);

    __shared__ float a0[NN];
    __shared__ float a1[NN];
    __shared__ float q0[NN];
    __shared__ float q1[NN];

    __shared__ int circle[N];
    __shared__ int partner[N];
    __shared__ int order[N];

    // For output column j:
    // J[j, j] = alpha[j]
    // J[partner[j], j] = beta[j]
    __shared__ float alpha[N];
    __shared__ float beta[N];

    const long long base =
        static_cast<long long>(matrix) * NN;

    if (tid < NN) {
        a0[tid] = input[base + tid];

        const int row = tid / N;
        const int col = tid - row * N;

        q0[tid] = row == col ? 1.0f : 0.0f;
    }

    if (tid < N) {
        circle[tid] = tid;
        partner[tid] = tid;
        alpha[tid] = 1.0f;
        beta[tid] = 0.0f;
        order[tid] = tid;
    }

    __syncthreads();

    bool parity = false;

    #pragma unroll 1
    for (int sweep = 0; sweep < SWEEPS; ++sweep) {
        if (tid < N) {
            circle[tid] = tid;
        }

        __syncthreads();

        #pragma unroll 1
        for (int round = 0; round < ROUNDS; ++round) {
            float* cur_a = parity ? a1 : a0;
            float* next_a = parity ? a0 : a1;

            float* cur_q = parity ? q1 : q0;
            float* next_q = parity ? q0 : q1;

            // Build 16 disjoint plane rotations.
            if (tid < PAIRS) {
                const int p = circle[tid];
                const int q = circle[N - 1 - tid];

                const float app = cur_a[p * N + p];
                const float aqq = cur_a[q * N + q];
                const float apq = cur_a[p * N + q];

                float c = 1.0f;
                float s = 0.0f;

                const float scale =
                    fabsf(app) + fabsf(aqq) + 1.0f;

                if (fabsf(apq) > 2.0e-7f * scale) {
                    const float tau =
                        (aqq - app) / (2.0f * apq);

                    const float t =
                        copysignf(1.0f, tau) /
                        (
                            fabsf(tau) +
                            hypotf(1.0f, tau)
                        );

                    c = rsqrtf(1.0f + t * t);
                    s = t * c;
                }

                partner[p] = q;
                partner[q] = p;

                alpha[p] = c;
                alpha[q] = c;

                // Q[:, p] = c*Q[:, p] - s*Q[:, q]
                // Q[:, q] = s*Q[:, p] + c*Q[:, q]
                beta[p] = -s;
                beta[q] = s;
            }

            __syncthreads();

            if (tid < NN) {
                const int i = tid / N;
                const int j = tid - i * N;

                const int pi = partner[i];
                const int pj = partner[j];

                const float ai = alpha[i];
                const float bi = beta[i];
                const float aj = alpha[j];
                const float bj = beta[j];

                // next_a = J^T * cur_a * J
                next_a[i * N + j] =
                    ai * aj * cur_a[i * N + j] +
                    bi * aj * cur_a[pi * N + j] +
                    ai * bj * cur_a[i * N + pj] +
                    bi * bj * cur_a[pi * N + pj];

                // next_q = cur_q * J
                next_q[i * N + j] =
                    aj * cur_q[i * N + j] +
                    bj * cur_q[i * N + pj];
            }

            __syncthreads();

            parity = !parity;

            // Circle-method schedule:
            // keep position 0 fixed and rotate positions 1..31.
            if (tid == 0) {
                const int last = circle[N - 1];

                for (int k = N - 1; k > 1; --k) {
                    circle[k] = circle[k - 1];
                }

                circle[1] = last;
            }

            __syncthreads();
        }
    }

    float* final_a = parity ? a1 : a0;
    float* final_q = parity ? q1 : q0;

    if (tid == 0) {
        for (int i = 0; i < N; ++i) {
            order[i] = i;
        }

        // Sort eigenvalues ascending.
        for (int i = 0; i < N - 1; ++i) {
            int best = i;

            float best_value =
                final_a[order[i] * N + order[i]];

            for (int j = i + 1; j < N; ++j) {
                const float candidate =
                    final_a[order[j] * N + order[j]];

                if (candidate < best_value) {
                    best = j;
                    best_value = candidate;
                }
            }

            const int tmp = order[i];
            order[i] = order[best];
            order[best] = tmp;
        }

        for (int j = 0; j < N; ++j) {
            const int src = order[j];

            values[
                static_cast<long long>(matrix) * N + j
            ] = final_a[src * N + src];
        }
    }

    __syncthreads();

    if (tid < NN) {
        const int row = tid / N;
        const int out_col = tid - row * N;
        const int src_col = order[out_col];

        vectors[base + tid] =
            final_q[row * N + src_col];
    }
}

} // namespace


extern "C" void launch_jacobi_eigh_32(
    const float* input,
    float* vectors,
    float* values,
    int64_t batch
) {
    jacobi_eigh_32_kernel<<<
        static_cast<unsigned int>(batch),
        NN
    >>>(
        input,
        vectors,
        values
    );
}
"""


def _write_source_file(path: Path, source: str) -> None:
    try:
        if path.exists() and path.read_text(encoding="utf-8") == source:
            return
    except OSError:
        pass

    temporary = path.with_name(f"{path.name}.{os.getpid()}.tmp")
    temporary.write_text(source, encoding="utf-8")
    os.replace(temporary, path)


_SOURCE_DIR = (
    Path(tempfile.gettempdir()) /
    "gpumode_eigh_jacobi32_v6_sources"
)
_SOURCE_DIR.mkdir(parents=True, exist_ok=True)

_CPP_PATH = _SOURCE_DIR / "eigh_wrapper.cpp"
_CUDA_PATH = _SOURCE_DIR / "jacobi32.cu"

_write_source_file(_CPP_PATH, _CPP_SRC)
_write_source_file(_CUDA_PATH, _CUDA_SRC)


_mod = load(
    name="gpumode_eigh_jacobi32_v6",
    sources=[
        str(_CPP_PATH),
        str(_CUDA_PATH),
    ],
    extra_cflags=[
        "-O3",
        "-std=c++17",
    ],
    extra_cuda_cflags=[
        "-O3",
        "-std=c++17",
        "--use_fast_math",
    ],
    with_cuda=True,
    verbose=False,
)


def custom_kernel(data: input_t) -> output_t:
    if data.shape[-1] == 32:
        q, eigenvalues = _mod.eigh32_cuda(data)
        return q, eigenvalues

    eigenvalues, q = torch.linalg.eigh(data)
    return q, eigenvalues
scrolls · 391 lines total

Source code from GPU Mode and the KernelBot dataset · June 9 Researcher Reciprocity License v1.0

Best evidence level for this revision: reported

JSON