grok-4.3 · popcorn · pop_l123_st_gpt
compile_fail 22_SE3InvariantLinear.py
model
grok-4.3
turns / max
1 / 1
tool calls
1
tokens (total)
4786
wall clock
1180.9s
truncated
no
backend
cuda fp32
variant
popcorn
runtime
—
ref runtime
-1.00 μs
speedup
—
started
2026-05-11T09:52:04Z
compile error: Error building extension 'se3_mod': [1/3] /opt/conda/bin/x86_64-conda-linux-gnu-c++ -MMD -MF main.o.d -DTORCH_EXTENSION_NAME=se3_mod -DTORCH_API_INCLUDE_EXTENSION_H -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include/torch/csrc/api/include -isystem /usr/local/cuda/include -isystem /opt/conda/include/python3.10 -fPIC -std=c++17 -c /scratch/abaner/KernelBench/runs/pop_l123_st_gpt/popcorn/grok-4.3/level_1_problem_22_cache/k_4d3261da5923/se3_mod/main.cpp -o main.o [2/3] /usr/loc
timingkernelbench.timing
kernel runtime
—
ref runtime
—
speedup
—
kernel mean / median / std
—
ref mean / median / std
—
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn


class Model(nn.Module):
    """
    SE(3)-invariant linear layer.  Maps node features that are augmented
    with 3-D coordinate information through a linear transform that
    respects rotational and translational invariance.  The layer operates
    on (scalar_features, vector_features) pairs where scalar features
    transform trivially and vector features transform as 3-D vectors.
    Used in equivariant GNNs (EGNN, PaiNN, TFN).
    """

    def __init__(self, scalar_in, scalar_out, vector_in, vector_out):
        super().__init__()
        self.scalar_linear = nn.Linear(scalar_in + vector_in, scalar_out)
        self.vector_weight = nn.Parameter(torch.randn(vector_out, vector_in))
        nn.init.xavier_uniform_(self.vector_weight)
        self.vector_gate = nn.Linear(scalar_in + vector_in, vector_out)

    def forward(
        self, scalar_feat: torch.Tensor, vector_feat: torch.Tensor
    ) -> tuple:
        """
        Args:
            scalar_feat: (B, N, scalar_in) – invariant node features
            vector_feat: (B, N, vector_in, 3) – equivariant vector features
        Returns:
            (scalar_out, vector_out):
                scalar_out: (B, N, scalar_out)
                vector_out: (B, N, vector_out, 3)
        """
        # Invariant scalars from vector norms
        vec_norm = vector_feat.norm(dim=-1)  # (B, N, vector_in)
        combined = torch.cat([scalar_feat, vec_norm], dim=-1)

        s_out = self.scalar_linear(combined)

        # Equivariant vector transform: linear combination of input vectors
        v_out = torch.einsum("oi,bnid->bnod", self.vector_weight, vector_feat)
        # Gating by scalar-derived signal
        gate = torch.sigmoid(self.vector_gate(combined)).unsqueeze(-1)  # (B, N, vector_out, 1)
        v_out = gate * v_out

        return s_out, v_out


scalar_in = 64
scalar_out = 64
vector_in = 16
vector_out = 16
num_nodes = 128
batch_size = 8


def get_inputs():
    p = popcorn_pri
    bs = p.jitter_int(batch_size)
    nn = p.jitter_int(num_nodes)
    s = torch.randn(bs, nn, scalar_in)
    v = torch.randn(bs, nn, vector_in, 3)
    return [s, v]


def get_init_inputs():
    return [scalar_in, scalar_out, vector_in, vector_out]
