correct
10_GaussianMixtureModelEM.py
started
2026-05-11T13:46:07Z
timingkernelbench.timing
kernel mean / median / std
56.50 / — / 0.04 μs
ref mean / median / std
1.73 / — / 0.05 μs
numerical precisioneval_kernel_against_ref
gpu memoryextended_metrics.measure_memory
kernel launches / fusiontorch.profiler
fusion ratio (ref / kernel)
38.69x
energyNVML (pynvml)
ratio (ref / kernel)
0.04x
SOL (speed-of-light)nsight_hardware_counters
roofline / occupancynsight_hardware_counters
warp stalls (top 5)nsight
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn
import math
class Model(nn.Module):
"""
One iteration of Expectation-Maximization for a Gaussian Mixture Model.
E-step: compute soft assignments (responsibilities).
M-step: update means, covariances, and mixing weights.
The dominant cost is the batched evaluation of K Gaussian densities
over N data points.
"""
def __init__(self, dim, num_components):
super().__init__()
self.dim = dim
self.K = num_components
self.register_buffer("mus", torch.randn(num_components, dim))
self.register_buffer("covs", torch.eye(dim).unsqueeze(0).expand(num_components, -1, -1).clone())
self.register_buffer("log_pi", torch.zeros(num_components))
def _log_gaussian(self, x, mu, cov):
"""Log N(x | mu, cov) for batched x."""
D = mu.shape[-1]
diff = x - mu.unsqueeze(0) # (N, D)
L = torch.linalg.cholesky(cov)
solve = torch.linalg.solve_triangular(L, diff.unsqueeze(-1), upper=False).squeeze(-1)
maha = (solve ** 2).sum(-1)
log_det = 2.0 * L.diagonal().log().sum()
return -0.5 * (D * math.log(2 * math.pi) + log_det + maha)
def forward(self, x: torch.Tensor) -> tuple:
"""
Args:
x: (N, dim) – data points
Returns:
(new_mus, new_covs, new_log_pi, log_lik):
new_mus: (K, dim)
new_covs: (K, dim, dim)
new_log_pi: (K,)
log_lik: scalar – total log-likelihood
"""
N, D = x.shape
K = self.K
log_pi = self.log_pi - torch.logsumexp(self.log_pi, dim=0)
# E-step
log_resp = torch.stack([
self._log_gaussian(x, self.mus[k], self.covs[k]) + log_pi[k]
for k in range(K)
], dim=-1) # (N, K)
log_lik = torch.logsumexp(log_resp, dim=-1).sum()
log_resp = log_resp - torch.logsumexp(log_resp, dim=-1, keepdim=True)
resp = torch.exp(log_resp) # (N, K)
# M-step
N_k = resp.sum(dim=0).clamp(min=1e-8) # (K,)
new_mus = (resp.t() @ x) / N_k.unsqueeze(-1)
new_covs = torch.zeros_like(self.covs)
for k in range(K):
diff = x - new_mus[k].unsqueeze(0)
weighted = diff * resp[:, k:k+1]
new_covs[k] = (weighted.t() @ diff) / N_k[k] + 1e-4 * torch.eye(D, device=x.device)
new_log_pi = N_k.log() - math.log(N)
return new_mus, new_covs, new_log_pi, log_lik
dim = 8
num_components = 5
num_data = 2048
def get_inputs():
p = popcorn_pri
return [torch.randn(num_data, dim)]
def get_init_inputs():
return [dim, num_components]
turn 065.8s turn · 4.9s LLM · 60.9s tools · tokens in=1,990 · out=6 · total=2,249
reasoning
The task is to replace the forward computation of this GMM EM step with a custom CUDA kernel that's faster.
call → get_gpu_specs
callget_gpu_specs
{}
result ← get_gpu_specs (ok)
okget_gpu_specs
GPU specs for NVIDIA H100 80GB HBM3:
total memory (runtime): 79.1 GB
spec entry: H100
GPU Architecture: Hopper
GPU Memory: 80GB
Memory Bandwidth: 3.35 TB/s
FP64 TFLOPS: 34
FP64 Tensor Core TFLOPS: 67
FP32 TFLOPS: 67
TF32 Tensor Core TFLOPS: 989 with sparsity
BFLOAT16 Tensore Core TFLOPS: 1979 with sparsity
FP16 Tensor Core TFLOPS: 1979 with sparsity
FP8 Tensor Core TFLOPS: 3958 with sparsity
INT8 Tensor Core TOPS: 3958 with sparsity
Register File Size: 64K 32-bit registers per SM
Maximum number of registers per thread: 255
Maximum number of thread blocks per SM: 32
Shared memory capacity per SM: 228 KB
Maximum shared memory per thread block: 227 KB
turn 1 · 1 tool call(s) · 216.1s turn · 32.6s LLM · tokens in=2,482 · out=1,750 · total=4,593 · 6,877 chars (click to expand)
turn 1216.1s turn · 32.6s LLM · 183.5s tools · tokens in=2,482 · out=1,750 · total=4,593
reasoning
H100, high bandwidth, but data is tiny: N=2048, D=8, K=5, so ~ few KB, compute bound probably not.
I need to replace the forward computation of a PyTorch model with a custom CUDA kernel. The new model, ModelNew, must take the same inputs as the original and produce the same outputs.
call → compile_kernel
callcompile_kernel
{
"kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\nimport math\n\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <math.h>\n\n__device__ float log_gaussian(const float* x, const float* mu, const float* cov, int D, float* buffer) {\n float diff[16];\n for(int d=0; d<D; d++) diff[d] = x[d] - mu[d];\n // Cholesky - simple for small D\n float L[16*16];\n for(int i=0; i<D; i++) for(int j=0; j<D; j++) L[i*D+j] = cov[i*D+j];\n for(int i=0; i<D; i++) {\n for(int j=0; j<i; j++) {\n float sum = 0;\n for(int k=0; k<j; k++) sum += L[i*D+k]*L[j*D+k];\n L[i*D+j] = (L[i*D+j] - sum) / L[j*D+j];\n }\n float sum = 0;\n for(int k=0; k<i; k++) sum += L[i*D+k]*L[i*D+k];\n L[i*D+i] = sqrtf(L[i*D+i] - sum);\n for(int j=i+1; j<D; j++) L[i*D+j] = 0;\n }\n // solve L y = diff\n float y[16];\n for(int i=0; i<D; i++) {\n float sum = 0;\n for(int j=0; j<i; j++) sum += L[i*D+j] * y[j];\n y[i] = (diff[i] - sum) / L[i*D+i];\n }\n float maha = 0;\n for(int i=0; i<D; i++) maha += y[i]*y[i];\n float log_det = 0;\n for(int i=0; i<D; i++) log_det += logf(L[i*D+i]);\n log_det *= 2;\n float Dlog = D * logf(2 * 3.14159265f);\n return -0.5f * (Dlog + log_det + maha);\n}\n\n__global__ void em_step_kernel(\n const float* x, const float* mus, const float* covs, const float* log_pi_in,\n float* new_mus, float* new_covs, float* new_log_pi, float* log_lik_out,\n int N, int D, int K\n) {\n // For simplicity, single thread block does all since N small\n extern __shared__ float shared[];\n // Compute log_pi\n float sum_exp = 0;\n for(int k=0; k<K; k++) sum_exp += expf(log_pi_in[k]);\n float log_sum = logf(sum_exp);\n float log_pi[16];\n for(int k=0; k<K; k++) log_pi[k] = log_pi_in[k] - log_sum;\n\n // E-step: compute log_resp\n float* log_resp = shared;\n float total_log_lik = 0;\n for(int n=0; n<N; n++) {\n float max_lr = -1e30f;\n for(int k=0; k<K; k++) {\n float lg = log_gaussian(x + n*D, mus + k*D, covs + k*D*D, D, nullptr);\n float lr = lg + log_pi[k];\n log_resp[n*K + k] = lr;\n if(lr > max_lr) max_lr = lr;\n }\n float sum = 0;\n for(int k=0; k<K; k++) {\n sum += expf(log_resp[n*K + k] - max_lr);\n }\n float norm = max_lr + logf(sum);\n total_log_lik += norm;\n for(int k=0; k<K; k++) {\n log_resp[n*K + k] = expf(log_resp[n*K + k] - norm);\n }\n }\n if(threadIdx.x == 0) *log_lik_out = total_log_lik;\n\n // M-step\n float N_k[16];\n for(int k=0; k<K; k++) {\n N_k[k] = 0;\n for(int n=0; n<N; n++) N_k[k] += log_resp[n*K + k];\n if(N_k[k] < 1e-8f) N_k[k] = 1e-8f;\n }\n for(int k=0; k<K; k++) {\n for(int d=0; d<D; d++) {\n float sum = 0;\n for(int n=0; n<N; n++) sum += log_resp[n*K + k] * x[n*D + d];\n new_mus[k*D + d] = sum / N_k[k];\n }\n }\n for(int k=0; k<K; k++) {\n for(int i=0; i<D; i++) for(int j=0; j<D; j++) {\n float sum = 0;\n for(int n=0; n<N; n++) {\n float di = x[n*D+i] - new_mus[k*D + i];\n float dj = x[n*D+j] - new_mus[k*D + j];\n sum += log_resp[n*K + k] * di * dj;\n }\n new_covs[k*D*D + i*D + j] = sum / N_k[k];\n }\n for(int d=0; d<D; d++) new_covs[k*D*D + d*D + d] += 1e-4f;\n }\n float Nlog = logf((float)N);\n for(int k=0; k<K; k++) new_log_pi[k] = logf(N_k[k]) - Nlog;\n}\n\ntorch::Tensor em_forward(torch::Tensor x, torch::Tensor mus, torch::Tensor covs, torch::Tensor log_pi) {\n int N = x.size(0);\n int D = x.size(1);\n int K = mus.size(0);\n auto options = torch::TensorOptions().device(x.device()).dtype(torch::kFloat32);\n auto new_mus = torch::zeros({K, D}, options);\n auto new_covs = torch::zeros({K, D, D}, options);\n auto new_log_pi = torch::zeros({K}, options);\n auto log_lik = torch::zeros({1}, options);\n int shmem = N * K * sizeof(float);\n em_step_kernel<<<1, 1, shmem>>>(x.data_ptr<float>(), mus.data_ptr<float>(), covs.data_ptr<float>(), log_pi.data_ptr<float>(),\n new_mus.data_ptr<float>(), new_covs.data_ptr<float>(), new_log_pi.data_ptr<float>(), log_lik.data_ptr<float>(),\n N, D, K);\n return std::vector<torch::Tensor>{new_mus, new_covs, new_log_pi, log_lik.squeeze()};\n}\n\"\"\"\n\ncpp_source = \"\"\"\ntorch::Tensor em_forward(torch::Tensor x, torch::Tensor mus, torch::Tensor covs, torch::Tensor log_pi);\n\"\"\"\n\nmodule = load_inline(\n name=\"gmm_em\",\n cpp_sources=[cpp_source],\n cuda_sources=[cuda_source],\n functions=[\"em_forward\"],\n verbose=True,\n extra_cuda_cflags=[\"-O3\"]\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, dim, num_components):\n super().__init__()\n self.dim = dim\n self.K = num_components\n self.register_buffer(\"mus\", torch.randn(num_components, dim))\n self.register_buffer(\"covs\", torch.eye(dim).unsqueeze(0).expand(num_components, -1, -1).clone())\n self.register_buffer(\"log_pi\", torch.zeros(num_components))\n\n def forward(self, x: torch.Tensor) -> tuple:\n outs = module.em_forward(x, self.mus, self.covs, self.log_pi)\n return tuple(outs)\n"
}
result ← compile_kernel (fail)
failcompile_kernel
compile_kernel FAILED: RuntimeError.
RuntimeError: Error building extension 'gmm_em'
turn 2 · 1 tool call(s) · 117.4s turn · 24.4s LLM · tokens in=4,618 · out=1,730 · total=6,535 · 6,559 chars (click to expand)
turn 2117.4s turn · 24.4s LLM · 92.9s tools · tokens in=4,618 · out=1,730 · total=6,535
reasoning
Compilation failed. I need to fix errors. Probably bugs in code, and also return type wrong.
call → compile_kernel
callcompile_kernel
{
"kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\nimport math\n\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <math.h>\n\n__device__ float cholesky_logdet_maha(const float* cov, const float* diff, int D, float* L) {\n for(int i=0; i<D*D; i++) L[i] = cov[i];\n for(int i=0; i<D; i++) {\n for(int j=0; j<i; j++) {\n float sum = 0.0f;\n for(int k=0; k<j; k++) sum += L[i*D + k] * L[j*D + k];\n L[i*D + j] = (L[i*D + j] - sum) / L[j*D + j];\n }\n float sum = 0.0f;\n for(int k=0; k<i; k++) sum += L[i*D + k] * L[i*D + k];\n L[i*D + i] = sqrtf(L[i*D + i] - sum);\n }\n float log_det = 0.0f;\n for(int i=0; i<D; i++) log_det += logf(L[i*D + i]);\n float y[16];\n for(int i=0; i<D; i++) {\n float sum = 0.0f;\n for(int j=0; j<i; j++) sum += L[i*D + j] * y[j];\n y[i] = (diff[i] - sum) / L[i*D + i];\n }\n float maha = 0.0f;\n for(int i=0; i<D; i++) maha += y[i] * y[i];\n return log_det + maha;\n}\n\n__global__ void em_step_kernel(\n const float* __restrict__ x, const float* __restrict__ mus, const float* __restrict__ covs, const float* __restrict__ log_pi_in,\n float* __restrict__ new_mus, float* __restrict__ new_covs, float* __restrict__ new_log_pi, float* __restrict__ log_lik_out,\n int N, int D, int K\n) {\n extern __shared__ float log_resp[];\n float log_pi[16];\n float sum_exp = 0.0f;\n for(int k=0; k<K; k++) sum_exp += expf(log_pi_in[k]);\n float log_sum = logf(sum_exp);\n for(int k=0; k<K; k++) log_pi[k] = log_pi_in[k] - log_sum;\n\n float total_log_lik = 0.0f;\n float L[256];\n float diff[16];\n for(int n=0; n<N; n++) {\n float max_lr = -1e30f;\n for(int k=0; k<K; k++) {\n for(int d=0; d<D; d++) diff[d] = x[n*D + d] - mus[k*D + d];\n float lm = cholesky_logdet_maha(covs + k*D*D, diff, D, L);\n float lr = -0.5f * (D * logf(2*3.14159265f) + lm) + log_pi[k];\n log_resp[n*K + k] = lr;\n if (lr > max_lr) max_lr = lr;\n }\n float sum = 0.0f;\n for(int k=0; k<K; k++) sum += expf(log_resp[n*K + k] - max_lr);\n float norm = max_lr + logf(sum);\n total_log_lik += norm;\n for(int k=0; k<K; k++) {\n log_resp[n*K + k] = expf(log_resp[n*K + k] - norm);\n }\n }\n if (threadIdx.x == 0) *log_lik_out = total_log_lik;\n\n float N_k[16];\n for(int k=0; k<K; k++) {\n N_k[k] = 0.0f;\n for(int n=0; n<N; n++) N_k[k] += log_resp[n*K + k];\n if(N_k[k] < 1e-8f) N_k[k] = 1e-8f;\n }\n for(int k=0; k<K; k++) {\n for(int d=0; d<D; d++) {\n float sum = 0.0f;\n for(int n=0; n<N; n++) sum += log_resp[n*K + k] * x[n*D + d];\n new_mus[k*D + d] = sum / N_k[k];\n }\n }\n for(int k=0; k<K; k++) {\n for(int i=0; i<D; i++) {\n for(int j=0; j<D; j++) {\n float sum = 0.0f;\n for(int n=0; n<N; n++) {\n float di = x[n*D + i] - new_mus[k*D + i];\n float dj = x[n*D + j] - new_mus[k*D + j];\n sum += log_resp[n*K + k] * di * dj;\n }\n new_covs[k*D*D + i*D + j] = sum / N_k[k];\n }\n }\n for(int d=0; d<D; d++) new_covs[k*D*D + d*D + d] += 1e-4f;\n }\n float Nlog = logf((float)N);\n for(int k=0; k<K; k++) new_log_pi[k] = logf(N_k[k]) - Nlog;\n}\n\nstd::vector<torch::Tensor> em_forward(torch::Tensor x, torch::Tensor mus, torch::Tensor covs, torch::Tensor log_pi) {\n int N = x.size(0);\n int D = x.size(1);\n int K = mus.size(0);\n auto options = torch::TensorOptions().device(x.device()).dtype(torch::kFloat32);\n auto new_mus = torch::zeros({K, D}, options);\n auto new_covs = torch::zeros({K, D, D}, options);\n auto new_log_pi = torch::zeros({K}, options);\n auto log_lik_t = torch::zeros({}, options);\n int shmem = N * K * sizeof(float);\n em_step_kernel<<<1, 1, shmem>>>(\n x.data_ptr<float>(), mus.data_ptr<float>(), covs.data_ptr<float>(), log_pi.data_ptr<float>(),\n new_mus.data_ptr<float>(), new_covs.data_ptr<float>(), new_log_pi.data_ptr<float>(), log_lik_t.data_ptr<float>(),\n N, D, K);\n return {new_mus, new_covs, new_log_pi, log_lik_t};\n}\n\"\"\"\n\ncpp_source = \"\"\"\n#include <vector>\nstd::vector<torch::Tensor> em_forward(torch::Tensor x, torch::Tensor mus, torch::Tensor covs, torch::Tensor log_pi);\n\"\"\"\n\nmodule = load_inline(\n name=\"gmm_em\",\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=[\"em_forward\"],\n verbose=True,\n extra_cuda_cflags=[\"-O3\"]\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, dim, num_components):\n super().__init__()\n self.dim = dim\n self.K = num_components\n self.register_buffer(\"mus\", torch.randn(num_components, dim))\n self.register_buffer(\"covs\", torch.eye(dim).unsqueeze(0).expand(num_components, -1, -1).clone())\n self.register_buffer(\"log_pi\", torch.zeros(num_components))\n\n def forward(self, x: torch.Tensor) -> tuple:\n outs = module.em_forward(x.contiguous(), self.mus.contiguous(), self.covs.contiguous(), self.log_pi.contiguous())\n return tuple(outs)\n"
}
result ← compile_kernel (ok)
okcompile_kernel
compile_kernel PASSED: kernel compiled without errors.
turn 3 · 1 tool call(s) · 25.8s turn · 19.2s LLM · tokens in=6,552 · out=1,731 · total=8,290 · 6,611 chars (click to expand)
turn 325.8s turn · 19.2s LLM · 6.6s tools · tokens in=6,552 · out=1,731 · total=8,290
reasoning
Compiled successfully. Now check correctness.
call → run_correctness
callrun_correctness
{
"kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\nimport math\n\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <math.h>\n\n__device__ float cholesky_logdet_maha(const float* cov, const float* diff, int D, float* L) {\n for(int i=0; i<D*D; i++) L[i] = cov[i];\n for(int i=0; i<D; i++) {\n for(int j=0; j<i; j++) {\n float sum = 0.0f;\n for(int k=0; k<j; k++) sum += L[i*D + k] * L[j*D + k];\n L[i*D + j] = (L[i*D + j] - sum) / L[j*D + j];\n }\n float sum = 0.0f;\n for(int k=0; k<i; k++) sum += L[i*D + k] * L[i*D + k];\n L[i*D + i] = sqrtf(L[i*D + i] - sum);\n }\n float log_det = 0.0f;\n for(int i=0; i<D; i++) log_det += logf(L[i*D + i]);\n float y[16];\n for(int i=0; i<D; i++) {\n float sum = 0.0f;\n for(int j=0; j<i; j++) sum += L[i*D + j] * y[j];\n y[i] = (diff[i] - sum) / L[i*D + i];\n }\n float maha = 0.0f;\n for(int i=0; i<D; i++) maha += y[i] * y[i];\n return log_det + maha;\n}\n\n__global__ void em_step_kernel(\n const float* __restrict__ x, const float* __restrict__ mus, const float* __restrict__ covs, const float* __restrict__ log_pi_in,\n float* __restrict__ new_mus, float* __restrict__ new_covs, float* __restrict__ new_log_pi, float* __restrict__ log_lik_out,\n int N, int D, int K\n) {\n extern __shared__ float log_resp[];\n float log_pi[16];\n float sum_exp = 0.0f;\n for(int k=0; k<K; k++) sum_exp += expf(log_pi_in[k]);\n float log_sum = logf(sum_exp);\n for(int k=0; k<K; k++) log_pi[k] = log_pi_in[k] - log_sum;\n\n float total_log_lik = 0.0f;\n float L[256];\n float diff[16];\n for(int n=0; n<N; n++) {\n float max_lr = -1e30f;\n for(int k=0; k<K; k++) {\n for(int d=0; d<D; d++) diff[d] = x[n*D + d] - mus[k*D + d];\n float lm = cholesky_logdet_maha(covs + k*D*D, diff, D, L);\n float lr = -0.5f * (D * logf(2*3.14159265f) + lm) + log_pi[k];\n log_resp[n*K + k] = lr;\n if (lr > max_lr) max_lr = lr;\n }\n float sum = 0.0f;\n for(int k=0; k<K; k++) sum += expf(log_resp[n*K + k] - max_lr);\n float norm = max_lr + logf(sum);\n total_log_lik += norm;\n for(int k=0; k<K; k++) {\n log_resp[n*K + k] = expf(log_resp[n*K + k] - norm);\n }\n }\n if (threadIdx.x == 0) *log_lik_out = total_log_lik;\n\n float N_k[16];\n for(int k=0; k<K; k++) {\n N_k[k] = 0.0f;\n for(int n=0; n<N; n++) N_k[k] += log_resp[n*K + k];\n if(N_k[k] < 1e-8f) N_k[k] = 1e-8f;\n }\n for(int k=0; k<K; k++) {\n for(int d=0; d<D; d++) {\n float sum = 0.0f;\n for(int n=0; n<N; n++) sum += log_resp[n*K + k] * x[n*D + d];\n new_mus[k*D + d] = sum / N_k[k];\n }\n }\n for(int k=0; k<K; k++) {\n for(int i=0; i<D; i++) {\n for(int j=0; j<D; j++) {\n float sum = 0.0f;\n for(int n=0; n<N; n++) {\n float di = x[n*D + i] - new_mus[k*D + i];\n float dj = x[n*D + j] - new_mus[k*D + j];\n sum += log_resp[n*K + k] * di * dj;\n }\n new_covs[k*D*D + i*D + j] = sum / N_k[k];\n }\n }\n for(int d=0; d<D; d++) new_covs[k*D*D + d*D + d] += 1e-4f;\n }\n float Nlog = logf((float)N);\n for(int k=0; k<K; k++) new_log_pi[k] = logf(N_k[k]) - Nlog;\n}\n\nstd::vector<torch::Tensor> em_forward(torch::Tensor x, torch::Tensor mus, torch::Tensor covs, torch::Tensor log_pi) {\n int N = x.size(0);\n int D = x.size(1);\n int K = mus.size(0);\n auto options = torch::TensorOptions().device(x.device()).dtype(torch::kFloat32);\n auto new_mus = torch::zeros({K, D}, options);\n auto new_covs = torch::zeros({K, D, D}, options);\n auto new_log_pi = torch::zeros({K}, options);\n auto log_lik_t = torch::zeros({}, options);\n int shmem = N * K * sizeof(float);\n em_step_kernel<<<1, 1, shmem>>>(\n x.data_ptr<float>(), mus.data_ptr<float>(), covs.data_ptr<float>(), log_pi.data_ptr<float>(),\n new_mus.data_ptr<float>(), new_covs.data_ptr<float>(), new_log_pi.data_ptr<float>(), log_lik_t.data_ptr<float>(),\n N, D, K);\n return {new_mus, new_covs, new_log_pi, log_lik_t};\n}\n\"\"\"\n\ncpp_source = \"\"\"\n#include <vector>\nstd::vector<torch::Tensor> em_forward(torch::Tensor x, torch::Tensor mus, torch::Tensor covs, torch::Tensor log_pi);\n\"\"\"\n\nmodule = load_inline(\n name=\"gmm_em\",\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=[\"em_forward\"],\n verbose=True,\n extra_cuda_cflags=[\"-O3\"]\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, dim, num_components):\n super().__init__()\n self.dim = dim\n self.K = num_components\n self.register_buffer(\"mus\", torch.randn(num_components, dim))\n self.register_buffer(\"covs\", torch.eye(dim).unsqueeze(0).expand(num_components, -1, -1).clone())\n self.register_buffer(\"log_pi\", torch.zeros(num_components))\n\n def forward(self, x: torch.Tensor) -> tuple:\n outs = module.em_forward(x.contiguous(), self.mus.contiguous(), self.covs.contiguous(), self.log_pi.contiguous())\n return tuple(outs)\n"
}
result ← run_correctness (ok)
okrun_correctness
run_correctness PASSED: (5 / 5) trials all matched the reference.
Numerical precision: max_abs_err=3.71e-02 mean_abs_err=5.25e-05 max_rel_err=5.15e-04
turn 4 · FINAL · 1 tool call(s) · 264.4s turn · 19.5s LLM · tokens in=8,350 · out=1,775 · total=10,263 · 7,194 chars (click to expand)
turn 4264.4s turn · 19.5s LLM · 244.9s tools · tokens in=8,350 · out=1,775 · total=10,263 · FINAL
reasoning
Correctness passed! Note some error but within fp32 tolerance? atol 1e-4, but max 3e-2? Wait, it says passed, but max_abs 0.037, probably for log_lik or something, but tool says matched.
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 = \"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <math.h>\n\n__device__ float cholesky_logdet_maha(const float* cov, const float* diff, int D, float* L) {\n for(int i=0; i<D*D; i++) L[i] = cov[i];\n for(int i=0; i<D; i++) {\n for(int j=0; j<i; j++) {\n float sum = 0.0f;\n for(int k=0; k<j; k++) sum += L[i*D + k] * L[j*D + k];\n L[i*D + j] = (L[i*D + j] - sum) / L[j*D + j];\n }\n float sum = 0.0f;\n for(int k=0; k<i; k++) sum += L[i*D + k] * L[i*D + k];\n L[i*D + i] = sqrtf(L[i*D + i] - sum);\n }\n float log_det = 0.0f;\n for(int i=0; i<D; i++) log_det += logf(L[i*D + i]);\n float y[16];\n for(int i=0; i<D; i++) {\n float sum = 0.0f;\n for(int j=0; j<i; j++) sum += L[i*D + j] * y[j];\n y[i] = (diff[i] - sum) / L[i*D + i];\n }\n float maha = 0.0f;\n for(int i=0; i<D; i++) maha += y[i] * y[i];\n return log_det + maha;\n}\n\n__global__ void em_step_kernel(\n const float* __restrict__ x, const float* __restrict__ mus, const float* __restrict__ covs, const float* __restrict__ log_pi_in,\n float* __restrict__ new_mus, float* __restrict__ new_covs, float* __restrict__ new_log_pi, float* __restrict__ log_lik_out,\n int N, int D, int K\n) {\n extern __shared__ float log_resp[];\n float log_pi[16];\n float sum_exp = 0.0f;\n for(int k=0; k<K; k++) sum_exp += expf(log_pi_in[k]);\n float log_sum = logf(sum_exp);\n for(int k=0; k<K; k++) log_pi[k] = log_pi_in[k] - log_sum;\n\n float total_log_lik = 0.0f;\n float L[256];\n float diff[16];\n for(int n=threadIdx.x; n<N; n += blockDim.x) {\n float max_lr = -1e30f;\n for(int k=0; k<K; k++) {\n for(int d=0; d<D; d++) diff[d] = x[n*D + d] - mus[k*D + d];\n float lm = cholesky_logdet_maha(covs + k*D*D, diff, D, L);\n float lr = -0.5f * (D * logf(2*3.14159265f) + lm) + log_pi[k];\n log_resp[n*K + k] = lr;\n if (lr > max_lr) max_lr = lr;\n }\n float sum = 0.0f;\n for(int k=0; k<K; k++) sum += expf(log_resp[n*K + k] - max_lr);\n float norm = max_lr + logf(sum);\n total_log_lik += norm;\n for(int k=0; k<K; k++) {\n log_resp[n*K + k] = expf(log_resp[n*K + k] - norm);\n }\n }\n // reduce log_lik, simple atomic for now\n atomicAdd(log_lik_out, total_log_lik);\n\n __syncthreads();\n // only thread 0 does M step\n if(threadIdx.x == 0) {\n float N_k[16];\n for(int k=0; k<K; k++) {\n N_k[k] = 0.0f;\n for(int n=0; n<N; n++) N_k[k] += log_resp[n*K + k];\n if(N_k[k] < 1e-8f) N_k[k] = 1e-8f;\n }\n for(int k=0; k<K; k++) {\n for(int d=0; d<D; d++) {\n float sum = 0.0f;\n for(int n=0; n<N; n++) sum += log_resp[n*K + k] * x[n*D + d];\n new_mus[k*D + d] = sum / N_k[k];\n }\n }\n for(int k=0; k<K; k++) {\n for(int i=0; i<D; i++) {\n for(int j=0; j<D; j++) {\n float sum = 0.0f;\n for(int n=0; n<N; n++) {\n float di = x[n*D + i] - new_mus[k*D + i];\n float dj = x[n*D + j] - new_mus[k*D + j];\n sum += log_resp[n*K + k] * di * dj;\n }\n new_covs[k*D*D + i*D + j] = sum / N_k[k];\n }\n }\n for(int d=0; d<D; d++) new_covs[k*D*D + d*D + d] += 1e-4f;\n }\n float Nlog = logf((float)N);\n for(int k=0; k<K; k++) new_log_pi[k] = logf(N_k[k]) - Nlog;\n }\n}\n\nstd::vector<torch::Tensor> em_forward(torch::Tensor x, torch::Tensor mus, torch::Tensor covs, torch::Tensor log_pi) {\n int N = x.size(0);\n int D = x.size(1);\n int K = mus.size(0);\n auto options = torch::TensorOptions().device(x.device()).dtype(torch::kFloat32);\n auto new_mus = torch::zeros({K, D}, options);\n auto new_covs = torch::zeros({K, D, D}, options);\n auto new_log_pi = torch::zeros({K}, options);\n auto log_lik_t = torch::zeros({}, options);\n int shmem = N * K * sizeof(float);\n int threads = 256;\n em_step_kernel<<<1, threads, shmem>>>(\n x.data_ptr<float>(), mus.data_ptr<float>(), covs.data_ptr<float>(), log_pi.data_ptr<float>(),\n new_mus.data_ptr<float>(), new_covs.data_ptr<float>(), new_log_pi.data_ptr<float>(), log_lik_t.data_ptr<float>(),\n N, D, K);\n return {new_mus, new_covs, new_log_pi, log_lik_t};\n}\n\"\"\"\n\ncpp_source = \"\"\"\n#include <vector>\nstd::vector<torch::Tensor> em_forward(torch::Tensor x, torch::Tensor mus, torch::Tensor covs, torch::Tensor log_pi);\n\"\"\"\n\nmodule = load_inline(\n name=\"gmm_em\",\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=[\"em_forward\"],\n verbose=True,\n extra_cuda_cflags=[\"-O3\"]\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, dim, num_components):\n super().__init__()\n self.dim = dim\n self.K = num_components\n self.register_buffer(\"mus\", torch.randn(num_components, dim))\n self.register_buffer(\"covs\", torch.eye(dim).unsqueeze(0).expand(num_components, -1, -1).clone())\n self.register_buffer(\"log_pi\", torch.zeros(num_components))\n\n def forward(self, x: torch.Tensor) -> tuple:\n outs = module.em_forward(x.contiguous(), self.mus.contiguous(), self.covs.contiguous(), self.log_pi.contiguous())\n return tuple(outs)\n"
}
result ← submit_kernel (ok)
oksubmit_kernel
submit_kernel PASSED: (5 / 5) correctness trials all passed.
Kernel runtime: 56.50 μs
Runtime stats: mean=56.50μs median=?μs std=0.04μs
Numerical precision: max_abs_err=3.91e-03 mean_abs_err=6.56e-06 max_rel_err=5.15e-04
Memory: 33.07 MB peak (ref: 33.76 MB, ratio: 0.9797x)
Kernel launches: 16 (ref: 619, fusion ratio: 38.6875)
Energy: 6917.22 mJ/run (ref: 244.98 mJ, ratio: 0.0354x)
SOL score: 0.0019
final submitted kernel (152 lines, 5,445 chars) — full source, untruncated
import torch
import torch.nn as nn
from torch.utils.cpp_extension import load_inline
import math
cuda_source = """
#include <torch/extension.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <math.h>
__device__ float cholesky_logdet_maha(const float* cov, const float* diff, int D, float* L) {
for(int i=0; i<D*D; i++) L[i] = cov[i];
for(int i=0; i<D; i++) {
for(int j=0; j<i; j++) {
float sum = 0.0f;
for(int k=0; k<j; k++) sum += L[i*D + k] * L[j*D + k];
L[i*D + j] = (L[i*D + j] - sum) / L[j*D + j];
}
float sum = 0.0f;
for(int k=0; k<i; k++) sum += L[i*D + k] * L[i*D + k];
L[i*D + i] = sqrtf(L[i*D + i] - sum);
}
float log_det = 0.0f;
for(int i=0; i<D; i++) log_det += logf(L[i*D + i]);
float y[16];
for(int i=0; i<D; i++) {
float sum = 0.0f;
for(int j=0; j<i; j++) sum += L[i*D + j] * y[j];
y[i] = (diff[i] - sum) / L[i*D + i];
}
float maha = 0.0f;
for(int i=0; i<D; i++) maha += y[i] * y[i];
return log_det + maha;
}
__global__ void em_step_kernel(
const float* __restrict__ x, const float* __restrict__ mus, const float* __restrict__ covs, const float* __restrict__ log_pi_in,
float* __restrict__ new_mus, float* __restrict__ new_covs, float* __restrict__ new_log_pi, float* __restrict__ log_lik_out,
int N, int D, int K
) {
extern __shared__ float log_resp[];
float log_pi[16];
float sum_exp = 0.0f;
for(int k=0; k<K; k++) sum_exp += expf(log_pi_in[k]);
float log_sum = logf(sum_exp);
for(int k=0; k<K; k++) log_pi[k] = log_pi_in[k] - log_sum;
float total_log_lik = 0.0f;
float L[256];
float diff[16];
for(int n=threadIdx.x; n<N; n += blockDim.x) {
float max_lr = -1e30f;
for(int k=0; k<K; k++) {
for(int d=0; d<D; d++) diff[d] = x[n*D + d] - mus[k*D + d];
float lm = cholesky_logdet_maha(covs + k*D*D, diff, D, L);
float lr = -0.5f * (D * logf(2*3.14159265f) + lm) + log_pi[k];
log_resp[n*K + k] = lr;
if (lr > max_lr) max_lr = lr;
}
float sum = 0.0f;
for(int k=0; k<K; k++) sum += expf(log_resp[n*K + k] - max_lr);
float norm = max_lr + logf(sum);
total_log_lik += norm;
for(int k=0; k<K; k++) {
log_resp[n*K + k] = expf(log_resp[n*K + k] - norm);
}
}
// reduce log_lik, simple atomic for now
atomicAdd(log_lik_out, total_log_lik);
__syncthreads();
// only thread 0 does M step
if(threadIdx.x == 0) {
float N_k[16];
for(int k=0; k<K; k++) {
N_k[k] = 0.0f;
for(int n=0; n<N; n++) N_k[k] += log_resp[n*K + k];
if(N_k[k] < 1e-8f) N_k[k] = 1e-8f;
}
for(int k=0; k<K; k++) {
for(int d=0; d<D; d++) {
float sum = 0.0f;
for(int n=0; n<N; n++) sum += log_resp[n*K + k] * x[n*D + d];
new_mus[k*D + d] = sum / N_k[k];
}
}
for(int k=0; k<K; k++) {
for(int i=0; i<D; i++) {
for(int j=0; j<D; j++) {
float sum = 0.0f;
for(int n=0; n<N; n++) {
float di = x[n*D + i] - new_mus[k*D + i];
float dj = x[n*D + j] - new_mus[k*D + j];
sum += log_resp[n*K + k] * di * dj;
}
new_covs[k*D*D + i*D + j] = sum / N_k[k];
}
}
for(int d=0; d<D; d++) new_covs[k*D*D + d*D + d] += 1e-4f;
}
float Nlog = logf((float)N);
for(int k=0; k<K; k++) new_log_pi[k] = logf(N_k[k]) - Nlog;
}
}
std::vector<torch::Tensor> em_forward(torch::Tensor x, torch::Tensor mus, torch::Tensor covs, torch::Tensor log_pi) {
int N = x.size(0);
int D = x.size(1);
int K = mus.size(0);
auto options = torch::TensorOptions().device(x.device()).dtype(torch::kFloat32);
auto new_mus = torch::zeros({K, D}, options);
auto new_covs = torch::zeros({K, D, D}, options);
auto new_log_pi = torch::zeros({K}, options);
auto log_lik_t = torch::zeros({}, options);
int shmem = N * K * sizeof(float);
int threads = 256;
em_step_kernel<<<1, threads, shmem>>>(
x.data_ptr<float>(), mus.data_ptr<float>(), covs.data_ptr<float>(), log_pi.data_ptr<float>(),
new_mus.data_ptr<float>(), new_covs.data_ptr<float>(), new_log_pi.data_ptr<float>(), log_lik_t.data_ptr<float>(),
N, D, K);
return {new_mus, new_covs, new_log_pi, log_lik_t};
}
"""
cpp_source = """
#include <vector>
std::vector<torch::Tensor> em_forward(torch::Tensor x, torch::Tensor mus, torch::Tensor covs, torch::Tensor log_pi);
"""
module = load_inline(
name="gmm_em",
cpp_sources=cpp_source,
cuda_sources=cuda_source,
functions=["em_forward"],
verbose=True,
extra_cuda_cflags=["-O3"]
)
class ModelNew(nn.Module):
def __init__(self, dim, num_components):
super().__init__()
self.dim = dim
self.K = num_components
self.register_buffer("mus", torch.randn(num_components, dim))
self.register_buffer("covs", torch.eye(dim).unsqueeze(0).expand(num_components, -1, -1).clone())
self.register_buffer("log_pi", torch.zeros(num_components))
def forward(self, x: torch.Tensor) -> tuple:
outs = module.em_forward(x.contiguous(), self.mus.contiguous(), self.covs.contiguous(), self.log_pi.contiguous())
return tuple(outs)