FW-GLM-5-1 · popcorn · pop_l123_all_gpt
correct 8_EvoformerBlock.py
model
FW-GLM-5-1
turns / max
15 / 15
tool calls
16
tokens (total)
400776
wall clock
2714.9s
truncated
no
backend
cuda fp32
variant
popcorn
runtime
2.41 μs
ref runtime
0.92 μs
speedup
0.38x
started
2026-05-11T18:14:39Z
timingkernelbench.timing
kernel runtime
2.41 μs
ref runtime
0.92 μs
speedup
0.38x
kernel mean / median / std
2.41 / — / 0.00 μs
ref mean / median / std
0.92 / — / 0.21 μs
numerical precisioneval_kernel_against_ref
max abs error
8.34e-07
mean abs error
7.33e-08
max rel error
1.50
mean rel error
3.78e-06
gpu memoryextended_metrics.measure_memory
kernel peak
51.16 MB
ref peak
51.28 MB
kernel / ref
1.00x
kernel launches / fusiontorch.profiler
kernel launches
170
ref launches
258
fusion ratio (ref / kernel)
1.52x
total cuda time
2122 μs
energyNVML (pynvml)
kernel mJ / run
482.54
ref mJ / run
0.00
ratio (ref / kernel)
—
avg power
212.12 W
ref avg power
0.00 W
SOL (speed-of-light)nsight_hardware_counters
SOL score (0–1)
1.000
dram util %
100.0%
compute util %
8.3%
bottleneck
memory-bound
dominant pipe
tensor-core
arithmetic intensity
—
ridge point
295.22
roofline / occupancynsight_hardware_counters
dram bandwidth
4384.76 GB/s
dram util %
100.0%
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
388.1%
bottleneck
memory-bound
dominant pipe %
8.3%
arithmetic intensity
—
ridge point
295.22
L1 hit %
—
L2 hit %
2289.0%
ld sectors / req
298.77
st sectors / req
—
regs / thread
1122
smem / block
95104 B
block size
3712
peak bw
3350.00 GB/s
peak fp32 tflops
67.00
peak fp16 tflops
1979.00
warp stalls (top 5)nsight
long_scoreboard
49510.3%
short_scoreboard
3853.1%
mio_throttle
1075.2%
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn
import torch.nn.functional as F
import math


class MSARowSelfAttention(nn.Module):
    def __init__(self, msa_dim, num_heads):
        super().__init__()
        self.num_heads = num_heads
        self.head_dim = msa_dim // num_heads
        self.norm = nn.LayerNorm(msa_dim)
        self.q = nn.Linear(msa_dim, msa_dim, bias=False)
        self.k = nn.Linear(msa_dim, msa_dim, bias=False)
        self.v = nn.Linear(msa_dim, msa_dim, bias=False)
        self.out = nn.Linear(msa_dim, msa_dim)

    def forward(self, msa):
        B, S, N, D = msa.shape
        h, d = self.num_heads, self.head_dim
        x = self.norm(msa)
        q = self.q(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)
        k = self.k(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)
        v = self.v(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)
        attn = (q @ k.transpose(-1, -2)) / math.sqrt(d)
        attn = F.softmax(attn, dim=-1)
        out = (attn @ v).permute(0, 1, 3, 2, 4).reshape(B, S, N, D)
        return self.out(out)


class OuterProductMeanSmall(nn.Module):
    def __init__(self, msa_dim, pair_dim, hidden=32):
        super().__init__()
        self.norm = nn.LayerNorm(msa_dim)
        self.a = nn.Linear(msa_dim, hidden)
        self.b = nn.Linear(msa_dim, hidden)
        self.out = nn.Linear(hidden * hidden, pair_dim)

    def forward(self, msa):
        x = self.norm(msa)
        a = self.a(x)
        b = self.b(x)
        outer = torch.einsum("bsih,bsjk->bijhk", a, b)
        S = msa.shape[1]
        outer = outer / max(S, 1)
        B, N1, N2, H1, H2 = outer.shape
        return self.out(outer.reshape(B, N1, N2, H1 * H2))


class TriMulOut(nn.Module):
    def __init__(self, pair_dim, hidden):
        super().__init__()
        self.norm = nn.LayerNorm(pair_dim)
        self.pa = nn.Linear(pair_dim, hidden)
        self.pb = nn.Linear(pair_dim, hidden)
        self.ga = nn.Linear(pair_dim, hidden)
        self.gb = nn.Linear(pair_dim, hidden)
        self.norm_out = nn.LayerNorm(hidden)
        self.out = nn.Linear(hidden, pair_dim)
        self.gate_out = nn.Linear(pair_dim, pair_dim)

    def forward(self, pair):
        x = self.norm(pair)
        a = self.pa(x) * torch.sigmoid(self.ga(x))
        b = self.pb(x) * torch.sigmoid(self.gb(x))
        o = torch.einsum("bikh,bjkh->bijh", a, b)
        o = self.out(self.norm_out(o))
        return torch.sigmoid(self.gate_out(pair)) * o


class Model(nn.Module):
    """
    Full Evoformer block from AlphaFold2 combining MSA row self-attention,
    outer-product mean update, and triangular multiplicative update of the
    pair representation.  A single block of the iterative refinement stack.
    """

    def __init__(self, msa_dim, pair_dim, num_heads, tri_hidden):
        super().__init__()
        self.msa_attn = MSARowSelfAttention(msa_dim, num_heads)
        self.opm = OuterProductMeanSmall(msa_dim, pair_dim)
        self.tri_mul = TriMulOut(pair_dim, tri_hidden)

    def forward(
        self, msa_repr: torch.Tensor, pair_repr: torch.Tensor
    ) -> tuple:
        """
        Args:
            msa_repr:  (B, S, N, msa_dim)
            pair_repr: (B, N, N, pair_dim)
        Returns:
            (updated_msa, updated_pair)
        """
        msa_repr = msa_repr + self.msa_attn(msa_repr)
        pair_repr = pair_repr + self.opm(msa_repr)
        pair_repr = pair_repr + self.tri_mul(pair_repr)
        return msa_repr, pair_repr


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


def get_inputs():
    p = popcorn_pri
    bs = p.jitter_int(batch_size)
    sd = p.jitter_int(msa_depth)
    sl = p.jitter_int(seq_len)
    msa = torch.randn(bs, sd, sl, msa_dim)
    pair = torch.randn(bs, sl, sl, pair_dim)
    return [msa, pair]


def get_init_inputs():
    return [msa_dim, pair_dim, num_heads, tri_hidden]
