FW-GLM-5-1 · popcorn · pop_l123_st_gpt
incorrect 5_FlowMatchingProbabilityFlowODE.py
model
FW-GLM-5-1
turns / max
1 / 1
tool calls
1
tokens (total)
22503
wall clock
571.8s
truncated
yes
backend
cuda fp32
variant
popcorn
runtime
—
ref runtime
3.64 μs
speedup
—
started
2026-05-11T09:08:58Z
correctness issue:
timingkernelbench.timing
kernel runtime
—
ref runtime
3.64 μs
speedup
—
kernel mean / median / std
—
ref mean / median / std
3.64 / — / 0.16 μs
roofline / occupancynsight_hardware_counters
dram bandwidth
—
dram util %
—
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
—
bottleneck
unknown
dominant pipe %
—
arithmetic intensity
—
ridge point
20.00
L1 hit %
—
L2 hit %
—
ld sectors / req
—
st sectors / req
—
regs / thread
—
smem / block
—
block size
—
peak bw
3350.00 GB/s
peak fp32 tflops
67.00
peak fp16 tflops
1979.00
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn

# Default ModelNew is PyTorch-only. run_and_check: use check_kernel=False with backend=cuda
# until the candidate file contains a real CUDA kernel (see kernel_static_checker).


class Model(nn.Module):
    """
    **Fused probability-flow ODE integration** (flow-matching style) in one ``forward``.

    Models the ODE ``dx/dt = v_θ(x, t)`` on ``t ∈ [0, 1]`` with a learned velocity
    field. A **single fused pass** unrolls **fixed-step Euler** integration for
    ``num_steps`` substeps—typical for **real-time trajectory / action generation**
    where latency is dominated by repeated ``v_θ`` evaluations.

    This is the continuous-time analogue of a rectified-flow / conditional-flow
    sampler: one ``forward`` returns an approximate **x(1)** from **x(0)** without a
    Python loop across separate module calls (fusion target for the integrator +
    field).
    """

    def __init__(self, dim: int, num_steps: int):
        super().__init__()
        assert dim >= 1 and num_steps >= 1
        self.dim = dim
        self.num_steps = num_steps
        self.register_buffer("dt", torch.tensor(1.0 / float(num_steps)))

        self.velocity = nn.Sequential(
            nn.Linear(dim + 1, dim * 4),
            nn.SiLU(),
            nn.Linear(dim * 4, dim * 4),
            nn.SiLU(),
            nn.Linear(dim * 4, dim),
        )

    def forward(self, x0: torch.Tensor) -> torch.Tensor:
        """
        Args:
            x0: (B, dim) initial state at ``t = 0`` (e.g. noise or anchor trajectory).
        Returns:
            (B, dim) approximate state at ``t = 1`` after fused Euler integration.
        """
        B, d = x0.shape
        assert d == self.dim
        x = x0
        dt = self.dt.to(dtype=x0.dtype, device=x0.device)
        for k in range(self.num_steps):
            t = (float(k) + 0.5) / float(self.num_steps)
            t_b = torch.full((B, 1), t, device=x0.device, dtype=x0.dtype)
            inp = torch.cat([x, t_b], dim=-1)
            v = self.velocity(inp)
            x = x + dt * v
        return x


class ModelNew(nn.Module):
    """KernelBench candidate entry point; default delegates to ``Model``."""

    def __init__(self, dim: int, num_steps: int):
        super().__init__()
        self._impl = Model(dim, num_steps)

    def forward(self, x0: torch.Tensor) -> torch.Tensor:
        return self._impl(x0)


batch_size = 8
dim = 64
num_steps = 32


def get_inputs():
    return [torch.randn(popcorn_pri.jitter_int(batch_size), dim)]


def get_init_inputs():
    return [dim, num_steps]
