gpt-5.5-priority · popcorn · l123_single_gpt
compile_fail 29_MSARowAttention.py
model
gpt-5.5-priority
turns / max
1 / 1
tool calls
1
tokens (total)
0
wall clock
—
truncated
no
backend
cuda fp32
variant
popcorn
runtime
—
ref runtime
-1.00 μs
speedup
—
started
2026-05-05T02:27:06Z
compile error: Error building extension 'row_msa_attn_ext_v1': [1/3] /opt/conda/bin/x86_64-conda-linux-gnu-c++ -MMD -MF main.o.d -DTORCH_EXTENSION_NAME=row_msa_attn_ext_v1 -DTORCH_API_INCLUDE_EXTENSION_H -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include/torch/csrc/api/include -isystem /usr/local/cuda/include -isystem /opt/conda/include/python3.10 -fPIC -std=c++17 -O3 -c /scratch/abaner/KernelBench/runs/l123_single_gpt/popcorn/gpt-5.5-priority/level_2_problem_29_cache/k_6dd38bc1a0cb/row_ms
timingkernelbench.timing
kernel runtime
—
ref runtime
—
speedup
—
kernel mean / median / std
—
ref mean / median / std
—
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn
import torch.nn.functional as F
import math


class Model(nn.Module):
    """
    Row-wise gated self-attention over Multiple Sequence Alignment (MSA)
    representations.  Each row of the MSA is an independent sequence; for
    each row, standard multi-head attention is performed across residue
    positions, with an additive bias from the pair representation.
    """

    def __init__(self, msa_dim, pair_dim, num_heads, dropout=0.0):
        super().__init__()
        self.num_heads = num_heads
        self.head_dim = msa_dim // num_heads
        assert msa_dim % num_heads == 0

        self.layer_norm = nn.LayerNorm(msa_dim)
        self.query = nn.Linear(msa_dim, msa_dim, bias=False)
        self.key = nn.Linear(msa_dim, msa_dim, bias=False)
        self.value = nn.Linear(msa_dim, msa_dim, bias=False)
        self.gate = nn.Linear(msa_dim, msa_dim)
        self.pair_bias = nn.Linear(pair_dim, num_heads, bias=False)
        self.out_proj = nn.Linear(msa_dim, msa_dim)
        self.dropout = nn.Dropout(dropout)

    def forward(
        self,
        msa_repr: torch.Tensor,
        pair_repr: torch.Tensor,
        msa_mask: torch.Tensor,
    ) -> torch.Tensor:
        """
        Args:
            msa_repr:  (B, S, N, msa_dim)
            pair_repr: (B, N, N, pair_dim)
            msa_mask:  (B, S, N)
        Returns:
            updated MSA (B, S, N, msa_dim)
        """
        B, S, N, D = msa_repr.shape
        h = self.num_heads
        d = self.head_dim

        x = self.layer_norm(msa_repr)
        q = self.query(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)
        k = self.key(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)
        v = self.value(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)
        g = torch.sigmoid(self.gate(x)).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)

        # Pair bias: shared across MSA rows
        bias = self.pair_bias(pair_repr).permute(0, 3, 1, 2)  # (B, h, N, N)

        attn = torch.einsum("bshid,bshjd->bshij", q, k) / math.sqrt(d)
        attn = attn + bias.unsqueeze(1)

        mask = msa_mask.view(B, S, 1, 1, N).expand_as(attn)
        attn = attn.masked_fill(mask == 0, float("-inf"))
        attn = F.softmax(attn, dim=-1)
        attn = self.dropout(attn)

        out = torch.einsum("bshij,bshjd->bshid", attn, v)
        out = g * out
        out = out.permute(0, 1, 3, 2, 4).reshape(B, S, N, D)
        return self.out_proj(out)


msa_dim = 64
pair_dim = 64
num_heads = 4
seq_len = 32
msa_depth = 8
batch_size = 2