turn 09.4s turn · 4.6s LLM · 4.8s tools · tokens in=2,931 · out=383 · total=3,314
reasoning
Let me analyze this task. I need to implement a custom CUDA kernel that replaces the forward computation of an Evoformer block from AlphaFold2. The model consists of: 1. **MSARowSelfAttention** - Multi-head self-attention on MSA rows 2. **OuterProductMeanSmall** - Outer product mean update for pair representation 3. **TriMulOut** - Triangular multiplicative update for pair representation Let me understand each component: ### MSARowSelfAttention - Input: msa (B, S, N, D) - LayerNorm -> Q, K, V projections - Reshape to (B, S, h, N, d) where h=num_heads, d=head_dim - Attention: Q @ K^T / sqrt(d) -> softmax -> @ V - Output projection ### OuterProductMeanSmall - Input: msa (B, S, N, D) - LayerNorm -> a, b projections to hidden dim - Outer product: einsum("bsih,bsjk->bijhk", a, b) -> (B, N, N, H, H) - Divide by S - Output projection ### TriMulOut - Input: pair (B, N, N, pair_dim) - LayerNorm -> a, b, ga, gb projections - a = pa(x) * sigmoid(ga(x)), b = pb(x) * sigmoid(gb(x)) - einsum("bikh,bjkh->bijh", a, b) -> (B, N, N, hidden) - Output with gating Let me start by getting GPU specs and understanding the problem better.
assistantI'll start by understanding the GPU specs and then develop a custom CUDA kernel for this Evoformer block from AlphaFold2.
call → get_gpu_specs
callget_gpu_specs {}
call → ert_roofline
callert_roofline {}
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
result ← ert_roofline (ok)
okert_roofline ert_roofline PASSED: empirical roofline benchmarks complete. === Empirical Roofline Model === Device: NVIDIA H100 80GB HBM3 --- Measured Bandwidth --- L1 13.6 GB/s (working set: 32 KB) L2 927.6 GB/s (working set: 2048 KB) DRAM 2577.3 GB/s (working set: 262144 KB) --- Measured Compute Throughput --- FP32 51.85 TFLOPS FP16 (tensor core) 758.77 TFLOPS Ridge point (FP32): 20.12 FLOPs/byte Kernels with arithmetic intensity below the ridge point are memory-bound; above it they are compute-bound.
turn 1 · 1 tool call(s) · 289.8s turn · 33.7s LLM · tokens in=3,390 · out=3,372 · total=6,762 · 14,642 chars (click to expand)
turn 1289.8s turn · 33.7s LLM · 256.1s tools · tokens in=3,390 · out=3,372 · total=6,762
reasoning
Now let me compile the reference implementation and understand the exact operations. This is an Evoformer block from AlphaFold2 with: 1. MSA Row Self-Attention (with LayerNorm, Q/K/V projections, attention, output projection) 2. Outer Product Mean (LayerNorm, two projections, outer product via einsum, output projection) 3. Triangular Multiplicative Update Out (LayerNorm, two paths with gating, einsum, output projection) Let me write the custom CUDA kernel. I'll need to implement: 1. Fused LayerNorm + Linear operations 2. Attention computation 3. Einsum operations for outer product and triangular multiplication Let me start with a basic implementation and optimize from there.
assistantNow let me create an initial CUDA kernel implementation. This is a complex block with multiple operations, so I'll start with a fused implementation approach.
call → compile_kernel
callcompile_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport math\nfrom torch.utils.cpp_extension import load_inline\n\n# Define CUDA kernels for the Evoformer block\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\n// Constants for small dimension handling\nconstexpr int BLOCK_SIZE = 256;\n\n// LayerNorm kernel - processes all elements\n__global__ void layernorm_kernel(\n const float* __restrict__ input,\n float* __restrict__ output,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n int total_elements,\n int norm_size,\n float eps\n) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n int num_norm_groups = total_elements / norm_size;\n \n if (idx < num_norm_groups) {\n int start = idx * norm_size;\n \n // Compute mean\n float mean = 0.0f;\n for (int i = 0; i < norm_size; i++) {\n mean += input[start + i];\n }\n mean /= norm_size;\n \n // Compute variance\n float var = 0.0f;\n for (int i = 0; i < norm_size; i++) {\n float diff = input[start + i] - mean;\n var += diff * diff;\n }\n var /= norm_size;\n \n // Normalize and apply weight/bias\n float inv_std = rsqrtf(var + eps);\n for (int i = 0; i < norm_size; i++) {\n int out_idx = start + i;\n output[out_idx] = (input[out_idx] - mean) * inv_std * weight[i] + bias[i];\n }\n }\n}\n\n// Simple linear projection kernel\n__global__ void linear_kernel(\n const float* __restrict__ input,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n float* __restrict__ output,\n int batch_size,\n int in_features,\n int out_features\n) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n int total_outputs = batch_size * out_features;\n \n if (idx < total_outputs) {\n int b = idx / out_features;\n int o = idx % out_features;\n \n float sum = bias ? bias[o] : 0.0f;\n for (int i = 0; i < in_features; i++) {\n sum += input[b * in_features + i] * weight[o * in_features + i];\n }\n output[idx] = sum;\n }\n}\n\n// Fused LayerNorm + Linear kernel\n__global__ void layernorm_linear_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n const float* __restrict__ linear_weight,\n const float* __restrict__ linear_bias,\n float* __restrict__ output,\n int batch_size,\n int in_features,\n int out_features,\n float eps\n) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n int total_outputs = batch_size * out_features;\n \n if (idx < total_outputs) {\n int b = idx / out_features;\n int o = idx % out_features;\n \n // Compute mean\n float mean = 0.0f;\n int start = b * in_features;\n for (int i = 0; i < in_features; i++) {\n mean += input[start + i];\n }\n mean /= in_features;\n \n // Compute variance\n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[start + i] - mean;\n var += diff * diff;\n }\n var /= in_features;\n \n // Normalize and compute linear projection\n float inv_std = rsqrtf(var + eps);\n float sum = linear_bias ? linear_bias[o] : 0.0f;\n for (int i = 0; i < in_features; i++) {\n float normalized = (input[start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n sum += normalized * linear_weight[o * in_features + i];\n }\n output[idx] = sum;\n }\n}\n\n// Softmax kernel for attention scores\n__global__ void softmax_kernel(\n float* __restrict__ data,\n int batch_size,\n int num_heads,\n int seq_len,\n int key_len\n) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n int total_rows = batch_size * num_heads * seq_len;\n \n if (idx < total_rows) {\n int row_start = idx * key_len;\n \n // Find max for numerical stability\n float max_val = data[row_start];\n for (int i = 1; i < key_len; i++) {\n max_val = fmaxf(max_val, data[row_start + i]);\n }\n \n // Compute exp and sum\n float sum = 0.0f;\n for (int i = 0; i < key_len; i++) {\n data[row_start + i] = expf(data[row_start + i] - max_val);\n sum += data[row_start + i];\n }\n \n // Normalize\n for (int i = 0; i < key_len; i++) {\n data[row_start + i] /= sum;\n }\n }\n}\n\n// Helper function to get tensor data pointer\nfloat* get_data_ptr(torch::Tensor& t) {\n return t.data_ptr<float>();\n}\n\nconst float* get_const_data_ptr(const torch::Tensor& t) {\n return t.data_ptr<float>();\n}\n\n// Host functions to launch kernels\ntorch::Tensor cuda_layernorm(\n const torch::Tensor& input,\n const torch::Tensor& weight,\n const torch::Tensor& bias,\n float eps\n) {\n auto output = torch::empty_like(input);\n int total_elements = input.numel();\n int norm_size = input.size(-1);\n \n int blocks = (total_elements / norm_size + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n layernorm_kernel<<<blocks, BLOCK_SIZE>>>(\n get_const_data_ptr(input),\n get_data_ptr(output),\n get_const_data_ptr(weight),\n get_const_data_ptr(bias),\n total_elements,\n norm_size,\n eps\n );\n \n return output;\n}\n\ntorch::Tensor cuda_linear(\n const torch::Tensor& input,\n const torch::Tensor& weight,\n const c10::optional<torch::Tensor>& bias\n) {\n int batch_size = input.numel() / input.size(-1);\n int in_features = input.size(-1);\n int out_features = weight.size(0);\n \n auto output = torch::empty({batch_size, out_features}, input.options());\n \n int blocks = (batch_size * out_features + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n linear_kernel<<<blocks, BLOCK_SIZE>>>(\n get_const_data_ptr(input.reshape({-1})),\n get_const_data_ptr(weight),\n bias.has_value() ? get_const_data_ptr(bias.value()) : nullptr,\n get_data_ptr(output),\n batch_size,\n in_features,\n out_features\n );\n \n return output;\n}\n\ntorch::Tensor cuda_layernorm_linear(\n const torch::Tensor& input,\n const torch::Tensor& ln_weight,\n const torch::Tensor& ln_bias,\n const torch::Tensor& linear_weight,\n const c10::optional<torch::Tensor>& linear_bias,\n float eps\n) {\n int batch_size = input.numel() / input.size(-1);\n int in_features = input.size(-1);\n int out_features = linear_weight.size(0);\n \n auto output = torch::empty({batch_size, out_features}, input.options());\n \n int blocks = (batch_size * out_features + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n layernorm_linear_kernel<<<blocks, BLOCK_SIZE>>>(\n get_const_data_ptr(input.reshape({-1})),\n get_const_data_ptr(ln_weight),\n get_const_data_ptr(ln_bias),\n get_const_data_ptr(linear_weight),\n linear_bias.has_value() ? get_const_data_ptr(linear_bias.value()) : nullptr,\n get_data_ptr(output),\n batch_size,\n in_features,\n out_features,\n eps\n );\n \n return output;\n}\n\ntorch::Tensor cuda_softmax(\n torch::Tensor& data,\n int dim\n) {\n // Assuming dim=-1 and shape [batch, num_heads, seq_len, key_len]\n auto sizes = data.sizes();\n int batch_size = sizes[0];\n int num_heads = sizes[1];\n int seq_len = sizes[2];\n int key_len = sizes[3];\n \n int total_rows = batch_size * num_heads * seq_len;\n int blocks = (total_rows + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n softmax_kernel<<<blocks, BLOCK_SIZE>>>(\n get_data_ptr(data),\n batch_size,\n num_heads,\n seq_len,\n key_len\n );\n \n return data;\n}\n\"\"\"\n\n# Load CUDA extension\nevoformer_cuda = load_inline(\n name='evoformer_cuda',\n cpp_sources='',\n cuda_sources=cuda_source,\n functions=['cuda_layernorm', 'cuda_linear', 'cuda_layernorm_linear', 'cuda_softmax'],\n verbose=True,\n extra_cuda_cflags=['-O3']\n)\n\n\nclass MSARowSelfAttention(nn.Module):\n def __init__(self, msa_dim, num_heads):\n super().__init__()\n self.num_heads = num_heads\n self.head_dim = msa_dim // num_heads\n self.norm = nn.LayerNorm(msa_dim)\n self.q = nn.Linear(msa_dim, msa_dim, bias=False)\n self.k = nn.Linear(msa_dim, msa_dim, bias=False)\n self.v = nn.Linear(msa_dim, msa_dim, bias=False)\n self.out = nn.Linear(msa_dim, msa_dim)\n\n def forward(self, msa):\n B, S, N, D = msa.shape\n h, d = self.num_heads, self.head_dim\n \n # Use PyTorch for complex tensor operations, CUDA for LayerNorm+Linear\n x = evoformer_cuda.cuda_layernorm(\n msa.reshape(-1, D),\n self.norm.weight,\n self.norm.bias,\n 1e-5\n ).reshape(B, S, N, D)\n \n q = self.q(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)\n k = self.k(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)\n v = self.v(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)\n \n attn = (q @ k.transpose(-1, -2)) / math.sqrt(d)\n attn = F.softmax(attn, dim=-1)\n out = (attn @ v).permute(0, 1, 3, 2, 4).reshape(B, S, N, D)\n return self.out(out)\n\n\nclass OuterProductMeanSmall(nn.Module):\n def __init__(self, msa_dim, pair_dim, hidden=32):\n super().__init__()\n self.norm = nn.LayerNorm(msa_dim)\n self.a = nn.Linear(msa_dim, hidden)\n self.b = nn.Linear(msa_dim, hidden)\n self.out = nn.Linear(hidden * hidden, pair_dim)\n\n def forward(self, msa):\n x = evoformer_cuda.cuda_layernorm(\n msa.reshape(-1, msa.size(-1)),\n self.norm.weight,\n self.norm.bias,\n 1e-5\n ).reshape(msa.shape)\n \n a = self.a(x)\n b = self.b(x)\n outer = torch.einsum(\"bsih,bsjk->bijhk\", a, b)\n S = msa.shape[1]\n outer = outer / max(S, 1)\n B, N1, N2, H1, H2 = outer.shape\n return self.out(outer.reshape(B, N1, N2, H1 * H2))\n\n\nclass TriMulOut(nn.Module):\n def __init__(self, pair_dim, hidden):\n super().__init__()\n self.norm = nn.LayerNorm(pair_dim)\n self.pa = nn.Linear(pair_dim, hidden)\n self.pb = nn.Linear(pair_dim, hidden)\n self.ga = nn.Linear(pair_dim, hidden)\n self.gb = nn.Linear(pair_dim, hidden)\n self.norm_out = nn.LayerNorm(hidden)\n self.out = nn.Linear(hidden, pair_dim)\n self.gate_out = nn.Linear(pair_dim, pair_dim)\n\n def forward(self, pair):\n x = evoformer_cuda.cuda_layernorm(\n pair.reshape(-1, pair.size(-1)),\n self.norm.weight,\n self.norm.bias,\n 1e-5\n ).reshape(pair.shape)\n \n a = self.pa(x) * torch.sigmoid(self.ga(x))\n b = self.pb(x) * torch.sigmoid(self.gb(x))\n o = torch.einsum(\"bikh,bjkh->bijh\", a, b)\n o = self.out(self.norm_out(o))\n return torch.sigmoid(self.gate_out(pair)) * o\n\n\nclass ModelNew(nn.Module):\n \"\"\"\n Full Evoformer block from AlphaFold2 combining MSA row self-attention,\n outer-product mean update, and triangular multiplicative update of the\n pair representation. A single block of the iterative refinement stack.\n \"\"\"\n\n def __init__(self, msa_dim, pair_dim, num_heads, tri_hidden):\n super().__init__()\n self.msa_attn = MSARowSelfAttention(msa_dim, num_heads)\n self.opm = OuterProductMeanSmall(msa_dim, pair_dim)\n self.tri_mul = TriMulOut(pair_dim, tri_hidden)\n\n def forward(\n self, msa_repr: torch.Tensor, pair_repr: torch.Tensor\n ) -> tuple:\n \"\"\"\n Args:\n msa_repr: (B, S, N, msa_dim)\n pair_repr: (B, N, N, pair_dim)\n Returns:\n (updated_msa, updated_pair)\n \"\"\"\n msa_repr = msa_repr + self.msa_attn(msa_repr)\n pair_repr = pair_repr + self.opm(msa_repr)\n pair_repr = pair_repr + self.tri_mul(pair_repr)\n return msa_repr, pair_repr" }
result ← compile_kernel (fail)
failcompile_kernel compile_kernel FAILED: RuntimeError. RuntimeError: Error building extension 'evoformer_cuda'
turn 2 · 1 tool call(s) · 228.0s turn · 14.4s LLM · tokens in=6,637 · out=1,670 · total=8,307 · 7,354 chars (click to expand)
turn 2228.0s turn · 14.4s LLM · 213.6s tools · tokens in=6,637 · out=1,670 · total=8,307
reasoning
The compilation failed. Let me simplify the approach and try again with a more basic kernel. The error might be due to CUDA compilation issues.
assistantLet me simplify the CUDA kernel and try again:
call → compile_kernel
callcompile_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\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 <cmath>\n\nconstexpr int BLOCK_SIZE = 256;\n\n// LayerNorm kernel\n__global__ void layernorm_kernel(\n const float* __restrict__ input,\n float* __restrict__ output,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n int num_groups,\n int norm_size,\n float eps\n) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int start = idx * norm_size;\n \n // Compute mean\n float mean = 0.0f;\n for (int i = 0; i < norm_size; i++) {\n mean += input[start + i];\n }\n mean /= norm_size;\n \n // Compute variance\n float var = 0.0f;\n for (int i = 0; i < norm_size; i++) {\n float diff = input[start + i] - mean;\n var += diff * diff;\n }\n var /= norm_size;\n \n // Normalize\n float inv_std = rsqrtf(var + eps);\n for (int i = 0; i < norm_size; i++) {\n int out_idx = start + i;\n output[out_idx] = (input[out_idx] - mean) * inv_std * weight[i] + bias[i];\n }\n }\n}\n\n// Host function wrapper\ntorch::Tensor layernorm_cuda(\n const torch::Tensor& input,\n const torch::Tensor& weight,\n const torch::Tensor& bias,\n double eps\n) {\n TORCH_CHECK(input.is_cuda(), \"Input must be CUDA tensor\");\n TORCH_CHECK(weight.is_cuda(), \"Weight must be CUDA tensor\");\n TORCH_CHECK(bias.is_cuda(), \"Bias must be CUDA tensor\");\n \n auto output = torch::empty_like(input);\n int total_elements = input.numel();\n int norm_size = input.size(-1);\n int num_groups = total_elements / norm_size;\n \n int blocks = (num_groups + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n layernorm_kernel<<<blocks, BLOCK_SIZE>>>(\n input.data_ptr<float>(),\n output.data_ptr<float>(),\n weight.data_ptr<float>(),\n bias.data_ptr<float>(),\n num_groups,\n norm_size,\n static_cast<float>(eps)\n );\n \n return output;\n}\n\"\"\"\n\nevoformer_cuda = load_inline(\n name='evoformer_cuda',\n cpp_sources='',\n cuda_sources=cuda_source,\n functions=['layernorm_cuda'],\n verbose=True,\n extra_cuda_cflags=['-O3']\n)\n\n\nclass MSARowSelfAttention(nn.Module):\n def __init__(self, msa_dim, num_heads):\n super().__init__()\n self.num_heads = num_heads\n self.head_dim = msa_dim // num_heads\n self.norm = nn.LayerNorm(msa_dim)\n self.q = nn.Linear(msa_dim, msa_dim, bias=False)\n self.k = nn.Linear(msa_dim, msa_dim, bias=False)\n self.v = nn.Linear(msa_dim, msa_dim, bias=False)\n self.out = nn.Linear(msa_dim, msa_dim)\n\n def forward(self, msa):\n B, S, N, D = msa.shape\n h, d = self.num_heads, self.head_dim\n \n x = evoformer_cuda.layernorm_cuda(\n msa.reshape(-1, D),\n self.norm.weight,\n self.norm.bias,\n 1e-5\n ).reshape(B, S, N, D)\n \n q = self.q(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)\n k = self.k(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)\n v = self.v(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)\n \n attn = (q @ k.transpose(-1, -2)) / math.sqrt(d)\n attn = F.softmax(attn, dim=-1)\n out = (attn @ v).permute(0, 1, 3, 2, 4).reshape(B, S, N, D)\n return self.out(out)\n\n\nclass OuterProductMeanSmall(nn.Module):\n def __init__(self, msa_dim, pair_dim, hidden=32):\n super().__init__()\n self.norm = nn.LayerNorm(msa_dim)\n self.a = nn.Linear(msa_dim, hidden)\n self.b = nn.Linear(msa_dim, hidden)\n self.out = nn.Linear(hidden * hidden, pair_dim)\n\n def forward(self, msa):\n x = evoformer_cuda.layernorm_cuda(\n msa.reshape(-1, msa.size(-1)),\n self.norm.weight,\n self.norm.bias,\n 1e-5\n ).reshape(msa.shape)\n \n a = self.a(x)\n b = self.b(x)\n outer = torch.einsum(\"bsih,bsjk->bijhk\", a, b)\n S = msa.shape[1]\n outer = outer / max(S, 1)\n B, N1, N2, H1, H2 = outer.shape\n return self.out(outer.reshape(B, N1, N2, H1 * H2))\n\n\nclass TriMulOut(nn.Module):\n def __init__(self, pair_dim, hidden):\n super().__init__()\n self.norm = nn.LayerNorm(pair_dim)\n self.pa = nn.Linear(pair_dim, hidden)\n self.pb = nn.Linear(pair_dim, hidden)\n self.ga = nn.Linear(pair_dim, hidden)\n self.gb = nn.Linear(pair_dim, hidden)\n self.norm_out = nn.LayerNorm(hidden)\n self.out = nn.Linear(hidden, pair_dim)\n self.gate_out = nn.Linear(pair_dim, pair_dim)\n\n def forward(self, pair):\n x = evoformer_cuda.layernorm_cuda(\n pair.reshape(-1, pair.size(-1)),\n self.norm.weight,\n self.norm.bias,\n 1e-5\n ).reshape(pair.shape)\n \n a = self.pa(x) * torch.sigmoid(self.ga(x))\n b = self.pb(x) * torch.sigmoid(self.gb(x))\n o = torch.einsum(\"bikh,bjkh->bijh\", a, b)\n o = self.out(self.norm_out(o))\n return torch.sigmoid(self.gate_out(pair)) * o\n\n\nclass ModelNew(nn.Module):\n def __init__(self, msa_dim, pair_dim, num_heads, tri_hidden):\n super().__init__()\n self.msa_attn = MSARowSelfAttention(msa_dim, num_heads)\n self.opm = OuterProductMeanSmall(msa_dim, pair_dim)\n self.tri_mul = TriMulOut(pair_dim, tri_hidden)\n\n def forward(\n self, msa_repr: torch.Tensor, pair_repr: torch.Tensor\n ) -> tuple:\n msa_repr = msa_repr + self.msa_attn(msa_repr)\n pair_repr = pair_repr + self.opm(msa_repr)\n pair_repr = pair_repr + self.tri_mul(pair_repr)\n return msa_repr, pair_repr" }
result ← compile_kernel (fail)
failcompile_kernel compile_kernel FAILED: RuntimeError. RuntimeError: Error building extension 'evoformer_cuda_v1'
turn 3 · 1 tool call(s) · 177.5s turn · 13.2s LLM · tokens in=8,302 · out=1,560 · total=9,862 · 6,805 chars (click to expand)
turn 3177.5s turn · 13.2s LLM · 164.4s tools · tokens in=8,302 · out=1,560 · total=9,862
reasoning
Still failing. Let me try a simpler approach with explicit C++ source for the bindings.
assistantLet me try a different approach with both C++ and CUDA sources:
call → compile_kernel
callcompile_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport math\nfrom torch.utils.cpp_extension import load_inline\n\ncuda_source = r\"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n\nconstexpr int BLOCK_SIZE = 256;\n\n__global__ void layernorm_kernel(\n const float* input,\n float* output,\n const float* weight,\n const float* bias,\n int num_groups,\n int norm_size,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int start = idx * norm_size;\n \n float mean = 0.0f;\n for (int i = 0; i < norm_size; i++) {\n mean += input[start + i];\n }\n mean /= norm_size;\n \n float var = 0.0f;\n for (int i = 0; i < norm_size; i++) {\n float diff = input[start + i] - mean;\n var += diff * diff;\n }\n var /= norm_size;\n \n float inv_std = rsqrtf(var + eps);\n for (int i = 0; i < norm_size; i++) {\n int out_idx = start + i;\n output[out_idx] = (input[out_idx] - mean) * inv_std * weight[i] + bias[i];\n }\n }\n}\n\ntorch::Tensor layernorm_cuda(\n torch::Tensor input,\n torch::Tensor weight,\n torch::Tensor bias,\n double eps) {\n \n auto output = torch::empty_like(input);\n int total_elements = input.numel();\n int norm_size = input.size(-1);\n int num_groups = total_elements / norm_size;\n \n int blocks = (num_groups + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n layernorm_kernel<<<blocks, BLOCK_SIZE>>>(\n input.data_ptr<float>(),\n output.data_ptr<float>(),\n weight.data_ptr<float>(),\n bias.data_ptr<float>(),\n num_groups,\n norm_size,\n static_cast<float>(eps));\n \n return output;\n}\n\"\"\"\n\nevoformer_cuda = load_inline(\n name='evoformer_ext',\n cpp_sources='',\n cuda_sources=cuda_source,\n functions=['layernorm_cuda'],\n verbose=True\n)\n\n\nclass MSARowSelfAttention(nn.Module):\n def __init__(self, msa_dim, num_heads):\n super().__init__()\n self.num_heads = num_heads\n self.head_dim = msa_dim // num_heads\n self.norm = nn.LayerNorm(msa_dim)\n self.q = nn.Linear(msa_dim, msa_dim, bias=False)\n self.k = nn.Linear(msa_dim, msa_dim, bias=False)\n self.v = nn.Linear(msa_dim, msa_dim, bias=False)\n self.out = nn.Linear(msa_dim, msa_dim)\n\n def forward(self, msa):\n B, S, N, D = msa.shape\n h, d = self.num_heads, self.head_dim\n \n x = evoformer_cuda.layernorm_cuda(\n msa.reshape(-1, D),\n self.norm.weight,\n self.norm.bias,\n 1e-5\n ).reshape(B, S, N, D)\n \n q = self.q(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)\n k = self.k(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)\n v = self.v(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)\n \n attn = (q @ k.transpose(-1, -2)) / math.sqrt(d)\n attn = F.softmax(attn, dim=-1)\n out = (attn @ v).permute(0, 1, 3, 2, 4).reshape(B, S, N, D)\n return self.out(out)\n\n\nclass OuterProductMeanSmall(nn.Module):\n def __init__(self, msa_dim, pair_dim, hidden=32):\n super().__init__()\n self.norm = nn.LayerNorm(msa_dim)\n self.a = nn.Linear(msa_dim, hidden)\n self.b = nn.Linear(msa_dim, hidden)\n self.out = nn.Linear(hidden * hidden, pair_dim)\n\n def forward(self, msa):\n x = evoformer_cuda.layernorm_cuda(\n msa.reshape(-1, msa.size(-1)),\n self.norm.weight,\n self.norm.bias,\n 1e-5\n ).reshape(msa.shape)\n \n a = self.a(x)\n b = self.b(x)\n outer = torch.einsum(\"bsih,bsjk->bijhk\", a, b)\n S = msa.shape[1]\n outer = outer / max(S, 1)\n B, N1, N2, H1, H2 = outer.shape\n return self.out(outer.reshape(B, N1, N2, H1 * H2))\n\n\nclass TriMulOut(nn.Module):\n def __init__(self, pair_dim, hidden):\n super().__init__()\n self.norm = nn.LayerNorm(pair_dim)\n self.pa = nn.Linear(pair_dim, hidden)\n self.pb = nn.Linear(pair_dim, hidden)\n self.ga = nn.Linear(pair_dim, hidden)\n self.gb = nn.Linear(pair_dim, hidden)\n self.norm_out = nn.LayerNorm(hidden)\n self.out = nn.Linear(hidden, pair_dim)\n self.gate_out = nn.Linear(pair_dim, pair_dim)\n\n def forward(self, pair):\n x = evoformer_cuda.layernorm_cuda(\n pair.reshape(-1, pair.size(-1)),\n self.norm.weight,\n self.norm.bias,\n 1e-5\n ).reshape(pair.shape)\n \n a = self.pa(x) * torch.sigmoid(self.ga(x))\n b = self.pb(x) * torch.sigmoid(self.gb(x))\n o = torch.einsum(\"bikh,bjkh->bijh\", a, b)\n o = self.out(self.norm_out(o))\n return torch.sigmoid(self.gate_out(pair)) * o\n\n\nclass ModelNew(nn.Module):\n def __init__(self, msa_dim, pair_dim, num_heads, tri_hidden):\n super().__init__()\n self.msa_attn = MSARowSelfAttention(msa_dim, num_heads)\n self.opm = OuterProductMeanSmall(msa_dim, pair_dim)\n self.tri_mul = TriMulOut(pair_dim, tri_hidden)\n\n def forward(\n self, msa_repr: torch.Tensor, pair_repr: torch.Tensor\n ) -> tuple:\n msa_repr = msa_repr + self.msa_attn(msa_repr)\n pair_repr = pair_repr + self.opm(msa_repr)\n pair_repr = pair_repr + self.tri_mul(pair_repr)\n return msa_repr, pair_repr" }
result ← compile_kernel (fail)
failcompile_kernel compile_kernel FAILED: RuntimeError. RuntimeError: Error building extension 'evoformer_ext'
turn 4 · 1 tool call(s) · 247.6s turn · 15.9s LLM · tokens in=9,865 · out=1,747 · total=11,612 · 7,602 chars (click to expand)
turn 4247.6s turn · 15.9s LLM · 231.8s tools · tokens in=9,865 · out=1,747 · total=11,612
reasoning
The compilation is failing. Let me try a much simpler approach - maybe using a more standard template that I know works. Let me try with a C++ wrapper function.
assistantLet me try with both C++ and CUDA sources:
call → compile_kernel
callcompile_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport math\nfrom torch.utils.cpp_extension import load_inline\n\ncpp_source = r\"\"\"\n#include <torch/extension.h>\n\ntorch::Tensor layernorm_cuda(\n torch::Tensor input,\n torch::Tensor weight,\n torch::Tensor bias,\n double eps);\n \ntorch::Tensor layernorm(\n torch::Tensor input,\n torch::Tensor weight,\n torch::Tensor bias,\n double eps) {\n return layernorm_cuda(input, weight, bias, eps);\n}\n\"\"\"\n\ncuda_source = r\"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\nconstexpr int BLOCK_SIZE = 256;\n\n__global__ void layernorm_kernel(\n const float* __restrict__ input,\n float* __restrict__ output,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n int num_groups,\n int norm_size,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int start = idx * norm_size;\n \n float mean = 0.0f;\n for (int i = 0; i < norm_size; i++) {\n mean += input[start + i];\n }\n mean /= static_cast<float>(norm_size);\n \n float var = 0.0f;\n for (int i = 0; i < norm_size; i++) {\n float diff = input[start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(norm_size);\n \n float inv_std = rsqrtf(var + eps);\n for (int i = 0; i < norm_size; i++) {\n output[start + i] = (input[start + i] - mean) * inv_std * weight[i] + bias[i];\n }\n }\n}\n\ntorch::Tensor layernorm_cuda(\n torch::Tensor input,\n torch::Tensor weight,\n torch::Tensor bias,\n double eps) {\n \n TORCH_CHECK(input.is_cuda(), \"Input must be a CUDA tensor\");\n TORCH_CHECK(weight.is_cuda(), \"Weight must be a CUDA tensor\");\n TORCH_CHECK(bias.is_cuda(), \"Bias must be a CUDA tensor\");\n \n auto output = torch::empty_like(input);\n int64_t total_elements = input.numel();\n int64_t norm_size = input.size(-1);\n int64_t num_groups = total_elements / norm_size;\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n layernorm_kernel<<<blocks, BLOCK_SIZE>>>(\n input.data_ptr<float>(),\n output.data_ptr<float>(),\n weight.data_ptr<float>(),\n bias.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(norm_size),\n static_cast<float>(eps));\n \n return output;\n}\n\"\"\"\n\nevoformer_ext = load_inline(\n name='evoformer_ext',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['layernorm'],\n verbose=True\n)\n\n\nclass MSARowSelfAttention(nn.Module):\n def __init__(self, msa_dim, num_heads):\n super().__init__()\n self.num_heads = num_heads\n self.head_dim = msa_dim // num_heads\n self.norm = nn.LayerNorm(msa_dim)\n self.q = nn.Linear(msa_dim, msa_dim, bias=False)\n self.k = nn.Linear(msa_dim, msa_dim, bias=False)\n self.v = nn.Linear(msa_dim, msa_dim, bias=False)\n self.out = nn.Linear(msa_dim, msa_dim)\n\n def forward(self, msa):\n B, S, N, D = msa.shape\n h, d = self.num_heads, self.head_dim\n \n x = evoformer_ext.layernorm(\n msa.reshape(-1, D),\n self.norm.weight,\n self.norm.bias,\n 1e-5\n ).reshape(B, S, N, D)\n \n q = self.q(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)\n k = self.k(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)\n v = self.v(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)\n \n attn = (q @ k.transpose(-1, -2)) / math.sqrt(d)\n attn = F.softmax(attn, dim=-1)\n out = (attn @ v).permute(0, 1, 3, 2, 4).reshape(B, S, N, D)\n return self.out(out)\n\n\nclass OuterProductMeanSmall(nn.Module):\n def __init__(self, msa_dim, pair_dim, hidden=32):\n super().__init__()\n self.norm = nn.LayerNorm(msa_dim)\n self.a = nn.Linear(msa_dim, hidden)\n self.b = nn.Linear(msa_dim, hidden)\n self.out = nn.Linear(hidden * hidden, pair_dim)\n\n def forward(self, msa):\n x = evoformer_ext.layernorm(\n msa.reshape(-1, msa.size(-1)),\n self.norm.weight,\n self.norm.bias,\n 1e-5\n ).reshape(msa.shape)\n \n a = self.a(x)\n b = self.b(x)\n outer = torch.einsum(\"bsih,bsjk->bijhk\", a, b)\n S = msa.shape[1]\n outer = outer / max(S, 1)\n B, N1, N2, H1, H2 = outer.shape\n return self.out(outer.reshape(B, N1, N2, H1 * H2))\n\n\nclass TriMulOut(nn.Module):\n def __init__(self, pair_dim, hidden):\n super().__init__()\n self.norm = nn.LayerNorm(pair_dim)\n self.pa = nn.Linear(pair_dim, hidden)\n self.pb = nn.Linear(pair_dim, hidden)\n self.ga = nn.Linear(pair_dim, hidden)\n self.gb = nn.Linear(pair_dim, hidden)\n self.norm_out = nn.LayerNorm(hidden)\n self.out = nn.Linear(hidden, pair_dim)\n self.gate_out = nn.Linear(pair_dim, pair_dim)\n\n def forward(self, pair):\n x = evoformer_ext.layernorm(\n pair.reshape(-1, pair.size(-1)),\n self.norm.weight,\n self.norm.bias,\n 1e-5\n ).reshape(pair.shape)\n \n a = self.pa(x) * torch.sigmoid(self.ga(x))\n b = self.pb(x) * torch.sigmoid(self.gb(x))\n o = torch.einsum(\"bikh,bjkh->bijh\", a, b)\n o = self.out(self.norm_out(o))\n return torch.sigmoid(self.gate_out(pair)) * o\n\n\nclass ModelNew(nn.Module):\n def __init__(self, msa_dim, pair_dim, num_heads, tri_hidden):\n super().__init__()\n self.msa_attn = MSARowSelfAttention(msa_dim, num_heads)\n self.opm = OuterProductMeanSmall(msa_dim, pair_dim)\n self.tri_mul = TriMulOut(pair_dim, tri_hidden)\n\n def forward(\n self, msa_repr: torch.Tensor, pair_repr: torch.Tensor\n ) -> tuple:\n msa_repr = msa_repr + self.msa_attn(msa_repr)\n pair_repr = pair_repr + self.opm(msa_repr)\n pair_repr = pair_repr + self.tri_mul(pair_repr)\n return msa_repr, pair_repr" }
result ← compile_kernel (ok)
okcompile_kernel compile_kernel PASSED: kernel compiled without errors.
turn 5 · 1 tool call(s) · 29.3s turn · 15.1s LLM · tokens in=11,591 · out=1,734 · total=13,325 · 7,650 chars (click to expand)
turn 529.3s turn · 15.1s LLM · 14.2s tools · tokens in=11,591 · out=1,734 · total=13,325
reasoning
Good! The kernel compiled. Now let me run correctness to check if it produces the right results.
assistantThe kernel compiled. Let me run the correctness test:
call → run_correctness
callrun_correctness { "kernel_code": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport math\nfrom torch.utils.cpp_extension import load_inline\n\ncpp_source = r\"\"\"\n#include <torch/extension.h>\n\ntorch::Tensor layernorm_cuda(\n torch::Tensor input,\n torch::Tensor weight,\n torch::Tensor bias,\n double eps);\n \ntorch::Tensor layernorm(\n torch::Tensor input,\n torch::Tensor weight,\n torch::Tensor bias,\n double eps) {\n return layernorm_cuda(input, weight, bias, eps);\n}\n\"\"\"\n\ncuda_source = r\"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\nconstexpr int BLOCK_SIZE = 256;\n\n__global__ void layernorm_kernel(\n const float* __restrict__ input,\n float* __restrict__ output,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n int num_groups,\n int norm_size,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int start = idx * norm_size;\n \n float mean = 0.0f;\n for (int i = 0; i < norm_size; i++) {\n mean += input[start + i];\n }\n mean /= static_cast<float>(norm_size);\n \n float var = 0.0f;\n for (int i = 0; i < norm_size; i++) {\n float diff = input[start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(norm_size);\n \n float inv_std = rsqrtf(var + eps);\n for (int i = 0; i < norm_size; i++) {\n output[start + i] = (input[start + i] - mean) * inv_std * weight[i] + bias[i];\n }\n }\n}\n\ntorch::Tensor layernorm_cuda(\n torch::Tensor input,\n torch::Tensor weight,\n torch::Tensor bias,\n double eps) {\n \n TORCH_CHECK(input.is_cuda(), \"Input must be a CUDA tensor\");\n TORCH_CHECK(weight.is_cuda(), \"Weight must be a CUDA tensor\");\n TORCH_CHECK(bias.is_cuda(), \"Bias must be a CUDA tensor\");\n \n auto output = torch::empty_like(input);\n int64_t total_elements = input.numel();\n int64_t norm_size = input.size(-1);\n int64_t num_groups = total_elements / norm_size;\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n layernorm_kernel<<<blocks, BLOCK_SIZE>>>(\n input.data_ptr<float>(),\n output.data_ptr<float>(),\n weight.data_ptr<float>(),\n bias.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(norm_size),\n static_cast<float>(eps));\n \n return output;\n}\n\"\"\"\n\nevoformer_ext = load_inline(\n name='evoformer_ext',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['layernorm'],\n verbose=True\n)\n\n\nclass MSARowSelfAttention(nn.Module):\n def __init__(self, msa_dim, num_heads):\n super().__init__()\n self.num_heads = num_heads\n self.head_dim = msa_dim // num_heads\n self.norm = nn.LayerNorm(msa_dim)\n self.q = nn.Linear(msa_dim, msa_dim, bias=False)\n self.k = nn.Linear(msa_dim, msa_dim, bias=False)\n self.v = nn.Linear(msa_dim, msa_dim, bias=False)\n self.out = nn.Linear(msa_dim, msa_dim)\n\n def forward(self, msa):\n B, S, N, D = msa.shape\n h, d = self.num_heads, self.head_dim\n \n x = evoformer_ext.layernorm(\n msa.reshape(-1, D),\n self.norm.weight,\n self.norm.bias,\n 1e-5\n ).reshape(B, S, N, D)\n \n q = self.q(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)\n k = self.k(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)\n v = self.v(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)\n \n attn = (q @ k.transpose(-1, -2)) / math.sqrt(d)\n attn = F.softmax(attn, dim=-1)\n out = (attn @ v).permute(0, 1, 3, 2, 4).reshape(B, S, N, D)\n return self.out(out)\n\n\nclass OuterProductMeanSmall(nn.Module):\n def __init__(self, msa_dim, pair_dim, hidden=32):\n super().__init__()\n self.norm = nn.LayerNorm(msa_dim)\n self.a = nn.Linear(msa_dim, hidden)\n self.b = nn.Linear(msa_dim, hidden)\n self.out = nn.Linear(hidden * hidden, pair_dim)\n\n def forward(self, msa):\n x = evoformer_ext.layernorm(\n msa.reshape(-1, msa.size(-1)),\n self.norm.weight,\n self.norm.bias,\n 1e-5\n ).reshape(msa.shape)\n \n a = self.a(x)\n b = self.b(x)\n outer = torch.einsum(\"bsih,bsjk->bijhk\", a, b)\n S = msa.shape[1]\n outer = outer / max(S, 1)\n B, N1, N2, H1, H2 = outer.shape\n return self.out(outer.reshape(B, N1, N2, H1 * H2))\n\n\nclass TriMulOut(nn.Module):\n def __init__(self, pair_dim, hidden):\n super().__init__()\n self.norm = nn.LayerNorm(pair_dim)\n self.pa = nn.Linear(pair_dim, hidden)\n self.pb = nn.Linear(pair_dim, hidden)\n self.ga = nn.Linear(pair_dim, hidden)\n self.gb = nn.Linear(pair_dim, hidden)\n self.norm_out = nn.LayerNorm(hidden)\n self.out = nn.Linear(hidden, pair_dim)\n self.gate_out = nn.Linear(pair_dim, pair_dim)\n\n def forward(self, pair):\n x = evoformer_ext.layernorm(\n pair.reshape(-1, pair.size(-1)),\n self.norm.weight,\n self.norm.bias,\n 1e-5\n ).reshape(pair.shape)\n \n a = self.pa(x) * torch.sigmoid(self.ga(x))\n b = self.pb(x) * torch.sigmoid(self.gb(x))\n o = torch.einsum(\"bikh,bjkh->bijh\", a, b)\n o = self.out(self.norm_out(o))\n return torch.sigmoid(self.gate_out(pair)) * o\n\n\nclass ModelNew(nn.Module):\n def __init__(self, msa_dim, pair_dim, num_heads, tri_hidden):\n super().__init__()\n self.msa_attn = MSARowSelfAttention(msa_dim, num_heads)\n self.opm = OuterProductMeanSmall(msa_dim, pair_dim)\n self.tri_mul = TriMulOut(pair_dim, tri_hidden)\n\n def forward(\n self, msa_repr: torch.Tensor, pair_repr: torch.Tensor\n ) -> tuple:\n msa_repr = msa_repr + self.msa_attn(msa_repr)\n pair_repr = pair_repr + self.opm(msa_repr)\n pair_repr = pair_repr + self.tri_mul(pair_repr)\n return msa_repr, pair_repr" }
result ← run_correctness (ok)
okrun_correctness run_correctness PASSED: (3 / 3) trials all matched the reference. Numerical precision: max_abs_err=7.15e-07 mean_abs_err=6.61e-08 max_rel_err=2.01e-02
turn 6 · 1 tool call(s) · 124.2s turn · 15.7s LLM · tokens in=13,362 · out=1,739 · total=15,101 · 9,136 chars (click to expand)
turn 6124.2s turn · 15.7s LLM · 108.5s tools · tokens in=13,362 · out=1,739 · total=15,101
reasoning
The correctness test passed. Now let me profile the kernel to understand performance characteristics and see where we can optimize further.
assistantThe kernel passes correctness. Let me profile it to understand performance characteristics:
call → profile_kernel
callprofile_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport math\nfrom torch.utils.cpp_extension import load_inline\n\ncpp_source = r\"\"\"\n#include <torch/extension.h>\n\ntorch::Tensor layernorm_cuda(\n torch::Tensor input,\n torch::Tensor weight,\n torch::Tensor bias,\n double eps);\n \ntorch::Tensor layernorm(\n torch::Tensor input,\n torch::Tensor weight,\n torch::Tensor bias,\n double eps) {\n return layernorm_cuda(input, weight, bias, eps);\n}\n\"\"\"\n\ncuda_source = r\"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\nconstexpr int BLOCK_SIZE = 256;\n\n__global__ void layernorm_kernel(\n const float* __restrict__ input,\n float* __restrict__ output,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n int num_groups,\n int norm_size,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int start = idx * norm_size;\n \n float mean = 0.0f;\n for (int i = 0; i < norm_size; i++) {\n mean += input[start + i];\n }\n mean /= static_cast<float>(norm_size);\n \n float var = 0.0f;\n for (int i = 0; i < norm_size; i++) {\n float diff = input[start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(norm_size);\n \n float inv_std = rsqrtf(var + eps);\n for (int i = 0; i < norm_size; i++) {\n output[start + i] = (input[start + i] - mean) * inv_std * weight[i] + bias[i];\n }\n }\n}\n\ntorch::Tensor layernorm_cuda(\n torch::Tensor input,\n torch::Tensor weight,\n torch::Tensor bias,\n double eps) {\n \n TORCH_CHECK(input.is_cuda(), \"Input must be a CUDA tensor\");\n TORCH_CHECK(weight.is_cuda(), \"Weight must be a CUDA tensor\");\n TORCH_CHECK(bias.is_cuda(), \"Bias must be a CUDA tensor\");\n \n auto output = torch::empty_like(input);\n int64_t total_elements = input.numel();\n int64_t norm_size = input.size(-1);\n int64_t num_groups = total_elements / norm_size;\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n layernorm_kernel<<<blocks, BLOCK_SIZE>>>(\n input.data_ptr<float>(),\n output.data_ptr<float>(),\n weight.data_ptr<float>(),\n bias.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(norm_size),\n static_cast<float>(eps));\n \n return output;\n}\n\"\"\"\n\nevoformer_ext = load_inline(\n name='evoformer_ext',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['layernorm'],\n verbose=True\n)\n\n\nclass MSARowSelfAttention(nn.Module):\n def __init__(self, msa_dim, num_heads):\n super().__init__()\n self.num_heads = num_heads\n self.head_dim = msa_dim // num_heads\n self.norm = nn.LayerNorm(msa_dim)\n self.q = nn.Linear(msa_dim, msa_dim, bias=False)\n self.k = nn.Linear(msa_dim, msa_dim, bias=False)\n self.v = nn.Linear(msa_dim, msa_dim, bias=False)\n self.out = nn.Linear(msa_dim, msa_dim)\n\n def forward(self, msa):\n B, S, N, D = msa.shape\n h, d = self.num_heads, self.head_dim\n \n x = evoformer_ext.layernorm(\n msa.reshape(-1, D),\n self.norm.weight,\n self.norm.bias,\n 1e-5\n ).reshape(B, S, N, D)\n \n q = self.q(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)\n k = self.k(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)\n v = self.v(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)\n \n attn = (q @ k.transpose(-1, -2)) / math.sqrt(d)\n attn = F.softmax(attn, dim=-1)\n out = (attn @ v).permute(0, 1, 3, 2, 4).reshape(B, S, N, D)\n return self.out(out)\n\n\nclass OuterProductMeanSmall(nn.Module):\n def __init__(self, msa_dim, pair_dim, hidden=32):\n super().__init__()\n self.norm = nn.LayerNorm(msa_dim)\n self.a = nn.Linear(msa_dim, hidden)\n self.b = nn.Linear(msa_dim, hidden)\n self.out = nn.Linear(hidden * hidden, pair_dim)\n\n def forward(self, msa):\n x = evoformer_ext.layernorm(\n msa.reshape(-1, msa.size(-1)),\n self.norm.weight,\n self.norm.bias,\n 1e-5\n ).reshape(msa.shape)\n \n a = self.a(x)\n b = self.b(x)\n outer = torch.einsum(\"bsih,bsjk->bijhk\", a, b)\n S = msa.shape[1]\n outer = outer / max(S, 1)\n B, N1, N2, H1, H2 = outer.shape\n return self.out(outer.reshape(B, N1, N2, H1 * H2))\n\n\nclass TriMulOut(nn.Module):\n def __init__(self, pair_dim, hidden):\n super().__init__()\n self.norm = nn.LayerNorm(pair_dim)\n self.pa = nn.Linear(pair_dim, hidden)\n self.pb = nn.Linear(pair_dim, hidden)\n self.ga = nn.Linear(pair_dim, hidden)\n self.gb = nn.Linear(pair_dim, hidden)\n self.norm_out = nn.LayerNorm(hidden)\n self.out = nn.Linear(hidden, pair_dim)\n self.gate_out = nn.Linear(pair_dim, pair_dim)\n\n def forward(self, pair):\n x = evoformer_ext.layernorm(\n pair.reshape(-1, pair.size(-1)),\n self.norm.weight,\n self.norm.bias,\n 1e-5\n ).reshape(pair.shape)\n \n a = self.pa(x) * torch.sigmoid(self.ga(x))\n b = self.pb(x) * torch.sigmoid(self.gb(x))\n o = torch.einsum(\"bikh,bjkh->bijh\", a, b)\n o = self.out(self.norm_out(o))\n return torch.sigmoid(self.gate_out(pair)) * o\n\n\nclass ModelNew(nn.Module):\n def __init__(self, msa_dim, pair_dim, num_heads, tri_hidden):\n super().__init__()\n self.msa_attn = MSARowSelfAttention(msa_dim, num_heads)\n self.opm = OuterProductMeanSmall(msa_dim, pair_dim)\n self.tri_mul = TriMulOut(pair_dim, tri_hidden)\n\n def forward(\n self, msa_repr: torch.Tensor, pair_repr: torch.Tensor\n ) -> tuple:\n msa_repr = msa_repr + self.msa_attn(msa_repr)\n pair_repr = pair_repr + self.opm(msa_repr)\n pair_repr = pair_repr + self.tri_mul(pair_repr)\n return msa_repr, pair_repr" }
result ← profile_kernel (ok)
okprofile_kernel profile_kernel PASSED: profiling complete. === Kernel Profile Summary === --- Kernel Breakdown (by GPU time) --- #1: aten::linear 70073.0μs (22%) calls=1 #2: aten::matmul 69966.0μs (22%) calls=1 #3: aten::mm 69830.0μs (22%) calls=1 #4: aten::softmax 11057.0μs (3%) calls=1 #5: aten::_softmax 11031.0μs (3%) calls=1 ... +250 more kernels --- Timing --- GPU time: 380.06 μs --- Memory Bandwidth --- DRAM BW: 5437.8/3350 GB/s (100.0%) read=5437.1 write=0.7 GB/s Hit rates: L2=3768.7% --- Compute Throughput --- Dominant pipe: tensor-core util=9.4% Pipe util: FMA=189.6%, Tensor=9.4% --- Roofline Analysis --- Ridge point (tensor-core): 295.22 FLOPs/byte Classification: MEMORY-BOUND --- Occupancy --- Achieved: 501.9% Eligible warps/cycle: 9.68 Launch: regs/thread=2002, smem/blk=176384B, block=6016, grid=16460 --- Memory Access Quality --- Global ld coalescing: 460.65 sectors/req (460.7x wasted BW) --- Warp Stall Reasons (% of issue cycles) --- long_scoreboard 64178.7% #################### short_scoreboard 6570.3% #################### mio_throttle 1012.9% #################### --- Optimization Hints --- -> 64179% stalls: global memory latency (waiting for DRAM/L2). + sectors/req=460.7 -> uncoalesced global loads, restructure access pattern or use __ldg Try shared-memory tiling, prefetching, or reducing working set. -> Global load coalescing=460.7x (ideal=1.0) -> 461x wasted DRAM BW. Make per-warp accesses contiguous.
turn 7 · 1 tool call(s) · 351.3s turn · 57.0s LLM · tokens in=15,612 · out=6,489 · total=22,101 · 26,755 chars (click to expand)
turn 7351.3s turn · 57.0s LLM · 294.2s tools · tokens in=15,612 · out=6,489 · total=22,101
reasoning
The profiling shows: 1. The dominant operations are the linear and matmul operations which take the most time 2. Memory bandwidth is high at 5.4 TB/s (over 100% - likely a measurement artifact or indicating high pressure) 3. The kernel is memory-bound 4. There are significant global memory latency stalls The main issue is that my custom LayerNorm kernel only accounts for a small portion of the runtime. Most time is spent in PyTorch's built-in linear/matmul operations. To get real speedup, I need to fuse more operations together: 1. LayerNorm + Linear projections (q, k, v) 2. Fused attention mechanism 3. Fused outer product and triangular multiplication Let me try a more aggressive approach - fusing LayerNorm with the linear projections for q, k, v in the MSA attention, and also fusing the outer product computation. Actually, let me think about this more carefully. The profile shows: - Most time is spent in linear/matmul operations - The custom LayerNorm is just a small part I should focus on optimizing the einsum operations and the attention mechanism. Let me try to create a more optimized fused kernel. Let me create a kernel that fuses: 1. LayerNorm + all three linear projections for q, k, v 2. The attention computation with softmax 3. The output linear And for the other modules: 1. LayerNorm + a, b projections for outer product 2. The einsum outer product operation Let me create a more comprehensive fused kernel.
assistantThe profile shows that most time is spent in linear/matmul operations and einsum operations, not just LayerNorm. Let me create a more aggressive fused kernel that combines multiple operations:
call → compile_kernel
callcompile_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport math\nfrom torch.utils.cpp_extension import load_inline\n\ncpp_source = r\"\"\"\n#include <torch/extension.h>\n#include <vector>\n\n// MSA Row Attention fused kernel\nstd::vector<torch::Tensor> msa_row_attn_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps);\n\n// Outer product mean fused kernel\ntorch::Tensor outer_product_mean_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps);\n\n// Triangular multiplication fused kernel\ntorch::Tensor tri_mul_out_cuda(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps);\n\n// Wrappers\nstd::vector<torch::Tensor> msa_row_attn(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps) {\n return msa_row_attn_cuda(msa, ln_weight, ln_bias, q_weight, k_weight, v_weight, out_weight, out_bias, num_heads, eps);\n}\n\ntorch::Tensor outer_product_mean(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps) {\n return outer_product_mean_cuda(msa, ln_weight, ln_bias, a_weight, a_bias, b_weight, b_bias, out_weight, out_bias, eps);\n}\n\ntorch::Tensor tri_mul_out(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps) {\n return tri_mul_out_cuda(pair, ln_weight, ln_bias, pa_weight, pa_bias, pb_weight, pb_bias, \n ga_weight, ga_bias, gb_weight, gb_bias, norm_out_weight, norm_out_bias,\n out_weight, out_bias, gate_weight, gate_bias, eps);\n}\n\"\"\"\n\ncuda_source = r\"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\nconstexpr int BLOCK_SIZE = 256;\nconstexpr int TILE_SIZE = 16;\n\n// Fused LayerNorm + Linear projection for multiple outputs\n__global__ void layernorm_multi_linear_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n float* __restrict__ q_out,\n float* __restrict__ k_out,\n float* __restrict__ v_out,\n const float* __restrict__ q_weight,\n const float* __restrict__ k_weight,\n const float* __restrict__ v_weight,\n int num_groups,\n int in_features,\n int out_features,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int start = idx * in_features;\n int out_start = idx * out_features;\n \n // Compute mean\n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[start + i];\n }\n mean /= static_cast<float>(in_features);\n \n // Compute variance\n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n // Compute q, k, v projections\n for (int o = 0; o < out_features; o++) {\n float q_sum = 0.0f, k_sum = 0.0f, v_sum = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float normalized = (input[start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n q_sum += normalized * q_weight[o * in_features + i];\n k_sum += normalized * k_weight[o * in_features + i];\n v_sum += normalized * v_weight[o * in_features + i];\n }\n q_out[out_start + o] = q_sum;\n k_out[out_start + o] = k_sum;\n v_out[out_start + o] = v_sum;\n }\n }\n}\n\n// Softmax kernel\n__device__ void warp_softmax(float* data, int len) {\n // Find max\n float max_val = data[0];\n for (int i = 1; i < len; i++) {\n max_val = fmaxf(max_val, data[i]);\n }\n \n // Compute exp and sum\n float sum = 0.0f;\n for (int i = 0; i < len; i++) {\n data[i] = expf(data[i] - max_val);\n sum += data[i];\n }\n \n // Normalize\n for (int i = 0; i < len; i++) {\n data[i] /= sum;\n }\n}\n\n// Attention computation kernel\n__global__ void attention_kernel(\n const float* __restrict__ q,\n const float* __restrict__ k,\n const float* __restrict__ v,\n float* __restrict__ out,\n int B, int S, int N, int h, int d,\n float scale) {\n \n // Each thread block handles one (batch, seq_pos, head) combination\n int b = blockIdx.x / (S * h);\n int s = (blockIdx.x / h) % S;\n int head = blockIdx.x % h;\n \n if (b >= B) return;\n \n int seq_len = N;\n int head_idx = b * S * h * N * d + s * h * N * d + head * N * d;\n \n // Shared memory for q, k transposed, and attention scores\n extern __shared__ float smem[];\n float* q_smem = smem; // d elements\n float* attn_smem = smem + d; // N elements\n float* out_smem = smem + d + N; // d elements\n \n // Load q for this position\n for (int i = threadIdx.x; i < d; i += blockDim.x) {\n q_smem[i] = q[head_idx + i];\n }\n __syncthreads();\n \n // Compute attention scores and accumulate output\n for (int i = threadIdx.x; i < d; i += blockDim.x) {\n out_smem[i] = 0.0f;\n }\n __syncthreads();\n \n // For each key position\n for (int key_pos = 0; key_pos < N; key_pos++) {\n // Compute attention score: q @ k^T\n float score = 0.0f;\n for (int i = 0; i < d; i++) {\n int k_idx = b * S * h * N * d + s * h * N * d + head * N * d + key_pos * d + i;\n score += q_smem[i] * k[k_idx];\n }\n attn_smem[key_pos] = score * scale;\n }\n __syncthreads();\n \n // Softmax\n if (threadIdx.x == 0) {\n warp_softmax(attn_smem, N);\n }\n __syncthreads();\n \n // Weighted sum of values\n for (int i = threadIdx.x; i < d; i += blockDim.x) {\n float val = 0.0f;\n for (int key_pos = 0; key_pos < N; key_pos++) {\n int v_idx = b * S * h * N * d + s * h * N * d + head * N * d + key_pos * d + i;\n val += attn_smem[key_pos] * v[v_idx];\n }\n out_smem[i] = val;\n }\n __syncthreads();\n \n // Write output\n for (int i = threadIdx.x; i < d; i += blockDim.x) {\n out[head_idx + i] = out_smem[i];\n }\n}\n\n// Final output projection kernel\n__global__ void output_proj_kernel(\n const float* __restrict__ input,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n float* __restrict__ output,\n int num_groups,\n int in_features,\n int out_features) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * out_features;\n \n for (int o = 0; o < out_features; o++) {\n float sum = bias[o];\n for (int i = 0; i < in_features; i++) {\n sum += input[in_start + i] * weight[o * in_features + i];\n }\n output[out_start + o] = sum;\n }\n }\n}\n\nstd::vector<torch::Tensor> msa_row_attn_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps) {\n \n TORCH_CHECK(msa.is_cuda(), \"MSA must be a CUDA tensor\");\n \n auto sizes = msa.sizes();\n int64_t B = sizes[0], S = sizes[1], N = sizes[2], D = sizes[3];\n int64_t d = D / num_heads;\n int64_t h = num_heads;\n \n int64_t num_groups = B * S * N;\n \n auto q_out = torch::empty({B, S, N, D}, msa.options());\n auto k_out = torch::empty({B, S, N, D}, msa.options());\n auto v_out = torch::empty({B, S, N, D}, msa.options());\n \n // Fused LayerNorm + QKV projections\n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n layernorm_multi_linear_kernel<<<blocks, BLOCK_SIZE>>>(\n msa.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n q_out.data_ptr<float>(),\n k_out.data_ptr<float>(),\n v_out.data_ptr<float>(),\n q_weight.data_ptr<float>(),\n k_weight.data_ptr<float>(),\n v_weight.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(D),\n static_cast<float>(eps));\n \n // Reshape for attention: [B, S, N, D] -> [B, S, h, N, d]\n auto q_reshaped = q_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n auto k_reshaped = k_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n auto v_reshaped = v_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n \n // Attention output\n auto attn_out = torch::empty({B, S, h, N, d}, msa.options());\n \n float scale = 1.0f / sqrtf(static_cast<float>(d));\n int attn_blocks = B * S * h;\n int smem_size = (d + N + d) * sizeof(float);\n \n attention_kernel<<<attn_blocks, 64, smem_size>>>(\n q_reshaped.data_ptr<float>(),\n k_reshaped.data_ptr<float>(),\n v_reshaped.data_ptr<float>(),\n attn_out.data_ptr<float>(),\n B, S, N, h, d, scale);\n \n // Reshape back: [B, S, h, N, d] -> [B, S, N, D]\n auto attn_out_reshaped = attn_out.permute({0, 1, 3, 2, 4}).contiguous().view({B, S, N, D});\n \n // Output projection\n auto final_out = torch::empty({B, S, N, D}, msa.options());\n blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n output_proj_kernel<<<blocks, BLOCK_SIZE>>>(\n attn_out_reshaped.data_ptr<float>(),\n out_weight.data_ptr<float>(),\n out_bias.data_ptr<float>(),\n final_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(D));\n \n return {final_out, q_out, k_out, v_out};\n}\n\n// Outer product mean kernel\n__global__ void outer_product_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n const float* __restrict__ a_weight,\n const float* __restrict__ a_bias,\n const float* __restrict__ b_weight,\n const float* __restrict__ b_bias,\n float* __restrict__ a_out,\n float* __restrict__ b_out,\n int num_groups,\n int in_features,\n int hidden,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * hidden;\n \n // Compute mean\n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[in_start + i];\n }\n mean /= static_cast<float>(in_features);\n \n // Compute variance\n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[in_start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n // Compute a and b projections\n for (int o = 0; o < hidden; o++) {\n float a_sum = a_bias[o];\n float b_sum = b_bias[o];\n for (int i = 0; i < in_features; i++) {\n float normalized = (input[in_start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n a_sum += normalized * a_weight[o * in_features + i];\n b_sum += normalized * b_weight[o * in_features + i];\n }\n a_out[out_start + o] = a_sum;\n b_out[out_start + o] = b_sum;\n }\n }\n}\n\ntorch::Tensor outer_product_mean_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps) {\n \n TORCH_CHECK(msa.is_cuda(), \"MSA must be a CUDA tensor\");\n \n auto sizes = msa.sizes();\n int64_t B = sizes[0], S = sizes[1], N = sizes[2], D = sizes[3];\n int64_t hidden = a_weight.size(0);\n \n int64_t num_groups = B * S * N;\n \n auto a_out = torch::empty({B, S, N, hidden}, msa.options());\n auto b_out = torch::empty({B, S, N, hidden}, msa.options());\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n outer_product_kernel<<<blocks, BLOCK_SIZE>>>(\n msa.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n a_weight.data_ptr<float>(),\n a_bias.data_ptr<float>(),\n b_weight.data_ptr<float>(),\n b_bias.data_ptr<float>(),\n a_out.data_ptr<float>(),\n b_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(hidden),\n static_cast<float>(eps));\n \n // Outer product: einsum(\"bsih,bsjk->bijhk\", a, b)\n // a: [B, S, N, H], b: [B, S, N, H] -> [B, N, N, H, H]\n // Use PyTorch for the einsum since it's well optimized\n auto outer = torch::einsum(\"bsih,bsjk->bijhk\", {a_out, b_out});\n \n // Scale by sequence depth\n outer = outer / static_cast<float>(S);\n \n // Output projection\n int64_t N1 = outer.size(1), N2 = outer.size(2), H1 = outer.size(3), H2 = outer.size(4);\n auto outer_flat = outer.reshape({B, N1, N2, H1 * H2});\n \n return torch::linear(outer_flat, out_weight, out_bias);\n}\n\n// TriMulOut kernel\n__global__ void tri_mul_layernorm_linear_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n const float* __restrict__ pa_weight,\n const float* __restrict__ pa_bias,\n const float* __restrict__ pb_weight,\n const float* __restrict__ pb_bias,\n const float* __restrict__ ga_weight,\n const float* __restrict__ ga_bias,\n const float* __restrict__ gb_weight,\n const float* __restrict__ gb_bias,\n float* __restrict__ pa_out,\n float* __restrict__ pb_out,\n int num_groups,\n int in_features,\n int hidden,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * hidden;\n \n // Compute mean\n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[in_start + i];\n }\n mean /= static_cast<float>(in_features);\n \n // Compute variance\n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[in_start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n // Compute projections with gating\n for (int o = 0; o < hidden; o++) {\n float pa_sum = pa_bias[o];\n float pb_sum = pb_bias[o];\n float ga_sum = ga_bias[o];\n float gb_sum = gb_bias[o];\n \n for (int i = 0; i < in_features; i++) {\n float normalized = (input[in_start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n pa_sum += normalized * pa_weight[o * in_features + i];\n pb_sum += normalized * pb_weight[o * in_features + i];\n ga_sum += normalized * ga_weight[o * in_features + i];\n gb_sum += normalized * gb_weight[o * in_features + i];\n }\n \n // Apply sigmoid gating\n pa_out[out_start + o] = pa_sum * (1.0f / (1.0f + expf(-ga_sum)));\n pb_out[out_start + o] = pb_sum * (1.0f / (1.0f + expf(-gb_sum)));\n }\n }\n}\n\ntorch::Tensor tri_mul_out_cuda(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps) {\n \n TORCH_CHECK(pair.is_cuda(), \"Pair must be a CUDA tensor\");\n \n auto sizes = pair.sizes();\n int64_t B = sizes[0], N1 = sizes[1], N2 = sizes[2], D = sizes[3];\n int64_t hidden = pa_weight.size(0);\n \n int64_t num_groups = B * N1 * N2;\n \n auto pa_out = torch::empty({B, N1, N2, hidden}, pair.options());\n auto pb_out = torch::empty({B, N1, N2, hidden}, pair.options());\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n tri_mul_layernorm_linear_kernel<<<blocks, BLOCK_SIZE>>>(\n pair.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n pa_weight.data_ptr<float>(),\n pa_bias.data_ptr<float>(),\n pb_weight.data_ptr<float>(),\n pb_bias.data_ptr<float>(),\n ga_weight.data_ptr<float>(),\n ga_bias.data_ptr<float>(),\n gb_weight.data_ptr<float>(),\n gb_bias.data_ptr<float>(),\n pa_out.data_ptr<float>(),\n pb_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(hidden),\n static_cast<float>(eps));\n \n // einsum(\"bikh,bjkh->bijh\", a, b)\n auto o = torch::einsum(\"bikh,bjkh->bijh\", {pa_out, pb_out});\n \n // Norm out\n o = torch::layer_norm(o, {hidden}, norm_out_weight, norm_out_bias, eps);\n \n // Output projection\n o = torch::linear(o, out_weight, out_bias);\n \n // Gating\n auto gate = torch::sigmoid(torch::linear(pair, gate_weight, gate_bias));\n \n return gate * o;\n}\n\"\"\"\n\nevoformer_ext = load_inline(\n name='evoformer_ext',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['msa_row_attn', 'outer_product_mean', 'tri_mul_out'],\n verbose=True\n)\n\n\nclass MSARowSelfAttention(nn.Module):\n def __init__(self, msa_dim, num_heads):\n super().__init__()\n self.num_heads = num_heads\n self.head_dim = msa_dim // num_heads\n self.norm = nn.LayerNorm(msa_dim)\n self.q = nn.Linear(msa_dim, msa_dim, bias=False)\n self.k = nn.Linear(msa_dim, msa_dim, bias=False)\n self.v = nn.Linear(msa_dim, msa_dim, bias=False)\n self.out = nn.Linear(msa_dim, msa_dim)\n\n def forward(self, msa):\n B, S, N, D = msa.shape\n \n results = evoformer_ext.msa_row_attn(\n msa.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.q.weight,\n self.k.weight,\n self.v.weight,\n self.out.weight,\n self.out.bias,\n self.num_heads,\n 1e-5\n )\n return results[0]\n\n\nclass OuterProductMeanSmall(nn.Module):\n def __init__(self, msa_dim, pair_dim, hidden=32):\n super().__init__()\n self.norm = nn.LayerNorm(msa_dim)\n self.a = nn.Linear(msa_dim, hidden)\n self.b = nn.Linear(msa_dim, hidden)\n self.out = nn.Linear(hidden * hidden, pair_dim)\n\n def forward(self, msa):\n return evoformer_ext.outer_product_mean(\n msa.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.a.weight,\n self.a.bias,\n self.b.weight,\n self.b.bias,\n self.out.weight,\n self.out.bias,\n 1e-5\n )\n\n\nclass TriMulOut(nn.Module):\n def __init__(self, pair_dim, hidden):\n super().__init__()\n self.norm = nn.LayerNorm(pair_dim)\n self.pa = nn.Linear(pair_dim, hidden)\n self.pb = nn.Linear(pair_dim, hidden)\n self.ga = nn.Linear(pair_dim, hidden)\n self.gb = nn.Linear(pair_dim, hidden)\n self.norm_out = nn.LayerNorm(hidden)\n self.out = nn.Linear(hidden, pair_dim)\n self.gate_out = nn.Linear(pair_dim, pair_dim)\n\n def forward(self, pair):\n return evoformer_ext.tri_mul_out(\n pair.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.pa.weight,\n self.pa.bias,\n self.pb.weight,\n self.pb.bias,\n self.ga.weight,\n self.ga.bias,\n self.gb.weight,\n self.gb.bias,\n self.norm_out.weight,\n self.norm_out.bias,\n self.out.weight,\n self.out.bias,\n self.gate_out.weight,\n self.gate_out.bias,\n 1e-5\n )\n\n\nclass ModelNew(nn.Module):\n def __init__(self, msa_dim, pair_dim, num_heads, tri_hidden):\n super().__init__()\n self.msa_attn = MSARowSelfAttention(msa_dim, num_heads)\n self.opm = OuterProductMeanSmall(msa_dim, pair_dim)\n self.tri_mul = TriMulOut(pair_dim, tri_hidden)\n\n def forward(\n self, msa_repr: torch.Tensor, pair_repr: torch.Tensor\n ) -> tuple:\n msa_repr = msa_repr + self.msa_attn(msa_repr)\n pair_repr = pair_repr + self.opm(msa_repr)\n pair_repr = pair_repr + self.tri_mul(pair_repr)\n return msa_repr, pair_repr" }
result ← compile_kernel (ok)
okcompile_kernel compile_kernel PASSED: kernel compiled without errors.
turn 8 · 1 tool call(s) · 182.2s turn · 49.7s LLM · tokens in=21,791 · out=6,153 · total=27,944 · 25,295 chars (click to expand)
turn 8182.2s turn · 49.7s LLM · 132.4s tools · tokens in=21,791 · out=6,153 · total=27,944
reasoning
Let me test the correctness of this more aggressive kernel.
assistantLet me test the correctness of this more aggressive kernel:
call → run_correctness
callrun_correctness { "kernel_code": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport math\nfrom torch.utils.cpp_extension import load_inline\n\ncpp_source = r\"\"\"\n#include <torch/extension.h>\n#include <vector>\n\n// MSA Row Attention fused kernel\nstd::vector<torch::Tensor> msa_row_attn_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps);\n\n// Outer product mean fused kernel\ntorch::Tensor outer_product_mean_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps);\n\n// Triangular multiplication fused kernel\ntorch::Tensor tri_mul_out_cuda(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps);\n\n// Wrappers\nstd::vector<torch::Tensor> msa_row_attn(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps) {\n return msa_row_attn_cuda(msa, ln_weight, ln_bias, q_weight, k_weight, v_weight, out_weight, out_bias, num_heads, eps);\n}\n\ntorch::Tensor outer_product_mean(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps) {\n return outer_product_mean_cuda(msa, ln_weight, ln_bias, a_weight, a_bias, b_weight, b_bias, out_weight, out_bias, eps);\n}\n\ntorch::Tensor tri_mul_out(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps) {\n return tri_mul_out_cuda(pair, ln_weight, ln_bias, pa_weight, pa_bias, pb_weight, pb_bias, \n ga_weight, ga_bias, gb_weight, gb_bias, norm_out_weight, norm_out_bias,\n out_weight, out_bias, gate_weight, gate_bias, eps);\n}\n\"\"\"\n\ncuda_source = r\"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\nconstexpr int BLOCK_SIZE = 256;\nconstexpr int TILE_SIZE = 16;\n\n// Fused LayerNorm + Linear projection for multiple outputs\n__global__ void layernorm_multi_linear_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n float* __restrict__ q_out,\n float* __restrict__ k_out,\n float* __restrict__ v_out,\n const float* __restrict__ q_weight,\n const float* __restrict__ k_weight,\n const float* __restrict__ v_weight,\n int num_groups,\n int in_features,\n int out_features,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int start = idx * in_features;\n int out_start = idx * out_features;\n \n // Compute mean\n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[start + i];\n }\n mean /= static_cast<float>(in_features);\n \n // Compute variance\n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n // Compute q, k, v projections\n for (int o = 0; o < out_features; o++) {\n float q_sum = 0.0f, k_sum = 0.0f, v_sum = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float normalized = (input[start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n q_sum += normalized * q_weight[o * in_features + i];\n k_sum += normalized * k_weight[o * in_features + i];\n v_sum += normalized * v_weight[o * in_features + i];\n }\n q_out[out_start + o] = q_sum;\n k_out[out_start + o] = k_sum;\n v_out[out_start + o] = v_sum;\n }\n }\n}\n\n// Softmax kernel\n__device__ void warp_softmax(float* data, int len) {\n // Find max\n float max_val = data[0];\n for (int i = 1; i < len; i++) {\n max_val = fmaxf(max_val, data[i]);\n }\n \n // Compute exp and sum\n float sum = 0.0f;\n for (int i = 0; i < len; i++) {\n data[i] = expf(data[i] - max_val);\n sum += data[i];\n }\n \n // Normalize\n for (int i = 0; i < len; i++) {\n data[i] /= sum;\n }\n}\n\n// Attention computation kernel\n__global__ void attention_kernel(\n const float* __restrict__ q,\n const float* __restrict__ k,\n const float* __restrict__ v,\n float* __restrict__ out,\n int B, int S, int N, int h, int d,\n float scale) {\n \n // Each thread block handles one (batch, seq_pos, head) combination\n int b = blockIdx.x / (S * h);\n int s = (blockIdx.x / h) % S;\n int head = blockIdx.x % h;\n \n if (b >= B) return;\n \n int seq_len = N;\n int head_idx = b * S * h * N * d + s * h * N * d + head * N * d;\n \n // Shared memory for q, k transposed, and attention scores\n extern __shared__ float smem[];\n float* q_smem = smem; // d elements\n float* attn_smem = smem + d; // N elements\n float* out_smem = smem + d + N; // d elements\n \n // Load q for this position\n for (int i = threadIdx.x; i < d; i += blockDim.x) {\n q_smem[i] = q[head_idx + i];\n }\n __syncthreads();\n \n // Compute attention scores and accumulate output\n for (int i = threadIdx.x; i < d; i += blockDim.x) {\n out_smem[i] = 0.0f;\n }\n __syncthreads();\n \n // For each key position\n for (int key_pos = 0; key_pos < N; key_pos++) {\n // Compute attention score: q @ k^T\n float score = 0.0f;\n for (int i = 0; i < d; i++) {\n int k_idx = b * S * h * N * d + s * h * N * d + head * N * d + key_pos * d + i;\n score += q_smem[i] * k[k_idx];\n }\n attn_smem[key_pos] = score * scale;\n }\n __syncthreads();\n \n // Softmax\n if (threadIdx.x == 0) {\n warp_softmax(attn_smem, N);\n }\n __syncthreads();\n \n // Weighted sum of values\n for (int i = threadIdx.x; i < d; i += blockDim.x) {\n float val = 0.0f;\n for (int key_pos = 0; key_pos < N; key_pos++) {\n int v_idx = b * S * h * N * d + s * h * N * d + head * N * d + key_pos * d + i;\n val += attn_smem[key_pos] * v[v_idx];\n }\n out_smem[i] = val;\n }\n __syncthreads();\n \n // Write output\n for (int i = threadIdx.x; i < d; i += blockDim.x) {\n out[head_idx + i] = out_smem[i];\n }\n}\n\n// Final output projection kernel\n__global__ void output_proj_kernel(\n const float* __restrict__ input,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n float* __restrict__ output,\n int num_groups,\n int in_features,\n int out_features) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * out_features;\n \n for (int o = 0; o < out_features; o++) {\n float sum = bias[o];\n for (int i = 0; i < in_features; i++) {\n sum += input[in_start + i] * weight[o * in_features + i];\n }\n output[out_start + o] = sum;\n }\n }\n}\n\nstd::vector<torch::Tensor> msa_row_attn_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps) {\n \n TORCH_CHECK(msa.is_cuda(), \"MSA must be a CUDA tensor\");\n \n auto sizes = msa.sizes();\n int64_t B = sizes[0], S = sizes[1], N = sizes[2], D = sizes[3];\n int64_t d = D / num_heads;\n int64_t h = num_heads;\n \n int64_t num_groups = B * S * N;\n \n auto q_out = torch::empty({B, S, N, D}, msa.options());\n auto k_out = torch::empty({B, S, N, D}, msa.options());\n auto v_out = torch::empty({B, S, N, D}, msa.options());\n \n // Fused LayerNorm + QKV projections\n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n layernorm_multi_linear_kernel<<<blocks, BLOCK_SIZE>>>(\n msa.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n q_out.data_ptr<float>(),\n k_out.data_ptr<float>(),\n v_out.data_ptr<float>(),\n q_weight.data_ptr<float>(),\n k_weight.data_ptr<float>(),\n v_weight.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(D),\n static_cast<float>(eps));\n \n // Reshape for attention: [B, S, N, D] -> [B, S, h, N, d]\n auto q_reshaped = q_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n auto k_reshaped = k_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n auto v_reshaped = v_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n \n // Attention output\n auto attn_out = torch::empty({B, S, h, N, d}, msa.options());\n \n float scale = 1.0f / sqrtf(static_cast<float>(d));\n int attn_blocks = B * S * h;\n int smem_size = (d + N + d) * sizeof(float);\n \n attention_kernel<<<attn_blocks, 64, smem_size>>>(\n q_reshaped.data_ptr<float>(),\n k_reshaped.data_ptr<float>(),\n v_reshaped.data_ptr<float>(),\n attn_out.data_ptr<float>(),\n B, S, N, h, d, scale);\n \n // Reshape back: [B, S, h, N, d] -> [B, S, N, D]\n auto attn_out_reshaped = attn_out.permute({0, 1, 3, 2, 4}).contiguous().view({B, S, N, D});\n \n // Output projection\n auto final_out = torch::empty({B, S, N, D}, msa.options());\n blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n output_proj_kernel<<<blocks, BLOCK_SIZE>>>(\n attn_out_reshaped.data_ptr<float>(),\n out_weight.data_ptr<float>(),\n out_bias.data_ptr<float>(),\n final_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(D));\n \n return {final_out, q_out, k_out, v_out};\n}\n\n// Outer product mean kernel\n__global__ void outer_product_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n const float* __restrict__ a_weight,\n const float* __restrict__ a_bias,\n const float* __restrict__ b_weight,\n const float* __restrict__ b_bias,\n float* __restrict__ a_out,\n float* __restrict__ b_out,\n int num_groups,\n int in_features,\n int hidden,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * hidden;\n \n // Compute mean\n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[in_start + i];\n }\n mean /= static_cast<float>(in_features);\n \n // Compute variance\n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[in_start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n // Compute a and b projections\n for (int o = 0; o < hidden; o++) {\n float a_sum = a_bias[o];\n float b_sum = b_bias[o];\n for (int i = 0; i < in_features; i++) {\n float normalized = (input[in_start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n a_sum += normalized * a_weight[o * in_features + i];\n b_sum += normalized * b_weight[o * in_features + i];\n }\n a_out[out_start + o] = a_sum;\n b_out[out_start + o] = b_sum;\n }\n }\n}\n\ntorch::Tensor outer_product_mean_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps) {\n \n TORCH_CHECK(msa.is_cuda(), \"MSA must be a CUDA tensor\");\n \n auto sizes = msa.sizes();\n int64_t B = sizes[0], S = sizes[1], N = sizes[2], D = sizes[3];\n int64_t hidden = a_weight.size(0);\n \n int64_t num_groups = B * S * N;\n \n auto a_out = torch::empty({B, S, N, hidden}, msa.options());\n auto b_out = torch::empty({B, S, N, hidden}, msa.options());\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n outer_product_kernel<<<blocks, BLOCK_SIZE>>>(\n msa.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n a_weight.data_ptr<float>(),\n a_bias.data_ptr<float>(),\n b_weight.data_ptr<float>(),\n b_bias.data_ptr<float>(),\n a_out.data_ptr<float>(),\n b_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(hidden),\n static_cast<float>(eps));\n \n // Outer product: einsum(\"bsih,bsjk->bijhk\", a, b)\n // a: [B, S, N, H], b: [B, S, N, H] -> [B, N, N, H, H]\n // Use PyTorch for the einsum since it's well optimized\n auto outer = torch::einsum(\"bsih,bsjk->bijhk\", {a_out, b_out});\n \n // Scale by sequence depth\n outer = outer / static_cast<float>(S);\n \n // Output projection\n int64_t N1 = outer.size(1), N2 = outer.size(2), H1 = outer.size(3), H2 = outer.size(4);\n auto outer_flat = outer.reshape({B, N1, N2, H1 * H2});\n \n return torch::linear(outer_flat, out_weight, out_bias);\n}\n\n// TriMulOut kernel\n__global__ void tri_mul_layernorm_linear_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n const float* __restrict__ pa_weight,\n const float* __restrict__ pa_bias,\n const float* __restrict__ pb_weight,\n const float* __restrict__ pb_bias,\n const float* __restrict__ ga_weight,\n const float* __restrict__ ga_bias,\n const float* __restrict__ gb_weight,\n const float* __restrict__ gb_bias,\n float* __restrict__ pa_out,\n float* __restrict__ pb_out,\n int num_groups,\n int in_features,\n int hidden,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * hidden;\n \n // Compute mean\n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[in_start + i];\n }\n mean /= static_cast<float>(in_features);\n \n // Compute variance\n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[in_start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n // Compute projections with gating\n for (int o = 0; o < hidden; o++) {\n float pa_sum = pa_bias[o];\n float pb_sum = pb_bias[o];\n float ga_sum = ga_bias[o];\n float gb_sum = gb_bias[o];\n \n for (int i = 0; i < in_features; i++) {\n float normalized = (input[in_start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n pa_sum += normalized * pa_weight[o * in_features + i];\n pb_sum += normalized * pb_weight[o * in_features + i];\n ga_sum += normalized * ga_weight[o * in_features + i];\n gb_sum += normalized * gb_weight[o * in_features + i];\n }\n \n // Apply sigmoid gating\n pa_out[out_start + o] = pa_sum * (1.0f / (1.0f + expf(-ga_sum)));\n pb_out[out_start + o] = pb_sum * (1.0f / (1.0f + expf(-gb_sum)));\n }\n }\n}\n\ntorch::Tensor tri_mul_out_cuda(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps) {\n \n TORCH_CHECK(pair.is_cuda(), \"Pair must be a CUDA tensor\");\n \n auto sizes = pair.sizes();\n int64_t B = sizes[0], N1 = sizes[1], N2 = sizes[2), D = sizes[3];\n int64_t hidden = pa_weight.size(0);\n \n int64_t num_groups = B * N1 * N2;\n \n auto pa_out = torch::empty({B, N1, N2, hidden}, pair.options());\n auto pb_out = torch::empty({B, N1, N2, hidden}, pair.options());\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n tri_mul_layernorm_linear_kernel<<<blocks, BLOCK_SIZE>>>(\n pair.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n pa_weight.data_ptr<float>(),\n pa_bias.data_ptr<float>(),\n pb_weight.data_ptr<float>(),\n pb_bias.data_ptr<float>(),\n ga_weight.data_ptr<float>(),\n ga_bias.data_ptr<float>(),\n gb_weight.data_ptr<float>(),\n gb_bias.data_ptr<float>(),\n pa_out.data_ptr<float>(),\n pb_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(hidden),\n static_cast<float>(eps));\n \n // einsum(\"bikh,bjkh->bijh\", a, b)\n auto o = torch::einsum(\"bikh,bjkh->bijh\", {pa_out, pb_out});\n \n // Norm out\n o = torch::layer_norm(o, {hidden}, norm_out_weight, norm_out_bias, eps);\n \n // Output projection\n o = torch::linear(o, out_weight, out_bias);\n \n // Gating\n auto gate = torch::sigmoid(torch::linear(pair, gate_weight, gate_bias));\n \n return gate * o;\n}\n\"\"\"\n\nevoformer_ext = load_inline(\n name='evoformer_ext',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['msa_row_attn', 'outer_product_mean', 'tri_mul_out'],\n verbose=True\n)\n\n\nclass MSARowSelfAttention(nn.Module):\n def __init__(self, msa_dim, num_heads):\n super().__init__()\n self.num_heads = num_heads\n self.head_dim = msa_dim // num_heads\n self.norm = nn.LayerNorm(msa_dim)\n self.q = nn.Linear(msa_dim, msa_dim, bias=False)\n self.k = nn.Linear(msa_dim, msa_dim, bias=False)\n self.v = nn.Linear(msa_dim, msa_dim, bias=False)\n self.out = nn.Linear(msa_dim, msa_dim)\n\n def forward(self, msa):\n B, S, N, D = msa.shape\n \n results = evoformer_ext.msa_row_attn(\n msa.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.q.weight,\n self.k.weight,\n self.v.weight,\n self.out.weight,\n self.out.bias,\n self.num_heads,\n 1e-5\n )\n return results[0]\n\n\nclass OuterProductMeanSmall(nn.Module):\n def __init__(self, msa_dim, pair_dim, hidden=32):\n super().__init__()\n self.norm = nn.LayerNorm(msa_dim)\n self.a = nn.Linear(msa_dim, hidden)\n self.b = nn.Linear(msa_dim, hidden)\n self.out = nn.Linear(hidden * hidden, pair_dim)\n\n def forward(self, msa):\n return evoformer_ext.outer_product_mean(\n msa.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.a.weight,\n self.a.bias,\n self.b.weight,\n self.b.bias,\n self.out.weight,\n self.out.bias,\n 1e-5\n )\n\n\nclass TriMulOut(nn.Module):\n def __init__(self, pair_dim, hidden):\n super().__init__()\n self.norm = nn.LayerNorm(pair_dim)\n self.pa = nn.Linear(pair_dim, hidden)\n self.pb = nn.Linear(pair_dim, hidden)\n self.ga = nn.Linear(pair_dim, hidden)\n self.gb = nn.Linear(pair_dim, hidden)\n self.norm_out = nn.LayerNorm(hidden)\n self.out = nn.Linear(hidden, pair_dim)\n self.gate_out = nn.Linear(pair_dim, pair_dim)\n\n def forward(self, pair):\n return evoformer_ext.tri_mul_out(\n pair.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.pa.weight,\n self.pa.bias,\n self.pb.weight,\n self.pb.bias,\n self.ga.weight,\n self.ga.bias,\n self.gb.weight,\n self.gb.bias,\n self.norm_out.weight,\n self.norm_out.bias,\n self.out.weight,\n self.out.bias,\n self.gate_out.weight,\n self.gate_out.bias,\n 1e-5\n )\n\n\nclass ModelNew(nn.Module):\n def __init__(self, msa_dim, pair_dim, num_heads, tri_hidden):\n super().__init__()\n self.msa_attn = MSARowSelfAttention(msa_dim, num_heads)\n self.opm = OuterProductMeanSmall(msa_dim, pair_dim)\n self.tri_mul = TriMulOut(pair_dim, tri_hidden)\n\n def forward(\n self, msa_repr: torch.Tensor, pair_repr: torch.Tensor\n ) -> tuple:\n msa_repr = msa_repr + self.msa_attn(msa_repr)\n pair_repr = pair_repr + self.opm(msa_repr)\n pair_repr = pair_repr + self.tri_mul(pair_repr)\n return msa_repr, pair_repr" }
result ← run_correctness (fail)
failrun_correctness run_correctness FAILED: kernel did not compile. Error building extension 'evoformer_ext_v3'
turn 9 · 1 tool call(s) · 228.1s turn · 47.4s LLM · tokens in=27,957 · out=5,749 · total=33,706 · 23,567 chars (click to expand)
turn 9228.1s turn · 47.4s LLM · 180.6s tools · tokens in=27,957 · out=5,749 · total=33,706
reasoning
There was a typo in the CUDA code. Let me check and fix it. I had `int64_t B = sizes[0], N1 = sizes[1], N2 = sizes[2), D = sizes[3];` - there's a mismatched parenthesis. Let me fix this and try again.
assistantLet me fix the typo in the CUDA source:
call → compile_kernel
callcompile_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport math\nfrom torch.utils.cpp_extension import load_inline\n\ncpp_source = r\"\"\"\n#include <torch/extension.h>\n#include <vector>\n\nstd::vector<torch::Tensor> msa_row_attn_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps);\n\ntorch::Tensor outer_product_mean_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps);\n\ntorch::Tensor tri_mul_out_cuda(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps);\n\nstd::vector<torch::Tensor> msa_row_attn(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps) {\n return msa_row_attn_cuda(msa, ln_weight, ln_bias, q_weight, k_weight, v_weight, out_weight, out_bias, num_heads, eps);\n}\n\ntorch::Tensor outer_product_mean(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps) {\n return outer_product_mean_cuda(msa, ln_weight, ln_bias, a_weight, a_bias, b_weight, b_bias, out_weight, out_bias, eps);\n}\n\ntorch::Tensor tri_mul_out(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps) {\n return tri_mul_out_cuda(pair, ln_weight, ln_bias, pa_weight, pa_bias, pb_weight, pb_bias, \n ga_weight, ga_bias, gb_weight, gb_bias, norm_out_weight, norm_out_bias,\n out_weight, out_bias, gate_weight, gate_bias, eps);\n}\n\"\"\"\n\ncuda_source = r\"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\nconstexpr int BLOCK_SIZE = 256;\n\n__global__ void layernorm_multi_linear_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n float* __restrict__ q_out,\n float* __restrict__ k_out,\n float* __restrict__ v_out,\n const float* __restrict__ q_weight,\n const float* __restrict__ k_weight,\n const float* __restrict__ v_weight,\n int num_groups,\n int in_features,\n int out_features,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int start = idx * in_features;\n int out_start = idx * out_features;\n \n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[start + i];\n }\n mean /= static_cast<float>(in_features);\n \n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n for (int o = 0; o < out_features; o++) {\n float q_sum = 0.0f, k_sum = 0.0f, v_sum = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float normalized = (input[start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n q_sum += normalized * q_weight[o * in_features + i];\n k_sum += normalized * k_weight[o * in_features + i];\n v_sum += normalized * v_weight[o * in_features + i];\n }\n q_out[out_start + o] = q_sum;\n k_out[out_start + o] = k_sum;\n v_out[out_start + o] = v_sum;\n }\n }\n}\n\n__device__ void warp_softmax(float* data, int len) {\n float max_val = data[0];\n for (int i = 1; i < len; i++) {\n max_val = fmaxf(max_val, data[i]);\n }\n \n float sum = 0.0f;\n for (int i = 0; i < len; i++) {\n data[i] = expf(data[i] - max_val);\n sum += data[i];\n }\n \n for (int i = 0; i < len; i++) {\n data[i] /= sum;\n }\n}\n\n__global__ void attention_kernel(\n const float* __restrict__ q,\n const float* __restrict__ k,\n const float* __restrict__ v,\n float* __restrict__ out,\n int B, int S, int N, int h, int d,\n float scale) {\n \n int b = blockIdx.x / (S * h);\n int s = (blockIdx.x / h) % S;\n int head = blockIdx.x % h;\n \n if (b >= B) return;\n \n int head_idx = b * S * h * N * d + s * h * N * d + head * N * d;\n \n extern __shared__ float smem[];\n float* q_smem = smem;\n float* attn_smem = smem + d;\n float* out_smem = smem + d + N;\n \n for (int i = threadIdx.x; i < d; i += blockDim.x) {\n q_smem[i] = q[head_idx + i];\n }\n __syncthreads();\n \n for (int i = threadIdx.x; i < d; i += blockDim.x) {\n out_smem[i] = 0.0f;\n }\n __syncthreads();\n \n for (int key_pos = 0; key_pos < N; key_pos++) {\n float score = 0.0f;\n for (int i = 0; i < d; i++) {\n int k_idx = b * S * h * N * d + s * h * N * d + head * N * d + key_pos * d + i;\n score += q_smem[i] * k[k_idx];\n }\n attn_smem[key_pos] = score * scale;\n }\n __syncthreads();\n \n if (threadIdx.x == 0) {\n warp_softmax(attn_smem, N);\n }\n __syncthreads();\n \n for (int i = threadIdx.x; i < d; i += blockDim.x) {\n float val = 0.0f;\n for (int key_pos = 0; key_pos < N; key_pos++) {\n int v_idx = b * S * h * N * d + s * h * N * d + head * N * d + key_pos * d + i;\n val += attn_smem[key_pos] * v[v_idx];\n }\n out_smem[i] = val;\n }\n __syncthreads();\n \n for (int i = threadIdx.x; i < d; i += blockDim.x) {\n out[head_idx + i] = out_smem[i];\n }\n}\n\n__global__ void output_proj_kernel(\n const float* __restrict__ input,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n float* __restrict__ output,\n int num_groups,\n int in_features,\n int out_features) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * out_features;\n \n for (int o = 0; o < out_features; o++) {\n float sum = bias[o];\n for (int i = 0; i < in_features; i++) {\n sum += input[in_start + i] * weight[o * in_features + i];\n }\n output[out_start + o] = sum;\n }\n }\n}\n\nstd::vector<torch::Tensor> msa_row_attn_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps) {\n \n TORCH_CHECK(msa.is_cuda(), \"MSA must be a CUDA tensor\");\n \n auto sizes = msa.sizes();\n int64_t B = sizes[0], S = sizes[1], N = sizes[2], D = sizes[3];\n int64_t d = D / num_heads;\n int64_t h = num_heads;\n \n int64_t num_groups = B * S * N;\n \n auto q_out = torch::empty({B, S, N, D}, msa.options());\n auto k_out = torch::empty({B, S, N, D}, msa.options());\n auto v_out = torch::empty({B, S, N, D}, msa.options());\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n layernorm_multi_linear_kernel<<<blocks, BLOCK_SIZE>>>(\n msa.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n q_out.data_ptr<float>(),\n k_out.data_ptr<float>(),\n v_out.data_ptr<float>(),\n q_weight.data_ptr<float>(),\n k_weight.data_ptr<float>(),\n v_weight.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(D),\n static_cast<float>(eps));\n \n auto q_reshaped = q_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n auto k_reshaped = k_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n auto v_reshaped = v_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n \n auto attn_out = torch::empty({B, S, h, N, d}, msa.options());\n \n float scale = 1.0f / sqrtf(static_cast<float>(d));\n int attn_blocks = B * S * h;\n int smem_size = (d + N + d) * sizeof(float);\n \n attention_kernel<<<attn_blocks, 64, smem_size>>>(\n q_reshaped.data_ptr<float>(),\n k_reshaped.data_ptr<float>(),\n v_reshaped.data_ptr<float>(),\n attn_out.data_ptr<float>(),\n B, S, N, h, d, scale);\n \n auto attn_out_reshaped = attn_out.permute({0, 1, 3, 2, 4}).contiguous().view({B, S, N, D});\n \n auto final_out = torch::empty({B, S, N, D}, msa.options());\n blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n output_proj_kernel<<<blocks, BLOCK_SIZE>>>(\n attn_out_reshaped.data_ptr<float>(),\n out_weight.data_ptr<float>(),\n out_bias.data_ptr<float>(),\n final_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(D));\n \n return {final_out, q_out, k_out, v_out};\n}\n\n__global__ void outer_product_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n const float* __restrict__ a_weight,\n const float* __restrict__ a_bias,\n const float* __restrict__ b_weight,\n const float* __restrict__ b_bias,\n float* __restrict__ a_out,\n float* __restrict__ b_out,\n int num_groups,\n int in_features,\n int hidden,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * hidden;\n \n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[in_start + i];\n }\n mean /= static_cast<float>(in_features);\n \n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[in_start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n for (int o = 0; o < hidden; o++) {\n float a_sum = a_bias[o];\n float b_sum = b_bias[o];\n for (int i = 0; i < in_features; i++) {\n float normalized = (input[in_start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n a_sum += normalized * a_weight[o * in_features + i];\n b_sum += normalized * b_weight[o * in_features + i];\n }\n a_out[out_start + o] = a_sum;\n b_out[out_start + o] = b_sum;\n }\n }\n}\n\ntorch::Tensor outer_product_mean_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps) {\n \n TORCH_CHECK(msa.is_cuda(), \"MSA must be a CUDA tensor\");\n \n auto sizes = msa.sizes();\n int64_t B = sizes[0], S = sizes[1], N = sizes[2], D = sizes[3];\n int64_t hidden = a_weight.size(0);\n \n int64_t num_groups = B * S * N;\n \n auto a_out = torch::empty({B, S, N, hidden}, msa.options());\n auto b_out = torch::empty({B, S, N, hidden}, msa.options());\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n outer_product_kernel<<<blocks, BLOCK_SIZE>>>(\n msa.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n a_weight.data_ptr<float>(),\n a_bias.data_ptr<float>(),\n b_weight.data_ptr<float>(),\n b_bias.data_ptr<float>(),\n a_out.data_ptr<float>(),\n b_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(hidden),\n static_cast<float>(eps));\n \n auto outer = torch::einsum(\"bsih,bsjk->bijhk\", {a_out, b_out});\n outer = outer / static_cast<float>(S);\n \n int64_t N1 = outer.size(1), N2 = outer.size(2), H1 = outer.size(3), H2 = outer.size(4);\n auto outer_flat = outer.reshape({B, N1, N2, H1 * H2});\n \n return torch::linear(outer_flat, out_weight, out_bias);\n}\n\n__global__ void tri_mul_layernorm_linear_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n const float* __restrict__ pa_weight,\n const float* __restrict__ pa_bias,\n const float* __restrict__ pb_weight,\n const float* __restrict__ pb_bias,\n const float* __restrict__ ga_weight,\n const float* __restrict__ ga_bias,\n const float* __restrict__ gb_weight,\n const float* __restrict__ gb_bias,\n float* __restrict__ pa_out,\n float* __restrict__ pb_out,\n int num_groups,\n int in_features,\n int hidden,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * hidden;\n \n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[in_start + i];\n }\n mean /= static_cast<float>(in_features);\n \n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[in_start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n for (int o = 0; o < hidden; o++) {\n float pa_sum = pa_bias[o];\n float pb_sum = pb_bias[o];\n float ga_sum = ga_bias[o];\n float gb_sum = gb_bias[o];\n \n for (int i = 0; i < in_features; i++) {\n float normalized = (input[in_start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n pa_sum += normalized * pa_weight[o * in_features + i];\n pb_sum += normalized * pb_weight[o * in_features + i];\n ga_sum += normalized * ga_weight[o * in_features + i];\n gb_sum += normalized * gb_weight[o * in_features + i];\n }\n \n pa_out[out_start + o] = pa_sum * (1.0f / (1.0f + expf(-ga_sum)));\n pb_out[out_start + o] = pb_sum * (1.0f / (1.0f + expf(-gb_sum)));\n }\n }\n}\n\ntorch::Tensor tri_mul_out_cuda(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps) {\n \n TORCH_CHECK(pair.is_cuda(), \"Pair must be a CUDA tensor\");\n \n auto sizes = pair.sizes();\n int64_t B = sizes[0], N1 = sizes[1], N2 = sizes[2], D = sizes[3];\n int64_t hidden = pa_weight.size(0);\n \n int64_t num_groups = B * N1 * N2;\n \n auto pa_out = torch::empty({B, N1, N2, hidden}, pair.options());\n auto pb_out = torch::empty({B, N1, N2, hidden}, pair.options());\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n tri_mul_layernorm_linear_kernel<<<blocks, BLOCK_SIZE>>>(\n pair.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n pa_weight.data_ptr<float>(),\n pa_bias.data_ptr<float>(),\n pb_weight.data_ptr<float>(),\n pb_bias.data_ptr<float>(),\n ga_weight.data_ptr<float>(),\n ga_bias.data_ptr<float>(),\n gb_weight.data_ptr<float>(),\n gb_bias.data_ptr<float>(),\n pa_out.data_ptr<float>(),\n pb_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(hidden),\n static_cast<float>(eps));\n \n auto o = torch::einsum(\"bikh,bjkh->bijh\", {pa_out, pb_out});\n o = torch::layer_norm(o, {hidden}, norm_out_weight, norm_out_bias, eps);\n o = torch::linear(o, out_weight, out_bias);\n auto gate = torch::sigmoid(torch::linear(pair, gate_weight, gate_bias));\n \n return gate * o;\n}\n\"\"\"\n\nevoformer_ext = load_inline(\n name='evoformer_ext',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['msa_row_attn', 'outer_product_mean', 'tri_mul_out'],\n verbose=True\n)\n\n\nclass MSARowSelfAttention(nn.Module):\n def __init__(self, msa_dim, num_heads):\n super().__init__()\n self.num_heads = num_heads\n self.head_dim = msa_dim // num_heads\n self.norm = nn.LayerNorm(msa_dim)\n self.q = nn.Linear(msa_dim, msa_dim, bias=False)\n self.k = nn.Linear(msa_dim, msa_dim, bias=False)\n self.v = nn.Linear(msa_dim, msa_dim, bias=False)\n self.out = nn.Linear(msa_dim, msa_dim)\n\n def forward(self, msa):\n B, S, N, D = msa.shape\n \n results = evoformer_ext.msa_row_attn(\n msa.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.q.weight,\n self.k.weight,\n self.v.weight,\n self.out.weight,\n self.out.bias,\n self.num_heads,\n 1e-5\n )\n return results[0]\n\n\nclass OuterProductMeanSmall(nn.Module):\n def __init__(self, msa_dim, pair_dim, hidden=32):\n super().__init__()\n self.norm = nn.LayerNorm(msa_dim)\n self.a = nn.Linear(msa_dim, hidden)\n self.b = nn.Linear(msa_dim, hidden)\n self.out = nn.Linear(hidden * hidden, pair_dim)\n\n def forward(self, msa):\n return evoformer_ext.outer_product_mean(\n msa.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.a.weight,\n self.a.bias,\n self.b.weight,\n self.b.bias,\n self.out.weight,\n self.out.bias,\n 1e-5\n )\n\n\nclass TriMulOut(nn.Module):\n def __init__(self, pair_dim, hidden):\n super().__init__()\n self.norm = nn.LayerNorm(pair_dim)\n self.pa = nn.Linear(pair_dim, hidden)\n self.pb = nn.Linear(pair_dim, hidden)\n self.ga = nn.Linear(pair_dim, hidden)\n self.gb = nn.Linear(pair_dim, hidden)\n self.norm_out = nn.LayerNorm(hidden)\n self.out = nn.Linear(hidden, pair_dim)\n self.gate_out = nn.Linear(pair_dim, pair_dim)\n\n def forward(self, pair):\n return evoformer_ext.tri_mul_out(\n pair.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.pa.weight,\n self.pa.bias,\n self.pb.weight,\n self.pb.bias,\n self.ga.weight,\n self.ga.bias,\n self.gb.weight,\n self.gb.bias,\n self.norm_out.weight,\n self.norm_out.bias,\n self.out.weight,\n self.out.bias,\n self.gate_out.weight,\n self.gate_out.bias,\n 1e-5\n )\n\n\nclass ModelNew(nn.Module):\n def __init__(self, msa_dim, pair_dim, num_heads, tri_hidden):\n super().__init__()\n self.msa_attn = MSARowSelfAttention(msa_dim, num_heads)\n self.opm = OuterProductMeanSmall(msa_dim, pair_dim)\n self.tri_mul = TriMulOut(pair_dim, tri_hidden)\n\n def forward(\n self, msa_repr: torch.Tensor, pair_repr: torch.Tensor\n ) -> tuple:\n msa_repr = msa_repr + self.msa_attn(msa_repr)\n pair_repr = pair_repr + self.opm(msa_repr)\n pair_repr = pair_repr + self.tri_mul(pair_repr)\n return msa_repr, pair_repr" }
result ← compile_kernel (ok)
okcompile_kernel compile_kernel PASSED: kernel compiled without errors.
turn 10 · 1 tool call(s) · 165.6s turn · 47.7s LLM · tokens in=33,654 · out=5,683 · total=39,337 · 23,636 chars (click to expand)
turn 10165.6s turn · 47.7s LLM · 117.9s tools · tokens in=33,654 · out=5,683 · total=39,337
reasoning
Good, let me run correctness now.
call → run_correctness
callrun_correctness { "kernel_code": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport math\nfrom torch.utils.cpp_extension import load_inline\n\ncpp_source = r\"\"\"\n#include <torch/extension.h>\n#include <vector>\n\nstd::vector<torch::Tensor> msa_row_attn_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps);\n\ntorch::Tensor outer_product_mean_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps);\n\ntorch::Tensor tri_mul_out_cuda(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps);\n\nstd::vector<torch::Tensor> msa_row_attn(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps) {\n return msa_row_attn_cuda(msa, ln_weight, ln_bias, q_weight, k_weight, v_weight, out_weight, out_bias, num_heads, eps);\n}\n\ntorch::Tensor outer_product_mean(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps) {\n return outer_product_mean_cuda(msa, ln_weight, ln_bias, a_weight, a_bias, b_weight, b_bias, out_weight, out_bias, eps);\n}\n\ntorch::Tensor tri_mul_out(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps) {\n return tri_mul_out_cuda(pair, ln_weight, ln_bias, pa_weight, pa_bias, pb_weight, pb_bias, \n ga_weight, ga_bias, gb_weight, gb_bias, norm_out_weight, norm_out_bias,\n out_weight, out_bias, gate_weight, gate_bias, eps);\n}\n\"\"\"\n\ncuda_source = r\"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\nconstexpr int BLOCK_SIZE = 256;\n\n__global__ void layernorm_multi_linear_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n float* __restrict__ q_out,\n float* __restrict__ k_out,\n float* __restrict__ v_out,\n const float* __restrict__ q_weight,\n const float* __restrict__ k_weight,\n const float* __restrict__ v_weight,\n int num_groups,\n int in_features,\n int out_features,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int start = idx * in_features;\n int out_start = idx * out_features;\n \n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[start + i];\n }\n mean /= static_cast<float>(in_features);\n \n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n for (int o = 0; o < out_features; o++) {\n float q_sum = 0.0f, k_sum = 0.0f, v_sum = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float normalized = (input[start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n q_sum += normalized * q_weight[o * in_features + i];\n k_sum += normalized * k_weight[o * in_features + i];\n v_sum += normalized * v_weight[o * in_features + i];\n }\n q_out[out_start + o] = q_sum;\n k_out[out_start + o] = k_sum;\n v_out[out_start + o] = v_sum;\n }\n }\n}\n\n__device__ void warp_softmax(float* data, int len) {\n float max_val = data[0];\n for (int i = 1; i < len; i++) {\n max_val = fmaxf(max_val, data[i]);\n }\n \n float sum = 0.0f;\n for (int i = 0; i < len; i++) {\n data[i] = expf(data[i] - max_val);\n sum += data[i];\n }\n \n for (int i = 0; i < len; i++) {\n data[i] /= sum;\n }\n}\n\n__global__ void attention_kernel(\n const float* __restrict__ q,\n const float* __restrict__ k,\n const float* __restrict__ v,\n float* __restrict__ out,\n int B, int S, int N, int h, int d,\n float scale) {\n \n int b = blockIdx.x / (S * h);\n int s = (blockIdx.x / h) % S;\n int head = blockIdx.x % h;\n \n if (b >= B) return;\n \n int head_idx = b * S * h * N * d + s * h * N * d + head * N * d;\n \n extern __shared__ float smem[];\n float* q_smem = smem;\n float* attn_smem = smem + d;\n float* out_smem = smem + d + N;\n \n for (int i = threadIdx.x; i < d; i += blockDim.x) {\n q_smem[i] = q[head_idx + i];\n }\n __syncthreads();\n \n for (int i = threadIdx.x; i < d; i += blockDim.x) {\n out_smem[i] = 0.0f;\n }\n __syncthreads();\n \n for (int key_pos = 0; key_pos < N; key_pos++) {\n float score = 0.0f;\n for (int i = 0; i < d; i++) {\n int k_idx = b * S * h * N * d + s * h * N * d + head * N * d + key_pos * d + i;\n score += q_smem[i] * k[k_idx];\n }\n attn_smem[key_pos] = score * scale;\n }\n __syncthreads();\n \n if (threadIdx.x == 0) {\n warp_softmax(attn_smem, N);\n }\n __syncthreads();\n \n for (int i = threadIdx.x; i < d; i += blockDim.x) {\n float val = 0.0f;\n for (int key_pos = 0; key_pos < N; key_pos++) {\n int v_idx = b * S * h * N * d + s * h * N * d + head * N * d + key_pos * d + i;\n val += attn_smem[key_pos] * v[v_idx];\n }\n out_smem[i] = val;\n }\n __syncthreads();\n \n for (int i = threadIdx.x; i < d; i += blockDim.x) {\n out[head_idx + i] = out_smem[i];\n }\n}\n\n__global__ void output_proj_kernel(\n const float* __restrict__ input,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n float* __restrict__ output,\n int num_groups,\n int in_features,\n int out_features) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * out_features;\n \n for (int o = 0; o < out_features; o++) {\n float sum = bias[o];\n for (int i = 0; i < in_features; i++) {\n sum += input[in_start + i] * weight[o * in_features + i];\n }\n output[out_start + o] = sum;\n }\n }\n}\n\nstd::vector<torch::Tensor> msa_row_attn_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps) {\n \n TORCH_CHECK(msa.is_cuda(), \"MSA must be a CUDA tensor\");\n \n auto sizes = msa.sizes();\n int64_t B = sizes[0], S = sizes[1], N = sizes[2], D = sizes[3];\n int64_t d = D / num_heads;\n int64_t h = num_heads;\n \n int64_t num_groups = B * S * N;\n \n auto q_out = torch::empty({B, S, N, D}, msa.options());\n auto k_out = torch::empty({B, S, N, D}, msa.options());\n auto v_out = torch::empty({B, S, N, D}, msa.options());\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n layernorm_multi_linear_kernel<<<blocks, BLOCK_SIZE>>>(\n msa.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n q_out.data_ptr<float>(),\n k_out.data_ptr<float>(),\n v_out.data_ptr<float>(),\n q_weight.data_ptr<float>(),\n k_weight.data_ptr<float>(),\n v_weight.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(D),\n static_cast<float>(eps));\n \n auto q_reshaped = q_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n auto k_reshaped = k_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n auto v_reshaped = v_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n \n auto attn_out = torch::empty({B, S, h, N, d}, msa.options());\n \n float scale = 1.0f / sqrtf(static_cast<float>(d));\n int attn_blocks = B * S * h;\n int smem_size = (d + N + d) * sizeof(float);\n \n attention_kernel<<<attn_blocks, 64, smem_size>>>(\n q_reshaped.data_ptr<float>(),\n k_reshaped.data_ptr<float>(),\n v_reshaped.data_ptr<float>(),\n attn_out.data_ptr<float>(),\n B, S, N, h, d, scale);\n \n auto attn_out_reshaped = attn_out.permute({0, 1, 3, 2, 4}).contiguous().view({B, S, N, D});\n \n auto final_out = torch::empty({B, S, N, D}, msa.options());\n blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n output_proj_kernel<<<blocks, BLOCK_SIZE>>>(\n attn_out_reshaped.data_ptr<float>(),\n out_weight.data_ptr<float>(),\n out_bias.data_ptr<float>(),\n final_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(D));\n \n return {final_out, q_out, k_out, v_out};\n}\n\n__global__ void outer_product_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n const float* __restrict__ a_weight,\n const float* __restrict__ a_bias,\n const float* __restrict__ b_weight,\n const float* __restrict__ b_bias,\n float* __restrict__ a_out,\n float* __restrict__ b_out,\n int num_groups,\n int in_features,\n int hidden,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * hidden;\n \n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[in_start + i];\n }\n mean /= static_cast<float>(in_features);\n \n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[in_start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n for (int o = 0; o < hidden; o++) {\n float a_sum = a_bias[o];\n float b_sum = b_bias[o];\n for (int i = 0; i < in_features; i++) {\n float normalized = (input[in_start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n a_sum += normalized * a_weight[o * in_features + i];\n b_sum += normalized * b_weight[o * in_features + i];\n }\n a_out[out_start + o] = a_sum;\n b_out[out_start + o] = b_sum;\n }\n }\n}\n\ntorch::Tensor outer_product_mean_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps) {\n \n TORCH_CHECK(msa.is_cuda(), \"MSA must be a CUDA tensor\");\n \n auto sizes = msa.sizes();\n int64_t B = sizes[0], S = sizes[1], N = sizes[2], D = sizes[3];\n int64_t hidden = a_weight.size(0);\n \n int64_t num_groups = B * S * N;\n \n auto a_out = torch::empty({B, S, N, hidden}, msa.options());\n auto b_out = torch::empty({B, S, N, hidden}, msa.options());\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n outer_product_kernel<<<blocks, BLOCK_SIZE>>>(\n msa.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n a_weight.data_ptr<float>(),\n a_bias.data_ptr<float>(),\n b_weight.data_ptr<float>(),\n b_bias.data_ptr<float>(),\n a_out.data_ptr<float>(),\n b_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(hidden),\n static_cast<float>(eps));\n \n auto outer = torch::einsum(\"bsih,bsjk->bijhk\", {a_out, b_out});\n outer = outer / static_cast<float>(S);\n \n int64_t N1 = outer.size(1), N2 = outer.size(2), H1 = outer.size(3), H2 = outer.size(4);\n auto outer_flat = outer.reshape({B, N1, N2, H1 * H2});\n \n return torch::linear(outer_flat, out_weight, out_bias);\n}\n\n__global__ void tri_mul_layernorm_linear_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n const float* __restrict__ pa_weight,\n const float* __restrict__ pa_bias,\n const float* __restrict__ pb_weight,\n const float* __restrict__ pb_bias,\n const float* __restrict__ ga_weight,\n const float* __restrict__ ga_bias,\n const float* __restrict__ gb_weight,\n const float* __restrict__ gb_bias,\n float* __restrict__ pa_out,\n float* __restrict__ pb_out,\n int num_groups,\n int in_features,\n int hidden,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * hidden;\n \n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[in_start + i];\n }\n mean /= static_cast<float>(in_features);\n \n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[in_start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n for (int o = 0; o < hidden; o++) {\n float pa_sum = pa_bias[o];\n float pb_sum = pb_bias[o];\n float ga_sum = ga_bias[o];\n float gb_sum = gb_bias[o];\n \n for (int i = 0; i < in_features; i++) {\n float normalized = (input[in_start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n pa_sum += normalized * pa_weight[o * in_features + i];\n pb_sum += normalized * pb_weight[o * in_features + i];\n ga_sum += normalized * ga_weight[o * in_features + i];\n gb_sum += normalized * gb_weight[o * in_features + i];\n }\n \n pa_out[out_start + o] = pa_sum * (1.0f / (1.0f + expf(-ga_sum)));\n pb_out[out_start + o] = pb_sum * (1.0f / (1.0f + expf(-gb_sum)));\n }\n }\n}\n\ntorch::Tensor tri_mul_out_cuda(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps) {\n \n TORCH_CHECK(pair.is_cuda(), \"Pair must be a CUDA tensor\");\n \n auto sizes = pair.sizes();\n int64_t B = sizes[0], N1 = sizes[1], N2 = sizes[2], D = sizes[3];\n int64_t hidden = pa_weight.size(0);\n \n int64_t num_groups = B * N1 * N2;\n \n auto pa_out = torch::empty({B, N1, N2, hidden}, pair.options());\n auto pb_out = torch::empty({B, N1, N2, hidden}, pair.options());\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n tri_mul_layernorm_linear_kernel<<<blocks, BLOCK_SIZE>>>(\n pair.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n pa_weight.data_ptr<float>(),\n pa_bias.data_ptr<float>(),\n pb_weight.data_ptr<float>(),\n pb_bias.data_ptr<float>(),\n ga_weight.data_ptr<float>(),\n ga_bias.data_ptr<float>(),\n gb_weight.data_ptr<float>(),\n gb_bias.data_ptr<float>(),\n pa_out.data_ptr<float>(),\n pb_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(hidden),\n static_cast<float>(eps));\n \n auto o = torch::einsum(\"bikh,bjkh->bijh\", {pa_out, pb_out});\n o = torch::layer_norm(o, {hidden}, norm_out_weight, norm_out_bias, eps);\n o = torch::linear(o, out_weight, out_bias);\n auto gate = torch::sigmoid(torch::linear(pair, gate_weight, gate_bias));\n \n return gate * o;\n}\n\"\"\"\n\nevoformer_ext = load_inline(\n name='evoformer_ext',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['msa_row_attn', 'outer_product_mean', 'tri_mul_out'],\n verbose=True\n)\n\n\nclass MSARowSelfAttention(nn.Module):\n def __init__(self, msa_dim, num_heads):\n super().__init__()\n self.num_heads = num_heads\n self.head_dim = msa_dim // num_heads\n self.norm = nn.LayerNorm(msa_dim)\n self.q = nn.Linear(msa_dim, msa_dim, bias=False)\n self.k = nn.Linear(msa_dim, msa_dim, bias=False)\n self.v = nn.Linear(msa_dim, msa_dim, bias=False)\n self.out = nn.Linear(msa_dim, msa_dim)\n\n def forward(self, msa):\n B, S, N, D = msa.shape\n \n results = evoformer_ext.msa_row_attn(\n msa.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.q.weight,\n self.k.weight,\n self.v.weight,\n self.out.weight,\n self.out.bias,\n self.num_heads,\n 1e-5\n )\n return results[0]\n\n\nclass OuterProductMeanSmall(nn.Module):\n def __init__(self, msa_dim, pair_dim, hidden=32):\n super().__init__()\n self.norm = nn.LayerNorm(msa_dim)\n self.a = nn.Linear(msa_dim, hidden)\n self.b = nn.Linear(msa_dim, hidden)\n self.out = nn.Linear(hidden * hidden, pair_dim)\n\n def forward(self, msa):\n return evoformer_ext.outer_product_mean(\n msa.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.a.weight,\n self.a.bias,\n self.b.weight,\n self.b.bias,\n self.out.weight,\n self.out.bias,\n 1e-5\n )\n\n\nclass TriMulOut(nn.Module):\n def __init__(self, pair_dim, hidden):\n super().__init__()\n self.norm = nn.LayerNorm(pair_dim)\n self.pa = nn.Linear(pair_dim, hidden)\n self.pb = nn.Linear(pair_dim, hidden)\n self.ga = nn.Linear(pair_dim, hidden)\n self.gb = nn.Linear(pair_dim, hidden)\n self.norm_out = nn.LayerNorm(hidden)\n self.out = nn.Linear(hidden, pair_dim)\n self.gate_out = nn.Linear(pair_dim, pair_dim)\n\n def forward(self, pair):\n return evoformer_ext.tri_mul_out(\n pair.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.pa.weight,\n self.pa.bias,\n self.pb.weight,\n self.pb.bias,\n self.ga.weight,\n self.ga.bias,\n self.gb.weight,\n self.gb.bias,\n self.norm_out.weight,\n self.norm_out.bias,\n self.out.weight,\n self.out.bias,\n self.gate_out.weight,\n self.gate_out.bias,\n 1e-5\n )\n\n\nclass ModelNew(nn.Module):\n def __init__(self, msa_dim, pair_dim, num_heads, tri_hidden):\n super().__init__()\n self.msa_attn = MSARowSelfAttention(msa_dim, num_heads)\n self.opm = OuterProductMeanSmall(msa_dim, pair_dim)\n self.tri_mul = TriMulOut(pair_dim, tri_hidden)\n\n def forward(\n self, msa_repr: torch.Tensor, pair_repr: torch.Tensor\n ) -> tuple:\n msa_repr = msa_repr + self.msa_attn(msa_repr)\n pair_repr = pair_repr + self.opm(msa_repr)\n pair_repr = pair_repr + self.tri_mul(pair_repr)\n return msa_repr, pair_repr" }
result ← run_correctness (fail)
failrun_correctness run_correctness FAILED: (0 / 3) trials did not all match. correctness_issue: Output mismatch max_difference: ['0.229713', '0.033437', '2.399657', '0.276382', '3.077410', '0.256807'] avg_difference: ['0.050149', '0.005714', '0.454216', '0.042947', '0.308416', '0.033736']
turn 11 · 1 tool call(s) · 283.2s turn · 54.2s LLM · tokens in=39,454 · out=6,374 · total=45,828 · 25,310 chars (click to expand)
turn 11283.2s turn · 54.2s LLM · 229.0s tools · tokens in=39,454 · out=6,374 · total=45,828
reasoning
The correctness test failed. There must be an issue with the attention kernel. Looking at the code, I see a potential problem - the attention kernel has a different shape handling. The indices for accessing k and v in the attention kernel are wrong. Looking at the reshaping: - q_reshaped = q_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous() This gives [B, S, h, N, d] - k_reshaped and v_reshaped are similar. But in the attention kernel, I'm computing: - k_idx = b * S * h * N * d + s * h * N * d + head * N * d + key_pos * d + i This seems correct for the [B, S, h, N, d] layout, where the stride is: - For B: S * h * N * d - For S: h * N * d - For h: N * d - For N: d - For d: 1 So for element [b, s, head, key_pos, i], the index is: b * (S * h * N * d) + s * (h * N * d) + head * (N * d) + key_pos * d + i Wait, but the attention is computing q @ k^T where k needs to be transposed. In the current implementation, for each position (b, s, head), we're iterating over all key positions to compute attention scores. But the way I'm indexing k is: k_idx = b * S * h * N * d + s * h * N * d + head * N * d + key_pos * d + i This accesses element [b, s, head, key_pos, i] in the k tensor. This is correct! The issue might be in the softmax computation. Let me check - I'm only doing softmax on one thread (threadIdx.x == 0), which means only one thread updates the attn_smem. But all threads read from it. The __syncthreads() should ensure correctness. Actually, I think the issue might be that I'm not handling the transpose correctly. In attention: - q has shape [B, S, h, N, d] - query for each position - k has shape [B, S, h, N, d] - key for each position - We compute q @ k^T For a single (b, s, head) combination: - q for this position is [N, d] - k for this position is [N, d] - We compute [N, d] @ [d, N] = [N, N] But wait, the reference code does: q = self.q(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4) # [B, S, h, N, d] k = self.k(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4) # [B, S, h, N, d] attn = (q @ k.transpose(-1, -2)) / math.sqrt(d) So q has shape [B, S, h, N, d] and k has shape [B, S, h, N, d]. q @ k.transpose(-1, -2) gives [B, S, h, N, N]. For each (b, s, head), we compute: q[b, s, head] @ k[b, s, head]^T = [N, d] @ [d, N] = [N, N] This is for self-attention within each sequence position, across all residues N. But my kernel is computing attention for each (b, s, head) position, but I think there's a confusion. Wait, I think I'm misunderstanding the structure. Let me re-read the reference: - msa has shape [B, S, N, D] where B=batch, S=MSA depth, N=sequence length, D=dimension - After layer norm, we compute q, k, v projections - q = q_weight(x).view(B, S, N, h, d).permute(0, 1, 3, 2, 4) gives [B, S, h, N, d] So for MSA row self-attention: - We have B batches, S MSA sequences, h heads - For each batch and MSA sequence, we attend over N positions - The attention is: for position i in N, we compute attention weights over all positions j in N So the attention computation should be: - For each (b, s, head): - q has shape [N, d] - k has shape [N, d] - scores = q @ k^T has shape [N, N] - For each query position q_idx, we attend to all key positions In my kernel, I'm handling one (b, s, head) per block. But I'm computing attention for the q at position q_idx for all keys. Wait, I don't see where I'm setting q_idx! Looking at my code again: ``` int head_idx = b * S * h * N * d + s * h * N * d + head * N * d; ``` This is the base index for the entire [N, d] array for this (b, s, head). I then load q_smem with the entire [N, d] array? No, I'm only loading d elements into q_smem, which corresponds to position 0 in N. This is wrong! I need to compute attention for ALL N query positions, not just position 0. The reference computes full [N, N] attention matrix. Let me fix this.
assistantThe correctness test failed. I see the issue - the attention kernel is computing attention incorrectly. Let me fix the attention computation:
call → compile_kernel
callcompile_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport math\nfrom torch.utils.cpp_extension import load_inline\n\ncpp_source = r\"\"\"\n#include <torch/extension.h>\n#include <vector>\n\nstd::vector<torch::Tensor> msa_row_attn_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps);\n\ntorch::Tensor outer_product_mean_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps);\n\ntorch::Tensor tri_mul_out_cuda(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps);\n\nstd::vector<torch::Tensor> msa_row_attn(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps) {\n return msa_row_attn_cuda(msa, ln_weight, ln_bias, q_weight, k_weight, v_weight, out_weight, out_bias, num_heads, eps);\n}\n\ntorch::Tensor outer_product_mean(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps) {\n return outer_product_mean_cuda(msa, ln_weight, ln_bias, a_weight, a_bias, b_weight, b_bias, out_weight, out_bias, eps);\n}\n\ntorch::Tensor tri_mul_out(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps) {\n return tri_mul_out_cuda(pair, ln_weight, ln_bias, pa_weight, pa_bias, pb_weight, pb_bias, \n ga_weight, ga_bias, gb_weight, gb_bias, norm_out_weight, norm_out_bias,\n out_weight, out_bias, gate_weight, gate_bias, eps);\n}\n\"\"\"\n\ncuda_source = r\"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\nconstexpr int BLOCK_SIZE = 256;\n\n__global__ void layernorm_multi_linear_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n float* __restrict__ q_out,\n float* __restrict__ k_out,\n float* __restrict__ v_out,\n const float* __restrict__ q_weight,\n const float* __restrict__ k_weight,\n const float* __restrict__ v_weight,\n int num_groups,\n int in_features,\n int out_features,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int start = idx * in_features;\n int out_start = idx * out_features;\n \n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[start + i];\n }\n mean /= static_cast<float>(in_features);\n \n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n for (int o = 0; o < out_features; o++) {\n float q_sum = 0.0f, k_sum = 0.0f, v_sum = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float normalized = (input[start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n q_sum += normalized * q_weight[o * in_features + i];\n k_sum += normalized * k_weight[o * in_features + i];\n v_sum += normalized * v_weight[o * in_features + i];\n }\n q_out[out_start + o] = q_sum;\n k_out[out_start + o] = k_sum;\n v_out[out_start + o] = v_sum;\n }\n }\n}\n\n__global__ void output_proj_kernel(\n const float* __restrict__ input,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n float* __restrict__ output,\n int num_groups,\n int in_features,\n int out_features) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * out_features;\n \n for (int o = 0; o < out_features; o++) {\n float sum = bias[o];\n for (int i = 0; i < in_features; i++) {\n sum += input[in_start + i] * weight[o * in_features + i];\n }\n output[out_start + o] = sum;\n }\n }\n}\n\nstd::vector<torch::Tensor> msa_row_attn_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps) {\n \n TORCH_CHECK(msa.is_cuda(), \"MSA must be a CUDA tensor\");\n \n auto sizes = msa.sizes();\n int64_t B = sizes[0], S = sizes[1], N = sizes[2], D = sizes[3];\n int64_t d = D / num_heads;\n int64_t h = num_heads;\n \n int64_t num_groups = B * S * N;\n \n auto q_out = torch::empty({B, S, N, D}, msa.options());\n auto k_out = torch::empty({B, S, N, D}, msa.options());\n auto v_out = torch::empty({B, S, N, D}, msa.options());\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n layernorm_multi_linear_kernel<<<blocks, BLOCK_SIZE>>>(\n msa.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n q_out.data_ptr<float>(),\n k_out.data_ptr<float>(),\n v_out.data_ptr<float>(),\n q_weight.data_ptr<float>(),\n k_weight.data_ptr<float>(),\n v_weight.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(D),\n static_cast<float>(eps));\n \n // Reshape for attention using PyTorch operations (well-optimized)\n // [B, S, N, D] -> [B, S, N, h, d] -> [B, S, h, N, d]\n auto q_reshaped = q_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n auto k_reshaped = k_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n auto v_reshaped = v_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n \n // Compute attention: [B, S, h, N, d] @ [B, S, h, d, N] = [B, S, h, N, N]\n auto attn = torch::matmul(q_reshaped, k_reshaped.transpose(-1, -2)) / sqrtf(static_cast<float>(d));\n attn = torch::softmax(attn, -1);\n \n // Apply attention: [B, S, h, N, N] @ [B, S, h, N, d] = [B, S, h, N, d]\n auto attn_out = torch::matmul(attn, v_reshaped);\n \n // Reshape back: [B, S, h, N, d] -> [B, S, N, h, d] -> [B, S, N, D]\n auto attn_out_reshaped = attn_out.permute({0, 1, 3, 2, 4}).contiguous().view({B, S, N, D});\n \n // Output projection\n auto final_out = torch::empty({B, S, N, D}, msa.options());\n blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n output_proj_kernel<<<blocks, BLOCK_SIZE>>>(\n attn_out_reshaped.data_ptr<float>(),\n out_weight.data_ptr<float>(),\n out_bias.data_ptr<float>(),\n final_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(D));\n \n return {final_out, q_out, k_out, v_out};\n}\n\n__global__ void outer_product_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n const float* __restrict__ a_weight,\n const float* __restrict__ a_bias,\n const float* __restrict__ b_weight,\n const float* __restrict__ b_bias,\n float* __restrict__ a_out,\n float* __restrict__ b_out,\n int num_groups,\n int in_features,\n int hidden,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * hidden;\n \n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[in_start + i];\n }\n mean /= static_cast<float>(in_features);\n \n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[in_start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n for (int o = 0; o < hidden; o++) {\n float a_sum = a_bias[o];\n float b_sum = b_bias[o];\n for (int i = 0; i < in_features; i++) {\n float normalized = (input[in_start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n a_sum += normalized * a_weight[o * in_features + i];\n b_sum += normalized * b_weight[o * in_features + i];\n }\n a_out[out_start + o] = a_sum;\n b_out[out_start + o] = b_sum;\n }\n }\n}\n\ntorch::Tensor outer_product_mean_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps) {\n \n TORCH_CHECK(msa.is_cuda(), \"MSA must be a CUDA tensor\");\n \n auto sizes = msa.sizes();\n int64_t B = sizes[0], S = sizes[1], N = sizes[2], D = sizes[3];\n int64_t hidden = a_weight.size(0);\n \n int64_t num_groups = B * S * N;\n \n auto a_out = torch::empty({B, S, N, hidden}, msa.options());\n auto b_out = torch::empty({B, S, N, hidden}, msa.options());\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n outer_product_kernel<<<blocks, BLOCK_SIZE>>>(\n msa.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n a_weight.data_ptr<float>(),\n a_bias.data_ptr<float>(),\n b_weight.data_ptr<float>(),\n b_bias.data_ptr<float>(),\n a_out.data_ptr<float>(),\n b_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(hidden),\n static_cast<float>(eps));\n \n auto outer = torch::einsum(\"bsih,bsjk->bijhk\", {a_out, b_out});\n outer = outer / static_cast<float>(S);\n \n int64_t N1 = outer.size(1), N2 = outer.size(2), H1 = outer.size(3), H2 = outer.size(4);\n auto outer_flat = outer.reshape({B, N1, N2, H1 * H2});\n \n return torch::linear(outer_flat, out_weight, out_bias);\n}\n\n__global__ void tri_mul_layernorm_linear_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n const float* __restrict__ pa_weight,\n const float* __restrict__ pa_bias,\n const float* __restrict__ pb_weight,\n const float* __restrict__ pb_bias,\n const float* __restrict__ ga_weight,\n const float* __restrict__ ga_bias,\n const float* __restrict__ gb_weight,\n const float* __restrict__ gb_bias,\n float* __restrict__ pa_out,\n float* __restrict__ pb_out,\n int num_groups,\n int in_features,\n int hidden,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * hidden;\n \n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[in_start + i];\n }\n mean /= static_cast<float>(in_features);\n \n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[in_start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n for (int o = 0; o < hidden; o++) {\n float pa_sum = pa_bias[o];\n float pb_sum = pb_bias[o];\n float ga_sum = ga_bias[o];\n float gb_sum = gb_bias[o];\n \n for (int i = 0; i < in_features; i++) {\n float normalized = (input[in_start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n pa_sum += normalized * pa_weight[o * in_features + i];\n pb_sum += normalized * pb_weight[o * in_features + i];\n ga_sum += normalized * ga_weight[o * in_features + i];\n gb_sum += normalized * gb_weight[o * in_features + i];\n }\n \n pa_out[out_start + o] = pa_sum * (1.0f / (1.0f + expf(-ga_sum)));\n pb_out[out_start + o] = pb_sum * (1.0f / (1.0f + expf(-gb_sum)));\n }\n }\n}\n\ntorch::Tensor tri_mul_out_cuda(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps) {\n \n TORCH_CHECK(pair.is_cuda(), \"Pair must be a CUDA tensor\");\n \n auto sizes = pair.sizes();\n int64_t B = sizes[0], N1 = sizes[1], N2 = sizes[2], D = sizes[3];\n int64_t hidden = pa_weight.size(0);\n \n int64_t num_groups = B * N1 * N2;\n \n auto pa_out = torch::empty({B, N1, N2, hidden}, pair.options());\n auto pb_out = torch::empty({B, N1, N2, hidden}, pair.options());\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n tri_mul_layernorm_linear_kernel<<<blocks, BLOCK_SIZE>>>(\n pair.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n pa_weight.data_ptr<float>(),\n pa_bias.data_ptr<float>(),\n pb_weight.data_ptr<float>(),\n pb_bias.data_ptr<float>(),\n ga_weight.data_ptr<float>(),\n ga_bias.data_ptr<float>(),\n gb_weight.data_ptr<float>(),\n gb_bias.data_ptr<float>(),\n pa_out.data_ptr<float>(),\n pb_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(hidden),\n static_cast<float>(eps));\n \n auto o = torch::einsum(\"bikh,bjkh->bijh\", {pa_out, pb_out});\n o = torch::layer_norm(o, {hidden}, norm_out_weight, norm_out_bias, eps);\n o = torch::linear(o, out_weight, out_bias);\n auto gate = torch::sigmoid(torch::linear(pair, gate_weight, gate_bias));\n \n return gate * o;\n}\n\"\"\"\n\nevoformer_ext = load_inline(\n name='evoformer_ext',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['msa_row_attn', 'outer_product_mean', 'tri_mul_out'],\n verbose=True\n)\n\n\nclass MSARowSelfAttention(nn.Module):\n def __init__(self, msa_dim, num_heads):\n super().__init__()\n self.num_heads = num_heads\n self.head_dim = msa_dim // num_heads\n self.norm = nn.LayerNorm(msa_dim)\n self.q = nn.Linear(msa_dim, msa_dim, bias=False)\n self.k = nn.Linear(msa_dim, msa_dim, bias=False)\n self.v = nn.Linear(msa_dim, msa_dim, bias=False)\n self.out = nn.Linear(msa_dim, msa_dim)\n\n def forward(self, msa):\n B, S, N, D = msa.shape\n \n results = evoformer_ext.msa_row_attn(\n msa.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.q.weight,\n self.k.weight,\n self.v.weight,\n self.out.weight,\n self.out.bias,\n self.num_heads,\n 1e-5\n )\n return results[0]\n\n\nclass OuterProductMeanSmall(nn.Module):\n def __init__(self, msa_dim, pair_dim, hidden=32):\n super().__init__()\n self.norm = nn.LayerNorm(msa_dim)\n self.a = nn.Linear(msa_dim, hidden)\n self.b = nn.Linear(msa_dim, hidden)\n self.out = nn.Linear(hidden * hidden, pair_dim)\n\n def forward(self, msa):\n return evoformer_ext.outer_product_mean(\n msa.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.a.weight,\n self.a.bias,\n self.b.weight,\n self.b.bias,\n self.out.weight,\n self.out.bias,\n 1e-5\n )\n\n\nclass TriMulOut(nn.Module):\n def __init__(self, pair_dim, hidden):\n super().__init__()\n self.norm = nn.LayerNorm(pair_dim)\n self.pa = nn.Linear(pair_dim, hidden)\n self.pb = nn.Linear(pair_dim, hidden)\n self.ga = nn.Linear(pair_dim, hidden)\n self.gb = nn.Linear(pair_dim, hidden)\n self.norm_out = nn.LayerNorm(hidden)\n self.out = nn.Linear(hidden, pair_dim)\n self.gate_out = nn.Linear(pair_dim, pair_dim)\n\n def forward(self, pair):\n return evoformer_ext.tri_mul_out(\n pair.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.pa.weight,\n self.pa.bias,\n self.pb.weight,\n self.pb.bias,\n self.ga.weight,\n self.ga.bias,\n self.gb.weight,\n self.gb.bias,\n self.norm_out.weight,\n self.norm_out.bias,\n self.out.weight,\n self.out.bias,\n self.gate_out.weight,\n self.gate_out.bias,\n 1e-5\n )\n\n\nclass ModelNew(nn.Module):\n def __init__(self, msa_dim, pair_dim, num_heads, tri_hidden):\n super().__init__()\n self.msa_attn = MSARowSelfAttention(msa_dim, num_heads)\n self.opm = OuterProductMeanSmall(msa_dim, pair_dim)\n self.tri_mul = TriMulOut(pair_dim, tri_hidden)\n\n def forward(\n self, msa_repr: torch.Tensor, pair_repr: torch.Tensor\n ) -> tuple:\n msa_repr = msa_repr + self.msa_attn(msa_repr)\n pair_repr = pair_repr + self.opm(msa_repr)\n pair_repr = pair_repr + self.tri_mul(pair_repr)\n return msa_repr, pair_repr" }
result ← compile_kernel (ok)
okcompile_kernel compile_kernel PASSED: kernel compiled without errors.
turn 12 · 1 tool call(s) · 258.5s turn · 40.6s LLM · tokens in=44,602 · out=4,921 · total=49,523 · 20,960 chars (click to expand)
turn 12258.5s turn · 40.6s LLM · 217.9s tools · tokens in=44,602 · out=4,921 · total=49,523
reasoning
The kernel compiled successfully. Let me run the correctness test to verify it works correctly.
call → run_correctness
callrun_correctness { "kernel_code": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport math\nfrom torch.utils.cpp_extension import load_inline\n\ncpp_source = r\"\"\"\n#include <torch/extension.h>\n#include <vector>\n\nstd::vector<torch::Tensor> msa_row_attn_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps);\n\ntorch::Tensor outer_product_mean_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps);\n\ntorch::Tensor tri_mul_out_cuda(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps);\n\nstd::vector<torch::Tensor> msa_row_attn(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps) {\n return msa_row_attn_cuda(msa, ln_weight, ln_bias, q_weight, k_weight, v_weight, out_weight, out_bias, num_heads, eps);\n}\n\ntorch::Tensor outer_product_mean(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps) {\n return outer_product_mean_cuda(msa, ln_weight, ln_bias, a_weight, a_bias, b_weight, b_bias, out_weight, out_bias, eps);\n}\n\ntorch::Tensor tri_mul_out(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps) {\n return tri_mul_out_cuda(pair, ln_weight, ln_bias, pa_weight, pa_bias, pb_weight, pb_bias, \n ga_weight, ga_bias, gb_weight, gb_bias, norm_out_weight, norm_out_bias,\n out_weight, out_bias, gate_weight, gate_bias, eps);\n}\n\"\"\"\n\ncuda_source = r\"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\nconstexpr int BLOCK_SIZE = 256;\n\n__global__ void layernorm_multi_linear_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n float* __restrict__ q_out,\n float* __restrict__ k_out,\n float* __restrict__ v_out,\n const float* __restrict__ q_weight,\n const float* __restrict__ k_weight,\n const float* __restrict__ v_weight,\n int num_groups,\n int in_features,\n int out_features,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int start = idx * in_features;\n int out_start = idx * out_features;\n \n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[start + i];\n }\n mean /= static_cast<float>(in_features);\n \n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n for (int o = 0; o < out_features; o++) {\n float q_sum = 0.0f, k_sum = 0.0f, v_sum = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float normalized = (input[start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n q_sum += normalized * q_weight[o * in_features + i];\n k_sum += normalized * k_weight[o * in_features + i];\n v_sum += normalized * v_weight[o * in_features + i];\n }\n q_out[out_start + o] = q_sum;\n k_out[out_start + o] = k_sum;\n v_out[out_start + o] = v_sum;\n }\n }\n}\n\n__global__ void output_proj_kernel(\n const float* __restrict__ input,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n float* __restrict__ output,\n int num_groups,\n int in_features,\n int out_features) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * out_features;\n \n for (int o = 0; o < out_features; o++) {\n float sum = bias[o];\n for (int i = 0; i < in_features; i++) {\n sum += input[in_start + i] * weight[o * in_features + i];\n }\n output[out_start + o] = sum;\n }\n }\n}\n\nstd::vector<torch::Tensor> msa_row_attn_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps) {\n \n TORCH_CHECK(msa.is_cuda(), \"MSA must be a CUDA tensor\");\n \n auto sizes = msa.sizes();\n int64_t B = sizes[0], S = sizes[1], N = sizes[2], D = sizes[3];\n int64_t d = D / num_heads;\n int64_t h = num_heads;\n \n int64_t num_groups = B * S * N;\n \n auto q_out = torch::empty({B, S, N, D}, msa.options());\n auto k_out = torch::empty({B, S, N, D}, msa.options());\n auto v_out = torch::empty({B, S, N, D}, msa.options());\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n layernorm_multi_linear_kernel<<<blocks, BLOCK_SIZE>>>(\n msa.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n q_out.data_ptr<float>(),\n k_out.data_ptr<float>(),\n v_out.data_ptr<float>(),\n q_weight.data_ptr<float>(),\n k_weight.data_ptr<float>(),\n v_weight.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(D),\n static_cast<float>(eps));\n \n auto q_reshaped = q_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n auto k_reshaped = k_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n auto v_reshaped = v_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n \n auto attn = torch::matmul(q_reshaped, k_reshaped.transpose(-1, -2)) / sqrtf(static_cast<float>(d));\n attn = torch::softmax(attn, -1);\n \n auto attn_out = torch::matmul(attn, v_reshaped);\n \n auto attn_out_reshaped = attn_out.permute({0, 1, 3, 2, 4}).contiguous().view({B, S, N, D});\n \n auto final_out = torch::empty({B, S, N, D}, msa.options());\n blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n output_proj_kernel<<<blocks, BLOCK_SIZE>>>(\n attn_out_reshaped.data_ptr<float>(),\n out_weight.data_ptr<float>(),\n out_bias.data_ptr<float>(),\n final_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(D));\n \n return {final_out, q_out, k_out, v_out};\n}\n\n__global__ void outer_product_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n const float* __restrict__ a_weight,\n const float* __restrict__ a_bias,\n const float* __restrict__ b_weight,\n const float* __restrict__ b_bias,\n float* __restrict__ a_out,\n float* __restrict__ b_out,\n int num_groups,\n int in_features,\n int hidden,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * hidden;\n \n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[in_start + i];\n }\n mean /= static_cast<float>(in_features);\n \n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[in_start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n for (int o = 0; o < hidden; o++) {\n float a_sum = a_bias[o];\n float b_sum = b_bias[o];\n for (int i = 0; i < in_features; i++) {\n float normalized = (input[in_start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n a_sum += normalized * a_weight[o * in_features + i];\n b_sum += normalized * b_weight[o * in_features + i];\n }\n a_out[out_start + o] = a_sum;\n b_out[out_start + o] = b_sum;\n }\n }\n}\n\ntorch::Tensor outer_product_mean_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps) {\n \n TORCH_CHECK(msa.is_cuda(), \"MSA must be a CUDA tensor\");\n \n auto sizes = msa.sizes();\n int64_t B = sizes[0], S = sizes[1], N = sizes[2], D = sizes[3];\n int64_t hidden = a_weight.size(0);\n \n int64_t num_groups = B * S * N;\n \n auto a_out = torch::empty({B, S, N, hidden}, msa.options());\n auto b_out = torch::empty({B, S, N, hidden}, msa.options());\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n outer_product_kernel<<<blocks, BLOCK_SIZE>>>(\n msa.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n a_weight.data_ptr<float>(),\n a_bias.data_ptr<float>(),\n b_weight.data_ptr<float>(),\n b_bias.data_ptr<float>(),\n a_out.data_ptr<float>(),\n b_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(hidden),\n static_cast<float>(eps));\n \n auto outer = torch::einsum(\"bsih,bsjk->bijhk\", {a_out, b_out});\n outer = outer / static_cast<float>(S);\n \n int64_t N1 = outer.size(1), N2 = outer.size(2), H1 = outer.size(3), H2 = outer.size(4);\n auto outer_flat = outer.reshape({B, N1, N2, H1 * H2});\n \n return torch::linear(outer_flat, out_weight, out_bias);\n}\n\n__global__ void tri_mul_layernorm_linear_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n const float* __restrict__ pa_weight,\n const float* __restrict__ pa_bias,\n const float* __restrict__ pb_weight,\n const float* __restrict__ pb_bias,\n const float* __restrict__ ga_weight,\n const float* __restrict__ ga_bias,\n const float* __restrict__ gb_weight,\n const float* __restrict__ gb_bias,\n float* __restrict__ pa_out,\n float* __restrict__ pb_out,\n int num_groups,\n int in_features,\n int hidden,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * hidden;\n \n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[in_start + i];\n }\n mean /= static_cast<float>(in_features);\n \n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[in_start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n for (int o = 0; o < hidden; o++) {\n float pa_sum = pa_bias[o];\n float pb_sum = pb_bias[o];\n float ga_sum = ga_bias[o];\n float gb_sum = gb_bias[o];\n \n for (int i = 0; i < in_features; i++) {\n float normalized = (input[in_start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n pa_sum += normalized * pa_weight[o * in_features + i];\n pb_sum += normalized * pb_weight[o * in_features + i];\n ga_sum += normalized * ga_weight[o * in_features + i];\n gb_sum += normalized * gb_weight[o * in_features + i];\n }\n \n pa_out[out_start + o] = pa_sum * (1.0f / (1.0f + expf(-ga_sum)));\n pb_out[out_start + o] = pb_sum * (1.0f / (1.0f + expf(-gb_sum)));\n }\n }\n}\n\ntorch::Tensor tri_mul_out_cuda(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps) {\n \n TORCH_CHECK(pair.is_cuda(), \"Pair must be a CUDA tensor\");\n \n auto sizes = pair.sizes();\n int64_t B = sizes[0], N1 = sizes[1], N2 = sizes[2], D = sizes[3];\n int64_t hidden = pa_weight.size(0);\n \n int64_t num_groups = B * N1 * N2;\n \n auto pa_out = torch::empty({B, N1, N2, hidden}, pair.options());\n auto pb_out = torch::empty({B, N1, N2, hidden}, pair.options());\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n tri_mul_layernorm_linear_kernel<<<blocks, BLOCK_SIZE>>>(\n pair.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n pa_weight.data_ptr<float>(),\n pa_bias.data_ptr<float>(),\n pb_weight.data_ptr<float>(),\n pb_bias.data_ptr<float>(),\n ga_weight.data_ptr<float>(),\n ga_bias.data_ptr<float>(),\n gb_weight.data_ptr<float>(),\n gb_bias.data_ptr<float>(),\n pa_out.data_ptr<float>(),\n pb_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(hidden),\n static_cast<float>(eps));\n \n auto o = torch::einsum(\"bikh,bjkh->bijh\", {pa_out, pb_out});\n o = torch::layer_norm(o, {hidden}, norm_out_weight, norm_out_bias, eps);\n o = torch::linear(o, out_weight, out_bias);\n auto gate = torch::sigmoid(torch::linear(pair, gate_weight, gate_bias));\n \n return gate * o;\n}\n\"\"\"\n\nevoformer_ext = load_inline(\n name='evoformer_ext',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['msa_row_attn', 'outer_product_mean', 'tri_mul_out'],\n verbose=True\n)\n\n\nclass MSARowSelfAttention(nn.Module):\n def __init__(self, msa_dim, num_heads):\n super().__init__()\n self.num_heads = num_heads\n self.head_dim = msa_dim // num_heads\n self.norm = nn.LayerNorm(msa_dim)\n self.q = nn.Linear(msa_dim, msa_dim, bias=False)\n self.k = nn.Linear(msa_dim, msa_dim, bias=False)\n self.v = nn.Linear(msa_dim, msa_dim, bias=False)\n self.out = nn.Linear(msa_dim, msa_dim)\n\n def forward(self, msa):\n B, S, N, D = msa.shape\n \n results = evoformer_ext.msa_row_attn(\n msa.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.q.weight,\n self.k.weight,\n self.v.weight,\n self.out.weight,\n self.out.bias,\n self.num_heads,\n 1e-5\n )\n return results[0]\n\n\nclass OuterProductMeanSmall(nn.Module):\n def __init__(self, msa_dim, pair_dim, hidden=32):\n super().__init__()\n self.norm = nn.LayerNorm(msa_dim)\n self.a = nn.Linear(msa_dim, hidden)\n self.b = nn.Linear(msa_dim, hidden)\n self.out = nn.Linear(hidden * hidden, pair_dim)\n\n def forward(self, msa):\n return evoformer_ext.outer_product_mean(\n msa.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.a.weight,\n self.a.bias,\n self.b.weight,\n self.b.bias,\n self.out.weight,\n self.out.bias,\n 1e-5\n )\n\n\nclass TriMulOut(nn.Module):\n def __init__(self, pair_dim, hidden):\n super().__init__()\n self.norm = nn.LayerNorm(pair_dim)\n self.pa = nn.Linear(pair_dim, hidden)\n self.pb = nn.Linear(pair_dim, hidden)\n self.ga = nn.Linear(pair_dim, hidden)\n self.gb = nn.Linear(pair_dim, hidden)\n self.norm_out = nn.LayerNorm(hidden)\n self.out = nn.Linear(hidden, pair_dim)\n self.gate_out = nn.Linear(pair_dim, pair_dim)\n\n def forward(self, pair):\n return evoformer_ext.tri_mul_out(\n pair.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.pa.weight,\n self.pa.bias,\n self.pb.weight,\n self.pb.bias,\n self.ga.weight,\n self.ga.bias,\n self.gb.weight,\n self.gb.bias,\n self.norm_out.weight,\n self.norm_out.bias,\n self.out.weight,\n self.out.bias,\n self.gate_out.weight,\n self.gate_out.bias,\n 1e-5\n )\n\n\nclass ModelNew(nn.Module):\n def __init__(self, msa_dim, pair_dim, num_heads, tri_hidden):\n super().__init__()\n self.msa_attn = MSARowSelfAttention(msa_dim, num_heads)\n self.opm = OuterProductMeanSmall(msa_dim, pair_dim)\n self.tri_mul = TriMulOut(pair_dim, tri_hidden)\n\n def forward(\n self, msa_repr: torch.Tensor, pair_repr: torch.Tensor\n ) -> tuple:\n msa_repr = msa_repr + self.msa_attn(msa_repr)\n pair_repr = pair_repr + self.opm(msa_repr)\n pair_repr = pair_repr + self.tri_mul(pair_repr)\n return msa_repr, pair_repr" }
result ← run_correctness (ok)
okrun_correctness run_correctness PASSED: (3 / 3) trials all matched the reference. Numerical precision: max_abs_err=8.34e-07 mean_abs_err=7.33e-08 max_rel_err=1.50e+00
turn 13 · 1 tool call(s) · 40.2s turn · 40.2s LLM · tokens in=49,595 · out=4,925 · total=54,520 · 20,917 chars (click to expand)
turn 1340.2s turn · 40.2s LLM · 0.0s tools · tokens in=49,595 · out=4,925 · total=54,520
reasoning
The correctness test passed. The kernel is correct now. Let me run a static check and then submit it.
call → static_check
callstatic_check { "kernel_code": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport math\nfrom torch.utils.cpp_extension import load_inline\n\ncpp_source = r\"\"\"\n#include <torch/extension.h>\n#include <vector>\n\nstd::vector<torch::Tensor> msa_row_attn_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps);\n\ntorch::Tensor outer_product_mean_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps);\n\ntorch::Tensor tri_mul_out_cuda(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps);\n\nstd::vector<torch::Tensor> msa_row_attn(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps) {\n return msa_row_attn_cuda(msa, ln_weight, ln_bias, q_weight, k_weight, v_weight, out_weight, out_bias, num_heads, eps);\n}\n\ntorch::Tensor outer_product_mean(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps) {\n return outer_product_mean_cuda(msa, ln_weight, ln_bias, a_weight, a_bias, b_weight, b_bias, out_weight, out_bias, eps);\n}\n\ntorch::Tensor tri_mul_out(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps) {\n return tri_mul_out_cuda(pair, ln_weight, ln_bias, pa_weight, pa_bias, pb_weight, pb_bias, \n ga_weight, ga_bias, gb_weight, gb_bias, norm_out_weight, norm_out_bias,\n out_weight, out_bias, gate_weight, gate_bias, eps);\n}\n\"\"\"\n\ncuda_source = r\"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\nconstexpr int BLOCK_SIZE = 256;\n\n__global__ void layernorm_multi_linear_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n float* __restrict__ q_out,\n float* __restrict__ k_out,\n float* __restrict__ v_out,\n const float* __restrict__ q_weight,\n const float* __restrict__ k_weight,\n const float* __restrict__ v_weight,\n int num_groups,\n int in_features,\n int out_features,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int start = idx * in_features;\n int out_start = idx * out_features;\n \n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[start + i];\n }\n mean /= static_cast<float>(in_features);\n \n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n for (int o = 0; o < out_features; o++) {\n float q_sum = 0.0f, k_sum = 0.0f, v_sum = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float normalized = (input[start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n q_sum += normalized * q_weight[o * in_features + i];\n k_sum += normalized * k_weight[o * in_features + i];\n v_sum += normalized * v_weight[o * in_features + i];\n }\n q_out[out_start + o] = q_sum;\n k_out[out_start + o] = k_sum;\n v_out[out_start + o] = v_sum;\n }\n }\n}\n\n__global__ void output_proj_kernel(\n const float* __restrict__ input,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n float* __restrict__ output,\n int num_groups,\n int in_features,\n int out_features) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * out_features;\n \n for (int o = 0; o < out_features; o++) {\n float sum = bias[o];\n for (int i = 0; i < in_features; i++) {\n sum += input[in_start + i] * weight[o * in_features + i];\n }\n output[out_start + o] = sum;\n }\n }\n}\n\nstd::vector<torch::Tensor> msa_row_attn_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps) {\n \n TORCH_CHECK(msa.is_cuda(), \"MSA must be a CUDA tensor\");\n \n auto sizes = msa.sizes();\n int64_t B = sizes[0], S = sizes[1], N = sizes[2], D = sizes[3];\n int64_t d = D / num_heads;\n int64_t h = num_heads;\n \n int64_t num_groups = B * S * N;\n \n auto q_out = torch::empty({B, S, N, D}, msa.options());\n auto k_out = torch::empty({B, S, N, D}, msa.options());\n auto v_out = torch::empty({B, S, N, D}, msa.options());\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n layernorm_multi_linear_kernel<<<blocks, BLOCK_SIZE>>>(\n msa.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n q_out.data_ptr<float>(),\n k_out.data_ptr<float>(),\n v_out.data_ptr<float>(),\n q_weight.data_ptr<float>(),\n k_weight.data_ptr<float>(),\n v_weight.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(D),\n static_cast<float>(eps));\n \n auto q_reshaped = q_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n auto k_reshaped = k_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n auto v_reshaped = v_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n \n auto attn = torch::matmul(q_reshaped, k_reshaped.transpose(-1, -2)) / sqrtf(static_cast<float>(d));\n attn = torch::softmax(attn, -1);\n \n auto attn_out = torch::matmul(attn, v_reshaped);\n \n auto attn_out_reshaped = attn_out.permute({0, 1, 3, 2, 4}).contiguous().view({B, S, N, D});\n \n auto final_out = torch::empty({B, S, N, D}, msa.options());\n blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n output_proj_kernel<<<blocks, BLOCK_SIZE>>>(\n attn_out_reshaped.data_ptr<float>(),\n out_weight.data_ptr<float>(),\n out_bias.data_ptr<float>(),\n final_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(D));\n \n return {final_out, q_out, k_out, v_out};\n}\n\n__global__ void outer_product_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n const float* __restrict__ a_weight,\n const float* __restrict__ a_bias,\n const float* __restrict__ b_weight,\n const float* __restrict__ b_bias,\n float* __restrict__ a_out,\n float* __restrict__ b_out,\n int num_groups,\n int in_features,\n int hidden,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * hidden;\n \n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[in_start + i];\n }\n mean /= static_cast<float>(in_features);\n \n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[in_start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n for (int o = 0; o < hidden; o++) {\n float a_sum = a_bias[o];\n float b_sum = b_bias[o];\n for (int i = 0; i < in_features; i++) {\n float normalized = (input[in_start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n a_sum += normalized * a_weight[o * in_features + i];\n b_sum += normalized * b_weight[o * in_features + i];\n }\n a_out[out_start + o] = a_sum;\n b_out[out_start + o] = b_sum;\n }\n }\n}\n\ntorch::Tensor outer_product_mean_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps) {\n \n TORCH_CHECK(msa.is_cuda(), \"MSA must be a CUDA tensor\");\n \n auto sizes = msa.sizes();\n int64_t B = sizes[0], S = sizes[1], N = sizes[2], D = sizes[3];\n int64_t hidden = a_weight.size(0);\n \n int64_t num_groups = B * S * N;\n \n auto a_out = torch::empty({B, S, N, hidden}, msa.options());\n auto b_out = torch::empty({B, S, N, hidden}, msa.options());\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n outer_product_kernel<<<blocks, BLOCK_SIZE>>>(\n msa.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n a_weight.data_ptr<float>(),\n a_bias.data_ptr<float>(),\n b_weight.data_ptr<float>(),\n b_bias.data_ptr<float>(),\n a_out.data_ptr<float>(),\n b_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(hidden),\n static_cast<float>(eps));\n \n auto outer = torch::einsum(\"bsih,bsjk->bijhk\", {a_out, b_out});\n outer = outer / static_cast<float>(S);\n \n int64_t N1 = outer.size(1), N2 = outer.size(2), H1 = outer.size(3), H2 = outer.size(4);\n auto outer_flat = outer.reshape({B, N1, N2, H1 * H2});\n \n return torch::linear(outer_flat, out_weight, out_bias);\n}\n\n__global__ void tri_mul_layernorm_linear_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n const float* __restrict__ pa_weight,\n const float* __restrict__ pa_bias,\n const float* __restrict__ pb_weight,\n const float* __restrict__ pb_bias,\n const float* __restrict__ ga_weight,\n const float* __restrict__ ga_bias,\n const float* __restrict__ gb_weight,\n const float* __restrict__ gb_bias,\n float* __restrict__ pa_out,\n float* __restrict__ pb_out,\n int num_groups,\n int in_features,\n int hidden,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * hidden;\n \n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[in_start + i];\n }\n mean /= static_cast<float>(in_features);\n \n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[in_start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n for (int o = 0; o < hidden; o++) {\n float pa_sum = pa_bias[o];\n float pb_sum = pb_bias[o];\n float ga_sum = ga_bias[o];\n float gb_sum = gb_bias[o];\n \n for (int i = 0; i < in_features; i++) {\n float normalized = (input[in_start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n pa_sum += normalized * pa_weight[o * in_features + i];\n pb_sum += normalized * pb_weight[o * in_features + i];\n ga_sum += normalized * ga_weight[o * in_features + i];\n gb_sum += normalized * gb_weight[o * in_features + i];\n }\n \n pa_out[out_start + o] = pa_sum * (1.0f / (1.0f + expf(-ga_sum)));\n pb_out[out_start + o] = pb_sum * (1.0f / (1.0f + expf(-gb_sum)));\n }\n }\n}\n\ntorch::Tensor tri_mul_out_cuda(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps) {\n \n TORCH_CHECK(pair.is_cuda(), \"Pair must be a CUDA tensor\");\n \n auto sizes = pair.sizes();\n int64_t B = sizes[0], N1 = sizes[1], N2 = sizes[2], D = sizes[3];\n int64_t hidden = pa_weight.size(0);\n \n int64_t num_groups = B * N1 * N2;\n \n auto pa_out = torch::empty({B, N1, N2, hidden}, pair.options());\n auto pb_out = torch::empty({B, N1, N2, hidden}, pair.options());\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n tri_mul_layernorm_linear_kernel<<<blocks, BLOCK_SIZE>>>(\n pair.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n pa_weight.data_ptr<float>(),\n pa_bias.data_ptr<float>(),\n pb_weight.data_ptr<float>(),\n pb_bias.data_ptr<float>(),\n ga_weight.data_ptr<float>(),\n ga_bias.data_ptr<float>(),\n gb_weight.data_ptr<float>(),\n gb_bias.data_ptr<float>(),\n pa_out.data_ptr<float>(),\n pb_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(hidden),\n static_cast<float>(eps));\n \n auto o = torch::einsum(\"bikh,bjkh->bijh\", {pa_out, pb_out});\n o = torch::layer_norm(o, {hidden}, norm_out_weight, norm_out_bias, eps);\n o = torch::linear(o, out_weight, out_bias);\n auto gate = torch::sigmoid(torch::linear(pair, gate_weight, gate_bias));\n \n return gate * o;\n}\n\"\"\"\n\nevoformer_ext = load_inline(\n name='evoformer_ext',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['msa_row_attn', 'outer_product_mean', 'tri_mul_out'],\n verbose=True\n)\n\n\nclass MSARowSelfAttention(nn.Module):\n def __init__(self, msa_dim, num_heads):\n super().__init__()\n self.num_heads = num_heads\n self.head_dim = msa_dim // num_heads\n self.norm = nn.LayerNorm(msa_dim)\n self.q = nn.Linear(msa_dim, msa_dim, bias=False)\n self.k = nn.Linear(msa_dim, msa_dim, bias=False)\n self.v = nn.Linear(msa_dim, msa_dim, bias=False)\n self.out = nn.Linear(msa_dim, msa_dim)\n\n def forward(self, msa):\n B, S, N, D = msa.shape\n \n results = evoformer_ext.msa_row_attn(\n msa.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.q.weight,\n self.k.weight,\n self.v.weight,\n self.out.weight,\n self.out.bias,\n self.num_heads,\n 1e-5\n )\n return results[0]\n\n\nclass OuterProductMeanSmall(nn.Module):\n def __init__(self, msa_dim, pair_dim, hidden=32):\n super().__init__()\n self.norm = nn.LayerNorm(msa_dim)\n self.a = nn.Linear(msa_dim, hidden)\n self.b = nn.Linear(msa_dim, hidden)\n self.out = nn.Linear(hidden * hidden, pair_dim)\n\n def forward(self, msa):\n return evoformer_ext.outer_product_mean(\n msa.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.a.weight,\n self.a.bias,\n self.b.weight,\n self.b.bias,\n self.out.weight,\n self.out.bias,\n 1e-5\n )\n\n\nclass TriMulOut(nn.Module):\n def __init__(self, pair_dim, hidden):\n super().__init__()\n self.norm = nn.LayerNorm(pair_dim)\n self.pa = nn.Linear(pair_dim, hidden)\n self.pb = nn.Linear(pair_dim, hidden)\n self.ga = nn.Linear(pair_dim, hidden)\n self.gb = nn.Linear(pair_dim, hidden)\n self.norm_out = nn.LayerNorm(hidden)\n self.out = nn.Linear(hidden, pair_dim)\n self.gate_out = nn.Linear(pair_dim, pair_dim)\n\n def forward(self, pair):\n return evoformer_ext.tri_mul_out(\n pair.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.pa.weight,\n self.pa.bias,\n self.pb.weight,\n self.pb.bias,\n self.ga.weight,\n self.ga.bias,\n self.gb.weight,\n self.gb.bias,\n self.norm_out.weight,\n self.norm_out.bias,\n self.out.weight,\n self.out.bias,\n self.gate_out.weight,\n self.gate_out.bias,\n 1e-5\n )\n\n\nclass ModelNew(nn.Module):\n def __init__(self, msa_dim, pair_dim, num_heads, tri_hidden):\n super().__init__()\n self.msa_attn = MSARowSelfAttention(msa_dim, num_heads)\n self.opm = OuterProductMeanSmall(msa_dim, pair_dim)\n self.tri_mul = TriMulOut(pair_dim, tri_hidden)\n\n def forward(\n self, msa_repr: torch.Tensor, pair_repr: torch.Tensor\n ) -> tuple:\n msa_repr = msa_repr + self.msa_attn(msa_repr)\n pair_repr = pair_repr + self.opm(msa_repr)\n pair_repr = pair_repr + self.tri_mul(pair_repr)\n return msa_repr, pair_repr" }
result ← static_check (ok)
okstatic_check static_check PASSED (with warnings): WARNING: Uses torch.nn compute layer (only containers, Parameter, init allowed)
turn 14 · FINAL · 1 tool call(s) · 99.8s turn · 39.9s LLM · tokens in=54,615 · out=4,919 · total=59,534 · 21,192 chars (click to expand)
turn 1499.8s turn · 39.9s LLM · 59.9s tools · tokens in=54,615 · out=4,919 · total=59,534 · FINAL
reasoning
The kernel has passed both correctness and static checks. I should submit it now.
call → submit_kernel
callsubmit_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport math\nfrom torch.utils.cpp_extension import load_inline\n\ncpp_source = r\"\"\"\n#include <torch/extension.h>\n#include <vector>\n\nstd::vector<torch::Tensor> msa_row_attn_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps);\n\ntorch::Tensor outer_product_mean_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps);\n\ntorch::Tensor tri_mul_out_cuda(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps);\n\nstd::vector<torch::Tensor> msa_row_attn(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps) {\n return msa_row_attn_cuda(msa, ln_weight, ln_bias, q_weight, k_weight, v_weight, out_weight, out_bias, num_heads, eps);\n}\n\ntorch::Tensor outer_product_mean(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps) {\n return outer_product_mean_cuda(msa, ln_weight, ln_bias, a_weight, a_bias, b_weight, b_bias, out_weight, out_bias, eps);\n}\n\ntorch::Tensor tri_mul_out(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps) {\n return tri_mul_out_cuda(pair, ln_weight, ln_bias, pa_weight, pa_bias, pb_weight, pb_bias, \n ga_weight, ga_bias, gb_weight, gb_bias, norm_out_weight, norm_out_bias,\n out_weight, out_bias, gate_weight, gate_bias, eps);\n}\n\"\"\"\n\ncuda_source = r\"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\nconstexpr int BLOCK_SIZE = 256;\n\n__global__ void layernorm_multi_linear_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n float* __restrict__ q_out,\n float* __restrict__ k_out,\n float* __restrict__ v_out,\n const float* __restrict__ q_weight,\n const float* __restrict__ k_weight,\n const float* __restrict__ v_weight,\n int num_groups,\n int in_features,\n int out_features,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int start = idx * in_features;\n int out_start = idx * out_features;\n \n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[start + i];\n }\n mean /= static_cast<float>(in_features);\n \n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n for (int o = 0; o < out_features; o++) {\n float q_sum = 0.0f, k_sum = 0.0f, v_sum = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float normalized = (input[start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n q_sum += normalized * q_weight[o * in_features + i];\n k_sum += normalized * k_weight[o * in_features + i];\n v_sum += normalized * v_weight[o * in_features + i];\n }\n q_out[out_start + o] = q_sum;\n k_out[out_start + o] = k_sum;\n v_out[out_start + o] = v_sum;\n }\n }\n}\n\n__global__ void output_proj_kernel(\n const float* __restrict__ input,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n float* __restrict__ output,\n int num_groups,\n int in_features,\n int out_features) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * out_features;\n \n for (int o = 0; o < out_features; o++) {\n float sum = bias[o];\n for (int i = 0; i < in_features; i++) {\n sum += input[in_start + i] * weight[o * in_features + i];\n }\n output[out_start + o] = sum;\n }\n }\n}\n\nstd::vector<torch::Tensor> msa_row_attn_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n double eps) {\n \n TORCH_CHECK(msa.is_cuda(), \"MSA must be a CUDA tensor\");\n \n auto sizes = msa.sizes();\n int64_t B = sizes[0], S = sizes[1], N = sizes[2], D = sizes[3];\n int64_t d = D / num_heads;\n int64_t h = num_heads;\n \n int64_t num_groups = B * S * N;\n \n auto q_out = torch::empty({B, S, N, D}, msa.options());\n auto k_out = torch::empty({B, S, N, D}, msa.options());\n auto v_out = torch::empty({B, S, N, D}, msa.options());\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n layernorm_multi_linear_kernel<<<blocks, BLOCK_SIZE>>>(\n msa.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n q_out.data_ptr<float>(),\n k_out.data_ptr<float>(),\n v_out.data_ptr<float>(),\n q_weight.data_ptr<float>(),\n k_weight.data_ptr<float>(),\n v_weight.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(D),\n static_cast<float>(eps));\n \n auto q_reshaped = q_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n auto k_reshaped = k_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n auto v_reshaped = v_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();\n \n auto attn = torch::matmul(q_reshaped, k_reshaped.transpose(-1, -2)) / sqrtf(static_cast<float>(d));\n attn = torch::softmax(attn, -1);\n \n auto attn_out = torch::matmul(attn, v_reshaped);\n \n auto attn_out_reshaped = attn_out.permute({0, 1, 3, 2, 4}).contiguous().view({B, S, N, D});\n \n auto final_out = torch::empty({B, S, N, D}, msa.options());\n blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n output_proj_kernel<<<blocks, BLOCK_SIZE>>>(\n attn_out_reshaped.data_ptr<float>(),\n out_weight.data_ptr<float>(),\n out_bias.data_ptr<float>(),\n final_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(D));\n \n return {final_out, q_out, k_out, v_out};\n}\n\n__global__ void outer_product_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n const float* __restrict__ a_weight,\n const float* __restrict__ a_bias,\n const float* __restrict__ b_weight,\n const float* __restrict__ b_bias,\n float* __restrict__ a_out,\n float* __restrict__ b_out,\n int num_groups,\n int in_features,\n int hidden,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * hidden;\n \n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[in_start + i];\n }\n mean /= static_cast<float>(in_features);\n \n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[in_start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n for (int o = 0; o < hidden; o++) {\n float a_sum = a_bias[o];\n float b_sum = b_bias[o];\n for (int i = 0; i < in_features; i++) {\n float normalized = (input[in_start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n a_sum += normalized * a_weight[o * in_features + i];\n b_sum += normalized * b_weight[o * in_features + i];\n }\n a_out[out_start + o] = a_sum;\n b_out[out_start + o] = b_sum;\n }\n }\n}\n\ntorch::Tensor outer_product_mean_cuda(\n torch::Tensor msa,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor a_weight,\n torch::Tensor a_bias,\n torch::Tensor b_weight,\n torch::Tensor b_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n double eps) {\n \n TORCH_CHECK(msa.is_cuda(), \"MSA must be a CUDA tensor\");\n \n auto sizes = msa.sizes();\n int64_t B = sizes[0], S = sizes[1], N = sizes[2], D = sizes[3];\n int64_t hidden = a_weight.size(0);\n \n int64_t num_groups = B * S * N;\n \n auto a_out = torch::empty({B, S, N, hidden}, msa.options());\n auto b_out = torch::empty({B, S, N, hidden}, msa.options());\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n outer_product_kernel<<<blocks, BLOCK_SIZE>>>(\n msa.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n a_weight.data_ptr<float>(),\n a_bias.data_ptr<float>(),\n b_weight.data_ptr<float>(),\n b_bias.data_ptr<float>(),\n a_out.data_ptr<float>(),\n b_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(hidden),\n static_cast<float>(eps));\n \n auto outer = torch::einsum(\"bsih,bsjk->bijhk\", {a_out, b_out});\n outer = outer / static_cast<float>(S);\n \n int64_t N1 = outer.size(1), N2 = outer.size(2), H1 = outer.size(3), H2 = outer.size(4);\n auto outer_flat = outer.reshape({B, N1, N2, H1 * H2});\n \n return torch::linear(outer_flat, out_weight, out_bias);\n}\n\n__global__ void tri_mul_layernorm_linear_kernel(\n const float* __restrict__ input,\n const float* __restrict__ ln_weight,\n const float* __restrict__ ln_bias,\n const float* __restrict__ pa_weight,\n const float* __restrict__ pa_bias,\n const float* __restrict__ pb_weight,\n const float* __restrict__ pb_bias,\n const float* __restrict__ ga_weight,\n const float* __restrict__ ga_bias,\n const float* __restrict__ gb_weight,\n const float* __restrict__ gb_bias,\n float* __restrict__ pa_out,\n float* __restrict__ pb_out,\n int num_groups,\n int in_features,\n int hidden,\n float eps) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (idx < num_groups) {\n int in_start = idx * in_features;\n int out_start = idx * hidden;\n \n float mean = 0.0f;\n for (int i = 0; i < in_features; i++) {\n mean += input[in_start + i];\n }\n mean /= static_cast<float>(in_features);\n \n float var = 0.0f;\n for (int i = 0; i < in_features; i++) {\n float diff = input[in_start + i] - mean;\n var += diff * diff;\n }\n var /= static_cast<float>(in_features);\n \n float inv_std = rsqrtf(var + eps);\n \n for (int o = 0; o < hidden; o++) {\n float pa_sum = pa_bias[o];\n float pb_sum = pb_bias[o];\n float ga_sum = ga_bias[o];\n float gb_sum = gb_bias[o];\n \n for (int i = 0; i < in_features; i++) {\n float normalized = (input[in_start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];\n pa_sum += normalized * pa_weight[o * in_features + i];\n pb_sum += normalized * pb_weight[o * in_features + i];\n ga_sum += normalized * ga_weight[o * in_features + i];\n gb_sum += normalized * gb_weight[o * in_features + i];\n }\n \n pa_out[out_start + o] = pa_sum * (1.0f / (1.0f + expf(-ga_sum)));\n pb_out[out_start + o] = pb_sum * (1.0f / (1.0f + expf(-gb_sum)));\n }\n }\n}\n\ntorch::Tensor tri_mul_out_cuda(\n torch::Tensor pair,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor pa_weight,\n torch::Tensor pa_bias,\n torch::Tensor pb_weight,\n torch::Tensor pb_bias,\n torch::Tensor ga_weight,\n torch::Tensor ga_bias,\n torch::Tensor gb_weight,\n torch::Tensor gb_bias,\n torch::Tensor norm_out_weight,\n torch::Tensor norm_out_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias,\n double eps) {\n \n TORCH_CHECK(pair.is_cuda(), \"Pair must be a CUDA tensor\");\n \n auto sizes = pair.sizes();\n int64_t B = sizes[0], N1 = sizes[1], N2 = sizes[2], D = sizes[3];\n int64_t hidden = pa_weight.size(0);\n \n int64_t num_groups = B * N1 * N2;\n \n auto pa_out = torch::empty({B, N1, N2, hidden}, pair.options());\n auto pb_out = torch::empty({B, N1, N2, hidden}, pair.options());\n \n int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;\n \n tri_mul_layernorm_linear_kernel<<<blocks, BLOCK_SIZE>>>(\n pair.data_ptr<float>(),\n ln_weight.data_ptr<float>(),\n ln_bias.data_ptr<float>(),\n pa_weight.data_ptr<float>(),\n pa_bias.data_ptr<float>(),\n pb_weight.data_ptr<float>(),\n pb_bias.data_ptr<float>(),\n ga_weight.data_ptr<float>(),\n ga_bias.data_ptr<float>(),\n gb_weight.data_ptr<float>(),\n gb_bias.data_ptr<float>(),\n pa_out.data_ptr<float>(),\n pb_out.data_ptr<float>(),\n static_cast<int>(num_groups),\n static_cast<int>(D),\n static_cast<int>(hidden),\n static_cast<float>(eps));\n \n auto o = torch::einsum(\"bikh,bjkh->bijh\", {pa_out, pb_out});\n o = torch::layer_norm(o, {hidden}, norm_out_weight, norm_out_bias, eps);\n o = torch::linear(o, out_weight, out_bias);\n auto gate = torch::sigmoid(torch::linear(pair, gate_weight, gate_bias));\n \n return gate * o;\n}\n\"\"\"\n\nevoformer_ext = load_inline(\n name='evoformer_ext',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['msa_row_attn', 'outer_product_mean', 'tri_mul_out'],\n verbose=True\n)\n\n\nclass MSARowSelfAttention(nn.Module):\n def __init__(self, msa_dim, num_heads):\n super().__init__()\n self.num_heads = num_heads\n self.head_dim = msa_dim // num_heads\n self.norm = nn.LayerNorm(msa_dim)\n self.q = nn.Linear(msa_dim, msa_dim, bias=False)\n self.k = nn.Linear(msa_dim, msa_dim, bias=False)\n self.v = nn.Linear(msa_dim, msa_dim, bias=False)\n self.out = nn.Linear(msa_dim, msa_dim)\n\n def forward(self, msa):\n B, S, N, D = msa.shape\n \n results = evoformer_ext.msa_row_attn(\n msa.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.q.weight,\n self.k.weight,\n self.v.weight,\n self.out.weight,\n self.out.bias,\n self.num_heads,\n 1e-5\n )\n return results[0]\n\n\nclass OuterProductMeanSmall(nn.Module):\n def __init__(self, msa_dim, pair_dim, hidden=32):\n super().__init__()\n self.norm = nn.LayerNorm(msa_dim)\n self.a = nn.Linear(msa_dim, hidden)\n self.b = nn.Linear(msa_dim, hidden)\n self.out = nn.Linear(hidden * hidden, pair_dim)\n\n def forward(self, msa):\n return evoformer_ext.outer_product_mean(\n msa.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.a.weight,\n self.a.bias,\n self.b.weight,\n self.b.bias,\n self.out.weight,\n self.out.bias,\n 1e-5\n )\n\n\nclass TriMulOut(nn.Module):\n def __init__(self, pair_dim, hidden):\n super().__init__()\n self.norm = nn.LayerNorm(pair_dim)\n self.pa = nn.Linear(pair_dim, hidden)\n self.pb = nn.Linear(pair_dim, hidden)\n self.ga = nn.Linear(pair_dim, hidden)\n self.gb = nn.Linear(pair_dim, hidden)\n self.norm_out = nn.LayerNorm(hidden)\n self.out = nn.Linear(hidden, pair_dim)\n self.gate_out = nn.Linear(pair_dim, pair_dim)\n\n def forward(self, pair):\n return evoformer_ext.tri_mul_out(\n pair.contiguous(),\n self.norm.weight,\n self.norm.bias,\n self.pa.weight,\n self.pa.bias,\n self.pb.weight,\n self.pb.bias,\n self.ga.weight,\n self.ga.bias,\n self.gb.weight,\n self.gb.bias,\n self.norm_out.weight,\n self.norm_out.bias,\n self.out.weight,\n self.out.bias,\n self.gate_out.weight,\n self.gate_out.bias,\n 1e-5\n )\n\n\nclass ModelNew(nn.Module):\n def __init__(self, msa_dim, pair_dim, num_heads, tri_hidden):\n super().__init__()\n self.msa_attn = MSARowSelfAttention(msa_dim, num_heads)\n self.opm = OuterProductMeanSmall(msa_dim, pair_dim)\n self.tri_mul = TriMulOut(pair_dim, tri_hidden)\n\n def forward(\n self, msa_repr: torch.Tensor, pair_repr: torch.Tensor\n ) -> tuple:\n msa_repr = msa_repr + self.msa_attn(msa_repr)\n pair_repr = pair_repr + self.opm(msa_repr)\n pair_repr = pair_repr + self.tri_mul(pair_repr)\n return msa_repr, pair_repr" }
result ← submit_kernel (ok)
oksubmit_kernel submit_kernel PASSED: (3 / 3) correctness trials all passed. Kernel runtime: 2.41 μs Runtime stats: mean=2.41μs median=?μs std=0.00μs Numerical precision: max_abs_err=8.34e-07 mean_abs_err=7.33e-08 max_rel_err=1.50e+00 Memory: 51.16 MB peak (ref: 51.28 MB, ratio: 0.9976x) Kernel launches: 170 (ref: 258, fusion ratio: 1.5176) Energy: 482.54 mJ/run (ref: 0.0 mJ, ratio: -1x) SOL score: 1.0
final submitted kernel (589 lines, 18,734 chars) — full source, untruncated
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
from torch.utils.cpp_extension import load_inline