turn 0 · FINAL · 1 tool call(s) · 571.8s turn · 189.3s LLM · tokens in=1,481 · out=21,022 · total=22,503 · 22,407 chars (click to expand)
turn 0571.8s turn · 189.3s LLM · 382.5s tools · tokens in=1,481 · out=21,022 · total=22,503 · FINAL · reasoning clipped
reasoning
reasoning (truncated 53961 chars): This task requires me to implement a custom CUDA kernel to replace the forward computation of the PyTorch model. The model implements a probability-flow ODE integration using Euler method with a learned velocity field. Let me understand the model: 1. It takes an initial state `x0` of shape (B, dim) 2. It performs `num_steps` Euler integration steps 3. Each step: - Computes time `t = (k + 0.5) / num_steps` - Creates a tensor `t_b` of shape (B, 1) filled with `t` - Concatenates `x` and `t_b` along the last dimension -> shape (B, dim+1) - Passes through velocity network: Linear(dim+1, dim*4) -> SiLU -> Linear(dim*4, dim*4) -> SiLU -> Linear(dim*4, dim) - Updates `x = x + dt * v` 4. Returns the final `x` The velocity network is: - Linear(dim+1, dim*4) - SiLU activation - Linear(dim*4, dim*4) - SiLU activation - Linear(dim*4, dim) To implement this efficiently in CUDA, I need to: 1. Fuse all the operations into a single kernel 2. Avoid the Python loop overhead 3. Minimize memory transfers Key challenges: - The velocity network has three linear layers with SiLU activations - Need to unroll the `num_steps` iterations - Each step depends on the previous step's output Optimization strategies: 1. Fuse all linear layers and activations into a single kernel per step, or better yet, fuse all steps 2. Use shared memory for intermediate results 3. Ensure coalesced memory access Let me think about the data flow: - Input: x0 of shape (B, dim) - For each step: - Input to velocity: [x, t] of shape (B, dim+1) - Layer 1 output: (B, dim*4) - Layer 2 output: (B, dim*4) - Layer 3 output: (B, dim) - Update: x = x + dt * v I need to access the weights from the PyTorch model. The approach is: 1. Load the weights from the trained model 2. Use them in the CUDA kernel Let me write the CUDA kernel. I'll need to: 1. Store the weights as constants or pass them as pointers 2. Implement matrix-vector multiplication for each linear layer 3. Implement SiLU activation (x * sigmoid(x)) 4. Loop through the steps Since batch_size=8, dim=64, num_steps=32: - Each batch element is independent, so I can use one thread per batch element - Each linear layer involves a matrix multiplication For a batch of 8 elements with dimension 64: - Layer 1: (dim+1) x (dim*4) = 65 x 256 matrix - Layer 2: (dim*4) x (dim*4) = 256 x 256 matrix - Layer 3: (dim*4) x dim = 256 x 64 matrix The total computation per step is about 65*256 + 256*256 + 256*64 = 16640 + 65536 + 16384 = 98560 multiplications. With 32 steps, that's about 3.15M operations per batch element, or 25.2M total. Given the small batch size (8), I should probably have each thread handle one batch element and iterate through all operations. Let me design the kernel: - Grid: (B,) - one block per batch element? Or (1,) with threads for each batch element? - Actually, with B=8, I could have one block with 8 threads (or more for parallelism within each element) Better approach: Use thread blocks where each block handles a batch element, and threads within the block cooperate on the matrix-vector products. But wait, with dim=64 and hidden_dim=256, the intermediate tensors are relatively small. I could: 1. Use one thread per batch element (8 threads total) - too few, poor GPU utilization 2. Use one warp per batch element (32 threads, 8 warps = 256 threads total for batch) - better 3. Use multiple warps per batch element for parallelization Actually, let me think differently. The main computation is the linear layers. For each linear layer: - For input size N and output size M, we compute output[i] = sum_j(W[i,j] * input[j]) + bias[i] - This is a matrix-vector product For the velocity network: - Input: 65 elements (64 from x + 1 from t) - Hidden: 256 elements - Output: 64 elements Let me use one thread block per batch element, with threads cooperating on the computation: - Block size: 256 threads (one warp per 32 output elements, or spread differently) - Each thread computes a portion of the output For a matrix-vector product with output size M: - Each thread computes one or more output elements - Threads can load input cooperatively into shared memory Let me write a simpler version first: - One thread per batch element (B threads) - Each thread serially computes all the linear layers and steps This won't be optimal but will work. Then I can optimize. Actually, with such small dimensions, the overhead of kernel launches and synchronization might dominate. Let me consider a different approach: - Fuse everything into one kernel - Use shared memory for intermediate results - Parallelize across batch elements and computation within each batch element Let me write the kernel: ```cpp #include <cuda.h> #include <cuda_runtime.h> __device__ float silu(float x) { return x * (1.0f / (1.0f + expf(-x))); } // Assumes weights are in global memory, passed as pointers // Grid: (B, 1, 1), Block: (256, 1, 1) // Each block handles one batch element __global__ void flow_ode_kernel( const float* __restrict__ x0, float* __restrict__ x_out, const float* __restrict__ w1, // (dim*4, dim+1) const float* __restrict__ b1, // (dim*4,) const float* __restrict__ w2, // (dim*4, dim*4) const float* __restrict__ b2, // (dim*4,) const float* __restrict__ w3, // (dim, dim*4) const float* __restrict__ b3, // (dim,) int dim, int num_steps, float dt ) { int batch_idx = blockIdx.x; int tid = threadIdx.x; // Shared memory for intermediate results extern __shared__ float shared[]; float* x = shared; // dim float* hidden1 = x + dim; // dim*4 float* hidden2 = hidden1 + dim * 4; // dim*4 float* v = hidden2 + dim * 4; // dim float* input = v + dim; // dim + 1 // Load x0 into shared memory if (tid < dim) { x[tid] = x0[batch_idx * dim + tid]; } __syncthreads(); for (int step = 0; step < num_steps; step++) { float t = (float(step) + 0.5f) / float(num_steps); // Prepare input: [x, t] if (tid < dim) { input[tid] = x[tid]; } if (tid == 0) { input[dim] = t; } __syncthreads(); // Layer 1: Linear(dim+1, dim*4) + SiLU for (int i = tid; i < dim * 4; i += blockDim.x) { float sum = b1[i]; for (int j = 0; j < dim + 1; j++) { sum += w1[i * (dim + 1) + j] * input[j]; } hidden1[i] = silu(sum); } __syncthreads(); // Layer 2: Linear(dim*4, dim*4) + SiLU for (int i = tid; i < dim * 4; i += blockDim.x) { float sum = b2[i]; for (int j = 0; j < dim * 4; j++) { sum += w2[i * dim * 4 + j] * hidden1[j]; } hidden2[i] = silu(sum); } __syncthreads(); // Layer 3: Linear(dim*4, dim) for (int i = tid; i < dim; i += blockDim.x) { float sum = b3[i]; for (int j = 0; j < dim * 4; j++) { sum += w3[i * dim * 4 + j] * hidden2[j]; } v[i] = sum; } __syncthreads(); // Update: x = x + dt * v if (tid < dim) { x[tid] = x[tid] + dt * v[tid]; } __syncthreads(); } // Write result to global memory if (tid < dim) { x_out[batch_idx * dim + tid] = x[tid]; } } ``` Wait, the shared memory size needs to be computed properly: - x: dim = 64 floats - hidden1: dim * 4 = 256 floats - hidden2: dim * 4 = 256 floats - v: dim = 64 floats - input: dim + 1 = 65 floats - Total: 64 + 256 + 256 + 64 + 65 = 705 floats = 2820 bytes This fits easily in shared memory (typically 48KB or more). Let me think about how to pass the weights. In PyTorch, I need to: 1. Extract weights from the velocity network 2. Pass them to the CUDA kernel The velocity netwo ...[ omitted 53961 characters ]... main kernel doesn't need it. Let me also consider using `__launch_bounds__` to tell the compiler about the maximum block size: ```cpp __global__ void __launch_bounds__(256) flow_ode_kernel(...) ``` This helps the compiler optimize register usage for the given block size. Alright, let me update the kernel with these optimizations: ```cpp __global__ void __launch_bounds__(256) flow_ode_kernel( const float* __restrict__ x0, float* __restrict__ x_out, const float* __restrict__ w1, const float* __restrict__ b1, const float* __restrict__ w2, const float* __restrict__ b2, const float* __restrict__ w3, const float* __restrict__ b3, int dim, int num_steps, float dt ) { const int batch_idx = blockIdx.x; const int tid = threadIdx.x; const int block_size = blockDim.x; const int hidden_size = dim * 4; const int input_size = dim + 1; extern __shared__ float shared[]; float* x = shared; float* hidden1 = x + dim; float* hidden2 = hidden1 + hidden_size; float* v = hidden2 + hidden_size; float* input = v + dim; for (int i = tid; i < dim; i += block_size) { x[i] = __ldg(&x0[batch_idx * dim + i]); } __syncthreads(); for (int step = 0; step < num_steps; step++) { const float t = (float(step) + 0.5f) / float(num_steps); for (int i = tid; i < dim; i += block_size) { input[i] = x[i]; } if (tid == 0) input[dim] = t; __syncthreads(); for (int i = tid; i < hidden_size; i += block_size) { float sum = b1[i]; const float* w_row = w1 + i * input_size; for (int j = 0; j < input_size; j++) { sum += __ldg(&w_row[j]) * input[j]; } hidden1[i] = silu(sum); } __syncthreads(); for (int i = tid; i < hidden_size; i += block_size) { float sum = b2[i]; const float* w_row = w2 + i * hidden_size; for (int j = 0; j < hidden_size; j++) { sum += __ldg(&w_row[j]) * hidden1[j]; } hidden2[i] = silu(sum); } __syncthreads(); for (int i = tid; i < dim; i += block_size) { float sum = b3[i]; const float* w_row = w3 + i * hidden_size; for (int j = 0; j < hidden_size; j++) { sum += __ldg(&w_row[j]) * hidden2[j]; } v[i] = sum; } __syncthreads(); for (int i = tid; i < dim; i += block_size) { x[i] = fmaf(dt, v[i], x[i]); } __syncthreads(); } for (int i = tid; i < dim; i += block_size) { x_out[batch_idx * dim + i] = x[i]; } } ``` I added `__ldg` for reading from `x0` initially, and for reading weights. I also pre-computed the row pointer `w_row` to reduce address computation overhead. Let me finalize and submit. One more thing: I should check if the bias vectors need `__ldg`. They're small and accessed once per output element, so caching them shouldn't make a big difference. But for consistency, I'll add it: ```cpp float sum = __ldg(&b1[i]); ``` Alright, final version: ```python import torch import torch.nn as nn from torch.utils.cpp_extension import load_inline cuda_source = r""" #include <cuda.h> #include <cuda_runtime.h> __device__ __forceinline__ float silu(float x) { return x / (1.0f + expf(-x)); } __global__ void __launch_bounds__(256) flow_ode_kernel( const float* __restrict__ x0, float* __restrict__ x_out, const float* __restrict__ w1, const float* __restrict__ b1, const float* __restrict__ w2, const float* __restrict__ b2, const float* __restrict__ w3, const float* __restrict__ b3, int dim, int num_steps, float dt ) { const int batch_idx = blockIdx.x; const int tid = threadIdx.x; const int block_size = blockDim.x; const int hidden_size = dim * 4; const int input_size = dim + 1; extern __shared__ float shared[]; float* x = shared; float* hidden1 = x + dim; float* hidden2 = hidden1 + hidden_size; float* v = hidden2 + hidden_size; float* input = v + dim; for (int i = tid; i < dim; i += block_size) { x[i] = __ldg(&x0[batch_idx * dim + i]); } __syncthreads(); for (int step = 0; step < num_steps; step++) { const float t = (float(step) + 0.5f) / float(num_steps); for (int i = tid; i < dim; i += block_size) { input[i] = x[i]; } if (tid == 0) input[dim] = t; __syncthreads(); for (int i = tid; i < hidden_size; i += block_size) { float sum = __ldg(&b1[i]); for (int j = 0; j < input_size; j++) { sum += __ldg(&w1[i * input_size + j]) * input[j]; } hidden1[i] = silu(sum); } __syncthreads(); for (int i = tid; i < hidden_size; i += block_size) { float sum = __ldg(&b2[i]); for (int j = 0; j < hidden_size; j++) { sum += __ldg(&w2[i * hidden_size + j]) * hidden1[j]; } hidden2[i] = silu(sum); } __syncthreads(); for (int i = tid; i < dim; i += block_size) { float sum = __ldg(&b3[i]); for (int j = 0; j < hidden_size; j++) { sum += __ldg(&w3[i * hidden_size + j]) * hidden2[j]; } v[i] = sum; } __syncthreads(); for (int i = tid; i < dim; i += block_size) { x[i] = fmaf(dt, v[i], x[i]); } __syncthreads(); } for (int i = tid; i < dim; i += block_size) { x_out[batch_idx * dim + i] = x[i]; } } torch::Tensor flow_ode_cuda( torch::Tensor x0, torch::Tensor w1, torch::Tensor b1, torch::Tensor w2, torch::Tensor b2, torch::Tensor w3, torch::Tensor b3, int dim, int num_steps, float dt ) { const int B = x0.size(0); auto x_out = torch::empty_like(x0); const int threads = 256; const int shared_mem = (11 * dim + 1) * sizeof(float); flow_ode_kernel<<<B, threads, shared_mem>>>( x0.data_ptr<float>(), x_out.data_ptr<float>(), w1.data_ptr<float>(), b1.data_ptr<float>(), w2.data_ptr<float>(), b2.data_ptr<float>(), w3.data_ptr<float>(), b3.data_ptr<float>(), dim, num_steps, dt ); return x_out; } """ flow_ode_module = load_inline( name='flow_ode', cpp_sources=['torch::Tensor flow_ode_cuda(torch::Tensor x0, torch::Tensor w1, torch::Tensor b1, torch::Tensor w2, torch::Tensor b2, torch::Tensor w3, torch::Tensor b3, int dim, int num_steps, float dt);'], cuda_sources=[cuda_source], functions=['flow_ode_cuda'], extra_cuda_cflags=['-O3', '-use_fast_math'], verbose=False ) class ModelNew(nn.Module): def __init__(self, dim: int, num_steps: int): super().__init__() self.dim = dim self.num_steps = num_steps self.velocity = nn.Sequential( nn.Linear(dim + 1, dim * 4), nn.SiLU(), nn.Linear(dim * 4, dim * 4), nn.SiLU(), nn.Linear(dim * 4, dim), ) self.register_buffer("dt", torch.tensor(1.0 / float(num_steps))) def forward(self, x0: torch.Tensor) -> torch.Tensor: B, d = x0.shape assert d == self.dim w1 = self.velocity[0].weight b1 = self.velocity[0].bias w2 = self.velocity[3].weight b2 = self.velocity[3].bias w3 = self.velocity[6].weight b3 = self.velocity[6].bias return flow_ode_module.flow_ode_cuda( x0, w1, b1, w2, b2, w3, b3, self.dim, self.num_steps, self.dt.item() ) ``` This is my final version. Let me submit it.
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 <cuda.h>\n#include <cuda_runtime.h>\n\n__device__ __forceinline__ float silu(float x) {\n return x / (1.0f + expf(-x));\n}\n\n__global__ void __launch_bounds__(256) flow_ode_kernel(\n const float* __restrict__ x0,\n float* __restrict__ x_out,\n const float* __restrict__ w1,\n const float* __restrict__ b1,\n const float* __restrict__ w2,\n const float* __restrict__ b2,\n const float* __restrict__ w3,\n const float* __restrict__ b3,\n int dim,\n int num_steps,\n float dt\n) {\n const int batch_idx = blockIdx.x;\n const int tid = threadIdx.x;\n const int block_size = blockDim.x;\n const int hidden_size = dim * 4;\n const int input_size = dim + 1;\n \n extern __shared__ float shared[];\n float* x = shared;\n float* hidden1 = x + dim;\n float* hidden2 = hidden1 + hidden_size;\n float* v = hidden2 + hidden_size;\n float* input = v + dim;\n \n for (int i = tid; i < dim; i += block_size) {\n x[i] = __ldg(&x0[batch_idx * dim + i]);\n }\n __syncthreads();\n \n for (int step = 0; step < num_steps; step++) {\n const float t = (float(step) + 0.5f) / float(num_steps);\n \n for (int i = tid; i < dim; i += block_size) {\n input[i] = x[i];\n }\n if (tid == 0) input[dim] = t;\n __syncthreads();\n \n for (int i = tid; i < hidden_size; i += block_size) {\n float sum = __ldg(&b1[i]);\n for (int j = 0; j < input_size; j++) {\n sum += __ldg(&w1[i * input_size + j]) * input[j];\n }\n hidden1[i] = silu(sum);\n }\n __syncthreads();\n \n for (int i = tid; i < hidden_size; i += block_size) {\n float sum = __ldg(&b2[i]);\n for (int j = 0; j < hidden_size; j++) {\n sum += __ldg(&w2[i * hidden_size + j]) * hidden1[j];\n }\n hidden2[i] = silu(sum);\n }\n __syncthreads();\n \n for (int i = tid; i < dim; i += block_size) {\n float sum = __ldg(&b3[i]);\n for (int j = 0; j < hidden_size; j++) {\n sum += __ldg(&w3[i * hidden_size + j]) * hidden2[j];\n }\n v[i] = sum;\n }\n __syncthreads();\n \n for (int i = tid; i < dim; i += block_size) {\n x[i] = fmaf(dt, v[i], x[i]);\n }\n __syncthreads();\n }\n \n for (int i = tid; i < dim; i += block_size) {\n x_out[batch_idx * dim + i] = x[i];\n }\n}\n\ntorch::Tensor flow_ode_cuda(\n torch::Tensor x0,\n torch::Tensor w1, torch::Tensor b1,\n torch::Tensor w2, torch::Tensor b2,\n torch::Tensor w3, torch::Tensor b3,\n int dim, int num_steps, float dt\n) {\n const int B = x0.size(0);\n auto x_out = torch::empty_like(x0);\n \n const int threads = 256;\n const int shared_mem = (11 * dim + 1) * sizeof(float);\n \n flow_ode_kernel<<<B, threads, shared_mem>>>(\n x0.data_ptr<float>(),\n x_out.data_ptr<float>(),\n w1.data_ptr<float>(),\n b1.data_ptr<float>(),\n w2.data_ptr<float>(),\n b2.data_ptr<float>(),\n w3.data_ptr<float>(),\n b3.data_ptr<float>(),\n dim, num_steps, dt\n );\n \n return x_out;\n}\n\"\"\"\n\nflow_ode_module = load_inline(\n name='flow_ode',\n cpp_sources=['torch::Tensor flow_ode_cuda(torch::Tensor x0, torch::Tensor w1, torch::Tensor b1, torch::Tensor w2, torch::Tensor b2, torch::Tensor w3, torch::Tensor b3, int dim, int num_steps, float dt);'],\n cuda_sources=[cuda_source],\n functions=['flow_ode_cuda'],\n extra_cuda_cflags=['-O3', '-use_fast_math'],\n verbose=False\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, dim: int, num_steps: int):\n super().__init__()\n self.dim = dim\n self.num_steps = num_steps\n \n self.velocity = nn.Sequential(\n nn.Linear(dim + 1, dim * 4),\n nn.SiLU(),\n nn.Linear(dim * 4, dim * 4),\n nn.SiLU(),\n nn.Linear(dim * 4, dim),\n )\n \n self.register_buffer(\"dt\", torch.tensor(1.0 / float(num_steps)))\n \n def forward(self, x0: torch.Tensor) -> torch.Tensor:\n B, d = x0.shape\n assert d == self.dim\n \n w1 = self.velocity[0].weight\n b1 = self.velocity[0].bias\n w2 = self.velocity[3].weight\n b2 = self.velocity[3].bias\n w3 = self.velocity[6].weight\n b3 = self.velocity[6].bias\n \n return flow_ode_module.flow_ode_cuda(\n x0, w1, b1, w2, b2, w3, b3,\n self.dim, self.num_steps, self.dt.item()\n )" }
result ← submit_kernel (fail)
failsubmit_kernel submit_kernel FAILED: correctness check did not pass (? trials). runtime_error: 'SiLU' object has no attribute 'weight'
final submitted kernel (159 lines, 4,688 chars) — full source, untruncated
import torch
import torch.nn as nn
from torch.utils.cpp_extension import load_inline