def get_inputs():
    p = popcorn_pri
    msa_repr = torch.randn(p.jitter_int(batch_size), p.jitter_int(msa_depth), p.jitter_int(seq_len), msa_dim)
    pair_repr = torch.randn(p.jitter_int(batch_size), p.jitter_int(seq_len), p.jitter_int(seq_len), pair_dim)
    msa_mask = torch.ones(p.jitter_int(batch_size), p.jitter_int(msa_depth), p.jitter_int(seq_len))
    return [msa_repr, pair_repr, msa_mask]


def get_init_inputs():
    return [msa_dim, pair_dim, num_heads]
turn 0 · FINAL · 1 tool call(s) · 56.7s turn · 56.7s LLM · 13,777 chars (click to expand)
turn 056.7s turn · 56.7s LLM · 0.0s tools · FINAL
call → submit_kernel
callsubmit_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n\n_cpp_src = r'''\n#include <torch/extension.h>\n\ntorch::Tensor row_msa_attn_forward(\n torch::Tensor msa, torch::Tensor pair, torch::Tensor mask,\n torch::Tensor ln_w, torch::Tensor ln_b,\n torch::Tensor qw, torch::Tensor kw, torch::Tensor vw,\n torch::Tensor gw, torch::Tensor gb,\n torch::Tensor pbw,\n torch::Tensor ow, torch::Tensor ob,\n int64_t num_heads);\n'''\n\n_cuda_src = r'''\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <math_constants.h>\n\n__global__ void prep_qkvg_kernel(\n const float* __restrict__ msa,\n const float* __restrict__ ln_w,\n const float* __restrict__ ln_b,\n const float* __restrict__ qw,\n const float* __restrict__ kw,\n const float* __restrict__ vw,\n const float* __restrict__ gw,\n const float* __restrict__ gb,\n float* __restrict__ q,\n float* __restrict__ k,\n float* __restrict__ v,\n float* __restrict__ g,\n int T, int D)\n{\n int tok = blockIdx.x;\n int tid = threadIdx.x;\n __shared__ float red[128];\n __shared__ float xn[128];\n\n float s = 0.0f;\n for (int c = tid; c < D; c += blockDim.x) s += msa[tok * D + c];\n red[tid] = s;\n __syncthreads();\n for (int off = blockDim.x >> 1; off > 0; off >>= 1) {\n if (tid < off) red[tid] += red[tid + off];\n __syncthreads();\n }\n float mean = red[0] / (float)D;\n\n float vsum = 0.0f;\n for (int c = tid; c < D; c += blockDim.x) {\n float z = msa[tok * D + c] - mean;\n vsum += z * z;\n }\n red[tid] = vsum;\n __syncthreads();\n for (int off = blockDim.x >> 1; off > 0; off >>= 1) {\n if (tid < off) red[tid] += red[tid + off];\n __syncthreads();\n }\n float invstd = rsqrtf(red[0] / (float)D + 1.0e-5f);\n\n if (tid < D) {\n float y = (msa[tok * D + tid] - mean) * invstd;\n xn[tid] = y * ln_w[tid] + ln_b[tid];\n }\n __syncthreads();\n\n if (tid < D) {\n int oc = tid;\n float sq = 0.0f, sk = 0.0f, sv = 0.0f, sg = 0.0f;\n const float* wq = qw + oc * D;\n const float* wk = kw + oc * D;\n const float* wv = vw + oc * D;\n const float* wg = gw + oc * D;\n #pragma unroll\n for (int c = 0; c < 64; ++c) {\n if (c < D) {\n float x = xn[c];\n sq += x * wq[c];\n sk += x * wk[c];\n sv += x * wv[c];\n sg += x * wg[c];\n }\n }\n q[tok * D + oc] = sq;\n k[tok * D + oc] = sk;\n v[tok * D + oc] = sv;\n float zz = sg + gb[oc];\n g[tok * D + oc] = 1.0f / (1.0f + expf(-zz));\n }\n}\n\n__global__ void pair_bias_kernel(\n const float* __restrict__ pair,\n const float* __restrict__ pbw,\n float* __restrict__ pb,\n int B, int N, int PD, int H)\n{\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n int total = B * H * N * N;\n if (idx >= total) return;\n int j = idx % N;\n int t = idx / N;\n int i = t % N;\n t /= N;\n int h = t % H;\n int b = t / H;\n const float* pr = pair + ((b * N + i) * N + j) * PD;\n const float* w = pbw + h * PD;\n float sum = 0.0f;\n #pragma unroll\n for (int p = 0; p < 64; ++p) {\n if (p < PD) sum += pr[p] * w[p];\n }\n pb[idx] = sum;\n}\n\n__global__ void attn_kernel(\n const float* __restrict__ q,\n const float* __restrict__ k,\n const float* __restrict__ v,\n const float* __restrict__ gate,\n const float* __restrict__ pb,\n const float* __restrict__ mask,\n float* __restrict__ mid,\n int B, int S, int N, int D, int H, int d)\n{\n int bid = blockIdx.x;\n int tid = threadIdx.x;\n int i = bid % N;\n int t = bid / N;\n int h = t % H;\n t /= H;\n int s = t % S;\n int b = t / S;\n\n __shared__ float vals[64];\n __shared__ float red[64];\n\n int tok_i = (b * S + s) * N + i;\n int qbase = tok_i * D + h * d;\n float score = -CUDART_INF_F;\n if (tid < N) {\n int j = tid;\n int tok_j = (b * S + s) * N + j;\n int kbase = tok_j * D + h * d;\n float dot = 0.0f;\n #pragma unroll\n for (int r = 0; r < 16; ++r) {\n if (r < d) dot += q[qbase + r] * k[kbase + r];\n }\n score = dot * rsqrtf((float)d) + pb[((b * H + h) * N + i) * N + j];\n if (mask[(b * S + s) * N + j] == 0.0f) score = -CUDART_INF_F;\n vals[j] = score;\n }\n red[tid] = (tid < N) ? score : -CUDART_INF_F;\n __syncthreads();\n for (int off = 32; off > 0; off >>= 1) {\n if (tid < off) red[tid] = fmaxf(red[tid], red[tid + off]);\n __syncthreads();\n }\n float m = red[0];\n float e = 0.0f;\n if (tid < N) {\n e = expf(vals[tid] - m);\n vals[tid] = e;\n }\n red[tid] = (tid < N) ? e : 0.0f;\n __syncthreads();\n for (int off = 32; off > 0; off >>= 1) {\n if (tid < off) red[tid] += red[tid + off];\n __syncthreads();\n }\n float invsum = 1.0f / red[0];\n\n if (tid < d) {\n int r = tid;\n float out = 0.0f;\n for (int j = 0; j < N; ++j) {\n int tok_j = (b * S + s) * N + j;\n out += vals[j] * invsum * v[tok_j * D + h * d + r];\n }\n mid[qbase + r] = out * gate[qbase + r];\n }\n}\n\n__global__ void out_proj_kernel(\n const float* __restrict__ mid,\n const float* __restrict__ ow,\n const float* __restrict__ ob,\n float* __restrict__ out,\n int T, int D)\n{\n int tok = blockIdx.x;\n int tid = threadIdx.x;\n if (tok >= T) return;\n if (tid < D) {\n int oc = tid;\n const float* w = ow + oc * D;\n float sum = ob[oc];\n #pragma unroll\n for (int c = 0; c < 64; ++c) {\n if (c < D) sum += mid[tok * D + c] * w[c];\n }\n out[tok * D + oc] = sum;\n }\n}\n\ntorch::Tensor row_msa_attn_forward(\n torch::Tensor msa, torch::Tensor pair, torch::Tensor mask,\n torch::Tensor ln_w, torch::Tensor ln_b,\n torch::Tensor qw, torch::Tensor kw, torch::Tensor vw,\n torch::Tensor gw, torch::Tensor gb,\n torch::Tensor pbw,\n torch::Tensor ow, torch::Tensor ob,\n int64_t num_heads)\n{\n int B = (int)msa.size(0);\n int S = (int)msa.size(1);\n int N = (int)msa.size(2);\n int D = (int)msa.size(3);\n int PD = (int)pair.size(3);\n int H = (int)num_heads;\n int d = D / H;\n int T = B * S * N;\n\n auto q = torch::empty({T, D}, msa.options());\n auto k = torch::empty({T, D}, msa.options());\n auto v = torch::empty({T, D}, msa.options());\n auto g = torch::empty({T, D}, msa.options());\n auto pb = torch::empty({B, H, N, N}, msa.options());\n auto mid = torch::empty({T, D}, msa.options());\n auto out = torch::empty({B, S, N, D}, msa.options());\n\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n prep_qkvg_kernel<<<T, 128, 0, stream>>>(\n msa.data_ptr<float>(), ln_w.data_ptr<float>(), ln_b.data_ptr<float>(),\n qw.data_ptr<float>(), kw.data_ptr<float>(), vw.data_ptr<float>(),\n gw.data_ptr<float>(), gb.data_ptr<float>(),\n q.data_ptr<float>(), k.data_ptr<float>(), v.data_ptr<float>(), g.data_ptr<float>(), T, D);\n\n int pbtot = B * H * N * N;\n pair_bias_kernel<<<(pbtot + 127) / 128, 128, 0, stream>>>(\n pair.data_ptr<float>(), pbw.data_ptr<float>(), pb.data_ptr<float>(), B, N, PD, H);\n\n attn_kernel<<<B * S * H * N, 64, 0, stream>>>(\n q.data_ptr<float>(), k.data_ptr<float>(), v.data_ptr<float>(), g.data_ptr<float>(),\n pb.data_ptr<float>(), mask.data_ptr<float>(), mid.data_ptr<float>(),\n B, S, N, D, H, d);\n\n out_proj_kernel<<<T, 128, 0, stream>>>(\n mid.data_ptr<float>(), ow.data_ptr<float>(), ob.data_ptr<float>(), out.data_ptr<float>(), T, D);\n\n return out;\n}\n'''\n\n_ext = load_inline(\n name=\"row_msa_attn_ext_v1\",\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=[\"row_msa_attn_forward\"],\n extra_cuda_cflags=[\"-O3\"],\n extra_cflags=[\"-O3\"],\n verbose=False,\n)\n\n\nclass ModelNew(nn.Module):\n def __init__(self, msa_dim, pair_dim, num_heads, dropout=0.0):\n super().__init__()\n self.num_heads = num_heads\n self.head_dim = msa_dim // num_heads\n assert msa_dim % num_heads == 0\n self.layer_norm = nn.LayerNorm(msa_dim)\n self.query = nn.Linear(msa_dim, msa_dim, bias=False)\n self.key = nn.Linear(msa_dim, msa_dim, bias=False)\n self.value = nn.Linear(msa_dim, msa_dim, bias=False)\n self.gate = nn.Linear(msa_dim, msa_dim)\n self.pair_bias = nn.Linear(pair_dim, num_heads, bias=False)\n self.out_proj = nn.Linear(msa_dim, msa_dim)\n self.dropout = nn.Dropout(dropout)\n\n def forward(self, msa_repr: torch.Tensor, pair_repr: torch.Tensor, msa_mask: torch.Tensor) -> torch.Tensor:\n return _ext.row_msa_attn_forward(\n msa_repr.contiguous(), pair_repr.contiguous(), msa_mask.contiguous(),\n self.layer_norm.weight.contiguous(), self.layer_norm.bias.contiguous(),\n self.query.weight.contiguous(), self.key.weight.contiguous(), self.value.weight.contiguous(),\n self.gate.weight.contiguous(), self.gate.bias.contiguous(),\n self.pair_bias.weight.contiguous(),\n self.out_proj.weight.contiguous(), self.out_proj.bias.contiguous(),\n int(self.num_heads),\n )\n" }
result ← submit_kernel (fail)
failsubmit_kernel submit_kernel FAILED: kernel did not compile. Error building extension 'row_msa_attn_ext_v1': [1/3] /opt/conda/bin/x86_64-conda-linux-gnu-c++ -MMD -MF main.o.d -DTORCH_EXTENSION_NAME=row_msa_attn_ext_v1 -DTORCH_API_INCLUDE_EXTENSION_H -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include/torch/csrc/api/include -isystem /usr/local/cuda/include -isystem /opt/conda/include/python3.10 -fPIC -std=c++17 -O3 -c /scratch/abaner/KernelBench/runs/l123_single_gpt/popcorn/gpt-5.5-priority/level_2_problem_29_cache/k_6dd38bc1a0cb/row_msa_attn_ext_v1/main.cpp -o main.o [2/3] /usr/local/cuda/bin/nvcc -MD -MF cuda.cuda.o.d -ccbin /opt/conda/bin/x86_64-conda-linux-gnu-cc -DTORCH_EXTENSION_NAME=row_msa_attn_ext_v1 -DTORCH_API_INCLUDE_EXTENSION_H -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include/torch/csrc/api/include -isystem /usr/local/cuda/include -isystem /opt/conda/include/python3.10 -D__CUDA_NO_HALF_OPERATORS__ -D__CUDA_NO_HALF_CONVERSIONS__ -D__CUDA_NO_BFLOAT16_CONVERSIONS__ -D__CUDA_NO_HALF2_OPERATORS__ --expt-relaxed-constexpr -gencode=arch=compute_90,code=compute_90 -gencode=arch=compute_90,code=sm_90 --compiler-options '-fPIC' -O3 -std=c++17 -c /scratch/abaner/KernelBench/runs/l123_single_gpt/popcorn/gpt-5.5-priority/level_2_problem_29_cache/k_6dd38bc1a0cb/row_msa_attn_ext_v1/cuda.cu -o cuda.cuda.o FAILED: [code=2] cuda.cuda.o /usr/local/cuda/bin/nvcc -MD -MF cuda.cuda.o.d -ccbin /opt/conda/bin/x86_64-conda-linux-gnu-cc -DTORCH_EXTENSION_NAME=row_msa_attn_ext_v1 -DTORCH_API_INCLUDE_EXTENSION_H -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include/torch/csrc/api/include -isystem /usr/local/cuda/include -isystem /opt/conda/include/python3.10 -D__CUDA_NO_HALF_OPERATORS__ -D__CUDA_NO_HALF_CONVERSIONS__ -D__CUDA_NO_BFLOAT16_CONVERSIONS__ -D__CUDA_NO_HALF2_OPERATORS__ --expt-relaxed-constexpr -gencode=arch=compute_90,code=compute_90 -gencode=arch=compute_90,code=sm_90 --compiler-options '-fPIC' -O3 -std=c++17 -c /scratch/abaner/KernelBench/runs/l123_single_gpt/popcorn/gpt-5.5-priority/level_2_problem_29_cache/k_6dd38bc1a0cb/row_msa_attn_ext_v1/cuda.cu -o cuda.cuda.o nvcc warning : incompatible redefinition for option 'compiler-bindir', the last value of this option was used /scratch/abaner/KernelBench/runs/l123_single_gpt/popcorn/gpt-5.5-priority/level_2_problem_29_cache/k_6dd38bc1a0cb/row_msa_attn_ext_v1/cuda.cu(226): error: namespace "at::cuda" has no member "getCurrentCUDAStream" cudaStream_t stream = at::cuda::getCurrentCUDAStream(); ^ 1 error detected in the compilation of "/scratch/abaner/KernelBench/runs/l123_single_gpt/popcorn/gpt-5.5-priority/level_2_problem_29_cache/k_6dd38bc1a0cb/row_msa_attn_ext_v1/cuda.cu". ninja: build stopped: subcommand failed.
final submitted kernel (300 lines, 9,307 chars) — full source, untruncated
import torch
import torch.nn as nn
from torch.utils.cpp_extension import load_inline