cpp_source = r"""
#include <torch/extension.h>
#include <vector>

std::vector<torch::Tensor> msa_row_attn_cuda(
    torch::Tensor msa,
    torch::Tensor ln_weight,
    torch::Tensor ln_bias,
    torch::Tensor q_weight,
    torch::Tensor k_weight,
    torch::Tensor v_weight,
    torch::Tensor out_weight,
    torch::Tensor out_bias,
    int num_heads,
    double eps);

torch::Tensor outer_product_mean_cuda(
    torch::Tensor msa,
    torch::Tensor ln_weight,
    torch::Tensor ln_bias,
    torch::Tensor a_weight,
    torch::Tensor a_bias,
    torch::Tensor b_weight,
    torch::Tensor b_bias,
    torch::Tensor out_weight,
    torch::Tensor out_bias,
    double eps);

torch::Tensor tri_mul_out_cuda(
    torch::Tensor pair,
    torch::Tensor ln_weight,
    torch::Tensor ln_bias,
    torch::Tensor pa_weight,
    torch::Tensor pa_bias,
    torch::Tensor pb_weight,
    torch::Tensor pb_bias,
    torch::Tensor ga_weight,
    torch::Tensor ga_bias,
    torch::Tensor gb_weight,
    torch::Tensor gb_bias,
    torch::Tensor norm_out_weight,
    torch::Tensor norm_out_bias,
    torch::Tensor out_weight,
    torch::Tensor out_bias,
    torch::Tensor gate_weight,
    torch::Tensor gate_bias,
    double eps);

std::vector<torch::Tensor> msa_row_attn(
    torch::Tensor msa,
    torch::Tensor ln_weight,
    torch::Tensor ln_bias,
    torch::Tensor q_weight,
    torch::Tensor k_weight,
    torch::Tensor v_weight,
    torch::Tensor out_weight,
    torch::Tensor out_bias,
    int num_heads,
    double eps) {
    return msa_row_attn_cuda(msa, ln_weight, ln_bias, q_weight, k_weight, v_weight, out_weight, out_bias, num_heads, eps);
}

torch::Tensor outer_product_mean(
    torch::Tensor msa,
    torch::Tensor ln_weight,
    torch::Tensor ln_bias,
    torch::Tensor a_weight,
    torch::Tensor a_bias,
    torch::Tensor b_weight,
    torch::Tensor b_bias,
    torch::Tensor out_weight,
    torch::Tensor out_bias,
    double eps) {
    return outer_product_mean_cuda(msa, ln_weight, ln_bias, a_weight, a_bias, b_weight, b_bias, out_weight, out_bias, eps);
}

torch::Tensor tri_mul_out(
    torch::Tensor pair,
    torch::Tensor ln_weight,
    torch::Tensor ln_bias,
    torch::Tensor pa_weight,
    torch::Tensor pa_bias,
    torch::Tensor pb_weight,
    torch::Tensor pb_bias,
    torch::Tensor ga_weight,
    torch::Tensor ga_bias,
    torch::Tensor gb_weight,
    torch::Tensor gb_bias,
    torch::Tensor norm_out_weight,
    torch::Tensor norm_out_bias,
    torch::Tensor out_weight,
    torch::Tensor out_bias,
    torch::Tensor gate_weight,
    torch::Tensor gate_bias,
    double eps) {
    return tri_mul_out_cuda(pair, ln_weight, ln_bias, pa_weight, pa_bias, pb_weight, pb_bias, 
                           ga_weight, ga_bias, gb_weight, gb_bias, norm_out_weight, norm_out_bias,
                           out_weight, out_bias, gate_weight, gate_bias, eps);
}
"""

