FW-GLM-5-1 · popcorn · pop_l123_st_gpt
incorrect 46_SteinVariationalGradient.py
model
FW-GLM-5-1
turns / max
1 / 1
tool calls
1
tokens (total)
4154
wall clock
544.8s
truncated
no
backend
cuda fp32
variant
popcorn
runtime
—
ref runtime
0.96 μs
speedup
—
started
2026-05-11T09:24:26Z
correctness issue: Output mismatch
timingkernelbench.timing
kernel runtime
—
ref runtime
0.96 μs
speedup
—
kernel mean / median / std
—
ref mean / median / std
0.96 / — / 0.01 μs
roofline / occupancyheuristic
dram bandwidth
—
dram util %
—
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
—
bottleneck
—
dominant pipe %
—
arithmetic intensity
—
ridge point
—
L1 hit %
—
L2 hit %
—
ld sectors / req
—
st sectors / req
—
regs / thread
—
smem / block
—
block size
—
peak bw
—
peak fp32 tflops
—
peak fp16 tflops
—
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn


class Model(nn.Module):
    """
    Stein Variational Gradient Descent (SVGD) update step.
    Maintains a set of particles approximating a posterior and updates
    them via a kernel-smoothed gradient that balances fitting the target
    with repulsion between particles.  The RBF kernel bandwidth is set
    via the median heuristic.
    """

    def __init__(self, dim):
        super().__init__()
        self.dim = dim
        # Target: N(0, I) for benchmarking
        self.register_buffer("target_mean", torch.zeros(dim))

    def _score_fn(self, x: torch.Tensor) -> torch.Tensor:
        """Gradient of log p(x) for N(0, I)."""
        return -x

    def forward(self, particles: torch.Tensor) -> torch.Tensor:
        """
        Args:
            particles: (N, dim) – current particle positions
        Returns:
            phi: (N, dim) – SVGD update direction
        """
        N = particles.shape[0]
        score = self._score_fn(particles)  # (N, dim)

        # Pairwise squared distances
        diff = particles.unsqueeze(0) - particles.unsqueeze(1)  # (N, N, dim)
        dist_sq = (diff ** 2).sum(-1)  # (N, N)

        # Median heuristic for bandwidth
        median_sq = torch.median(dist_sq.view(-1)).clamp(min=1e-5)
        h = median_sq / max(torch.log(torch.tensor(float(N))).item(), 1.0)

        K = torch.exp(-dist_sq / (2.0 * h))  # (N, N)
        grad_K = -diff / h * K.unsqueeze(-1)  # (N, N, dim)

        # SVGD update: phi(x_i) = (1/N) sum_j [ K(x_j, x_i) * score(x_j) + grad_K(x_j, x_i) ]
        phi = (K @ score + grad_K.sum(dim=0)) / N
        return phi


dim = 32
num_particles = 1024


def get_inputs():
    p = popcorn_pri
    return [torch.randn(num_particles, dim)]


def get_init_inputs():
    return [dim]
