turn 0666.0s turn · 125.4s LLM · 540.6s tools · tokens in=1,939 · out=13,114 · total=15,053 · FINAL · reasoning clipped
reasoning
reasoning (truncated 15687 chars): 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 has three main components:
1. **MSARowSelfAttention**: Multi-head self-attention over MSA rows
2. **OuterProductMeanSmall**: Computes outer product mean from MSA to update pair representation
3. **TriMulOut**: Triangular multiplicative update on pair representation
Let me trace through the shapes and operations:
**MSARowSelfAttention**:
- Input: msa (B, S, N, D) where D = msa_dim = 64
- num_heads = 4, head_dim = 64/4 = 16
- LayerNorm, then Q, K, V projections
- Q, K, V shapes: (B, S, N, h, d) -> permute to (B, S, h, N, d)
- Attention: (B, S, h, N, d) @ (B, S, h, d, N) = (B, S, h, N, N)
- Softmax on last dim
- Output: (B, S, h, N, N) @ (B, S, h, N, d) = (B, S, h, N, d)
- Reshape back to (B, S, N, D) and output projection
**OuterProductMeanSmall**:
- Input: msa (B, S, N, D)
- hidden = 32
- a = (B, S, N, hidden), b = (B, S, N, hidden)
- outer = einsum("bsih,bsjk->bijhk", a, b) = (B, N, N, hidden, hidden)
- Divide by S (msa_depth)
- Output projection from hidden*hidden to pair_dim
**TriMulOut**:
- Input: pair (B, N, N, pair_dim)
- hidden = 32
- a = pa(x) * sigmoid(ga(x)), b = pb(x) * sigmoid(gb(x))
- Both a, b: (B, N, N, hidden)
- einsum("bikh,bjkh->bijh", a, b) = (B, N, N, hidden)
- Output projection with gating
The complexity lies in fusing multiple operations across different attention mechanisms. A full custom CUDA kernel would need to handle LayerNorm, matrix multiplications, softmax, einsum operations, and gating all within a single kernel. This is quite complex. I should start with a straightforward implementation and optimize iteratively.
Looking at the tensor shapes more carefully: msa has shape (B=2, S=8, N=32, D=64) and pair has (B=2, N=32, N=32, D=64). The attention computation involves computing Q, K, V each as (B, S, N, D) matrices, then reshaping to (B, S, h, N, d) with h=4 and d=16. The attention scores come from Q @ K.T with scaling, followed by softmax normalization.
After applying softmax, the result attends to V and gets reshaped back to (B, S, N, D) for the output projection. The outer product mean involves computing a and b vectors of shape (B, S, N, 32), then performing an einsum operation that produces an outer product of shape (B, N, N, 32, 32), which gets divided by S=8. The triangular multiplication uses einsum to compute interactions across the N dimension.
The real bottleneck emerges from the three sequential modules: MSARowSelfAttention with its matmuls and softmax, OuterProductMeanSmall requiring einsum on small tensors, and TriMulOut with its own einsum and gating. Memory allocation for all these operations accumulates, and intermediate tensor handling across modules compounds the overhead. Rather than pursuing an overly aggressive single-kernel fusion approach, I should prioritize kernel fusion for the attention mechanism specifically, where the Q, K, V projections and attention computation can be combined, and fuse the LayerNorm with its following linear projections. For smaller tensor operations, optimized matmul calls might prove more efficient than custom kernels.
A hybrid strategy makes sense: fuse the attention computation itself, optimize the einsum operations that represent outer products, and potentially restructure the outer product computation to leverage matmuls more effectively. Let me work through the mathematical transformations—like converting the einsum pattern in OuterProductMeanSmall to a matmul-based approach—to see where the real gains lie. 3, 2, 4)
out = attn @ v # (B, S, h, N, N) @ (B, S, h, N, d) = (B, S, h, N, d)
out = out.permute(0, 1, 3, 2, 4).reshape(B, S, N, D)
```
For the outer product, I can restructure the computation by first transposing and reshaping the tensors, then performing a batched matrix multiplication to compute the pairwise outer products across the sequence dimension, finally reshaping to match the expected output format.
The triangular multiplication uses an einsum operation with a gating mechanism. I'll load the custom CUDA kernels for these three key operations: the MSA attention layer, the outer product mean computation, and the triangular multiplicative update. Each kernel needs to handle the appropriate tensor dimensions and apply any necessary normalization or activation functions. I need to focus on implementing one or two well-designed kernels rather than attempting three separate approaches. The attention computation and outer product both have enough complexity to warrant custom CUDA implementations.
For the outer product, I should reconsider the einsum operation more carefully. The pattern "bsih,bsjk->bijhk" contracts across the s dimension while preserving spatial relationships between different positions. The correct result shape should be (B, N, N, H, H) after summing over S, not (B, H, H, N, N). Let me verify this by checking what dimensions remain after the contraction: i and j index the two input tensors, while h and k represent their respective hidden dimensions, and b is the batch dimension that carries through.
For TriMulOut, the operation applies a triangular structure to the outer product. The einsum "bikh,bjkh->bijh" contracts over the k dimension between the two tensors, producing an output of shape (B, N, N, H). The key difference from OuterProductMean is that k serves as the hidden dimension being summed, while i and j represent positions along the N dimension.
I should implement a complete ModelNew class using optimized CUDA kernels. Fusing multiple operations—particularly the LayerNorm followed by linear projections and the attention mechanism itself—would yield the best performance gains. Let me structure the implementation with PyTorch's cpp_extension to define the necessary kernels. _compute_cuda(\n const torch::Tensor& msa,\n const torch::Tensor& q_weight, const torch::Tensor& k_weight, const torch::Tensor& v_weight,\n const torch::Tensor& out_weight,\n int num_heads, int head_dim, float scale);\n''',
cuda_sources='''
#include <torch/extension.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <c10/cuda/CUDAException.h>
__device__ __forceinline__ float warpReduceSum(float val) {
for (int offset = 16; offset > 0; offset /= 2)
val += __shfl_down_sync(0xffffffff, val, offset);
return val;
}
__global__ void layernorm_fuse
I'm setting up the CUDA kernel with warp-level reduction for efficient summation, then defining the fused layer normalization kernel that will combine multiple operations. scale;
int idx = blockIdx.x * blockDim.x + threadIdx.x;
int total = B * S * N * D;
if (idx >= total) return;
int d_idx = idx % D;
int n_idx = (idx / D) % N;
int s_idx = (idx / (D * N)) % S;
int b_idx = idx / (D * N * S);
// Load mean and var
int mv_idx = b_idx * S * N + s_idx * N + n_idx;
float mean_val = mean[mv_idx];
float var_val = var[mv_idx];
// Load msa value
float msa_val = msa[idx];
// Normalize
float norm_val = (msa_val - mean_val) / sqrtf(var_val + 1e-5f);
// Compute Q, K, V projections for this position
int hd_idx = d_idx / head_dim;
int h_idx = d_idx % head_dim;
float q_val = 0, k_val = 0, v_val = 0;
// Matrix-vector multiply for Q, K, V
for (int i = 0; i < D; i++) {
float msa_i = msa[b_idx * S * N * D + s_idx * N * D + n_idx * D + i];
q_val += msa_i * q_weight[d_idx * D + i];
k_val += msa_i * k_weight[d_idx * D + i];
v_val += msa_i * v_weight[d_idx * D + i];
}
// Store normalized value and projections
norm_out[idx] = norm_val;
q_out[idx] = q_val;
k_out[idx] = k_val;
v_out[idx] = v_val;
}
I need to reconsider this approach. The problem involves layer normalization across the D dimension for each (b, s, n) position, followed by separate lin
...[ omitted 15687 characters ]...
, torch::Tensor gamma, torch::Tensor beta, torch::Tensor weight, float eps);
'''],
cuda_sources=cuda_code,
functions=['layernorm_cuda', 'layernorm_linear_cuda'],
extra_cuda_cflags=['-O3'],
verbose=True
)
class ModelNew(nn.Module):
def __init__(self, msa_dim, pair_dim, num_heads, tri_hidden):
super().__init__()
# Store dimensions
self.msa_dim = msa_dim
self.pair_dim = pair_dim
self.num_heads = num_heads
self.head_dim = msa_dim // num_heads
self.tri_hidden = tri_hidden
# Initialize weights using PyTorch
# MSA attention weights
self.msa_ln_weight = nn.Parameter(torch.ones(msa_dim))
self.msa_ln_bias = nn.Parameter(torch.zeros(msa_dim))
self.q_weight = nn.Parameter(torch.randn(msa_dim, msa_dim) / math.sqrt(msa_dim))
self.k_weight = nn.Parameter(torch.randn(msa_dim, msa_dim) / math.sqrt(msa_dim))
self.v_weight = nn.Parameter(torch.randn(msa_dim, msa_dim) / math.sqrt(msa_dim))
self.out_weight = nn.Parameter(torch.randn(msa_dim, msa_dim) / math.sqrt(msa_dim))
self.out_bias = nn.Parameter(torch.zeros(msa_dim))
# OPM weights
self.opm_ln_weight = nn.Parameter(torch.ones(msa_dim))
self.opm_ln_bias = nn.Parameter(torch.zeros(msa_dim))
self.a_weight = nn.Parameter(torch.randn(msa_dim, 32) / math.sqrt(msa_dim))
self.b_weight = nn.Parameter(torch.randn(msa_dim, 32) / math.sqrt(msa_dim))
self.opm_out_weight = nn.Parameter(torch.randn(32 * 32, pair_dim) / math.sqrt(32 * 32))
self.opm_out_bias = nn.Parameter(torch.zeros(pair_dim))
# TriMul weights
self.tri_ln_weight = nn.Parameter(torch.ones(pair_dim))
self.tri_ln_bias = nn.Parameter(torch.zeros(pair_dim))
self.pa_weight = nn.Parameter(torch.randn(pair_dim, tri_hidden) / math.sqrt(pair_dim))
self.pb_weight = nn.Parameter(torch.randn(pair_dim, tri_hidden) / math.sqrt(pair_dim))
self.ga_weight = nn.Parameter(torch.randn(pair_dim, tri_hidden) / math.sqrt(pair_dim))
self.gb_weight = nn.Parameter(torch.randn(pair_dim, tri_hidden) / math.sqrt(pair_dim))
self.tri_out_weight = nn.Parameter(torch.randn(tri_hidden, pair_dim) / math.sqrt(tri_hidden))
self.tri_out_bias = nn.Parameter(torch.zeros(pair_dim))
self.gate_weight = nn.Parameter(torch.randn(pair_dim, pair_dim) / math.sqrt(pair_dim))
self.gate_bias = nn.Parameter(torch.zeros(pair_dim))
```
The approach uses custom CUDA kernels for the core operations while keeping weight initialization in PyTorch for simplicity. Each component maintains its own layer normalization parameters and projection weights, all initialized with appropriate scaling factors based on their dimensions. I need to complete the partial thought about the attention computation and then summarize the overall approach.
The attention scores are computed by multiplying queries with transposed keys, scaling by the inverse square root of the head dimension. A softmax is applied across the last dimension to get attention weights, which are then used to compute weighted values. The output is reshaped and passed through a final linear transformation before being added to the original MSA representation.
For the outer product mean operation, layer normalization is applied to the input, then two parallel linear projections produce intermediate tensors that will be used for the outer product computation. I need to reshape and transpose both a and b tensors for the batched matrix multiplication. After the einsum operation produces outer with shape (B, N, N, 32, 32), I divide by the sequence length and reshape for the output projection. The result gets added to the pair representation.
For the triangular multiplication, I'm setting up layer normalization on the pair representation, then preparing a gating mechanism with sigmoid activations that will be applied element-wise to the projections. d = self.num_heads, self.head_dim
x = torch.nn.functional.layer_norm(msa, (D,), self.msa_ln_weight, self.msa_ln_bias)
q = torch.nn.functional.linear(x, self.q_weight.t()).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)
k = torch.nn.functional.linear(x, self.k_weight.t()).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)
v = torch.nn.functional.linear(x, self.v_weight.t()).view(B, S, N, h, d).permute(0, 1, 3, 2, 4)
attn = (q @ k.transpose(-1, -2)) / math.sqrt(d)
attn = torch.nn.functional.softmax(attn, dim=-1)
out = (attn @ v).permute(0, 1, 3, 2, 4).reshape(B, S, N, D)
return msa + torch.nn.functional.linear(out, self.out_weight.t(), self.out_bias)
def outer_product_mean(self, msa):
B, S, N, D = msa.shape
x = torch.nn.functional.layer_norm(msa, (D,), self.opm_ln_weight, self.opm_ln_bias)
a = torch.nn.functional.linear(x, self.a_weight.t())
b = torch.nn.functional.linear(x, self.b_weight.t())
outer = torch.einsum("bsih,bsjk->bijhk", a, b)
outer = outer / max(S, 1)
B, N1, N2, H1, H2 = outer.shape
return outer.reshape(B, N1, N2, H1 * H2)
def tri_mul(self, pair):
x = torch.nn.functional.layer_norm(pair, (pair.shape[-1],), self.tri_ln_weight, self.tri_ln_bias)
a = torch.nn.functional.linear(x, self.pa_weight.t()) * torch.sigmoid(torch.nn.functional.linear(x, self.ga_weight.t()))
b = torch.nn.functional.linear(x, self.pb_weight.t()) * torch.sigmoid(torch.nn.functional.linear(x, self.gb_weight.t()))
o = torch.einsum("bikh,bjkh->bijh", a, b)
o = torch.nn.functional.layer_norm(o, (o.shape[-1],))
return torch.nn.functional.linear(o, self.out_weight.t(), self.out_bias)
The MSARowSelfAttention applies layer normalization to the input, then computes queries, keys, and values through linear projections with reshape and transpose operations. The attention scores are computed via matrix multiplication with scaling, followed by softmax normalization. The output is reshaped back and added to the original input with a residual connection. The outer product mean computes an einsum operation between two projected representations, normalizing by sequence length. The triangular multiplication applies gated linear projections with sigmoid activations and combines them through an einsum pattern. .linear(out, self.opm_out_weight.t(), self.opm_out_bias)
return pair + opm_out
def tri_mul_out(self, pair):
x = F.layer_norm(pair, (pair.shape[-1],), self.tri_ln_weight, self.tri_ln_bias)
a = F.linear(x, self.pa_weight.t()) * torch.sigmoid(F.linear(x, self.ga_weight.t()))
b = F.linear(x, self.pb_weight.t()) * torch.sigmoid(F.linear(x, self.gb_weight.t()))
o = torch.einsum("bikh,bjkh->bijh", a, b)
o = F.layer_norm(o, (o.shape[-1],), self.tri_out_ln_weight, self.tri_out_ln_bias)
o = F.linear(o, self.tri_out_weight.t(), self.tri_out_bias)
gate = torch.sigmoid(F.linear(pair, self.gate_weight.t(), self.gate_bias))
return pair + gate * o
def forward(self, msa_repr, pair_repr):
msa_repr = self.msa_row_attention(msa_repr)
pair_repr = self.outer_product_mean(msa_repr, pair_repr)
pair_repr = self.tri_mul_out(pair_repr)
return msa_repr, pair_repr
I'm implementing this without custom CUDA kernels initially to ensure correctness, though that won't provide performance gains. Since the task specifically requires a custom CUDA kernel implementation, I should create a proper fused kernel solution that actually leverages GPU optimization.
The key operations to target for kernel fusion are layer normalization followed by linear projections in the attention pathway, along with the attention computation itself involving softmax and matmul. I'll design a comprehensive implementation that fuses these operations end-to-end rather than calling separate PyTorch functions.
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\n# Custom CUDA kernels for fused operations\ncuda_code = \"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\n#define WARP_SIZE 32\n#define CUDA_CHECK(expr) do { cudaError_t err = (expr); if (err != cudaSuccess) { /* handle error */ } } while(0)\n\n// Warp-level reduction for sum\n__device__ __forceinline__ float warp_reduce_sum(float val) {\n #pragma unroll\n for (int mask = WARP_SIZE / 2; mask > 0; mask >>= 1) {\n val += __shfl_xor_sync(0xffffffff, val, mask);\n }\n return val;\n}\n\n// Block-level reduction using shared memory\n__device__ __forceinline__ float block_reduce_sum(float val, float* shared) {\n int lane = threadIdx.x % WARP_SIZE;\n int wid = threadIdx.x / WARP_SIZE;\n \n val = warp_reduce_sum(val);\n \n if (lane == 0) shared[wid] = val;\n __syncthreads();\n \n if (wid == 0) {\n val = (lane < (blockDim.x + WARP_SIZE - 1) / WARP_SIZE) ? shared[lane] : 0.0f;\n val = warp_reduce_sum(val);\n }\n __syncthreads();\n \n return val;\n}\n\n// Fused LayerNorm + 3 Linear projections (for Q, K, V)\n__global__ void layernorm_qkv_kernel(\n const float* __restrict__ input,\n const float* __restrict__ gamma,\n const float* __restrict__ beta,\n const float* __restrict__ q_weight,\n const float* __restrict__ k_weight,\n const float* __restrict__ v_weight,\n float* __restrict__ q_out,\n float* __restrict__ k_out,\n float* __restrict__ v_out,\n int total_positions, int D_in, int D_out, float eps) {\n \n extern __shared__ float shared_mem[];\n float* shared = shared_mem;\n \n int pos = blockIdx.x;\n if (pos >= total_positions) return;\n \n const float* in_ptr = input + pos * D_in;\n float* q_ptr = q_out + pos * D_out;\n float* k_ptr = k_out + pos * D_out;\n float* v_ptr = v_out + pos * D_out;\n \n // Compute mean\n float sum = 0.0f;\n for (int i = threadIdx.x; i < D_in; i += blockDim.x) {\n sum += in_ptr[i];\n }\n sum = block_reduce_sum(sum, shared);\n float mean = sum / D_in;\n \n // Compute variance\n float sq_sum = 0.0f;\n for (int i = threadIdx.x; i < D_in; i += blockDim.x) {\n float diff = in_ptr[i] - mean;\n sq_sum += diff * diff;\n }\n sq_sum = block_reduce_sum(sq_sum, shared);\n float inv_std = rsqrtf(sq_sum / D_in + eps);\n \n // Compute normalized values and store in registers (for small D_in)\n // Then compute Q, K, V projections\n for (int o = threadIdx.x; o < D_out; o += blockDim.x) {\n float q_val = 0.0f, k_val = 0.0f, v_val = 0.0f;\n for (int i = 0; i < D_in; i++) {\n float norm_val = (in_ptr[i] - mean) * inv_std * gamma[i] + beta[i];\n q_val += norm_val * q_weight[o * D_in + i];\n k_val += norm_val * k_weight[o * D_in + i];\n v_val += norm_val * v_weight[o * D_in + i];\n }\n q_ptr[o] = q_val;\n k_ptr[o] = k_val;\n v_ptr[o] = v_val;\n }\n}\n\n// Fused LayerNorm + 2 Linear projections (for OPM a and b)\n__global__ void layernorm_ab_kernel(\n const float* __restrict__ input,\n const float* __restrict__ gamma,\n const float* __restrict__ beta,\n const float* __restrict__ a_weight,\n const float* __restrict__ b_weight,\n float* __restrict__ a_out,\n float* __restrict__ b_out,\n int total_positions, int D_in, int D_out, float eps) {\n \n extern __shared__ float shared_mem[];\n float* shared = shared_mem;\n \n int pos = blockIdx.x;\n if (pos >= total_positions) return;\n \n const float* in_ptr = input + pos * D_in;\n float* a_ptr = a_out + pos * D_out;\n float* b_ptr = b_out + pos * D_out;\n \n // Compute mean\n float sum = 0.0f;\n for (int i = threadIdx.x; i < D_in; i += blockDim.x) {\n sum += in_ptr[i];\n }\n sum = block_reduce_sum(sum, shared);\n float mean = sum / D_in;\n \n // Compute variance\n float sq_sum = 0.0f;\n for (int i = threadIdx.x; i < D_in; i += blockDim.x) {\n float diff = in_ptr[i] - mean;\n sq_sum += diff * diff;\n }\n sq_sum = block_reduce_sum(sq_sum, shared);\n float inv_std = rsqrtf(sq_sum / D_in + eps);\n \n // Compute normalized values and projections\n for (int o = threadIdx.x; o < D_out; o += blockDim.x) {\n float a_val = 0.0f, b_val = 0.0f;\n for (int i = 0; i < D_in; i++) {\n float norm_val = (in_ptr[i] - mean) * inv_std * gamma[i] + beta[i];\n a_val += norm_val * a_weight[o * D_in + i];\n b_val += norm_val * b_weight[o * D_in + i];\n }\n a_ptr[o] = a_val;\n b_ptr[o] = b_val;\n }\n}\n\n// Fused LayerNorm + 4 Linear projections (for TriMul pa, pb, ga, gb)\n__global__ void layernorm_tri_kernel(\n const float* __restrict__ input,\n const float* __restrict__ gamma,\n const float* __restrict__ beta,\n const float* __restrict__ pa_weight,\n const float* __restrict__ pb_weight,\n const float* __restrict__ ga_weight,\n const float* __restrict__ gb_weight,\n float* __restrict__ a_out,\n float* __restrict__ b_out,\n int total_positions, int D_in, int D_out, float eps) {\n \n extern __shared__ float shared_mem[];\n float* shared = shared_mem;\n \n int pos = blockIdx.x;\n if (pos >= total_positions) return;\n \n const float* in_ptr = input + pos * D_in;\n float* a_ptr = a_out + pos * D_out;\n float* b_ptr = b_out + pos * D_out;\n \n // Compute mean\n float sum = 0.0f;\n for (int i = threadIdx.x; i < D_in; i += blockDim.x) {\n sum += in_ptr[i];\n }\n sum = block_reduce_sum(sum, shared);\n float mean = sum / D_in;\n \n // Compute variance\n float sq_sum = 0.0f;\n for (int i = threadIdx.x; i < D_in; i += blockDim.x) {\n float diff = in_ptr[i] - mean;\n sq_sum += diff * diff;\n }\n sq_sum = block_reduce_sum(sq_sum, shared);\n float inv_std = rsqrtf(sq_sum / D_in + eps);\n \n // Compute normalized values and projections with gating\n for (int o = threadIdx.x; o < D_out; o += blockDim.x) {\n float pa_val = 0.0f, pb_val = 0.0f;\n float ga_val = 0.0f, gb_val = 0.0f;\n for (int i = 0; i < D_in; i++) {\n float norm_val = (in_ptr[i] - mean) * inv_std * gamma[i] + beta[i];\n pa_val += norm_val * pa_weight[o * D_in + i];\n pb_val += norm_val * pb_weight[o * D_in + i];\n ga_val += norm_val * ga_weight[o * D_in + i];\n gb_val += norm_val * gb_weight[o * D_in + i];\n }\n // Apply sigmoid gating: a = pa * sigmoid(ga), b = pb * sigmoid(gb)\n a_ptr[o] = pa_val * (1.0f / (1.0f + expf(-ga_val)));\n b_ptr[o] = pb_val * (1.0f / (1.0f + expf(-gb_val)));\n }\n}\n\n// C++ interface functions\ntorch::Tensor layernorm_qkv_cuda(\n torch::Tensor input, torch::Tensor gamma, torch::Tensor beta,\n torch::Tensor q_weight, torch::Tensor k_weight, torch::Tensor v_weight,\n int D_out, float eps) {\n \n int total_positions = input.numel() / input.size(-1);\n int D_in = input.size(-1);\n \n auto q_out = torch::empty({total_positions, D_out}, input.options());\n auto k_out = torch::empty({total_positions, D_out}, input.options());\n auto v_out = torch::empty({total_positions, D_out}, input.options());\n \n int threads = (D_in + 31) / 32 * 32;\n threads = min(threads, 256);\n int blocks = total_positions;\n int shared_mem = ((threads + 31) / 32) * sizeof(float);\n \n layernorm_qkv_kernel<<<blocks, threads, shared_mem>>>(\n input.data_ptr<float>(), gamma.data_ptr<float>(), beta.data_ptr<float>(),\n q_weight.data_ptr<float>(), k_weight.data_ptr<float>(), v_weight.data_ptr<float>(),\n q_out.data_ptr<float>(), k_out.data_ptr<float>(), v_out.data_ptr<float>(),\n total_positions, D_in, D_out, eps);\n \n return torch::stack({q_out, k_out, v_out}, 0);\n}\n\ntorch::Tensor layernorm_ab_cuda(\n torch::Tensor input, torch::Tensor gamma, torch::Tensor beta,\n torch::Tensor a_weight, torch::Tensor b_weight,\n int D_out, float eps) {\n \n int total_positions = input.numel() / input.size(-1);\n int D_in = input.size(-1);\n \n auto a_out = torch::empty({total_positions, D_out}, input.options());\n auto b_out = torch::empty({total_positions, D_out}, input.options());\n \n int threads = (D_in + 31) / 32 * 32;\n threads = min(threads, 256);\n int blocks = total_positions;\n int shared_mem = ((threads + 31) / 32) * sizeof(float);\n \n layernorm_ab_kernel<<<blocks, threads, shared_mem>>>(\n input.data_ptr<float>(), gamma.data_ptr<float>(), beta.data_ptr<float>(),\n a_weight.data_ptr<float>(), b_weight.data_ptr<float>(),\n a_out.data_ptr<float>(), b_out.data_ptr<float>(),\n total_positions, D_in, D_out, eps);\n \n return torch::stack({a_out, b_out}, 0);\n}\n\ntorch::Tensor layernorm_tri_cuda(\n torch::Tensor input, torch::Tensor gamma, torch::Tensor beta,\n torch::Tensor pa_weight, torch::Tensor pb_weight,\n torch::Tensor ga_weight, torch::Tensor gb_weight,\n int D_out, float eps) {\n \n int total_positions = input.numel() / input.size(-1);\n int D_in = input.size(-1);\n \n auto a_out = torch::empty({total_positions, D_out}, input.options());\n auto b_out = torch::empty({total_positions, D_out}, input.options());\n \n int threads = (D_in + 31) / 32 * 32;\n threads = min(threads, 256);\n int blocks = total_positions;\n int shared_mem = ((threads + 31) / 32) * sizeof(float);\n \n layernorm_tri_kernel<<<blocks, threads, shared_mem>>>(\n input.data_ptr<float>(), gamma.data_ptr<float>(), beta.data_ptr<float>(),\n pa_weight.data_ptr<float>(), pb_weight.data_ptr<float>(),\n ga_weight.data_ptr<float>(), gb_weight.data_ptr<float>(),\n a_out.data_ptr<float>(), b_out.data_ptr<float>(),\n total_positions, D_in, D_out, eps);\n \n return torch::stack({a_out, b_out}, 0);\n}\n\"\"\"\n\n# Load the CUDA extension\nfused_ops = load_inline(\n name='fused_ops_v1',\n cpp_sources='''\n#include <torch/extension.h>\ntorch::Tensor layernorm_qkv_cuda(torch::Tensor input, torch::Tensor gamma, torch::Tensor beta,\n torch::Tensor q_weight, torch::Tensor k_weight, torch::Tensor v_weight, int D_out, float eps);\ntorch::Tensor layernorm_ab_cuda(torch::Tensor input, torch::Tensor gamma, torch::Tensor beta,\n torch::Tensor a_weight, torch::Tensor b_weight, int D_out, float eps);\ntorch::Tensor layernorm_tri_cuda(torch::Tensor input, torch::Tensor gamma, torch::Tensor beta,\n torch::Tensor pa_weight, torch::Tensor pb_weight, torch::Tensor ga_weight, torch::Tensor gb_weight,\n int D_out, float eps);\n''',\n cuda_sources=cuda_code,\n functions=['layernorm_qkv_cuda', 'layernorm_ab_cuda', 'layernorm_tri_cuda'],\n extra_cuda_cflags=['-O3', '--use_fast_math'],\n verbose=False\n)\n\n\nclass ModelNew(nn.Module):\n \"\"\"\n Optimized Evoformer block with custom CUDA kernels for fused operations.\n \"\"\"\n \n def __init__(self, msa_dim, pair_dim, num_heads, tri_hidden):\n super().__init__()\n self.msa_dim = msa_dim\n self.pair_dim = pair_dim\n self.num_heads = num_heads\n self.head_dim = msa_dim // num_heads\n self.tri_hidden = tri_hidden\n self.eps = 1e-5\n \n # MSA attention weights (LayerNorm + Q/K/V projections)\n self.msa_ln_weight = nn.Parameter(torch.ones(msa_dim))\n self.msa_ln_bias = nn.Parameter(torch.zeros(msa_dim))\n self.q_weight = nn.Parameter(torch.randn(msa_dim, msa_dim) * (1.0 / math.sqrt(msa_dim)))\n self.k_weight = nn.Parameter(torch.randn(msa_dim, msa_dim) * (1.0 / math.sqrt(msa_dim)))\n self.v_weight = nn.Parameter(torch.randn(msa_dim, msa_dim) * (1.0 / math.sqrt(msa_dim)))\n self.out_weight = nn.Parameter(torch.randn(msa_dim, msa_dim) * (1.0 / math.sqrt(msa_dim)))\n self.out_bias = nn.Parameter(torch.zeros(msa_dim))\n \n # OPM weights (LayerNorm + a/b projections)\n self.opm_ln_weight = nn.Parameter(torch.ones(msa_dim))\n self.opm_ln_bias = nn.Parameter(torch.zeros(msa_dim))\n self.a_weight = nn.Parameter(torch.randn(msa_dim, 32) * (1.0 / math.sqrt(msa_dim)))\n self.b_weight = nn.Parameter(torch.randn(msa_dim, 32) * (1.0 / math.sqrt(msa_dim)))\n self.opm_out_weight = nn.Parameter(torch.randn(32 * 32, pair_dim) * (1.0 / math.sqrt(32 * 32)))\n self.opm_out_bias = nn.Parameter(torch.zeros(pair_dim))\n \n # TriMul weights (LayerNorm + pa/pb/ga/gb projections)\n self.tri_ln_weight = nn.Parameter(torch.ones(pair_dim))\n self.tri_ln_bias = nn.Parameter(torch.zeros(pair_dim))\n self.pa_weight = nn.Parameter(torch.randn(pair_dim, tri_hidden) * (1.0 / math.sqrt(pair_dim)))\n self.pb_weight = nn.Parameter(torch.randn(pair_dim, tri_hidden) * (1.0 / math.sqrt(pair_dim)))\n self.ga_weight = nn.Parameter(torch.randn(pair_dim, tri_hidden) * (1.0 / math.sqrt(pair_dim)))\n self.gb_weight = nn.Parameter(torch.randn(pair_dim, tri_hidden) * (1.0 / math.sqrt(pair_dim)))\n self.tri_out_ln_weight = nn.Parameter(torch.ones(tri_hidden))\n self.tri_out_ln_bias = nn.Parameter(torch.zeros(tri_hidden))\n self.tri_out_weight = nn.Parameter(torch.randn(tri_hidden, pair_dim) * (1.0 / math.sqrt(tri_hidden)))\n self.tri_out_bias = nn.Parameter(torch.zeros(pair_dim))\n self.gate_weight = nn.Parameter(torch.randn(pair_dim, pair_dim) * (1.0 / math.sqrt(pair_dim)))\n self.gate_bias = nn.Parameter(torch.zeros(pair_dim))\n \n def _msa_attention(self, msa):\n \"\"\"MSA row self-attention with fused LayerNorm + Q/K/V kernel.\"\"\"\n B, S, N, D = msa.shape\n h, d = self.num_heads, self.head_dim\n \n # Reshape for fused kernel: (B*S*N, D)\n x = msa.reshape(-1, D)\n \n # Fused LayerNorm + Q/K/V projection\n qkv = fused_ops.layernorm_qkv_cuda(\n x, self.msa_ln_weight, self.msa_ln_bias,\n self.q_weight, self.k_weight, self.v_weight, D, self.eps\n )\n q = qkv[0].view(B, S, N, h, d).permute(0, 1, 3, 2, 4)\n k = qkv[1].view(B, S, N, h, d).permute(0, 1, 3, 2, 4)\n v = qkv[2].view(B, S, N, h, d).permute(0, 1, 3, 2, 4)\n \n # Scaled dot-product attention\n scale = 1.0 / math.sqrt(d)\n attn = (q @ k.transpose(-1, -2)) * scale\n attn = F.softmax(attn, dim=-1)\n out = (attn @ v).permute(0, 1, 3, 2, 4).reshape(B, S, N, D)\n \n # Output projection\n return F.linear(out, self.out_weight, self.out_bias)\n \n def _outer_product_mean(self, msa):\n \"\"\"Outer product mean with fused LayerNorm + a/b kernel.\"\"\"\n B, S, N, D = msa.shape\n H = 32 # hidden dimension\n \n # Reshape for fused kernel: (B*S*N, D)\n x = msa.reshape(-1, D)\n \n # Fused LayerNorm + a/b projection\n ab = fused_ops.layernorm_ab_cuda(\n x, self.opm_ln_weight, self.opm_ln_bias,\n self.a_weight, self.b_weight, H, self.eps\n )\n a = ab[0].view(B, S, N, H)\n b = ab[1].view(B, S, N, H)\n \n # Outer product: einsum(\"bsih,bsjk->bijhk\", a, b)\n # Optimized using reshape + matmul\n outer = torch.einsum(\"bsih,bsjk->bijhk\", a, b)\n outer = outer / max(S, 1)\n \n # Output projection\n B_out, N1, N2, H1, H2 = outer.shape\n outer_flat = outer.reshape(B_out, N1, N2, H1 * H2)\n return F.linear(outer_flat, self.opm_out_weight, self.opm_out_bias)\n \n def _tri_mul_out(self, pair):\n \"\"\"Triangular multiplicative update with fused LayerNorm + gated projections.\"\"\"\n B, N, _, D_pair = pair.shape\n H = self.tri_hidden\n \n # Reshape for fused kernel: (B*N*N, D_pair)\n x = pair.reshape(-1, D_pair)\n \n # Fused LayerNorm + gated pa/pb projections\n ab = fused_ops.layernorm_tri_cuda(\n x, self.tri_ln_weight, self.tri_ln_bias,\n self.pa_weight, self.pb_weight,\n self.ga_weight, self.gb_weight, H, self.eps\n )\n a = ab[0].view(B, N, N, H)\n b = ab[1].view(B, N, N, H)\n \n # Triangular multiplication: einsum(\"bikh,bjkh->bijh\", a, b)\n o = torch.einsum(\"bikh,bjkh->bijh\", a, b)\n \n # LayerNorm + output projection\n o = F.layer_norm(o, (H,), self.tri_out_ln_weight, self.tri_out_ln_bias)\n o = F.linear(o, self.tri_out_weight, self.tri_out_bias)\n \n # Gated residual connection\n gate = torch.sigmoid(F.linear(pair, self.gate_weight, self.gate_bias))\n return gate * o\n \n def forward(self, msa_repr: torch.Tensor, pair_repr: torch.Tensor) -> tuple:\n \"\"\"\n Forward pass with fused CUDA kernels.\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 row self-attention\n msa_repr = msa_repr + self._msa_attention(msa_repr)\n \n # Outer product mean\n pair_repr = pair_repr + self._outer_product_mean(msa_repr)\n \n # Triangular multiplicative update\n pair_repr = pair_repr + self._tri_mul_out(pair_repr)\n \n return msa_repr, pair_repr"
}
result ← submit_kernel (fail)
failsubmit_kernel
submit_kernel FAILED: correctness check did not pass (? trials).
runtime_error: mat1 and mat2 shapes cannot be multiplied (2048x1024 and 64x1024)