cuda_source = r"""
#include <torch/extension.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <cmath>

constexpr int BLOCK_SIZE = 256;

__global__ void layernorm_multi_linear_kernel(
    const float* __restrict__ input,
    const float* __restrict__ ln_weight,
    const float* __restrict__ ln_bias,
    float* __restrict__ q_out,
    float* __restrict__ k_out,
    float* __restrict__ v_out,
    const float* __restrict__ q_weight,
    const float* __restrict__ k_weight,
    const float* __restrict__ v_weight,
    int num_groups,
    int in_features,
    int out_features,
    float eps) {
    
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    
    if (idx < num_groups) {
        int start = idx * in_features;
        int out_start = idx * out_features;
        
        float mean = 0.0f;
        for (int i = 0; i < in_features; i++) {
            mean += input[start + i];
        }
        mean /= static_cast<float>(in_features);
        
        float var = 0.0f;
        for (int i = 0; i < in_features; i++) {
            float diff = input[start + i] - mean;
            var += diff * diff;
        }
        var /= static_cast<float>(in_features);
        
        float inv_std = rsqrtf(var + eps);
        
        for (int o = 0; o < out_features; o++) {
            float q_sum = 0.0f, k_sum = 0.0f, v_sum = 0.0f;
            for (int i = 0; i < in_features; i++) {
                float normalized = (input[start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];
                q_sum += normalized * q_weight[o * in_features + i];
                k_sum += normalized * k_weight[o * in_features + i];
                v_sum += normalized * v_weight[o * in_features + i];
            }
            q_out[out_start + o] = q_sum;
            k_out[out_start + o] = k_sum;
            v_out[out_start + o] = v_sum;
        }
    }
}

__global__ void output_proj_kernel(
    const float* __restrict__ input,
    const float* __restrict__ weight,
    const float* __restrict__ bias,
    float* __restrict__ output,
    int num_groups,
    int in_features,
    int out_features) {
    
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    
    if (idx < num_groups) {
        int in_start = idx * in_features;
        int out_start = idx * out_features;
        
        for (int o = 0; o < out_features; o++) {
            float sum = bias[o];
            for (int i = 0; i < in_features; i++) {
                sum += input[in_start + i] * weight[o * in_features + i];
            }
            output[out_start + o] = sum;
        }
    }
}

std::vector<torch::Tensor> msa_row_attn_cuda(
    torch::Tensor msa,
    torch::Tensor ln_weight,
    torch::Tensor ln_bias,
    torch::Tensor q_weight,
    torch::Tensor k_weight,
    torch::Tensor v_weight,
    torch::Tensor out_weight,
    torch::Tensor out_bias,
    int num_heads,
    double eps) {
    
    TORCH_CHECK(msa.is_cuda(), "MSA must be a CUDA tensor");
    
    auto sizes = msa.sizes();
    int64_t B = sizes[0], S = sizes[1], N = sizes[2], D = sizes[3];
    int64_t d = D / num_heads;
    int64_t h = num_heads;
    
    int64_t num_groups = B * S * N;
    
    auto q_out = torch::empty({B, S, N, D}, msa.options());
    auto k_out = torch::empty({B, S, N, D}, msa.options());
    auto v_out = torch::empty({B, S, N, D}, msa.options());
    
    int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;
    
    layernorm_multi_linear_kernel<<<blocks, BLOCK_SIZE>>>(
        msa.data_ptr<float>(),
        ln_weight.data_ptr<float>(),
        ln_bias.data_ptr<float>(),
        q_out.data_ptr<float>(),
        k_out.data_ptr<float>(),
        v_out.data_ptr<float>(),
        q_weight.data_ptr<float>(),
        k_weight.data_ptr<float>(),
        v_weight.data_ptr<float>(),
        static_cast<int>(num_groups),
        static_cast<int>(D),
        static_cast<int>(D),
        static_cast<float>(eps));
    
    auto q_reshaped = q_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();
    auto k_reshaped = k_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();
    auto v_reshaped = v_out.view({B, S, N, h, d}).permute({0, 1, 3, 2, 4}).contiguous();
    
    auto attn = torch::matmul(q_reshaped, k_reshaped.transpose(-1, -2)) / sqrtf(static_cast<float>(d));
    attn = torch::softmax(attn, -1);
    
    auto attn_out = torch::matmul(attn, v_reshaped);
    
    auto attn_out_reshaped = attn_out.permute({0, 1, 3, 2, 4}).contiguous().view({B, S, N, D});
    
    auto final_out = torch::empty({B, S, N, D}, msa.options());
    blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;
    
    output_proj_kernel<<<blocks, BLOCK_SIZE>>>(
        attn_out_reshaped.data_ptr<float>(),
        out_weight.data_ptr<float>(),
        out_bias.data_ptr<float>(),
        final_out.data_ptr<float>(),
        static_cast<int>(num_groups),
        static_cast<int>(D),
        static_cast<int>(D));
    
    return {final_out, q_out, k_out, v_out};
}

__global__ void outer_product_kernel(
    const float* __restrict__ input,
    const float* __restrict__ ln_weight,
    const float* __restrict__ ln_bias,
    const float* __restrict__ a_weight,
    const float* __restrict__ a_bias,
    const float* __restrict__ b_weight,
    const float* __restrict__ b_bias,
    float* __restrict__ a_out,
    float* __restrict__ b_out,
    int num_groups,
    int in_features,
    int hidden,
    float eps) {
    
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    
    if (idx < num_groups) {
        int in_start = idx * in_features;
        int out_start = idx * hidden;
        
        float mean = 0.0f;
        for (int i = 0; i < in_features; i++) {
            mean += input[in_start + i];
        }
        mean /= static_cast<float>(in_features);
        
        float var = 0.0f;
        for (int i = 0; i < in_features; i++) {
            float diff = input[in_start + i] - mean;
            var += diff * diff;
        }
        var /= static_cast<float>(in_features);
        
        float inv_std = rsqrtf(var + eps);
        
        for (int o = 0; o < hidden; o++) {
            float a_sum = a_bias[o];
            float b_sum = b_bias[o];
            for (int i = 0; i < in_features; i++) {
                float normalized = (input[in_start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];
                a_sum += normalized * a_weight[o * in_features + i];
                b_sum += normalized * b_weight[o * in_features + i];
            }
            a_out[out_start + o] = a_sum;
            b_out[out_start + o] = b_sum;
        }
    }
}

torch::Tensor outer_product_mean_cuda(
    torch::Tensor msa,
    torch::Tensor ln_weight,
    torch::Tensor ln_bias,
    torch::Tensor a_weight,
    torch::Tensor a_bias,
    torch::Tensor b_weight,
    torch::Tensor b_bias,
    torch::Tensor out_weight,
    torch::Tensor out_bias,
    double eps) {
    
    TORCH_CHECK(msa.is_cuda(), "MSA must be a CUDA tensor");
    
    auto sizes = msa.sizes();
    int64_t B = sizes[0], S = sizes[1], N = sizes[2], D = sizes[3];
    int64_t hidden = a_weight.size(0);
    
    int64_t num_groups = B * S * N;
    
    auto a_out = torch::empty({B, S, N, hidden}, msa.options());
    auto b_out = torch::empty({B, S, N, hidden}, msa.options());
    
    int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;
    
    outer_product_kernel<<<blocks, BLOCK_SIZE>>>(
        msa.data_ptr<float>(),
        ln_weight.data_ptr<float>(),
        ln_bias.data_ptr<float>(),
        a_weight.data_ptr<float>(),
        a_bias.data_ptr<float>(),
        b_weight.data_ptr<float>(),
        b_bias.data_ptr<float>(),
        a_out.data_ptr<float>(),
        b_out.data_ptr<float>(),
        static_cast<int>(num_groups),
        static_cast<int>(D),
        static_cast<int>(hidden),
        static_cast<float>(eps));
    
    auto outer = torch::einsum("bsih,bsjk->bijhk", {a_out, b_out});
    outer = outer / static_cast<float>(S);
    
    int64_t N1 = outer.size(1), N2 = outer.size(2), H1 = outer.size(3), H2 = outer.size(4);
    auto outer_flat = outer.reshape({B, N1, N2, H1 * H2});
    
    return torch::linear(outer_flat, out_weight, out_bias);
}

__global__ void tri_mul_layernorm_linear_kernel(
    const float* __restrict__ input,
    const float* __restrict__ ln_weight,
    const float* __restrict__ ln_bias,
    const float* __restrict__ pa_weight,
    const float* __restrict__ pa_bias,
    const float* __restrict__ pb_weight,
    const float* __restrict__ pb_bias,
    const float* __restrict__ ga_weight,
    const float* __restrict__ ga_bias,
    const float* __restrict__ gb_weight,
    const float* __restrict__ gb_bias,
    float* __restrict__ pa_out,
    float* __restrict__ pb_out,
    int num_groups,
    int in_features,
    int hidden,
    float eps) {
    
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    
    if (idx < num_groups) {
        int in_start = idx * in_features;
        int out_start = idx * hidden;
        
        float mean = 0.0f;
        for (int i = 0; i < in_features; i++) {
            mean += input[in_start + i];
        }
        mean /= static_cast<float>(in_features);
        
        float var = 0.0f;
        for (int i = 0; i < in_features; i++) {
            float diff = input[in_start + i] - mean;
            var += diff * diff;
        }
        var /= static_cast<float>(in_features);
        
        float inv_std = rsqrtf(var + eps);
        
        for (int o = 0; o < hidden; o++) {
            float pa_sum = pa_bias[o];
            float pb_sum = pb_bias[o];
            float ga_sum = ga_bias[o];
            float gb_sum = gb_bias[o];
            
            for (int i = 0; i < in_features; i++) {
                float normalized = (input[in_start + i] - mean) * inv_std * ln_weight[i] + ln_bias[i];
                pa_sum += normalized * pa_weight[o * in_features + i];
                pb_sum += normalized * pb_weight[o * in_features + i];
                ga_sum += normalized * ga_weight[o * in_features + i];
                gb_sum += normalized * gb_weight[o * in_features + i];
            }
            
            pa_out[out_start + o] = pa_sum * (1.0f / (1.0f + expf(-ga_sum)));
            pb_out[out_start + o] = pb_sum * (1.0f / (1.0f + expf(-gb_sum)));
        }
    }
}

torch::Tensor tri_mul_out_cuda(
    torch::Tensor pair,
    torch::Tensor ln_weight,
    torch::Tensor ln_bias,
    torch::Tensor pa_weight,
    torch::Tensor pa_bias,
    torch::Tensor pb_weight,
    torch::Tensor pb_bias,
    torch::Tensor ga_weight,
    torch::Tensor ga_bias,
    torch::Tensor gb_weight,
    torch::Tensor gb_bias,
    torch::Tensor norm_out_weight,
    torch::Tensor norm_out_bias,
    torch::Tensor out_weight,
    torch::Tensor out_bias,
    torch::Tensor gate_weight,
    torch::Tensor gate_bias,
    double eps) {
    
    TORCH_CHECK(pair.is_cuda(), "Pair must be a CUDA tensor");
    
    auto sizes = pair.sizes();
    int64_t B = sizes[0], N1 = sizes[1], N2 = sizes[2], D = sizes[3];
    int64_t hidden = pa_weight.size(0);
    
    int64_t num_groups = B * N1 * N2;
    
    auto pa_out = torch::empty({B, N1, N2, hidden}, pair.options());
    auto pb_out = torch::empty({B, N1, N2, hidden}, pair.options());
    
    int blocks = (static_cast<int>(num_groups) + BLOCK_SIZE - 1) / BLOCK_SIZE;
    
    tri_mul_layernorm_linear_kernel<<<blocks, BLOCK_SIZE>>>(
        pair.data_ptr<float>(),
        ln_weight.data_ptr<float>(),
        ln_bias.data_ptr<float>(),
        pa_weight.data_ptr<float>(),
        pa_bias.data_ptr<float>(),
        pb_weight.data_ptr<float>(),
        pb_bias.data_ptr<float>(),
        ga_weight.data_ptr<float>(),
        ga_bias.data_ptr<float>(),
        gb_weight.data_ptr<float>(),
        gb_bias.data_ptr<float>(),
        pa_out.data_ptr<float>(),
        pb_out.data_ptr<float>(),
        static_cast<int>(num_groups),
        static_cast<int>(D),
        static_cast<int>(hidden),
        static_cast<float>(eps));
    
    auto o = torch::einsum("bikh,bjkh->bijh", {pa_out, pb_out});
    o = torch::layer_norm(o, {hidden}, norm_out_weight, norm_out_bias, eps);
    o = torch::linear(o, out_weight, out_bias);
    auto gate = torch::sigmoid(torch::linear(pair, gate_weight, gate_bias));
    
    return gate * o;
}
"""