turn 0 · FINAL · 1 tool call(s) · 544.8s turn · 30.8s LLM · tokens in=1,302 · out=2,852 · total=4,154 · 11,447 chars (click to expand)
turn 0544.8s turn · 30.8s LLM · 514.1s tools · tokens in=1,302 · out=2,852 · total=4,154 · FINAL
reasoning
Let me understand the task: 1. I need to implement a CUDA kernel for SVGD (Stein Variational Gradient Descent) update step 2. The forward method takes particles (N, dim) and returns phi (N, dim) 3. I need to compute: - score = -particles (gradient of log p(x) for N(0,I)) - Pairwise squared distances dist_sq (N, N) - Median of dist_sq for bandwidth h - RBF kernel K = exp(-dist_sq / (2*h)) - Gradient of kernel grad_K = -diff / h * K.unsqueeze(-1) - phi = (K @ score + grad_K.sum(dim=0)) / N The main computational challenges are: 1. Computing pairwise squared distances (N x N matrix) 2. Finding the median of dist_sq 3. Computing the kernel matrix and its gradient 4. The final matrix-vector product and summation Let me write a CUDA kernel that efficiently handles this: Key operations: 1. `diff = particles.unsqueeze(0) - particles.unsqueeze(1)` -> (N, N, dim) - This is computing diff[i,j,k] = particles[j,k] - particles[i,k] 2. `dist_sq = (diff ** 2).sum(-1)` -> (N, N) - dist_sq[i,j] = sum_k (particles[j,k] - particles[i,k])^2 3. Find median of dist_sq (N*N elements) 4. `K = torch.exp(-dist_sq / (2.0 * h))` -> (N, N) 5. `grad_K = -diff / h * K.unsqueeze(-1)` -> (N, N, dim) - grad_K[i,j,k] = -diff[i,j,k] / h * K[i,j] 6. `phi = (K @ score + grad_K.sum(dim=0)) / N` - K @ score: (N, N) @ (N, dim) -> (N, dim) - phi1[i,k] = sum_j K[i,j] * score[j,k] - grad_K.sum(dim=0): sum over first axis -> (N, dim) - phi2[j,k] = sum_i grad_K[i,j,k] - phi = (phi1 + phi2) / N Now I'll implement an optimized CUDA kernel. The median computation on GPU is tricky—I'll use a Thrust-based approach for efficiency, but I need to ensure the kernel structure is correct and handles all edge cases properly.
call → submit_kernel
callsubmit_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\nimport math\n\ncuda_source = r'''\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <thrust/device_vector.h>\n#include <thrust/sort.h>\n#include <c10/cuda/CUDAStream.h>\n\n// Block sizes\n#define BLOCK_SIZE 256\n#define TILE_DIM 32\n\n// Compute pairwise squared distances and store in row-major\n__global__ void compute_dist_sq_kernel(\n const float* __restrict__ particles,\n float* __restrict__ dist_sq,\n int N, int dim\n) {\n int i = blockIdx.y;\n int j = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (i >= N || j >= N) return;\n \n float sum = 0.0f;\n for (int k = 0; k < dim; k++) {\n float diff = particles[j * dim + k] - particles[i * dim + k];\n sum += diff * diff;\n }\n dist_sq[i * N + j] = sum;\n}\n\n// Compute score = -particles\n__global__ void compute_score_kernel(\n const float* __restrict__ particles,\n float* __restrict__ score,\n int N, int dim\n) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n int total = N * dim;\n \n if (idx < total) {\n score[idx] = -particles[idx];\n }\n}\n\n// Compute kernel matrix K = exp(-dist_sq / (2*h))\n__global__ void compute_kernel_kernel(\n const float* __restrict__ dist_sq,\n float* __restrict__ K,\n float inv_2h,\n int N\n) {\n int i = blockIdx.y;\n int j = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (i >= N || j >= N) return;\n \n float d = dist_sq[i * N + j];\n K[i * N + j] = expf(-d * inv_2h);\n}\n\n// Compute phi = (K @ score + grad_K.sum(dim=0)) / N\n// grad_K[i,j,k] = -(particles[j,k] - particles[i,k]) / h * K[i,j]\n// phi1[j,k] = sum_i K[i,j] * score[j,k] = score[j,k] * sum_i K[i,j]\n// phi2[j,k] = sum_i grad_K[i,j,k] = sum_i -(particles[j,k] - particles[i,k]) / h * K[i,j]\n// = (1/h) * sum_i (particles[i,k] - particles[j,k]) * K[i,j]\n// = (1/h) * (sum_i particles[i,k] * K[i,j] - particles[j,k] * sum_i K[i,j])\n\n__global__ void compute_phi_kernel(\n const float* __restrict__ particles,\n const float* __restrict__ K,\n const float* __restrict__ score,\n float* __restrict__ phi,\n float inv_h,\n int N, int dim\n) {\n int j = blockIdx.x;\n int k = blockIdx.y;\n \n if (j >= N || k >= dim) return;\n \n float sum_K_col = 0.0f;\n float sum_pK = 0.0f;\n float K_score_sum = 0.0f;\n \n float score_jk = score[j * dim + k];\n float p_jk = particles[j * dim + k];\n \n // Iterate over i (rows of K)\n // K[i,j] is at K[i*N + j]\n for (int i = 0; i < N; i++) {\n float Kij = K[i * N + j];\n sum_K_col += Kij;\n sum_pK += particles[i * dim + k] * Kij;\n K_score_sum += Kij * score[i * dim + k];\n }\n \n // phi1[j,k] = sum_i K[i,j] * score[i,k] = K_score_sum\n // phi2[j,k] = (1/h) * (sum_pK - p_jk * sum_K_col)\n float phi1 = K_score_sum;\n float phi2 = inv_h * (sum_pK - p_jk * sum_K_col);\n \n phi[j * dim + k] = (phi1 + phi2) / (float)N;\n}\n\n// Optimized version using shared memory for reduction\n__global__ void compute_phi_optimized_kernel(\n const float* __restrict__ particles,\n const float* __restrict__ K,\n const float* __restrict__ score,\n float* __restrict__ phi,\n float inv_h,\n int N, int dim\n) {\n extern __shared__ float shared_data[];\n \n int j = blockIdx.x;\n int k = blockIdx.y;\n \n if (j >= N || k >= dim) return;\n \n int tid = threadIdx.x;\n int blockSize = blockDim.x;\n \n float score_jk = score[j * dim + k];\n float p_jk = particles[j * dim + k];\n \n float local_K_score = 0.0f;\n float local_sum_K_col = 0.0f;\n float local_sum_pK = 0.0f;\n \n // Strided loop over i\n for (int i = tid; i < N; i += blockSize) {\n float Kij = K[i * N + j];\n float pik = particles[i * dim + k];\n float sik = score[i * dim + k];\n \n local_K_score += Kij * sik;\n local_sum_K_col += Kij;\n local_sum_pK += pik * Kij;\n }\n \n // Shared memory for reduction\n float* s_K_score = shared_data;\n float* s_sum_K = shared_data + blockSize;\n float* s_sum_pK = shared_data + 2 * blockSize;\n \n s_K_score[tid] = local_K_score;\n s_sum_K[tid] = local_sum_K_col;\n s_sum_pK[tid] = local_sum_pK;\n \n __syncthreads();\n \n // Block reduction\n for (int s = blockSize / 2; s > 0; s >>= 1) {\n if (tid < s) {\n s_K_score[tid] += s_K_score[tid + s];\n s_sum_K[tid] += s_sum_K[tid + s];\n s_sum_pK[tid] += s_sum_pK[tid + s];\n }\n __syncthreads();\n }\n \n if (tid == 0) {\n float phi1 = s_K_score[0];\n float phi2 = inv_h * (s_sum_pK[0] - p_jk * s_sum_K[0]);\n phi[j * dim + k] = (phi1 + phi2) / (float)N;\n }\n}\n\ntorch::Tensor svgd_forward_cuda(torch::Tensor particles) {\n int N = particles.size(0);\n int dim = particles.size(1);\n \n auto options = particles.options();\n \n // Allocate intermediate tensors\n auto dist_sq = torch::empty({N, N}, options);\n auto K = torch::empty({N, N}, options);\n auto score = torch::empty({N, dim}, options);\n auto phi = torch::empty({N, dim}, options);\n \n // Compute score = -particles\n int total_threads = N * dim;\n int blocks = (total_threads + BLOCK_SIZE - 1) / BLOCK_SIZE;\n compute_score_kernel<<<blocks, BLOCK_SIZE>>>(\n particles.data_ptr<float>(),\n score.data_ptr<float>(),\n N, dim\n );\n \n // Compute pairwise squared distances\n dim3 grid((N + BLOCK_SIZE - 1) / BLOCK_SIZE, N);\n compute_dist_sq_kernel<<<grid, BLOCK_SIZE>>>(\n particles.data_ptr<float>(),\n dist_sq.data_ptr<float>(),\n N, dim\n );\n \n // Find median using Thrust sort\n auto dist_sq_flat = dist_sq.view(-1);\n thrust::device_ptr<float> d_ptr(dist_sq_flat.data_ptr<float>());\n thrust::sort(d_ptr, d_ptr + N * N);\n \n float median_sq;\n cudaMemcpy(&median_sq, dist_sq_flat.data_ptr<float>() + (N * N) / 2, sizeof(float), cudaMemcpyDeviceToHost);\n \n // Clamp median_sq to minimum\n if (median_sq < 1e-5f) median_sq = 1e-5f;\n \n // Compute h\n float log_N = logf((float)N);\n if (log_N < 1.0f) log_N = 1.0f;\n float h = median_sq / log_N;\n float inv_2h = 0.5f / h;\n float inv_h = 1.0f / h;\n \n // Compute kernel matrix\n compute_kernel_kernel<<<grid, BLOCK_SIZE>>>(\n dist_sq.data_ptr<float>(),\n K.data_ptr<float>(),\n inv_2h,\n N\n );\n \n // Compute phi using optimized kernel\n dim3 grid_phi(N, dim);\n int block_size = min(256, N);\n int shared_mem = block_size * 3 * sizeof(float);\n compute_phi_optimized_kernel<<<grid_phi, block_size, shared_mem>>>(\n particles.data_ptr<float>(),\n K.data_ptr<float>(),\n score.data_ptr<float>(),\n phi.data_ptr<float>(),\n inv_h,\n N, dim\n );\n \n return phi;\n}\n\nstd::vector<torch::Tensor> svgd_forward_batch_cuda(torch::Tensor particles) {\n auto phi = svgd_forward_cuda(particles);\n return {phi};\n}\n'''\n\ncpp_source = r'''\nstd::vector<torch::Tensor> svgd_forward_batch_cuda(torch::Tensor particles);\n'''\n\nsvgd_module = load_inline(\n name='svgd_cuda',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['svgd_forward_batch_cuda'],\n extra_cuda_cflags=['-O3', '--use_fast_math'],\n verbose=False\n)\n\n\nclass ModelNew(nn.Module):\n \"\"\"\n Stein Variational Gradient Descent (SVGD) update step.\n CUDA-optimized implementation.\n \"\"\"\n\n def __init__(self, dim):\n super().__init__()\n self.dim = dim\n self.register_buffer(\"target_mean\", torch.zeros(dim))\n\n def forward(self, particles: torch.Tensor) -> torch.Tensor:\n # Ensure contiguous float32 input on same device\n particles = particles.contiguous().float()\n \n # Call CUDA kernel\n results = svgd_module.svgd_forward_batch_cuda(particles)\n return results[0]" }
result ← submit_kernel (fail)
failsubmit_kernel submit_kernel FAILED: correctness check did not pass ((0 / 5) trials). correctness_issue: Output mismatch max_difference: ['0.031957', '0.035123', '0.033059', '0.030918', '0.030937'] avg_difference: ['0.003651', '0.003585', '0.003604', '0.003570', '0.003553']
final submitted kernel (284 lines, 7,940 chars) — full source, untruncated
import torch
import torch.nn as nn
from torch.utils.cpp_extension import load_inline
import math