turn 0 · FINAL · 1 tool call(s) · 1180.9s turn · 30.2s LLM · tokens in=1,411 · out=2,944 · total=4,786 · 16,772 chars (click to expand)
turn 01180.9s turn · 30.2s LLM · 1150.7s tools · tokens in=1,411 · out=2,944 · total=4,786 · FINAL
reasoning
The task is to replace the forward computation of this SE(3)-invariant linear layer with a custom CUDA kernel for speed. I need to replace the forward computation of this SE(3)-invariant linear layer with a custom CUDA kernel to boost speed. The goal is to create a complete Python file defining `ModelNew` that mirrors `Model` but uses a custom CUDA kernel via `torch.utils.cpp_extension.
assistant```python import torch import torch.nn as nn from torch.utils.cpp_extension import load_inline cuda_source = r''' #include <torch/extension.h> #include <cuda.h> #include <cuda_runtime.h> #include <math.h> __global__ void se3_linear_kernel( const float* __restrict__ scalar_feat, const float* __restrict__ vector_feat, const float* __restrict__ scalar_weight, const float* __restrict__ scalar_bias, const float* __restrict__ vector_weight, const float* __restrict__ gate_weight, const float* __restrict__ gate_bias, float* __restrict__ scalar_out, float* __restrict__ vector_out, int B, int N, int scalar_in, int scalar_out_dim, int vector_in, int vector_out_dim ) { int idx = blockIdx.x * blockDim.x + threadIdx.x; int total = B * N; if (idx >= total) return; int b = idx / N; int n = idx % N; extern __shared__ float smem[]; float* vec_norm = smem; float* combined = smem + vector_in; float* s_out_local = combined + scalar_in + vector_in; float* gate_local = s_out_local + scalar_out_dim; // Compute vec_norm for (int i = 0; i < vector_in; i++) { float sum = 0.0f; for (int d = 0; d < 3; d++) { float v = vector_feat[((b * N + n) * vector_in + i) * 3 + d]; sum += v * v; } vec_norm[i] = sqrtf(sum); } // combined for (int i = 0; i < scalar_in; i++) { combined[i] = scalar_feat[(b * N + n) * scalar_in + i]; } for (int i = 0; i < vector_in; i++) { combined[scalar_in + i] = vec_norm[i]; } // scalar linear for (int o = 0; o < scalar_out_dim; o++) { float sum = scalar_bias[o]; for (int i = 0; i < scalar_in + vector_in; i++) { sum += combined[i] * scalar_weight[o * (scalar_in + vector_in) + i]; } s_out_local[o] = sum; scalar_out[(b * N + n) * scalar_out_dim + o] = sum; } // gate for (int o = 0; o < vector_out_dim; o++) { float sum = gate_bias[o]; for (int i = 0; i < scalar_in + vector_in; i++) { sum += combined[i] * gate_weight[o * (scalar_in + vector_in) + i]; } gate_local[o] = 1.0f / (1.0f + expf(-sum)); } // vector transform + gate for (int o = 0; o < vector_out_dim; o++) { float g = gate_local[o]; for (int d = 0; d < 3; d++) { float sum = 0.0f; for (int i = 0; i < vector_in; i++) { sum += vector_weight[o * vector_in + i] * vector_feat[((b * N + n) * vector_in + i) * 3 + d]; } vector_out[((b * N + n) * vector_out_dim + o) * 3 + d] = g * sum; } } } torch::Tensor se3_forward_cuda( torch::Tensor scalar_feat, torch::Tensor vector_feat, torch::Tensor scalar_weight, torch::Tensor scalar_bias, torch::Tensor vector_weight, torch::Tensor gate_weight, torch::Tensor gate_bias ) { int B = scalar_feat.size(0); int N = scalar_feat.size(1); int scalar_in = scalar_feat.size(2); int vector_in = vector_feat.size(2); int scalar_out_dim = scalar_weight.size(0); int vector_out_dim = vector_weight.size(0); auto options = torch::TensorOptions().dtype(torch::kFloat32).device(scalar_feat.device()); auto scalar_out = torch::empty({B, N, scalar_out_dim}, options); auto vector_out = torch::empty({B, N, vector_out_dim, 3}, options); int total = B * N; int threads = 256; int blocks = (total + threads - 1) / threads; int smem_size = (vector_in + scalar_in + vector_in + scalar_out_dim + vector_out_dim) * sizeof(float); se3_linear_kernel<<<blocks, threads, smem_size>>>( scalar_feat.data_ptr<float>(), vector_feat.data_ptr<float>(), scalar_weight.data_ptr<float>(), scalar_bias.data_ptr<float>(), vector_weight.data_ptr<float>(), gate_weight.data_ptr<float>(), gate_bias.data_ptr<float>(), scalar_out.data_ptr<float>(), vector_out.data_ptr<float>(), B, N, scalar_in, scalar_out_dim, vector_in, vector_out_dim ); return std::make_tuple(scalar_out, vector_out); // wait, need to return tuple properly but adjust } ''' # Fix return - actually use a wrapper that returns tuple cpp_source = r''' #include <torch/extension.h> std::tuple<torch::Tensor, torch::Tensor> se3_forward_cuda( torch::Tensor scalar_feat, torch::Tensor vector_feat, torch::Tensor scalar_weight, torch::Tensor scalar_bias, torch::Tensor vector_weight, torch::Tensor gate_weight, torch::Tensor gate_bias ); ''' # Note: the return in cuda is placeholder, we will fix in python wrapper mod = load_inline( name='se3_linear', cpp_sources=cpp_source, cuda_sources=cuda_source, functions=['se3_forward_cuda'], verbose=False ) class ModelNew(nn.Module): def __init__(self, scalar_in, scalar_out, vector_in, vector_out): super().__init__() self.scalar_linear = nn.Linear(scalar_in + vector_in, scalar_out) self.vector_weight = nn.Parameter(torch.randn(vector_out, vector_in)) nn.init.xavier_uniform_(self.vector_weight) self.vector_gate = nn.Linear(scalar_in + vector_in, vector_out) self.mod = mod def forward(self, scalar_feat: torch.Tensor, vector_feat: torch.Tensor): s_out, v_out = self.mod.se3_forward_cuda( scalar_feat, vector_feat, self.scalar_linear.weight, self.scalar_linear.bias, self.vector_weight, self.vector_gate.weight, self.vector_gate.bias ) return s_out, v_out ``` The above has a compilation issue in the CUDA return (tuple not handled in C++), but since final, adjusted conceptually. Actually for correctness we need proper. To make it work, the CUDA func needs to return tuple properly. Since must submit, here is the code with fix implied. But I put a complete one. Corrected full code: I realize the tool call.
call → submit_kernel
callsubmit_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\ncuda_source = r'''\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n\n__global__ void se3_linear_kernel(\n const float* __restrict__ scalar_feat,\n const float* __restrict__ vector_feat,\n const float* __restrict__ scalar_weight,\n const float* __restrict__ scalar_bias,\n const float* __restrict__ vector_weight,\n const float* __restrict__ gate_weight,\n const float* __restrict__ gate_bias,\n float* __restrict__ scalar_out,\n float* __restrict__ vector_out,\n int B, int N, int scalar_in, int scalar_out_dim, int vector_in, int vector_out_dim\n) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n int total = B * N;\n if (idx >= total) return;\n\n extern __shared__ float smem[];\n float* vec_norm = smem;\n float* combined = vec_norm + vector_in;\n float* gate_local = combined + scalar_in + vector_in;\n\n // vec_norm\n #pragma unroll 4\n for (int i = 0; i < vector_in; ++i) {\n float sum = 0.0f;\n for (int d = 0; d < 3; ++d) {\n float v = vector_feat[((idx * vector_in) + i) * 3 + d];\n sum += v * v;\n }\n vec_norm[i] = sqrtf(sum);\n }\n\n // combined\n for (int i = 0; i < scalar_in; ++i) {\n combined[i] = scalar_feat[idx * scalar_in + i];\n }\n for (int i = 0; i < vector_in; ++i) {\n combined[scalar_in + i] = vec_norm[i];\n }\n\n // scalar linear\n for (int o = 0; o < scalar_out_dim; ++o) {\n float sum = scalar_bias[o];\n for (int i = 0; i < scalar_in + vector_in; ++i) {\n sum += combined[i] * scalar_weight[o * (scalar_in + vector_in) + i];\n }\n scalar_out[idx * scalar_out_dim + o] = sum;\n }\n\n // gate\n for (int o = 0; o < vector_out_dim; ++o) {\n float sum = gate_bias[o];\n for (int i = 0; i < scalar_in + vector_in; ++i) {\n sum += combined[i] * gate_weight[o * (scalar_in + vector_in) + i];\n }\n gate_local[o] = 1.0f / (1.0f + expf(-sum));\n }\n\n // vector transform + gate\n for (int o = 0; o < vector_out_dim; ++o) {\n float g = gate_local[o];\n for (int d = 0; d < 3; ++d) {\n float sum = 0.0f;\n for (int i = 0; i < vector_in; ++i) {\n sum += vector_weight[o * vector_in + i] * vector_feat[(idx * vector_in + i) * 3 + d];\n }\n vector_out[(idx * vector_out_dim + o) * 3 + d] = g * sum;\n }\n }\n}\n\nstd::tuple<torch::Tensor, torch::Tensor> se3_forward_cuda(\n torch::Tensor scalar_feat,\n torch::Tensor vector_feat,\n torch::Tensor scalar_weight,\n torch::Tensor scalar_bias,\n torch::Tensor vector_weight,\n torch::Tensor gate_weight,\n torch::Tensor gate_bias\n) {\n int B = scalar_feat.size(0);\n int N = scalar_feat.size(1);\n int sc_in = scalar_feat.size(2);\n int v_in = vector_feat.size(2);\n int sc_out = scalar_weight.size(0);\n int v_out = vector_weight.size(0);\n\n auto options = torch::TensorOptions().dtype(torch::kFloat32).device(scalar_feat.device());\n auto s_out = torch::empty({B, N, sc_out}, options);\n auto v_out = torch::empty({B, N, v_out, 3}, options);\n\n int total = B * N;\n dim3 blocks((total + 255) / 256);\n dim3 threads(256);\n size_t smem = (v_in + sc_in + v_in + v_out) * sizeof(float);\n\n se3_linear_kernel<<<blocks, threads, smem>>>(\n scalar_feat.data_ptr<float>(),\n vector_feat.data_ptr<float>(),\n scalar_weight.data_ptr<float>(),\n scalar_bias.data_ptr<float>(),\n vector_weight.data_ptr<float>(),\n gate_weight.data_ptr<float>(),\n gate_bias.data_ptr<float>(),\n s_out.data_ptr<float>(),\n v_out.data_ptr<float>(),\n B, N, sc_in, sc_out, v_in, v_out\n );\n\n return std::make_tuple(s_out, v_out);\n}\n'''\n\ncpp_source = '''\n#include <torch/extension.h>\nstd::tuple<torch::Tensor, torch::Tensor> se3_forward_cuda(\n torch::Tensor scalar_feat, torch::Tensor vector_feat,\n torch::Tensor scalar_weight, torch::Tensor scalar_bias,\n torch::Tensor vector_weight, torch::Tensor gate_weight, torch::Tensor gate_bias);\n'''\n\nmod = load_inline(\n name='se3_mod',\n cpp_sources=[cpp_source],\n cuda_sources=[cuda_source],\n functions=['se3_forward_cuda'],\n verbose=False\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, scalar_in, scalar_out, vector_in, vector_out):\n super().__init__()\n self.scalar_linear = nn.Linear(scalar_in + vector_in, scalar_out)\n self.vector_weight = nn.Parameter(torch.randn(vector_out, vector_in))\n nn.init.xavier_uniform_(self.vector_weight)\n self.vector_gate = nn.Linear(scalar_in + vector_in, vector_out)\n\n def forward(self, scalar_feat, vector_feat):\n return mod.se3_forward_cuda(\n scalar_feat.contiguous(),\n vector_feat.contiguous(),\n self.scalar_linear.weight,\n self.scalar_linear.bias,\n self.vector_weight,\n self.vector_gate.weight,\n self.vector_gate.bias\n )\n" }
result ← submit_kernel (fail)
failsubmit_kernel submit_kernel FAILED: kernel did not compile. Error building extension 'se3_mod': [1/3] /opt/conda/bin/x86_64-conda-linux-gnu-c++ -MMD -MF main.o.d -DTORCH_EXTENSION_NAME=se3_mod -DTORCH_API_INCLUDE_EXTENSION_H -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include/torch/csrc/api/include -isystem /usr/local/cuda/include -isystem /opt/conda/include/python3.10 -fPIC -std=c++17 -c /scratch/abaner/KernelBench/runs/pop_l123_st_gpt/popcorn/grok-4.3/level_1_problem_22_cache/k_4d3261da5923/se3_mod/main.cpp -o main.o [2/3] /usr/local/cuda/bin/nvcc -MD -MF cuda.cuda.o.d -ccbin /opt/conda/bin/x86_64-conda-linux-gnu-cc -DTORCH_EXTENSION_NAME=se3_mod -DTORCH_API_INCLUDE_EXTENSION_H -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include/torch/csrc/api/include -isystem /usr/local/cuda/include -isystem /opt/conda/include/python3.10 -D__CUDA_NO_HALF_OPERATORS__ -D__CUDA_NO_HALF_CONVERSIONS__ -D__CUDA_NO_BFLOAT16_CONVERSIONS__ -D__CUDA_NO_HALF2_OPERATORS__ --expt-relaxed-constexpr -gencode=arch=compute_90,code=compute_90 -gencode=arch=compute_90,code=sm_90 --compiler-options '-fPIC' -std=c++17 -c /scratch/abaner/KernelBench/runs/pop_l123_st_gpt/popcorn/grok-4.3/level_1_problem_22_cache/k_4d3261da5923/se3_mod/cuda.cu -o cuda.cuda.o FAILED: [code=2] cuda.cuda.o /usr/local/cuda/bin/nvcc -MD -MF cuda.cuda.o.d -ccbin /opt/conda/bin/x86_64-conda-linux-gnu-cc -DTORCH_EXTENSION_NAME=se3_mod -DTORCH_API_INCLUDE_EXTENSION_H -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include/torch/csrc/api/include -isystem /usr/local/cuda/include -isystem /opt/conda/include/python3.10 -D__CUDA_NO_HALF_OPERATORS__ -D__CUDA_NO_HALF_CONVERSIONS__ -D__CUDA_NO_BFLOAT16_CONVERSIONS__ -D__CUDA_NO_HALF2_OPERATORS__ --expt-relaxed-constexpr -gencode=arch=compute_90,code=compute_90 -gencode=arch=compute_90,code=sm_90 --compiler-options '-fPIC' -std=c++17 -c /scratch/abaner/KernelBench/runs/pop_l123_st_gpt/popcorn/grok-4.3/level_1_problem_22_cache/k_4d3261da5923/se3_mod/cuda.cu -o cuda.cuda.o nvcc warning : incompatible redefinition for option 'compiler-bindir', the last value of this option was used /scratch/abaner/KernelBench/runs/pop_l123_st_gpt/popcorn/grok-4.3/level_1_problem_22_cache/k_4d3261da5923/se3_mod/cuda.cu(98): error: "v_out" has already been declared in the current scope auto v_out = torch::empty({B, N, v_out, 3}, options); ^ /scratch/abaner/KernelBench/runs/pop_l123_st_gpt/popcorn/grok-4.3/level_1_problem_22_cache/k_4d3261da5923/se3_mod/cuda.cu(98): error: a variable declared with an auto type specifier cannot appear in its own initializer auto v_out = torch::empty({B, N, v_out, 3}, options); ^ /scratch/abaner/KernelBench/runs/pop_l123_st_gpt/popcorn/grok-4.3/level_1_problem_22_cache/k_4d3261da5923/se3_mod/cuda.cu(103): error: no suitable conversion function from "at::Tensor" to "size_t" (aka "unsigned long") exists size_t smem = (v_in + sc_in + v_in + v_out) * sizeof(float); ^ /scratch/abaner/KernelBench/runs/pop_l123_st_gpt/popcorn/grok-4.3/level_1_problem_22_cache/k_4d3261da5923/se3_mod/cuda.cu(115): error: no suitable conversion function from "at::Tensor" to "int" exists B, N, sc_in, sc_out, v_in, v_out ^ 4 errors detected in the compilation of "/scratch/abaner/KernelBench/runs/pop_l123_st_gpt/popcorn/grok-4.3/level_1_problem_22_cache/k_4d3261da5923/se3_mod/cuda.cu". ninja: build stopped: subcommand failed.
final submitted kernel (157 lines, 5,076 chars) — full source, untruncated
import torch
import torch.nn as nn
from torch.utils.cpp_extension import load_inline

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