cuda_source = r"""
#include <cuda.h>
#include <cuda_runtime.h>

__device__ __forceinline__ float silu(float x) {
    return x / (1.0f + expf(-x));
}

__global__ void __launch_bounds__(256) flow_ode_kernel(
    const float* __restrict__ x0,
    float* __restrict__ x_out,
    const float* __restrict__ w1,
    const float* __restrict__ b1,
    const float* __restrict__ w2,
    const float* __restrict__ b2,
    const float* __restrict__ w3,
    const float* __restrict__ b3,
    int dim,
    int num_steps,
    float dt
) {
    const int batch_idx = blockIdx.x;
    const int tid = threadIdx.x;
    const int block_size = blockDim.x;
    const int hidden_size = dim * 4;
    const int input_size = dim + 1;
    
    extern __shared__ float shared[];
    float* x = shared;
    float* hidden1 = x + dim;
    float* hidden2 = hidden1 + hidden_size;
    float* v = hidden2 + hidden_size;
    float* input = v + dim;
    
    for (int i = tid; i < dim; i += block_size) {
        x[i] = __ldg(&x0[batch_idx * dim + i]);
    }
    __syncthreads();
    
    for (int step = 0; step < num_steps; step++) {
        const float t = (float(step) + 0.5f) / float(num_steps);
        
        for (int i = tid; i < dim; i += block_size) {
            input[i] = x[i];
        }
        if (tid == 0) input[dim] = t;
        __syncthreads();
        
        for (int i = tid; i < hidden_size; i += block_size) {
            float sum = __ldg(&b1[i]);
            for (int j = 0; j < input_size; j++) {
                sum += __ldg(&w1[i * input_size + j]) * input[j];
            }
            hidden1[i] = silu(sum);
        }
        __syncthreads();
        
        for (int i = tid; i < hidden_size; i += block_size) {
            float sum = __ldg(&b2[i]);
            for (int j = 0; j < hidden_size; j++) {
                sum += __ldg(&w2[i * hidden_size + j]) * hidden1[j];
            }
            hidden2[i] = silu(sum);
        }
        __syncthreads();
        
        for (int i = tid; i < dim; i += block_size) {
            float sum = __ldg(&b3[i]);
            for (int j = 0; j < hidden_size; j++) {
                sum += __ldg(&w3[i * hidden_size + j]) * hidden2[j];
            }
            v[i] = sum;
        }
        __syncthreads();
        
        for (int i = tid; i < dim; i += block_size) {
            x[i] = fmaf(dt, v[i], x[i]);
        }
        __syncthreads();
    }
    
    for (int i = tid; i < dim; i += block_size) {
        x_out[batch_idx * dim + i] = x[i];
    }
}

torch::Tensor flow_ode_cuda(
    torch::Tensor x0,
    torch::Tensor w1, torch::Tensor b1,
    torch::Tensor w2, torch::Tensor b2,
    torch::Tensor w3, torch::Tensor b3,
    int dim, int num_steps, float dt
) {
    const int B = x0.size(0);
    auto x_out = torch::empty_like(x0);
    
    const int threads = 256;
    const int shared_mem = (11 * dim + 1) * sizeof(float);
    
    flow_ode_kernel<<<B, threads, shared_mem>>>(
        x0.data_ptr<float>(),
        x_out.data_ptr<float>(),
        w1.data_ptr<float>(),
        b1.data_ptr<float>(),
        w2.data_ptr<float>(),
        b2.data_ptr<float>(),
        w3.data_ptr<float>(),
        b3.data_ptr<float>(),
        dim, num_steps, dt
    );
    
    return x_out;
}
"""