_cpp_src = r'''
#include <torch/extension.h>

torch::Tensor row_msa_attn_forward(
    torch::Tensor msa, torch::Tensor pair, torch::Tensor mask,
    torch::Tensor ln_w, torch::Tensor ln_b,
    torch::Tensor qw, torch::Tensor kw, torch::Tensor vw,
    torch::Tensor gw, torch::Tensor gb,
    torch::Tensor pbw,
    torch::Tensor ow, torch::Tensor ob,
    int64_t num_heads);
'''

_cuda_src = r'''
#include <torch/extension.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <math_constants.h>

__global__ void prep_qkvg_kernel(
    const float* __restrict__ msa,
    const float* __restrict__ ln_w,
    const float* __restrict__ ln_b,
    const float* __restrict__ qw,
    const float* __restrict__ kw,
    const float* __restrict__ vw,
    const float* __restrict__ gw,
    const float* __restrict__ gb,
    float* __restrict__ q,
    float* __restrict__ k,
    float* __restrict__ v,
    float* __restrict__ g,
    int T, int D)
{
    int tok = blockIdx.x;
    int tid = threadIdx.x;
    __shared__ float red[128];
    __shared__ float xn[128];

    float s = 0.0f;
    for (int c = tid; c < D; c += blockDim.x) s += msa[tok * D + c];
    red[tid] = s;
    __syncthreads();
    for (int off = blockDim.x >> 1; off > 0; off >>= 1) {
        if (tid < off) red[tid] += red[tid + off];
        __syncthreads();
    }
    float mean = red[0] / (float)D;

    float vsum = 0.0f;
    for (int c = tid; c < D; c += blockDim.x) {
        float z = msa[tok * D + c] - mean;
        vsum += z * z;
    }
    red[tid] = vsum;
    __syncthreads();
    for (int off = blockDim.x >> 1; off > 0; off >>= 1) {
        if (tid < off) red[tid] += red[tid + off];
        __syncthreads();
    }
    float invstd = rsqrtf(red[0] / (float)D + 1.0e-5f);

    if (tid < D) {
        float y = (msa[tok * D + tid] - mean) * invstd;
        xn[tid] = y * ln_w[tid] + ln_b[tid];
    }
    __syncthreads();

    if (tid < D) {
        int oc = tid;
        float sq = 0.0f, sk = 0.0f, sv = 0.0f, sg = 0.0f;
        const float* wq = qw + oc * D;
        const float* wk = kw + oc * D;
        const float* wv = vw + oc * D;
        const float* wg = gw + oc * D;
        #pragma unroll
        for (int c = 0; c < 64; ++c) {
            if (c < D) {
                float x = xn[c];
                sq += x * wq[c];
                sk += x * wk[c];
                sv += x * wv[c];
                sg += x * wg[c];
            }
        }
        q[tok * D + oc] = sq;
        k[tok * D + oc] = sk;
        v[tok * D + oc] = sv;
        float zz = sg + gb[oc];
        g[tok * D + oc] = 1.0f / (1.0f + expf(-zz));
    }
}

__global__ void pair_bias_kernel(
    const float* __restrict__ pair,
    const float* __restrict__ pbw,
    float* __restrict__ pb,
    int B, int N, int PD, int H)
{
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    int total = B * H * N * N;
    if (idx >= total) return;
    int j = idx % N;
    int t = idx / N;
    int i = t % N;
    t /= N;
    int h = t % H;
    int b = t / H;
    const float* pr = pair + ((b * N + i) * N + j) * PD;
    const float* w = pbw + h * PD;
    float sum = 0.0f;
    #pragma unroll
    for (int p = 0; p < 64; ++p) {
        if (p < PD) sum += pr[p] * w[p];
    }
    pb[idx] = sum;
}

__global__ void attn_kernel(
    const float* __restrict__ q,
    const float* __restrict__ k,
    const float* __restrict__ v,
    const float* __restrict__ gate,
    const float* __restrict__ pb,
    const float* __restrict__ mask,
    float* __restrict__ mid,
    int B, int S, int N, int D, int H, int d)
{
    int bid = blockIdx.x;
    int tid = threadIdx.x;
    int i = bid % N;
    int t = bid / N;
    int h = t % H;
    t /= H;
    int s = t % S;
    int b = t / S;

    __shared__ float vals[64];
    __shared__ float red[64];

    int tok_i = (b * S + s) * N + i;
    int qbase = tok_i * D + h * d;
    float score = -CUDART_INF_F;
    if (tid < N) {
        int j = tid;
        int tok_j = (b * S + s) * N + j;
        int kbase = tok_j * D + h * d;
        float dot = 0.0f;
        #pragma unroll
        for (int r = 0; r < 16; ++r) {
            if (r < d) dot += q[qbase + r] * k[kbase + r];
        }
        score = dot * rsqrtf((float)d) + pb[((b * H + h) * N + i) * N + j];
        if (mask[(b * S + s) * N + j] == 0.0f) score = -CUDART_INF_F;
        vals[j] = score;
    }
    red[tid] = (tid < N) ? score : -CUDART_INF_F;
    __syncthreads();
    for (int off = 32; off > 0; off >>= 1) {
        if (tid < off) red[tid] = fmaxf(red[tid], red[tid + off]);
        __syncthreads();
    }
    float m = red[0];
    float e = 0.0f;
    if (tid < N) {
        e = expf(vals[tid] - m);
        vals[tid] = e;
    }
    red[tid] = (tid < N) ? e : 0.0f;
    __syncthreads();
    for (int off = 32; off > 0; off >>= 1) {
        if (tid < off) red[tid] += red[tid + off];
        __syncthreads();
    }
    float invsum = 1.0f / red[0];

    if (tid < d) {
        int r = tid;
        float out = 0.0f;
        for (int j = 0; j < N; ++j) {
            int tok_j = (b * S + s) * N + j;
            out += vals[j] * invsum * v[tok_j * D + h * d + r];
        }
        mid[qbase + r] = out * gate[qbase + r];
    }
}

__global__ void out_proj_kernel(
    const float* __restrict__ mid,
    const float* __restrict__ ow,
    const float* __restrict__ ob,
    float* __restrict__ out,
    int T, int D)
{
    int tok = blockIdx.x;
    int tid = threadIdx.x;
    if (tok >= T) return;
    if (tid < D) {
        int oc = tid;
        const float* w = ow + oc * D;
        float sum = ob[oc];
        #pragma unroll
        for (int c = 0; c < 64; ++c) {
            if (c < D) sum += mid[tok * D + c] * w[c];
        }
        out[tok * D + oc] = sum;
    }
}

torch::Tensor row_msa_attn_forward(
    torch::Tensor msa, torch::Tensor pair, torch::Tensor mask,
    torch::Tensor ln_w, torch::Tensor ln_b,
    torch::Tensor qw, torch::Tensor kw, torch::Tensor vw,
    torch::Tensor gw, torch::Tensor gb,
    torch::Tensor pbw,
    torch::Tensor ow, torch::Tensor ob,
    int64_t num_heads)
{
    int B = (int)msa.size(0);
    int S = (int)msa.size(1);
    int N = (int)msa.size(2);
    int D = (int)msa.size(3);
    int PD = (int)pair.size(3);
    int H = (int)num_heads;
    int d = D / H;
    int T = B * S * N;

    auto q = torch::empty({T, D}, msa.options());
    auto k = torch::empty({T, D}, msa.options());
    auto v = torch::empty({T, D}, msa.options());
    auto g = torch::empty({T, D}, msa.options());
    auto pb = torch::empty({B, H, N, N}, msa.options());
    auto mid = torch::empty({T, D}, msa.options());
    auto out = torch::empty({B, S, N, D}, msa.options());

    cudaStream_t stream = at::cuda::getCurrentCUDAStream();
    prep_qkvg_kernel<<<T, 128, 0, stream>>>(
        msa.data_ptr<float>(), ln_w.data_ptr<float>(), ln_b.data_ptr<float>(),
        qw.data_ptr<float>(), kw.data_ptr<float>(), vw.data_ptr<float>(),
        gw.data_ptr<float>(), gb.data_ptr<float>(),
        q.data_ptr<float>(), k.data_ptr<float>(), v.data_ptr<float>(), g.data_ptr<float>(), T, D);

    int pbtot = B * H * N * N;
    pair_bias_kernel<<<(pbtot + 127) / 128, 128, 0, stream>>>(
        pair.data_ptr<float>(), pbw.data_ptr<float>(), pb.data_ptr<float>(), B, N, PD, H);

    attn_kernel<<<B * S * H * N, 64, 0, stream>>>(
        q.data_ptr<float>(), k.data_ptr<float>(), v.data_ptr<float>(), g.data_ptr<float>(),
        pb.data_ptr<float>(), mask.data_ptr<float>(), mid.data_ptr<float>(),
        B, S, N, D, H, d);

    out_proj_kernel<<<T, 128, 0, stream>>>(
        mid.data_ptr<float>(), ow.data_ptr<float>(), ob.data_ptr<float>(), out.data_ptr<float>(), T, D);

    return out;
}
'''