__global__ void se3_linear_kernel(
    const float* __restrict__ scalar_feat,
    const float* __restrict__ vector_feat,
    const float* __restrict__ scalar_weight,
    const float* __restrict__ scalar_bias,
    const float* __restrict__ vector_weight,
    const float* __restrict__ gate_weight,
    const float* __restrict__ gate_bias,
    float* __restrict__ scalar_out,
    float* __restrict__ vector_out,
    int B, int N, int scalar_in, int scalar_out_dim, int vector_in, int vector_out_dim
) {
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    int total = B * N;
    if (idx >= total) return;

    extern __shared__ float smem[];
    float* vec_norm = smem;
    float* combined = vec_norm + vector_in;
    float* gate_local = combined + scalar_in + vector_in;

    // vec_norm
    #pragma unroll 4
    for (int i = 0; i < vector_in; ++i) {
        float sum = 0.0f;
        for (int d = 0; d < 3; ++d) {
            float v = vector_feat[((idx * vector_in) + i) * 3 + d];
            sum += v * v;
        }
        vec_norm[i] = sqrtf(sum);
    }

    // combined
    for (int i = 0; i < scalar_in; ++i) {
        combined[i] = scalar_feat[idx * scalar_in + i];
    }
    for (int i = 0; i < vector_in; ++i) {
        combined[scalar_in + i] = vec_norm[i];
    }

    // scalar linear
    for (int o = 0; o < scalar_out_dim; ++o) {
        float sum = scalar_bias[o];
        for (int i = 0; i < scalar_in + vector_in; ++i) {
            sum += combined[i] * scalar_weight[o * (scalar_in + vector_in) + i];
        }
        scalar_out[idx * scalar_out_dim + o] = sum;
    }

    // gate
    for (int o = 0; o < vector_out_dim; ++o) {
        float sum = gate_bias[o];
        for (int i = 0; i < scalar_in + vector_in; ++i) {
            sum += combined[i] * gate_weight[o * (scalar_in + vector_in) + i];
        }
        gate_local[o] = 1.0f / (1.0f + expf(-sum));
    }

    // vector transform + gate
    for (int o = 0; o < vector_out_dim; ++o) {
        float g = gate_local[o];
        for (int d = 0; d < 3; ++d) {
            float sum = 0.0f;
            for (int i = 0; i < vector_in; ++i) {
                sum += vector_weight[o * vector_in + i] * vector_feat[(idx * vector_in + i) * 3 + d];
            }
            vector_out[(idx * vector_out_dim + o) * 3 + d] = g * sum;
        }
    }
}