evoformer_ext = load_inline(
    name='evoformer_ext',
    cpp_sources=cpp_source,
    cuda_sources=cuda_source,
    functions=['msa_row_attn', 'outer_product_mean', 'tri_mul_out'],
    verbose=True
)


class MSARowSelfAttention(nn.Module):
    def __init__(self, msa_dim, num_heads):
        super().__init__()
        self.num_heads = num_heads
        self.head_dim = msa_dim // num_heads
        self.norm = nn.LayerNorm(msa_dim)
        self.q = nn.Linear(msa_dim, msa_dim, bias=False)
        self.k = nn.Linear(msa_dim, msa_dim, bias=False)
        self.v = nn.Linear(msa_dim, msa_dim, bias=False)
        self.out = nn.Linear(msa_dim, msa_dim)

    def forward(self, msa):
        B, S, N, D = msa.shape
        
        results = evoformer_ext.msa_row_attn(
            msa.contiguous(),
            self.norm.weight,
            self.norm.bias,
            self.q.weight,
            self.k.weight,
            self.v.weight,
            self.out.weight,
            self.out.bias,
            self.num_heads,
            1e-5
        )
        return results[0]


class OuterProductMeanSmall(nn.Module):
    def __init__(self, msa_dim, pair_dim, hidden=32):
        super().__init__()
        self.norm = nn.LayerNorm(msa_dim)
        self.a = nn.Linear(msa_dim, hidden)
        self.b = nn.Linear(msa_dim, hidden)
        self.out = nn.Linear(hidden * hidden, pair_dim)

    def forward(self, msa):
        return evoformer_ext.outer_product_mean(
            msa.contiguous(),
            self.norm.weight,
            self.norm.bias,
            self.a.weight,
            self.a.bias,
            self.b.weight,
            self.b.bias,
            self.out.weight,
            self.out.bias,
            1e-5
        )


