error
50_HiddenMarkovForward.py
started
2026-05-11T09:20:03Z
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn
class Model(nn.Module):
"""
Forward algorithm for Hidden Markov Models. Computes the log-
likelihood log p(observations) by iterating the forward recursion
in log-space for numerical stability. The dominant inner loop in
HMM training (Baum-Welch) and Viterbi decoding.
"""
def __init__(self, num_states, num_obs):
super().__init__()
self.num_states = num_states
self.num_obs = num_obs
self.log_init = nn.Parameter(torch.zeros(num_states))
self.log_trans = nn.Parameter(torch.zeros(num_states, num_states))
self.log_emit = nn.Parameter(torch.zeros(num_states, num_obs))
def _normalize_log(self, x, dim=-1):
return x - torch.logsumexp(x, dim=dim, keepdim=True)
def forward(self, observations: torch.Tensor) -> torch.Tensor:
"""
Args:
observations: (B, T) – integer observation indices (as long tensor)
Returns:
log_lik: (B,) – log p(observations) under the HMM
"""
B, T = observations.shape
log_pi = self._normalize_log(self.log_init)
log_A = self._normalize_log(self.log_trans, dim=-1)
log_B = self._normalize_log(self.log_emit, dim=-1)
# Initial step
emit_0 = log_B[:, observations[:, 0]].t() # (B, S)
log_alpha = log_pi.unsqueeze(0) + emit_0
for t in range(1, T):
# Transition: log_alpha[b, j] = logsumexp_i(log_alpha[b, i] + log_A[i, j])
log_alpha = log_alpha.unsqueeze(-1) + log_A.unsqueeze(0) # (B, S, S)
log_alpha = torch.logsumexp(log_alpha, dim=1) # (B, S)
emit_t = log_B[:, observations[:, t]].t()
log_alpha = log_alpha + emit_t
log_lik = torch.logsumexp(log_alpha, dim=-1)
return log_lik
num_states = 16
num_obs = 64
seq_len = 128
batch_size = 64
def get_inputs():
p = popcorn_pri
return [torch.randint(0, num_obs, (p.jitter_int(batch_size), p.jitter_int(seq_len)))]
def get_init_inputs():
return [num_states, num_obs]
turn 01275.1s turn · 12.5s LLM · 1262.7s tools · tokens in=1,394 · out=1,029 · total=2,732
reasoning
The task is to replace the forward computation of this PyTorch HMM model with a custom CUDA kernel. I need to provide a complete Python file defining `ModelNew` that uses `torch.utils.cpp_extension.lo...
call → submit_kernel
callsubmit_kernel
{
"kernel_code": "import torch\nimport torch.nn as nn\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 <cfloat>\n\n__device__ __forceinline__ float logsumexp2(float a, float b) {\n float max_val = fmaxf(a, b);\n return max_val + logf(expf(a - max_val) + expf(b - max_val));\n}\n\n__global__ void hmm_forward_kernel(\n const long* __restrict__ obs,\n const float* __restrict__ log_pi,\n const float* __restrict__ log_A,\n const float* __restrict__ log_B,\n float* __restrict__ log_lik,\n int B, int T, int S, int O\n) {\n int b = blockIdx.x * blockDim.x + threadIdx.x;\n if (b >= B) return;\n\n extern __shared__ float s_log_A[];\n // Load log_A into shared? but for simplicity, use registers since S=16 small\n\n float alpha[16]; // assume S <= 16\n\n // Initial\n int o0 = obs[b * T];\n for (int j = 0; j < S; j++) {\n alpha[j] = log_pi[j] + log_B[j * O + o0];\n }\n\n for (int t = 1; t < T; t++) {\n float new_alpha[16];\n int ot = obs[b * T + t];\n for (int j = 0; j < S; j++) {\n float mx = -FLT_MAX;\n for (int i = 0; i < S; i++) {\n float val = alpha[i] + log_A[i * S + j];\n if (val > mx) mx = val;\n }\n float sum = 0.0f;\n for (int i = 0; i < S; i++) {\n float val = alpha[i] + log_A[i * S + j];\n sum += expf(val - mx);\n }\n new_alpha[j] = mx + logf(sum) + log_B[j * O + ot];\n }\n for (int j = 0; j < S; j++) alpha[j] = new_alpha[j];\n }\n\n float mx = -FLT_MAX;\n for (int i = 0; i < S; i++) if (alpha[i] > mx) mx = alpha[i];\n float sum = 0.0f;\n for (int i = 0; i < S; i++) sum += expf(alpha[i] - mx);\n log_lik[b] = mx + logf(sum);\n}\n\ntorch::Tensor hmm_forward_cuda(\n torch::Tensor observations,\n torch::Tensor log_init,\n torch::Tensor log_trans,\n torch::Tensor log_emit\n) {\n auto B = observations.size(0);\n auto T = observations.size(1);\n auto S = log_init.size(0);\n auto O = log_emit.size(1);\n\n auto log_pi = log_init - torch::logsumexp(log_init, -1, true);\n auto log_A = log_trans - torch::logsumexp(log_trans, -1, true);\n auto log_B = log_emit - torch::logsumexp(log_emit, -1, true);\n\n auto log_lik = torch::empty({B}, log_init.options());\n\n const int threads = 256;\n const int blocks = (B + threads - 1) / threads;\n\n hmm_forward_kernel<<<blocks, threads>>>(\n observations.data_ptr<long>(),\n log_pi.data_ptr<float>(),\n log_A.data_ptr<float>(),\n log_B.data_ptr<float>(),\n log_lik.data_ptr<float>(),\n B, T, S, O\n );\n\n return log_lik;\n}\n'''\n\ncpp_source = '''\ntorch::Tensor hmm_forward_cuda(\n torch::Tensor observations,\n torch::Tensor log_init,\n torch::Tensor log_trans,\n torch::Tensor log_emit\n);\n'''\n\nmodule = load_inline(\n name='hmm_cuda',\n cpp_sources=[cpp_source],\n cuda_sources=[cuda_source],\n functions=['hmm_forward_cuda'],\n verbose=False\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, num_states, num_obs):\n super().__init__()\n self.num_states = num_states\n self.num_obs = num_obs\n self.log_init = nn.Parameter(torch.zeros(num_states))\n self.log_trans = nn.Parameter(torch.zeros(num_states, num_states))\n self.log_emit = nn.Parameter(torch.zeros(num_states, num_obs))\n\n def forward(self, observations: torch.Tensor) -> torch.Tensor:\n return module.hmm_forward_cuda(\n observations, self.log_init, self.log_trans, self.log_emit\n )\n"
}
result ← submit_kernel (fail)
failsubmit_kernel
submit_kernel FAILED: IndexError: tensors used as indices must be long, int, byte or bool tensors
final submitted kernel (127 lines, 3,634 chars) — full source, untruncated
import torch
import torch.nn as nn
from torch.utils.cpp_extension import load_inline
cuda_source = '''
#include <torch/extension.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <cfloat>
__device__ __forceinline__ float logsumexp2(float a, float b) {
float max_val = fmaxf(a, b);
return max_val + logf(expf(a - max_val) + expf(b - max_val));
}
__global__ void hmm_forward_kernel(
const long* __restrict__ obs,
const float* __restrict__ log_pi,
const float* __restrict__ log_A,
const float* __restrict__ log_B,
float* __restrict__ log_lik,
int B, int T, int S, int O
) {
int b = blockIdx.x * blockDim.x + threadIdx.x;
if (b >= B) return;
extern __shared__ float s_log_A[];
// Load log_A into shared? but for simplicity, use registers since S=16 small
float alpha[16]; // assume S <= 16
// Initial
int o0 = obs[b * T];
for (int j = 0; j < S; j++) {
alpha[j] = log_pi[j] + log_B[j * O + o0];
}
for (int t = 1; t < T; t++) {
float new_alpha[16];
int ot = obs[b * T + t];
for (int j = 0; j < S; j++) {
float mx = -FLT_MAX;
for (int i = 0; i < S; i++) {
float val = alpha[i] + log_A[i * S + j];
if (val > mx) mx = val;
}
float sum = 0.0f;
for (int i = 0; i < S; i++) {
float val = alpha[i] + log_A[i * S + j];
sum += expf(val - mx);
}
new_alpha[j] = mx + logf(sum) + log_B[j * O + ot];
}
for (int j = 0; j < S; j++) alpha[j] = new_alpha[j];
}
float mx = -FLT_MAX;
for (int i = 0; i < S; i++) if (alpha[i] > mx) mx = alpha[i];
float sum = 0.0f;
for (int i = 0; i < S; i++) sum += expf(alpha[i] - mx);
log_lik[b] = mx + logf(sum);
}
torch::Tensor hmm_forward_cuda(
torch::Tensor observations,
torch::Tensor log_init,
torch::Tensor log_trans,
torch::Tensor log_emit
) {
auto B = observations.size(0);
auto T = observations.size(1);
auto S = log_init.size(0);
auto O = log_emit.size(1);
auto log_pi = log_init - torch::logsumexp(log_init, -1, true);
auto log_A = log_trans - torch::logsumexp(log_trans, -1, true);
auto log_B = log_emit - torch::logsumexp(log_emit, -1, true);
auto log_lik = torch::empty({B}, log_init.options());
const int threads = 256;
const int blocks = (B + threads - 1) / threads;
hmm_forward_kernel<<<blocks, threads>>>(
observations.data_ptr<long>(),
log_pi.data_ptr<float>(),
log_A.data_ptr<float>(),
log_B.data_ptr<float>(),
log_lik.data_ptr<float>(),
B, T, S, O
);
return log_lik;
}
'''
cpp_source = '''
torch::Tensor hmm_forward_cuda(
torch::Tensor observations,
torch::Tensor log_init,
torch::Tensor log_trans,
torch::Tensor log_emit
);
'''
module = load_inline(
name='hmm_cuda',
cpp_sources=[cpp_source],
cuda_sources=[cuda_source],
functions=['hmm_forward_cuda'],
verbose=False
)
class ModelNew(nn.Module):
def __init__(self, num_states, num_obs):
super().__init__()
self.num_states = num_states
self.num_obs = num_obs
self.log_init = nn.Parameter(torch.zeros(num_states))
self.log_trans = nn.Parameter(torch.zeros(num_states, num_states))
self.log_emit = nn.Parameter(torch.zeros(num_states, num_obs))
def forward(self, observations: torch.Tensor) -> torch.Tensor:
return module.hmm_forward_cuda(
observations, self.log_init, self.log_trans, self.log_emit
)