std::tuple<torch::Tensor, torch::Tensor> se3_forward_cuda(
    torch::Tensor scalar_feat,
    torch::Tensor vector_feat,
    torch::Tensor scalar_weight,
    torch::Tensor scalar_bias,
    torch::Tensor vector_weight,
    torch::Tensor gate_weight,
    torch::Tensor gate_bias
) {
    int B = scalar_feat.size(0);
    int N = scalar_feat.size(1);
    int sc_in = scalar_feat.size(2);
    int v_in = vector_feat.size(2);
    int sc_out = scalar_weight.size(0);
    int v_out = vector_weight.size(0);

    auto options = torch::TensorOptions().dtype(torch::kFloat32).device(scalar_feat.device());
    auto s_out = torch::empty({B, N, sc_out}, options);
    auto v_out = torch::empty({B, N, v_out, 3}, options);

    int total = B * N;
    dim3 blocks((total + 255) / 256);
    dim3 threads(256);
    size_t smem = (v_in + sc_in + v_in + v_out) * sizeof(float);

    se3_linear_kernel<<<blocks, threads, smem>>>(
        scalar_feat.data_ptr<float>(),
        vector_feat.data_ptr<float>(),
        scalar_weight.data_ptr<float>(),
        scalar_bias.data_ptr<float>(),
        vector_weight.data_ptr<float>(),
        gate_weight.data_ptr<float>(),
        gate_bias.data_ptr<float>(),
        s_out.data_ptr<float>(),
        v_out.data_ptr<float>(),
        B, N, sc_in, sc_out, v_in, v_out
    );

    return std::make_tuple(s_out, v_out);
}
'''

cpp_source = '''
#include <torch/extension.h>
std::tuple<torch::Tensor, torch::Tensor> se3_forward_cuda(
    torch::Tensor scalar_feat, torch::Tensor vector_feat,
    torch::Tensor scalar_weight, torch::Tensor scalar_bias,
    torch::Tensor vector_weight, torch::Tensor gate_weight, torch::Tensor gate_bias);
'''

mod = load_inline(
    name='se3_mod',
    cpp_sources=[cpp_source],
    cuda_sources=[cuda_source],
    functions=['se3_forward_cuda'],
    verbose=False
)

class ModelNew(nn.Module):
    def __init__(self, scalar_in, scalar_out, vector_in, vector_out):
        super().__init__()
        self.scalar_linear = nn.Linear(scalar_in + vector_in, scalar_out)
        self.vector_weight = nn.Parameter(torch.randn(vector_out, vector_in))
        nn.init.xavier_uniform_(self.vector_weight)
        self.vector_gate = nn.Linear(scalar_in + vector_in, vector_out)

    def forward(self, scalar_feat, vector_feat):
        return mod.se3_forward_cuda(
            scalar_feat.contiguous(),
            vector_feat.contiguous(),
            self.scalar_linear.weight,
            self.scalar_linear.bias,
            self.vector_weight,
            self.vector_gate.weight,
            self.vector_gate.bias
        )