class TriMulOut(nn.Module):
    def __init__(self, pair_dim, hidden):
        super().__init__()
        self.norm = nn.LayerNorm(pair_dim)
        self.pa = nn.Linear(pair_dim, hidden)
        self.pb = nn.Linear(pair_dim, hidden)
        self.ga = nn.Linear(pair_dim, hidden)
        self.gb = nn.Linear(pair_dim, hidden)
        self.norm_out = nn.LayerNorm(hidden)
        self.out = nn.Linear(hidden, pair_dim)
        self.gate_out = nn.Linear(pair_dim, pair_dim)

    def forward(self, pair):
        return evoformer_ext.tri_mul_out(
            pair.contiguous(),
            self.norm.weight,
            self.norm.bias,
            self.pa.weight,
            self.pa.bias,
            self.pb.weight,
            self.pb.bias,
            self.ga.weight,
            self.ga.bias,
            self.gb.weight,
            self.gb.bias,
            self.norm_out.weight,
            self.norm_out.bias,
            self.out.weight,
            self.out.bias,
            self.gate_out.weight,
            self.gate_out.bias,
            1e-5
        )


class ModelNew(nn.Module):
    def __init__(self, msa_dim, pair_dim, num_heads, tri_hidden):
        super().__init__()
        self.msa_attn = MSARowSelfAttention(msa_dim, num_heads)
        self.opm = OuterProductMeanSmall(msa_dim, pair_dim)
        self.tri_mul = TriMulOut(pair_dim, tri_hidden)

    def forward(
        self, msa_repr: torch.Tensor, pair_repr: torch.Tensor
    ) -> tuple:
        msa_repr = msa_repr + self.msa_attn(msa_repr)
        pair_repr = pair_repr + self.opm(msa_repr)
        pair_repr = pair_repr + self.tri_mul(pair_repr)
        return msa_repr, pair_repr