flow_ode_module = load_inline(
    name='flow_ode',
    cpp_sources=['torch::Tensor flow_ode_cuda(torch::Tensor x0, torch::Tensor w1, torch::Tensor b1, torch::Tensor w2, torch::Tensor b2, torch::Tensor w3, torch::Tensor b3, int dim, int num_steps, float dt);'],
    cuda_sources=[cuda_source],
    functions=['flow_ode_cuda'],
    extra_cuda_cflags=['-O3', '-use_fast_math'],
    verbose=False
)

class ModelNew(nn.Module):
    def __init__(self, dim: int, num_steps: int):
        super().__init__()
        self.dim = dim
        self.num_steps = num_steps
        
        self.velocity = nn.Sequential(
            nn.Linear(dim + 1, dim * 4),
            nn.SiLU(),
            nn.Linear(dim * 4, dim * 4),
            nn.SiLU(),
            nn.Linear(dim * 4, dim),
        )
        
        self.register_buffer("dt", torch.tensor(1.0 / float(num_steps)))
    
    def forward(self, x0: torch.Tensor) -> torch.Tensor:
        B, d = x0.shape
        assert d == self.dim
        
        w1 = self.velocity[0].weight
        b1 = self.velocity[0].bias
        w2 = self.velocity[3].weight
        b2 = self.velocity[3].bias
        w3 = self.velocity[6].weight
        b3 = self.velocity[6].bias
        
        return flow_ode_module.flow_ode_cuda(
            x0, w1, b1, w2, b2, w3, b3,
            self.dim, self.num_steps, self.dt.item()
        )