cuda_source = r'''
#include <torch/extension.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <thrust/device_vector.h>
#include <thrust/sort.h>
#include <c10/cuda/CUDAStream.h>

// Block sizes
#define BLOCK_SIZE 256
#define TILE_DIM 32

// Compute pairwise squared distances and store in row-major
__global__ void compute_dist_sq_kernel(
    const float* __restrict__ particles,
    float* __restrict__ dist_sq,
    int N, int dim
) {
    int i = blockIdx.y;
    int j = blockIdx.x * blockDim.x + threadIdx.x;
    
    if (i >= N || j >= N) return;
    
    float sum = 0.0f;
    for (int k = 0; k < dim; k++) {
        float diff = particles[j * dim + k] - particles[i * dim + k];
        sum += diff * diff;
    }
    dist_sq[i * N + j] = sum;
}

// Compute score = -particles
__global__ void compute_score_kernel(
    const float* __restrict__ particles,
    float* __restrict__ score,
    int N, int dim
) {
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    int total = N * dim;
    
    if (idx < total) {
        score[idx] = -particles[idx];
    }
}

// Compute kernel matrix K = exp(-dist_sq / (2*h))
__global__ void compute_kernel_kernel(
    const float* __restrict__ dist_sq,
    float* __restrict__ K,
    float inv_2h,
    int N
) {
    int i = blockIdx.y;
    int j = blockIdx.x * blockDim.x + threadIdx.x;
    
    if (i >= N || j >= N) return;
    
    float d = dist_sq[i * N + j];
    K[i * N + j] = expf(-d * inv_2h);
}

// Compute phi = (K @ score + grad_K.sum(dim=0)) / N
// grad_K[i,j,k] = -(particles[j,k] - particles[i,k]) / h * K[i,j]
// phi1[j,k] = sum_i K[i,j] * score[j,k] = score[j,k] * sum_i K[i,j]
// phi2[j,k] = sum_i grad_K[i,j,k] = sum_i -(particles[j,k] - particles[i,k]) / h * K[i,j]
//          = (1/h) * sum_i (particles[i,k] - particles[j,k]) * K[i,j]
//          = (1/h) * (sum_i particles[i,k] * K[i,j] - particles[j,k] * sum_i K[i,j])

__global__ void compute_phi_kernel(
    const float* __restrict__ particles,
    const float* __restrict__ K,
    const float* __restrict__ score,
    float* __restrict__ phi,
    float inv_h,
    int N, int dim
) {
    int j = blockIdx.x;
    int k = blockIdx.y;
    
    if (j >= N || k >= dim) return;
    
    float sum_K_col = 0.0f;
    float sum_pK = 0.0f;
    float K_score_sum = 0.0f;
    
    float score_jk = score[j * dim + k];
    float p_jk = particles[j * dim + k];
    
    // Iterate over i (rows of K)
    // K[i,j] is at K[i*N + j]
    for (int i = 0; i < N; i++) {
        float Kij = K[i * N + j];
        sum_K_col += Kij;
        sum_pK += particles[i * dim + k] * Kij;
        K_score_sum += Kij * score[i * dim + k];
    }
    
    // phi1[j,k] = sum_i K[i,j] * score[i,k] = K_score_sum
    // phi2[j,k] = (1/h) * (sum_pK - p_jk * sum_K_col)
    float phi1 = K_score_sum;
    float phi2 = inv_h * (sum_pK - p_jk * sum_K_col);
    
    phi[j * dim + k] = (phi1 + phi2) / (float)N;
}

// Optimized version using shared memory for reduction
__global__ void compute_phi_optimized_kernel(
    const float* __restrict__ particles,
    const float* __restrict__ K,
    const float* __restrict__ score,
    float* __restrict__ phi,
    float inv_h,
    int N, int dim
) {
    extern __shared__ float shared_data[];
    
    int j = blockIdx.x;
    int k = blockIdx.y;
    
    if (j >= N || k >= dim) return;
    
    int tid = threadIdx.x;
    int blockSize = blockDim.x;
    
    float score_jk = score[j * dim + k];
    float p_jk = particles[j * dim + k];
    
    float local_K_score = 0.0f;
    float local_sum_K_col = 0.0f;
    float local_sum_pK = 0.0f;
    
    // Strided loop over i
    for (int i = tid; i < N; i += blockSize) {
        float Kij = K[i * N + j];
        float pik = particles[i * dim + k];
        float sik = score[i * dim + k];
        
        local_K_score += Kij * sik;
        local_sum_K_col += Kij;
        local_sum_pK += pik * Kij;
    }
    
    // Shared memory for reduction
    float* s_K_score = shared_data;
    float* s_sum_K = shared_data + blockSize;
    float* s_sum_pK = shared_data + 2 * blockSize;
    
    s_K_score[tid] = local_K_score;
    s_sum_K[tid] = local_sum_K_col;
    s_sum_pK[tid] = local_sum_pK;
    
    __syncthreads();
    
    // Block reduction
    for (int s = blockSize / 2; s > 0; s >>= 1) {
        if (tid < s) {
            s_K_score[tid] += s_K_score[tid + s];
            s_sum_K[tid] += s_sum_K[tid + s];
            s_sum_pK[tid] += s_sum_pK[tid + s];
        }
        __syncthreads();
    }
    
    if (tid == 0) {
        float phi1 = s_K_score[0];
        float phi2 = inv_h * (s_sum_pK[0] - p_jk * s_sum_K[0]);
        phi[j * dim + k] = (phi1 + phi2) / (float)N;
    }
}

torch::Tensor svgd_forward_cuda(torch::Tensor particles) {
    int N = particles.size(0);
    int dim = particles.size(1);
    
    auto options = particles.options();
    
    // Allocate intermediate tensors
    auto dist_sq = torch::empty({N, N}, options);
    auto K = torch::empty({N, N}, options);
    auto score = torch::empty({N, dim}, options);
    auto phi = torch::empty({N, dim}, options);
    
    // Compute score = -particles
    int total_threads = N * dim;
    int blocks = (total_threads + BLOCK_SIZE - 1) / BLOCK_SIZE;
    compute_score_kernel<<<blocks, BLOCK_SIZE>>>(
        particles.data_ptr<float>(),
        score.data_ptr<float>(),
        N, dim
    );
    
    // Compute pairwise squared distances
    dim3 grid((N + BLOCK_SIZE - 1) / BLOCK_SIZE, N);
    compute_dist_sq_kernel<<<grid, BLOCK_SIZE>>>(
        particles.data_ptr<float>(),
        dist_sq.data_ptr<float>(),
        N, dim
    );
    
    // Find median using Thrust sort
    auto dist_sq_flat = dist_sq.view(-1);
    thrust::device_ptr<float> d_ptr(dist_sq_flat.data_ptr<float>());
    thrust::sort(d_ptr, d_ptr + N * N);
    
    float median_sq;
    cudaMemcpy(&median_sq, dist_sq_flat.data_ptr<float>() + (N * N) / 2, sizeof(float), cudaMemcpyDeviceToHost);
    
    // Clamp median_sq to minimum
    if (median_sq < 1e-5f) median_sq = 1e-5f;
    
    // Compute h
    float log_N = logf((float)N);
    if (log_N < 1.0f) log_N = 1.0f;
    float h = median_sq / log_N;
    float inv_2h = 0.5f / h;
    float inv_h = 1.0f / h;
    
    // Compute kernel matrix
    compute_kernel_kernel<<<grid, BLOCK_SIZE>>>(
        dist_sq.data_ptr<float>(),
        K.data_ptr<float>(),
        inv_2h,
        N
    );
    
    // Compute phi using optimized kernel
    dim3 grid_phi(N, dim);
    int block_size = min(256, N);
    int shared_mem = block_size * 3 * sizeof(float);
    compute_phi_optimized_kernel<<<grid_phi, block_size, shared_mem>>>(
        particles.data_ptr<float>(),
        K.data_ptr<float>(),
        score.data_ptr<float>(),
        phi.data_ptr<float>(),
        inv_h,
        N, dim
    );
    
    return phi;
}

std::vector<torch::Tensor> svgd_forward_batch_cuda(torch::Tensor particles) {
    auto phi = svgd_forward_cuda(particles);
    return {phi};
}
'''

cpp_source = r'''
std::vector<torch::Tensor> svgd_forward_batch_cuda(torch::Tensor particles);
'''

svgd_module = load_inline(
    name='svgd_cuda',
    cpp_sources=cpp_source,
    cuda_sources=cuda_source,
    functions=['svgd_forward_batch_cuda'],
    extra_cuda_cflags=['-O3', '--use_fast_math'],
    verbose=False
)


class ModelNew(nn.Module):
    """
    Stein Variational Gradient Descent (SVGD) update step.
    CUDA-optimized implementation.
    """

    def __init__(self, dim):
        super().__init__()
        self.dim = dim
        self.register_buffer("target_mean", torch.zeros(dim))

    def forward(self, particles: torch.Tensor) -> torch.Tensor:
        # Ensure contiguous float32 input on same device
        particles = particles.contiguous().float()
        
        # Call CUDA kernel
        results = svgd_module.svgd_forward_batch_cuda(particles)
        return results[0]