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\n# Define the CUDA kernel with proper C++ wrappers\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n#include <c10/cuda/CUDAException.h>\n\nconstexpr int BLOCK_SIZE = 256;\nconstexpr double PI = 3.14159265358979323846;\n\n// Forward substitution: solve L * y = b where L is lower triangular\n__device__ __forceinline__ void forward_substitution_inline(const double* L, const double* b, double* y, int D) {\n #pragma unroll\n for (int i = 0; i < 8; i++) {\n if (i >= D) break;\n double sum = b[i];\n #pragma unroll\n for (int j = 0; j < 8; j++) {\n if (j >= i) break;\n sum -= L[i * 8 + j] * y[j];\n }\n y[i] = sum / L[i * 8 + i];\n }\n}\n\n// Kernel for E-step with precomputed Cholesky\n// Uses shared memory for mus and L matrices\n__global__ void e_step_optimized_kernel(\n const double* __restrict__ x, // (N, D)\n const double* __restrict__ mus, // (K, D)\n const double* __restrict__ L_chol, // (K, D, D) - precomputed Cholesky L\n const double* __restrict__ log_det, // (K,) - precomputed log determinants\n const double* __restrict__ log_pi, // (K,)\n double* __restrict__ log_resp, // (N, K)\n int N, int D, int K\n) {\n extern __shared__ double shared_data[];\n \n // Shared memory layout: \n // mus[K][D] + L_chol[K][D*D] + log_det[K] + log_pi[K]\n double* s_mus = shared_data;\n double* s_L = s_mus + K * D;\n double* s_log_det = s_L + K * D * D;\n double* s_log_pi = s_log_det + K;\n \n // Collaborative load of shared memory\n int tid = threadIdx.x;\n int total_shared = K * D + K * D * D + K + K;\n \n for (int i = tid; i < total_shared; i += blockDim.x) {\n if (i < K * D) {\n s_mus[i] = mus[i];\n } else if (i < K * D + K * D * D) {\n s_L[i - K * D] = L_chol[i - K * D];\n } else if (i < K * D + K * D * D + K) {\n s_log_det[i - K * D - K * D * D] = log_det[i - K * D - K * D * D];\n } else {\n s_log_pi[i - K * D - K * D * D - K] = log_pi[i - K * D - K * D * D - K];\n }\n }\n __syncthreads();\n \n // Each thread handles one (n, k) pair\n int n = blockIdx.x * blockDim.x + threadIdx.x;\n int k = blockIdx.y;\n \n if (n >= N || k >= K) return;\n \n // Get pointers to shared data for component k\n const double* mu_k = s_mus + k * D;\n const double* L_k = s_L + k * D * D;\n double log_det_k = s_log_det[k];\n double log_pi_k = s_log_pi[k];\n \n // Load x[n] into registers - coalesced access\n double x_local[8];\n for (int d = 0; d < D; d++) {\n x_local[d] = x[n * D + d];\n }\n \n // Compute diff = x[n] - mu[k]\n double diff[8];\n for (int d = 0; d < D; d++) {\n diff[d] = x_local[d] - mu_k[d];\n }\n \n // Solve L * y = diff using forward substitution\n double y[8];\n forward_substitution_inline(L_k, diff, y, D);\n \n // Compute Mahalanobis distance\n double maha = 0.0;\n for (int d = 0; d < D; d++) {\n maha += y[d] * y[d];\n }\n \n // Log Gaussian\n double log_gauss = -0.5 * (D * log(2.0 * PI) + log_det_k + maha);\n log_resp[n * K + k] = log_gauss + log_pi_k;\n}\n\n// Kernel to compute logsumexp and normalize log_resp\n__global__ void logsumexp_normalize_kernel(\n double* __restrict__ log_resp, // (N, K)\n double* __restrict__ log_lik, // scalar output\n double* __restrict__ resp, // (N, K) output\n int N, int K\n) {\n int n = blockIdx.x * blockDim.x + threadIdx.x;\n if (n >= N) return;\n \n // Find max for numerical stability\n double max_val = log_resp[n * K];\n for (int k = 1; k < K; k++) {\n max_val = fmax(max_val, log_resp[n * K + k]);\n }\n \n // Compute sum of exp(log_resp - max_val)\n double sum = 0.0;\n for (int k = 0; k < K; k++) {\n sum += exp(log_resp[n * K + k] - max_val);\n }\n \n double log_sum = max_val + log(sum);\n \n // Normalize and convert to resp\n for (int k = 0; k < K; k++) {\n resp[n * K + k] = exp(log_resp[n * K + k] - log_sum);\n log_resp[n * K + k] -= log_sum;\n }\n \n // Add to log likelihood using atomic add\n atomicAdd(log_lik, log_sum);\n}\n\n// M-step kernel: compute N_k\n__global__ void compute_Nk_kernel(\n const double* __restrict__ resp, // (N, K)\n double* __restrict__ N_k, // (K,)\n int N, int K\n) {\n int k = blockIdx.x * blockDim.x + threadIdx.x;\n if (k >= K) return;\n \n double sum = 0.0;\n for (int n = 0; n < N; n++) {\n sum += resp[n * K + k];\n }\n N_k[k] = fmax(sum, 1e-8);\n}\n\n// M-step kernel: compute new_mus\n__global__ void compute_new_mus_kernel(\n const double* __restrict__ x, // (N, D)\n const double* __restrict__ resp, // (N, K)\n const double* __restrict__ N_k, // (K,)\n double* __restrict__ new_mus, // (K, D)\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 double sum = 0.0;\n for (int n = 0; n < N; n++) {\n sum += resp[n * K + k] * x[n * D + d];\n }\n new_mus[k * D + d] = sum / N_k[k];\n}\n\n// M-step kernel: compute new_covs\n__global__ void compute_new_covs_kernel(\n const double* __restrict__ x, // (N, D)\n const double* __restrict__ resp, // (N, K)\n const double* __restrict__ new_mus, // (K, D)\n const double* __restrict__ N_k, // (K,)\n double* __restrict__ new_covs, // (K, D, D)\n int N, int D, int K\n) {\n int k = blockIdx.x;\n int idx = threadIdx.x;\n int i = idx / D;\n int j = idx % D;\n \n if (k >= K || i >= D || j >= D) return;\n \n double sum = 0.0;\n for (int n = 0; n < N; n++) {\n double diff_i = x[n * D + i] - new_mus[k * D + i];\n double diff_j = x[n * D + j] - new_mus[k * D + j];\n sum += resp[n * K + k] * diff_i * diff_j;\n }\n new_covs[k * D * D + i * D + j] = sum / N_k[k];\n \n // Add regularization to diagonal\n if (i == j) {\n new_covs[k * D * D + i * D + j] += 1e-4;\n }\n}\n\n// M-step kernel: compute new_log_pi\n__global__ void compute_new_log_pi_kernel(\n const double* __restrict__ N_k, // (K,)\n double* __restrict__ new_log_pi, // (K,)\n int N, int K\n) {\n int k = blockIdx.x * blockDim.x + threadIdx.x;\n if (k >= K) return;\n \n new_log_pi[k] = log(N_k[k]) - log((double)N);\n}\n\n// C++ wrapper functions\ntorch::Tensor e_step_optimized_cuda(torch::Tensor x, torch::Tensor mus, torch::Tensor L_chol, \n torch::Tensor log_det, torch::Tensor log_pi, int N, int D, int K) {\n auto options = torch::TensorOptions().dtype(torch::kFloat64).device(x.device());\n auto log_resp = torch::empty({N, K}, options);\n \n dim3 grid((N + BLOCK_SIZE - 1) / BLOCK_SIZE, K);\n dim3 block(BLOCK_SIZE);\n \n // Shared memory size: mus(K*D) + L(K*D*D) + log_det(K) + log_pi(K)\n size_t shared_mem_size = (K * D + K * D * D + K + K) * sizeof(double);\n \n e_step_optimized_kernel<<<grid, block, shared_mem_size>>>(\n x.data_ptr<double>(),\n mus.data_ptr<double>(),\n L_chol.data_ptr<double>(),\n log_det.data_ptr<double>(),\n log_pi.data_ptr<double>(),\n log_resp.data_ptr<double>(),\n N, D, K\n );\n \n C10_CUDA_CHECK(cudaGetLastError());\n return log_resp;\n}\n\nstd::tuple<torch::Tensor, torch::Tensor> logsumexp_cuda(torch::Tensor log_resp, int N, int K) {\n auto options = torch::TensorOptions().dtype(torch::kFloat64).device(log_resp.device());\n auto resp = torch::empty({N, K}, options);\n auto log_lik = torch::zeros({}, options);\n \n dim3 grid((N + BLOCK_SIZE - 1) / BLOCK_SIZE);\n dim3 block(BLOCK_SIZE);\n \n logsumexp_normalize_kernel<<<grid, block>>>(\n log_resp.data_ptr<double>(),\n log_lik.data_ptr<double>(),\n resp.data_ptr<double>(),\n N, K\n );\n \n C10_CUDA_CHECK(cudaGetLastError());\n return std::make_tuple(log_lik, resp);\n}\n\ntorch::Tensor compute_Nk_cuda(torch::Tensor resp, int N, int K) {\n auto options = torch::TensorOptions().dtype(torch::kFloat64).device(resp.device());\n auto N_k = torch::empty({K}, options);\n \n dim3 grid((K + BLOCK_SIZE - 1) / BLOCK_SIZE);\n dim3 block(BLOCK_SIZE);\n \n compute_Nk_kernel<<<grid, block>>>(\n resp.data_ptr<double>(),\n N_k.data_ptr<double>(),\n N, K\n );\n \n C10_CUDA_CHECK(cudaGetLastError());\n return N_k;\n}\n\ntorch::Tensor compute_new_mus_cuda(torch::Tensor x, torch::Tensor resp, torch::Tensor N_k, int N, int D, int K) {\n auto options = torch::TensorOptions().dtype(torch::kFloat64).device(x.device());\n auto new_mus = torch::empty({K, D}, options);\n \n dim3 grid(K);\n dim3 block(D);\n \n compute_new_mus_kernel<<<grid, block>>>(\n x.data_ptr<double>(),\n resp.data_ptr<double>(),\n N_k.data_ptr<double>(),\n new_mus.data_ptr<double>(),\n N, D, K\n );\n \n C10_CUDA_CHECK(cudaGetLastError());\n return new_mus;\n}\n\ntorch::Tensor compute_new_covs_cuda(torch::Tensor x, torch::Tensor resp, torch::Tensor new_mus, torch::Tensor N_k, int N, int D, int K) {\n auto options = torch::TensorOptions().dtype(torch::kFloat64).device(x.device());\n auto new_covs = torch::empty({K, D, D}, options);\n \n dim3 grid(K);\n dim3 block(D * D);\n \n compute_new_covs_kernel<<<grid, block>>>(\n x.data_ptr<double>(),\n resp.data_ptr<double>(),\n new_mus.data_ptr<double>(),\n N_k.data_ptr<double>(),\n new_covs.data_ptr<double>(),\n N, D, K\n );\n \n C10_CUDA_CHECK(cudaGetLastError());\n return new_covs;\n}\n\ntorch::Tensor compute_new_log_pi_cuda(torch::Tensor N_k, int N, int K) {\n auto options = torch::TensorOptions().dtype(torch::kFloat64).device(N_k.device());\n auto new_log_pi = torch::empty({K}, options);\n \n dim3 grid((K + BLOCK_SIZE - 1) / BLOCK_SIZE);\n dim3 block(BLOCK_SIZE);\n \n compute_new_log_pi_kernel<<<grid, block>>>(\n N_k.data_ptr<double>(),\n new_log_pi.data_ptr<double>(),\n N, K\n );\n \n C10_CUDA_CHECK(cudaGetLastError());\n return new_log_pi;\n}\n\"\"\"\n\ncpp_source = \"\"\"\ntorch::Tensor e_step_optimized_cuda(torch::Tensor x, torch::Tensor mus, torch::Tensor L_chol, \n torch::Tensor log_det, torch::Tensor log_pi, int N, int D, int K);\nstd::tuple<torch::Tensor, torch::Tensor> logsumexp_cuda(torch::Tensor log_resp, int N, int K);\ntorch::Tensor compute_Nk_cuda(torch::Tensor resp, int N, int K);\ntorch::Tensor compute_new_mus_cuda(torch::Tensor x, torch::Tensor resp, torch::Tensor N_k, int N, int D, int K);\ntorch::Tensor compute_new_covs_cuda(torch::Tensor x, torch::Tensor resp, torch::Tensor new_mus, torch::Tensor N_k, int N, int D, int K);\ntorch::Tensor compute_new_log_pi_cuda(torch::Tensor N_k, int N, int K);\n\"\"\"\n\n# Load the extension\ngmm_extension = load_inline(\n name='gmm_extension_v4',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['e_step_optimized_cuda', 'logsumexp_cuda', 'compute_Nk_cuda', 'compute_new_mus_cuda', \n 'compute_new_covs_cuda', 'compute_new_log_pi_cuda'],\n extra_cuda_cflags=['-O3'],\n verbose=True\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 # Cached Cholesky decompositions\n self.register_buffer(\"L_chol\", torch.zeros(num_components, dim, dim))\n self.register_buffer(\"log_det_chol\", torch.zeros(num_components))\n self._update_chol_cache()\n\n def _update_chol_cache(self):\n \"\"\"Precompute Cholesky decompositions and log determinants.\"\"\"\n K, D = self.K, self.dim\n covs_d = self.covs.double()\n for k in range(K):\n L = torch.linalg.cholesky(covs_d[k])\n self.L_chol[k] = L\n self.log_det_chol[k] = 2.0 * L.diagonal().log().sum()\n\n def forward(self, x: torch.Tensor) -> tuple:\n N, D = x.shape\n K = self.K\n \n # Normalize log_pi using PyTorch (this is a small K-sized operation)\n log_pi = self.log_pi - torch.logsumexp(self.log_pi, dim=0)\n \n # Convert to double for precision\n x_d = x.double().contiguous()\n mus_d = self.mus.double().contiguous()\n L_chol_d = self.L_chol.double().contiguous()\n log_det_d = self.log_det_chol.double().contiguous()\n log_pi_d = log_pi.double().contiguous()\n \n # E-step using custom CUDA kernels\n log_resp = gmm_extension.e_step_optimized_cuda(x_d, mus_d, L_chol_d, log_det_d, log_pi_d, N, D, K)\n log_lik, resp = gmm_extension.logsumexp_cuda(log_resp, N, K)\n \n # M-step using custom CUDA kernels\n N_k = gmm_extension.compute_Nk_cuda(resp, N, K)\n new_mus = gmm_extension.compute_new_mus_cuda(x_d, resp, N_k, N, D, K)\n new_covs = gmm_extension.compute_new_covs_cuda(x_d, resp, new_mus, N_k, N, D, K)\n new_log_pi = gmm_extension.compute_new_log_pi_cuda(N_k, N, K)\n \n # Convert back to original dtype\n return new_mus.float(), new_covs.float(), new_log_pi.float(), log_lik.float()"
}