_ext = load_inline(
    name="row_msa_attn_ext_v1",
    cpp_sources=_cpp_src,
    cuda_sources=_cuda_src,
    functions=["row_msa_attn_forward"],
    extra_cuda_cflags=["-O3"],
    extra_cflags=["-O3"],
    verbose=False,
)


class ModelNew(nn.Module):
    def __init__(self, msa_dim, pair_dim, num_heads, dropout=0.0):
        super().__init__()
        self.num_heads = num_heads
        self.head_dim = msa_dim // num_heads
        assert msa_dim % num_heads == 0
        self.layer_norm = nn.LayerNorm(msa_dim)
        self.query = nn.Linear(msa_dim, msa_dim, bias=False)
        self.key = nn.Linear(msa_dim, msa_dim, bias=False)
        self.value = nn.Linear(msa_dim, msa_dim, bias=False)
        self.gate = nn.Linear(msa_dim, msa_dim)
        self.pair_bias = nn.Linear(pair_dim, num_heads, bias=False)
        self.out_proj = nn.Linear(msa_dim, msa_dim)
        self.dropout = nn.Dropout(dropout)

    def forward(self, msa_repr: torch.Tensor, pair_repr: torch.Tensor, msa_mask: torch.Tensor) -> torch.Tensor:
        return _ext.row_msa_attn_forward(
            msa_repr.contiguous(), pair_repr.contiguous(), msa_mask.contiguous(),
            self.layer_norm.weight.contiguous(), self.layer_norm.bias.contiguous(),
            self.query.weight.contiguous(), self.key.weight.contiguous(), self.value.weight.contiguous(),
            self.gate.weight.contiguous(), self.gate.bias.contiguous(),
            self.pair_bias.weight.contiguous(),
            self.out_proj.weight.contiguous(), self.out_proj.bias.contiguous(),
            int(self.num_heads),
        )