call → compile_kernel
callcompile_kernel
{
"kernel_code": "import torch\nimport torch.nn as nn\nimport math\nfrom torch.utils.cpp_extension import load_inline\n\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <c10/cuda/CUDAException.h>\n\nconstexpr int BLOCK_SIZE = 256;\nconstexpr int TILE_SIZE = 16;\n\n// Cholesky decomposition for small matrices (dim x dim)\n__global__ void cholesky_kernel(const float* __restrict__ covs, float* __restrict__ L, int K, int D) {\n int k = blockIdx.x;\n if (k >= K) return;\n \n const float* cov = covs + k * D * D;\n float* l = L + k * D * D;\n \n // Load into shared memory\n __shared__ float A[64]; // Max dim=8, so 64 floats\n __shared__ float L_local[64];\n \n for (int i = threadIdx.x; i < D * D; i += blockDim.x) {\n A[i] = cov[i];\n L_local[i] = 0.0f;\n }\n __syncthreads();\n \n // Cholesky: L[i][j] = (A[i][j] - sum(L[i][m]*L[j][m])) / L[j][j]\n // Only one thread does the computation for small matrices\n if (threadIdx.x == 0) {\n for (int i = 0; i < D; i++) {\n for (int j = 0; j <= i; j++) {\n float sum = 0.0f;\n for (int m = 0; m < j; m++) {\n sum += L_local[i * D + m] * L_local[j * D + m];\n }\n if (i == j) {\n float val = A[i * D + i] - sum;\n L_local[i * D + i] = sqrtf(fmaxf(val, 1e-8f));\n } else {\n L_local[i * D + j] = (A[i * D + j] - sum) / L_local[j * D + j];\n }\n }\n }\n }\n __syncthreads();\n \n // Copy back\n for (int i = threadIdx.x; i < D * D; i += blockDim.x) {\n l[i] = L_local[i];\n }\n}\n\n// Compute log-gaussian for all components: returns log_resp (N, K)\n// Also returns log_det (K,) for each component\n__global__ void log_gaussian_kernel(\n const float* __restrict__ x, // (N, D)\n const float* __restrict__ mus, // (K, D)\n const float* __restrict__ L, // (K, D, D) - Cholesky factors\n const float* __restrict__ log_pi, // (K,)\n float* __restrict__ log_resp, // (N, K)\n int N, int D, int K\n) {\n int n = blockIdx.x * blockDim.x + threadIdx.x;\n if (n >= N) return;\n \n // Load x[n] into registers\n float x_local[8]; // Max D=8\n for (int d = 0; d < D; d++) {\n x_local[d] = x[n * D + d];\n }\n \n // For each component k\n for (int k = 0; k < K; k++) {\n const float* mu = mus + k * D;\n const float* l = L + k * D * D;\n \n // Compute diff = x - mu\n float diff[8];\n for (int d = 0; d < D; d++) {\n diff[d] = x_local[d] - mu[d];\n }\n \n // Solve L @ solve = diff (lower triangular)\n float solve[8];\n for (int i = 0; i < D; i++) {\n float sum = diff[i];\n for (int j = 0; j < i; j++) {\n sum -= l[i * D + j] * solve[j];\n }\n solve[i] = sum / l[i * D + i];\n }\n \n // Mahalanobis distance = sum(solve^2)\n float maha = 0.0f;\n for (int d = 0; d < D; d++) {\n maha += solve[d] * solve[d];\n }\n \n // Log-det from L diagonal (precomputed in another kernel or here)\n // We need to compute this efficiently\n \n // Log probability\n // log_p = -0.5 * (D * log(2*pi) + log_det + maha)\n // We'll compute log_det separately\n \n log_resp[n * K + k] = -0.5f * maha + log_pi[k];\n }\n}\n\n// Compute log-determinant from L diagonal\n__global__ void compute_log_det_kernel(\n const float* __restrict__ L,\n float* __restrict__ log_det,\n int K, int D\n) {\n int k = blockIdx.x;\n if (k >= K) return;\n \n const float* l = L + k * D * D;\n float sum = 0.0f;\n for (int d = 0; d < D; d++) {\n sum += logf(l[d * D + d]);\n }\n log_det[k] = 2.0f * sum; // log(det(A)) = 2 * sum(log(diag(L)))\n}\n\n// Add log_det and constant to log_resp\n__global__ void add_log_det_kernel(\n float* __restrict__ log_resp,\n const float* __restrict__ log_det,\n int N, int K, int D\n) {\n int n = blockIdx.x * blockDim.x + threadIdx.x;\n if (n >= N) return;\n \n float log_const = -0.5f * D * logf(2.0f * 3.14159265358979323846f);\n \n for (int k = 0; k < K; k++) {\n log_resp[n * K + k] += log_const - 0.5f * log_det[k];\n }\n}\n\n// Softmax: log_resp = log_resp - logsumexp(log_resp, dim=-1)\n__global__ void log_softmax_kernel(\n float* __restrict__ log_resp,\n int N, int K\n) {\n int n = blockIdx.x * blockDim.x + threadIdx.x;\n if (n >= N) return;\n \n float* row = log_resp + n * K;\n \n // Find max for numerical stability\n float max_val = row[0];\n for (int k = 1; k < K; k++) {\n max_val = fmaxf(max_val, row[k]);\n }\n \n // Compute logsumexp\n float sum = 0.0f;\n for (int k = 0; k < K; k++) {\n sum += expf(row[k] - max_val);\n }\n float log_sum = max_val + logf(sum);\n \n // Subtract logsumexp\n for (int k = 0; k < K; k++) {\n row[k] -= log_sum;\n }\n}\n\n// Compute total log-likelihood (reduction)\n__global__ void logsumexp_reduce_kernel(\n const float* __restrict__ log_resp,\n float* __restrict__ partial_sums,\n int N, int K\n) {\n __shared__ float sdata[BLOCK_SIZE];\n \n int tid = threadIdx.x;\n int n = blockIdx.x * blockDim.x + threadIdx.x;\n \n float val = 0.0f;\n if (n < N) {\n // Compute logsumexp for this row\n const float* row = log_resp + n * K;\n float max_val = row[0];\n for (int k = 1; k < K; k++) {\n max_val = fmaxf(max_val, row[k]);\n }\n float sum = 0.0f;\n for (int k = 0; k < K; k++) {\n sum += expf(row[k] - max_val);\n }\n val = max_val + logf(sum);\n }\n \n sdata[tid] = val;\n __syncthreads();\n \n // Block reduction\n for (int s = blockDim.x >> 1; s > 0; s >>= 1) {\n if (tid < s) {\n sdata[tid] += sdata[tid + s];\n }\n __syncthreads();\n }\n \n if (tid == 0) {\n partial_sums[blockIdx.x] = sdata[0];\n }\n}\n\n// M-step: compute new_mus = (resp^T @ x) / N_k\n__global__ void m_step_mus_kernel(\n const float* __restrict__ resp,\n const float* __restrict__ x,\n float* __restrict__ new_mus,\n float* __restrict__ N_k,\n int N, int D, int K\n) {\n int k = blockIdx.x;\n int d = threadIdx.x;\n \n if (k >= K || d >= D) return;\n \n float sum = 0.0f;\n float n_sum = 0.0f;\n \n for (int n = 0; n < N; n++) {\n float r = resp[n * K + k];\n sum += r * x[n * D + d];\n if (d == 0) {\n n_sum += r;\n }\n }\n \n if (d == 0) {\n N_k[k] = fmaxf(n_sum, 1e-8f);\n }\n __syncthreads();\n \n new_mus[k * D + d] = sum / N_k[k];\n}\n\n// M-step: compute new_covs[k] = (diff^T @ weighted_diff) / N_k[k] + reg\n__global__ void m_step_covs_kernel(\n const float* __restrict__ x,\n const float* __restrict__ new_mus,\n const float* __restrict__ resp,\n const float* __restrict__ N_k,\n float* __restrict__ new_covs,\n int N, int D, int K\n) {\n int k = blockIdx.x;\n if (k >= K) return;\n \n __shared__ float mu_local[8];\n __shared__ float cov_local[64];\n \n // Load mu to shared memory\n if (threadIdx.x < D) {\n mu_local[threadIdx.x] = new_mus[k * D + threadIdx.x];\n }\n \n // Initialize cov to 0\n for (int i = threadIdx.x; i < D * D; i += blockDim.x) {\n cov_local[i] = 0.0f;\n }\n __syncthreads();\n \n // Accumulate outer products\n for (int n = threadIdx.x; n < N; n += blockDim.x) {\n float r = resp[n * K + k];\n float diff[8];\n for (int d = 0; d < D; d++) {\n diff[d] = x[n * D + d] - mu_local[d];\n }\n \n // Outer product weighted by r\n for (int i = 0; i < D; i++) {\n for (int j = 0; j < D; j++) {\n atomicAdd(&cov_local[i * D + j], r * diff[i] * diff[j]);\n }\n }\n }\n __syncthreads();\n \n // Normalize and add regularization\n float n_k = N_k[k];\n for (int i = threadIdx.x; i < D * D; i += blockDim.x) {\n int row = i / D;\n int col = i % D;\n float val = cov_local[i] / n_k;\n if (row == col) {\n val += 1e-4f;\n }\n new_covs[k * D * D + i] = val;\n }\n}\n\n// Compute new_log_pi = log(N_k) - log(N)\n__global__ void compute_log_pi_kernel(\n const float* __restrict__ N_k,\n float* __restrict__ new_log_pi,\n int K, int N\n) {\n int k = blockIdx.x * blockDim.x + threadIdx.x;\n if (k >= K) return;\n \n new_log_pi[k] = logf(N_k[k]) - logf((float)N);\n}\n\n// Host function to launch kernels\nstd::tuple<torch::Tensor, torch::Tensor, torch::Tensor, torch::Tensor> \ngmm_em_forward_cuda(\n torch::Tensor x,\n torch::Tensor mus,\n torch::Tensor covs,\n torch::Tensor log_pi,\n int K\n) {\n int N = x.size(0);\n int D = x.size(1);\n \n auto options = x.options();\n \n // Compute Cholesky decomposition\n auto L = torch::empty({K, D, D}, options);\n int cholesky_blocks = K;\n cholesky_kernel<<<cholesky_blocks, BLOCK_SIZE>>>(\n covs.data_ptr<float>(), L.data_ptr<float>(), K, D\n );\n C10_CUDA_CHECK(cudaGetLastError());\n \n // Compute log_det from L\n auto log_det = torch::empty({K}, options);\n compute_log_det_kernel<<<K, BLOCK_SIZE>>>(\n L.data_ptr<float>(), log_det.data_ptr<float>(), K, D\n );\n C10_CUDA_CHECK(cudaGetLastError());\n \n // Normalize log_pi\n auto log_pi_data = log_pi.data_ptr<float>();\n // This should be done on CPU or in a kernel\n \n // Compute log_resp\n auto log_resp = torch::empty({N, K}, options);\n int log_gaussian_blocks = (N + BLOCK_SIZE - 1) / BLOCK_SIZE;\n log_gaussian_kernel<<<log_gaussian_blocks, BLOCK_SIZE>>>(\n x.data_ptr<float>(),\n mus.data_ptr<float>(),\n L.data_ptr<float>(),\n log_pi.data_ptr<float>(),\n log_resp.data_ptr<float>(),\n N, D, K\n );\n C10_CUDA_CHECK(cudaGetLastError());\n \n // Add log_det and constant\n add_log_det_kernel<<<log_gaussian_blocks, BLOCK_SIZE>>>(\n log_resp.data_ptr<float>(),\n log_det.data_ptr<float>(),\n N, K, D\n );\n C10_CUDA_CHECK(cudaGetLastError());\n \n // Compute total log-likelihood\n int num_blocks = (N + BLOCK_SIZE - 1) / BLOCK_SIZE;\n auto partial_sums = torch::empty({num_blocks}, options);\n logsumexp_reduce_kernel<<<num_blocks, BLOCK_SIZE>>>(\n log_resp.data_ptr<float>(),\n partial_sums.data_ptr<float>(),\n N, K\n );\n C10_CUDA_CHECK(cudaGetLastError());\n \n float log_lik_val = partial_sums.sum().item<float>();\n auto log_lik = torch::tensor(log_lik_val, options);\n \n // Normalize log_resp\n log_softmax_kernel<<<log_gaussian_blocks, BLOCK_SIZE>>>(\n log_resp.data_ptr<float>(),\n N, K\n );\n C10_CUDA_CHECK(cudaGetLastError());\n \n // Compute responsibilities\n auto resp = torch::exp(log_resp);\n \n // M-step: compute N_k and new_mus\n auto N_k = torch::empty({K}, options);\n auto new_mus = torch::empty({K, D}, options);\n m_step_mus_kernel<<<K, D>>>(\n resp.data_ptr<float>(),\n x.data_ptr<float>(),\n new_mus.data_ptr<float>(),\n N_k.data_ptr<float>(),\n N, D, K\n );\n C10_CUDA_CHECK(cudaGetLastError());\n \n // M-step: compute new_covs\n auto new_covs = torch::empty({K, D, D}, options);\n m_step_covs_kernel<<<K, BLOCK_SIZE>>>(\n x.data_ptr<float>(),\n new_mus.data_ptr<float>(),\n resp.data_ptr<float>(),\n N_k.data_ptr<float>(),\n new_covs.data_ptr<float>(),\n N, D, K\n );\n C10_CUDA_CHECK(cudaGetLastError());\n \n // Compute new_log_pi\n auto new_log_pi = torch::empty({K}, options);\n compute_log_pi_kernel<<<1, K>>>(\n N_k.data_ptr<float>(),\n new_log_pi.data_ptr<float>(),\n K, N\n );\n C10_CUDA_CHECK(cudaGetLastError());\n \n return std::make_tuple(new_mus, new_covs, new_log_pi, log_lik);\n}\n\"\"\"\n\ncpp_source = \"\"\"\nstd::tuple<torch::Tensor, torch::Tensor, torch::Tensor, torch::Tensor> \ngmm_em_forward_cuda(\n torch::Tensor x,\n torch::Tensor mus,\n torch::Tensor covs,\n torch::Tensor log_pi,\n int K\n);\n\"\"\"\n\ngmm_module = load_inline(\n name='gmm_em_cuda',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['gmm_em_forward_cuda'],\n verbose=False,\n extra_cuda_cflags=['-O3']\n)\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 # Normalize log_pi\n log_pi = self.log_pi - torch.logsumexp(self.log_pi, dim=0)\n \n # Call CUDA kernel\n new_mus, new_covs, new_log_pi, log_lik = gmm_module.gmm_em_forward_cuda(\n x, self.mus, self.covs, log_pi, self.K\n )\n \n return new_mus, new_covs, new_log_pi, log_lik"
}