incorrect
11_DegreeNormalizedAggregation.py
started
2026-05-11T21:45:07Z
correctness issue:
timingkernelbench.timing
kernel mean / median / std
—
ref mean / median / std
72.40 / — / 0.98 μs
SOL (speed-of-light)nsight_hardware_counters
roofline / occupancynsight_hardware_counters
warp stalls (top 5)nsight
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn
class Model(nn.Module):
"""
Degree-normalized neighbor aggregation.
Computes a GCN-style sparse aggregation where each incoming message is
scaled by 1 / sqrt(deg(dst) * deg(src)). This captures a common spectral
graph normalization pattern over CSR adjacency.
"""
def __init__(self):
super().__init__()
def forward(
self,
row_ptr: torch.Tensor,
col_idx: torch.Tensor,
node_feat: torch.Tensor,
degrees: torch.Tensor,
) -> torch.Tensor:
num_nodes = row_ptr.numel() - 1
feat_dim = node_feat.shape[1]
out = torch.zeros(num_nodes, feat_dim, dtype=node_feat.dtype, device=node_feat.device)
for dst in range(num_nodes):
start = int(row_ptr[dst].item())
end = int(row_ptr[dst + 1].item())
if end <= start:
continue
src = col_idx[start:end].long()
norm = 1.0 / torch.sqrt(degrees[dst] * degrees[src]).unsqueeze(-1)
out[dst] = (node_feat[src] * norm).sum(dim=0)
return out
num_nodes = 640
avg_degree = 16
feat_dim = 80
def get_inputs():
return list(popcorn_pri.make_csr_degree_normalized_graph(num_nodes, avg_degree, feat_dim))
def get_init_inputs():
return []
turn 0376.5s turn · 20.6s LLM · 355.9s tools · tokens in=2,259 · out=589 · total=3,727
reasoning
The task is to replace the forward computation of a PyTorch model with a custom CUDA kernel. The model performs degree-normalized neighbor aggregation, like a GCN-style sparse aggregation.
I need to replace the forward pass of a PyTorch model with a custom CUDA kernel for degree-normalized neighbor aggregation, similar to GCN. The inputs are CSR formatted adjacency (row_ptr, col_idx), node features, and degrees. For each destination node, I aggregate from its neighbors with scaling by 1 / sqrt(deg_dst * deg_src).
To implement this, I'll use a CUDA kernel where each block handles one destination node. It loops over neighbors, computes the normalization, and accumulates the scaled neighbor features into the output for that node. To handle the feature dimension (80), I'll parallelize across threads for the inner loop over features, using atomicAdd to avoid race conditions when updating the output array.
I'll use torch.utils.cpp_extension.load_inline to load the kernel without external files. The kernel code will include necessary headers and launch configuration.
call → compile_kernel
callcompile_kernel
{
"kernel_code": "import torch\nimport torch.nn as nn\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\n__global__ void gcn_agg_kernel(\n const int* __restrict__ row_ptr,\n const int* __restrict__ col_idx,\n const float* __restrict__ node_feat,\n const float* __restrict__ degrees,\n float* __restrict__ out,\n int num_nodes,\n int feat_dim\n) {\n int dst = blockIdx.x;\n if (dst >= num_nodes) return;\n int start = row_ptr[dst];\n int end = row_ptr[dst + 1];\n float deg_dst = degrees[dst];\n for (int i = start; i < end; ++i) {\n int src = col_idx[i];\n float deg_src = degrees[src];\n float norm = 1.0f / sqrtf(deg_dst * deg_src);\n for (int f = threadIdx.x; f < feat_dim; f += blockDim.x) {\n atomicAdd(&out[dst * feat_dim + f], node_feat[src * feat_dim + f] * norm);\n }\n }\n}\n\ntorch::Tensor gcn_aggregate_cuda(\n torch::Tensor row_ptr,\n torch::Tensor col_idx,\n torch::Tensor node_feat,\n torch::Tensor degrees\n) {\n int num_nodes = row_ptr.size(0) - 1;\n int feat_dim = node_feat.size(1);\n auto out = torch::zeros({num_nodes, feat_dim}, node_feat.options());\n dim3 grid(num_nodes);\n dim3 block(32); // 32 threads per block\n gcn_agg_kernel<<<grid, block>>>(\n row_ptr.data_ptr<int>(),\n col_idx.data_ptr<int>(),\n node_feat.data_ptr<float>(),\n degrees.data_ptr<float>(),\n out.data_ptr<float>(),\n num_nodes,\n feat_dim\n );\n return out;\n}\n'''\n\ncpp_source = '''\ntorch::Tensor gcn_aggregate_cuda(\n torch::Tensor row_ptr,\n torch::Tensor col_idx,\n torch::Tensor node_feat,\n torch::Tensor degrees\n);\n'''\n\nmodule = load_inline(\n name='gcn_agg',\n cpp_sources=[cpp_source],\n cuda_sources=[cuda_source],\n functions=['gcn_aggregate_cuda'],\n verbose=True,\n extra_cuda_cflags=['-O3']\n)\n\nclass ModelNew(nn.Module):\n def __init__(self):\n super().__init__()\n self.module = module\n\n def forward(self, row_ptr, col_idx, node_feat, degrees):\n return self.module.gcn_aggregate_cuda(row_ptr, col_idx, node_feat, degrees)\n"
}
result ← compile_kernel (ok)
okcompile_kernel
compile_kernel PASSED: kernel compiled without errors.
turn 1162.0s turn · 4.8s LLM · 157.3s tools · tokens in=3,744 · out=590 · total=4,342
reasoning
Kernel compiled successfully. Now check correctness.
call → run_correctness
callrun_correctness
{
"kernel_code": "import torch\nimport torch.nn as nn\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\n__global__ void gcn_agg_kernel(\n const int* __restrict__ row_ptr,\n const int* __restrict__ col_idx,\n const float* __restrict__ node_feat,\n const float* __restrict__ degrees,\n float* __restrict__ out,\n int num_nodes,\n int feat_dim\n) {\n int dst = blockIdx.x;\n if (dst >= num_nodes) return;\n int start = row_ptr[dst];\n int end = row_ptr[dst + 1];\n float deg_dst = degrees[dst];\n for (int i = start; i < end; ++i) {\n int src = col_idx[i];\n float deg_src = degrees[src];\n float norm = 1.0f / sqrtf(deg_dst * deg_src);\n for (int f = threadIdx.x; f < feat_dim; f += blockDim.x) {\n atomicAdd(&out[dst * feat_dim + f], node_feat[src * feat_dim + f] * norm);\n }\n }\n}\n\ntorch::Tensor gcn_aggregate_cuda(\n torch::Tensor row_ptr,\n torch::Tensor col_idx,\n torch::Tensor node_feat,\n torch::Tensor degrees\n) {\n int num_nodes = row_ptr.size(0) - 1;\n int feat_dim = node_feat.size(1);\n auto out = torch::zeros({num_nodes, feat_dim}, node_feat.options());\n dim3 grid(num_nodes);\n dim3 block(32); // 32 threads per block\n gcn_agg_kernel<<<grid, block>>>(\n row_ptr.data_ptr<int>(),\n col_idx.data_ptr<int>(),\n node_feat.data_ptr<float>(),\n degrees.data_ptr<float>(),\n out.data_ptr<float>(),\n num_nodes,\n feat_dim\n );\n return out;\n}\n'''\n\ncpp_source = '''\ntorch::Tensor gcn_aggregate_cuda(\n torch::Tensor row_ptr,\n torch::Tensor col_idx,\n torch::Tensor node_feat,\n torch::Tensor degrees\n);\n'''\n\nmodule = load_inline(\n name='gcn_agg',\n cpp_sources=[cpp_source],\n cuda_sources=[cuda_source],\n functions=['gcn_aggregate_cuda'],\n verbose=False,\n extra_cuda_cflags=['-O3']\n)\n\nclass ModelNew(nn.Module):\n def __init__(self):\n super().__init__()\n self.module = module\n\n def forward(self, row_ptr, col_idx, node_feat, degrees):\n return self.module.gcn_aggregate_cuda(row_ptr, col_idx, node_feat, degrees)\n"
}
result ← run_correctness (fail)
failrun_correctness
run_correctness FAILED: ? trials did not all match.
runtime_error: expected scalar type Int but found Float
turn 2242.6s turn · 12.8s LLM · 229.8s tools · tokens in=4,371 · out=592 · total=5,512
reasoning
Error: degrees is float but something expects int? No, row_ptr and col_idx are int32, degrees float.
I noticed an error message about expecting an integer type but finding a float. This seems to happen because degrees is computed as a float32 tensor, specifically with torch.bincount and then adding 1.0, making it float. Meanwhile, row_ptr and col_idx are int32.
call → compile_kernel
callcompile_kernel
{
"kernel_code": "import torch\nimport torch.nn as nn\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\n__global__ void gcn_agg_kernel(\n const int* __restrict__ row_ptr,\n const int* __restrict__ col_idx,\n const float* __restrict__ node_feat,\n const float* __restrict__ degrees,\n float* __restrict__ out,\n int num_nodes,\n int feat_dim\n) {\n int dst = blockIdx.x * blockDim.x + threadIdx.x;\n if (dst >= num_nodes) return;\n int start = row_ptr[dst];\n int end = row_ptr[dst + 1];\n float deg_dst = degrees[dst];\n for (int i = start; i < end; ++i) {\n int src = col_idx[i];\n float deg_src = degrees[src];\n float norm = 1.0f / sqrtf(deg_dst * deg_src);\n for (int f = 0; f < feat_dim; ++f) {\n out[dst * feat_dim + f] += node_feat[src * feat_dim + f] * norm;\n }\n }\n}\n\ntorch::Tensor gcn_aggregate_cuda(\n torch::Tensor row_ptr,\n torch::Tensor col_idx,\n torch::Tensor node_feat,\n torch::Tensor degrees\n) {\n int num_nodes = row_ptr.size(0) - 1;\n int feat_dim = node_feat.size(1);\n auto out = torch::zeros({num_nodes, feat_dim}, node_feat.options());\n int threads = 256;\n int blocks = (num_nodes + threads - 1) / threads;\n gcn_agg_kernel<<<blocks, threads>>>(\n row_ptr.data_ptr<int>(),\n col_idx.data_ptr<int>(),\n node_feat.data_ptr<float>(),\n degrees.data_ptr<float>(),\n out.data_ptr<float>(),\n num_nodes,\n feat_dim\n );\n return out;\n}\n'''\n\ncpp_source = '''\ntorch::Tensor gcn_aggregate_cuda(\n torch::Tensor row_ptr,\n torch::Tensor col_idx,\n torch::Tensor node_feat,\n torch::Tensor degrees\n);\n'''\n\nmodule = load_inline(\n name='gcn_agg',\n cpp_sources=[cpp_source],\n cuda_sources=[cuda_source],\n functions=['gcn_aggregate_cuda'],\n verbose=False,\n extra_cuda_cflags=['-O3']\n)\n\nclass ModelNew(nn.Module):\n def __init__(self):\n super().__init__()\n self.module = module\n\n def forward(self, row_ptr, col_idx, node_feat, degrees):\n return self.module.gcn_aggregate_cuda(row_ptr, col_idx, node_feat, degrees)\n"
}
result ← compile_kernel (ok)
okcompile_kernel
compile_kernel PASSED: kernel compiled without errors.
turn 3167.6s turn · 6.7s LLM · 160.9s tools · tokens in=5,529 · out=593 · total=6,126
reasoning
Now run correctness.
call → run_correctness
callrun_correctness
{
"kernel_code": "import torch\nimport torch.nn as nn\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\n__global__ void gcn_agg_kernel(\n const int* __restrict__ row_ptr,\n const int* __restrict__ col_idx,\n const float* __restrict__ node_feat,\n const float* __restrict__ degrees,\n float* __restrict__ out,\n int num_nodes,\n int feat_dim\n) {\n int dst = blockIdx.x * blockDim.x + threadIdx.x;\n if (dst >= num_nodes) return;\n int start = row_ptr[dst];\n int end = row_ptr[dst + 1];\n float deg_dst = degrees[dst];\n for (int i = start; i < end; ++i) {\n int src = col_idx[i];\n float deg_src = degrees[src];\n float norm = 1.0f / sqrtf(deg_dst * deg_src);\n for (int f = 0; f < feat_dim; ++f) {\n out[dst * feat_dim + f] += node_feat[src * feat_dim + f] * norm;\n }\n }\n}\n\ntorch::Tensor gcn_aggregate_cuda(\n torch::Tensor row_ptr,\n torch::Tensor col_idx,\n torch::Tensor node_feat,\n torch::Tensor degrees\n) {\n int num_nodes = row_ptr.size(0) - 1;\n int feat_dim = node_feat.size(1);\n auto out = torch::zeros({num_nodes, feat_dim}, node_feat.options());\n int threads = 256;\n int blocks = (num_nodes + threads - 1) / threads;\n gcn_agg_kernel<<<blocks, threads>>>(\n row_ptr.data_ptr<int>(),\n col_idx.data_ptr<int>(),\n node_feat.data_ptr<float>(),\n degrees.data_ptr<float>(),\n out.data_ptr<float>(),\n num_nodes,\n feat_dim\n );\n return out;\n}\n'''\n\ncpp_source = '''\ntorch::Tensor gcn_aggregate_cuda(\n torch::Tensor row_ptr,\n torch::Tensor col_idx,\n torch::Tensor node_feat,\n torch::Tensor degrees\n);\n'''\n\nmodule = load_inline(\n name='gcn_agg',\n cpp_sources=[cpp_source],\n cuda_sources=[cuda_source],\n functions=['gcn_aggregate_cuda'],\n verbose=False,\n extra_cuda_cflags=['-O3']\n)\n\nclass ModelNew(nn.Module):\n def __init__(self):\n super().__init__()\n self.module = module\n\n def forward(self, row_ptr, col_idx, node_feat, degrees):\n return self.module.gcn_aggregate_cuda(row_ptr, col_idx, node_feat, degrees)\n"
}
result ← run_correctness (fail)
failrun_correctness
run_correctness FAILED: ? trials did not all match.
runtime_error: expected scalar type Int but found Float
turn 4196.4s turn · 33.3s LLM · 163.1s tools · tokens in=6,155 · out=635 · total=8,207
reasoning
Still same error. The error must be coming from the reference implementation or the way inputs are handled.
I noticed the same error keeps occurring, and it seems to stem from the reference implementation or how the inputs are managed. The reference uses a Python loop with .item() and various PyTorch operations. The error "expected scalar type Int but found Float" probably arises when the reference tries to cast something like col_idx[start start:end].long(), but if col_idx is float instead of int, that could cause it.
In my ModelNew, I haven't touched anything related to casting types explicitly. The test runs both the reference and my new implementation simultaneously, and the output should match numerically except for some tolerances. The inputs from get_inputs() are supposed to be correct.
I suspect the runtime error occurs because the tensors are on CPU, but my CUDA kernel expects GPU tensors. The test might not call .cuda() on them, causing issues when launching the kernel on CPU tensors, since CUDA kernels can't run on CPU tensors directly.
To address this, I need Assistant: I need to ensure that the forward method moves the tensors to the CUDA device if they're not already there. However, that might not be ideal because the reference implementation is CPU-only and doesn't handle GPU tensors. The task requires numerical equivalence at FP32 and faster performance, so the evaluation likely happens on GPU.
The 1D nature of the message suggests that the CUDA kernel is meant to accelerate the computation, but the correctness check is done on CPU tensors. To resolve this, I can add checks in the wrapper to cast types explicitly and ensure tensors are contiguous and on the correct device.
call → compile_kernel
callcompile_kernel
{
"kernel_code": "import torch\nimport torch.nn as nn\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\n__global__ void gcn_agg_kernel(\n const int* __restrict__ row_ptr,\n const int* __restrict__ col_idx,\n const float* __restrict__ node_feat,\n const float* __restrict__ degrees,\n float* __restrict__ out,\n int num_nodes,\n int feat_dim\n) {\n int dst = blockIdx.x * blockDim.x + threadIdx.x;\n if (dst >= num_nodes) return;\n int start = row_ptr[dst];\n int end = row_ptr[dst + 1];\n float deg_dst = degrees[dst];\n for (int i = start; i < end; ++i) {\n int src = col_idx[i];\n float deg_src = degrees[src];\n float norm = 1.0f / sqrtf(deg_dst * deg_src);\n for (int f = 0; f < feat_dim; ++f) {\n out[dst * feat_dim + f] += node_feat[src * feat_dim + f] * norm;\n }\n }\n}\n\ntorch::Tensor gcn_aggregate_cuda(\n torch::Tensor row_ptr,\n torch::Tensor col_idx,\n torch::Tensor node_feat,\n torch::Tensor degrees\n) {\n row_ptr = row_ptr.contiguous();\n col_idx = col_idx.contiguous();\n node_feat = node_feat.contiguous();\n degrees = degrees.contiguous();\n int num_nodes = row_ptr.size(0) - 1;\n int feat_dim = node_feat.size(1);\n auto out = torch::zeros({num_nodes, feat_dim}, node_feat.options()).contiguous();\n int threads = 256;\n int blocks = (num_nodes + threads - 1) / threads;\n gcn_agg_kernel<<<blocks, threads>>>(\n row_ptr.data_ptr<int>(),\n col_idx.data_ptr<int>(),\n node_feat.data_ptr<float>(),\n degrees.data_ptr<float>(),\n out.data_ptr<float>(),\n num_nodes,\n feat_dim\n );\n cudaDeviceSynchronize();\n return out;\n}\n'''\n\ncpp_source = '''\ntorch::Tensor gcn_aggregate_cuda(\n torch::Tensor row_ptr,\n torch::Tensor col_idx,\n torch::Tensor node_feat,\n torch::Tensor degrees\n);\n'''\n\nmodule = load_inline(\n name='gcn_agg',\n cpp_sources=[cpp_source],\n cuda_sources=[cuda_source],\n functions=['gcn_aggregate_cuda'],\n verbose=False,\n extra_cuda_cflags=['-O3']\n)\n\nclass ModelNew(nn.Module):\n def __init__(self):\n super().__init__()\n self.module = module\n\n def forward(self, row_ptr, col_idx, node_feat, degrees):\n return self.module.gcn_aggregate_cuda(row_ptr, col_idx, node_feat, degrees)\n"
}
result ← compile_kernel (ok)
okcompile_kernel
compile_kernel PASSED: kernel compiled without errors.
turn 5183.9s turn · 5.6s LLM · 178.3s tools · tokens in=8,224 · out=636 · total=8,865
reasoning
Now check correctness again.
call → run_correctness
callrun_correctness
{
"kernel_code": "import torch\nimport torch.nn as nn\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\n__global__ void gcn_agg_kernel(\n const int* __restrict__ row_ptr,\n const int* __restrict__ col_idx,\n const float* __restrict__ node_feat,\n const float* __restrict__ degrees,\n float* __restrict__ out,\n int num_nodes,\n int feat_dim\n) {\n int dst = blockIdx.x * blockDim.x + threadIdx.x;\n if (dst >= num_nodes) return;\n int start = row_ptr[dst];\n int end = row_ptr[dst + 1];\n float deg_dst = degrees[dst];\n for (int i = start; i < end; ++i) {\n int src = col_idx[i];\n float deg_src = degrees[src];\n float norm = 1.0f / sqrtf(deg_dst * deg_src);\n for (int f = 0; f < feat_dim; ++f) {\n out[dst * feat_dim + f] += node_feat[src * feat_dim + f] * norm;\n }\n }\n}\n\ntorch::Tensor gcn_aggregate_cuda(\n torch::Tensor row_ptr,\n torch::Tensor col_idx,\n torch::Tensor node_feat,\n torch::Tensor degrees\n) {\n row_ptr = row_ptr.contiguous();\n col_idx = col_idx.contiguous();\n node_feat = node_feat.contiguous();\n degrees = degrees.contiguous();\n int num_nodes = row_ptr.size(0) - 1;\n int feat_dim = node_feat.size(1);\n auto out = torch::zeros({num_nodes, feat_dim}, node_feat.options()).contiguous();\n int threads = 256;\n int blocks = (num_nodes + threads - 1) / threads;\n gcn_agg_kernel<<<blocks, threads>>>(\n row_ptr.data_ptr<int>(),\n col_idx.data_ptr<int>(),\n node_feat.data_ptr<float>(),\n degrees.data_ptr<float>(),\n out.data_ptr<float>(),\n num_nodes,\n feat_dim\n );\n cudaDeviceSynchronize();\n return out;\n}\n'''\n\ncpp_source = '''\ntorch::Tensor gcn_aggregate_cuda(\n torch::Tensor row_ptr,\n torch::Tensor col_idx,\n torch::Tensor node_feat,\n torch::Tensor degrees\n);\n'''\n\nmodule = load_inline(\n name='gcn_agg',\n cpp_sources=[cpp_source],\n cuda_sources=[cuda_source],\n functions=['gcn_aggregate_cuda'],\n verbose=False,\n extra_cuda_cflags=['-O3']\n)\n\nclass ModelNew(nn.Module):\n def __init__(self):\n super().__init__()\n self.module = module\n\n def forward(self, row_ptr, col_idx, node_feat, degrees):\n return self.module.gcn_aggregate_cuda(row_ptr, col_idx, node_feat, degrees)\n"
}
result ← run_correctness (fail)
failrun_correctness
run_correctness FAILED: ? trials did not all match.
runtime_error: expected scalar type Int but found Float
turn 6246.5s turn · 8.8s LLM · 237.7s tools · tokens in=8,894 · out=635 · total=9,658 · FINAL
reasoning
Still failing. The runtime error is probably coming from the reference code when it's run on GPU tensors.
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 = '''\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\n__global__ void gcn_agg_kernel(\n const int* __restrict__ row_ptr,\n const int* __restrict__ col_idx,\n const float* __restrict__ node_feat,\n const float* __restrict__ degrees,\n float* __restrict__ out,\n int num_nodes,\n int feat_dim\n) {\n int dst = blockIdx.x * blockDim.x + threadIdx.x;\n if (dst >= num_nodes) return;\n int start = row_ptr[dst];\n int end = row_ptr[dst + 1];\n float deg_dst = degrees[dst];\n for (int i = start; i < end; ++i) {\n int src = col_idx[i];\n float deg_src = degrees[src];\n float norm = 1.0f / sqrtf(deg_dst * deg_src);\n for (int f = 0; f < feat_dim; ++f) {\n out[dst * feat_dim + f] += node_feat[src * feat_dim + f] * norm;\n }\n }\n}\n\ntorch::Tensor gcn_aggregate_cuda(\n torch::Tensor row_ptr,\n torch::Tensor col_idx,\n torch::Tensor node_feat,\n torch::Tensor degrees\n) {\n row_ptr = row_ptr.contiguous();\n col_idx = col_idx.contiguous();\n node_feat = node_feat.contiguous();\n degrees = degrees.contiguous();\n int num_nodes = row_ptr.size(0) - 1;\n int feat_dim = node_feat.size(1);\n auto out = torch::zeros({num_nodes, feat_dim}, node_feat.options()).contiguous();\n int threads = 256;\n int blocks = (num_nodes + threads - 1) / threads;\n gcn_agg_kernel<<<blocks, threads>>>(\n row_ptr.data_ptr<int>(),\n col_idx.data_ptr<int>(),\n node_feat.data_ptr<float>(),\n degrees.data_ptr<float>(),\n out.data_ptr<float>(),\n num_nodes,\n feat_dim\n );\n cudaDeviceSynchronize();\n return out;\n}\n'''\n\ncpp_source = '''\ntorch::Tensor gcn_aggregate_cuda(\n torch::Tensor row_ptr,\n torch::Tensor col_idx,\n torch::Tensor node_feat,\n torch::Tensor degrees\n);\n'''\n\nmodule = load_inline(\n name='gcn_agg',\n cpp_sources=[cpp_source],\n cuda_sources=[cuda_source],\n functions=['gcn_aggregate_cuda'],\n verbose=False,\n extra_cuda_cflags=['-O3']\n)\n\nclass ModelNew(nn.Module):\n def __init__(self):\n super().__init__()\n self.module = module\n\n def forward(self, row_ptr, col_idx, node_feat, degrees):\n return self.module.gcn_aggregate_cuda(row_ptr, col_idx, node_feat, degrees)\n"
}
result ← submit_kernel (fail)
failsubmit_kernel
submit_kernel FAILED: correctness check did not pass (? trials).
runtime_error: expected scalar type Int but found Float
final submitted kernel (89 lines, 2,393 chars) — full source, untruncated
import torch
import torch.nn as nn
from torch.utils.cpp_extension import load_inline
cuda_source = '''
#include <torch/extension.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <cmath>
__global__ void gcn_agg_kernel(
const int* __restrict__ row_ptr,
const int* __restrict__ col_idx,
const float* __restrict__ node_feat,
const float* __restrict__ degrees,
float* __restrict__ out,
int num_nodes,
int feat_dim
) {
int dst = blockIdx.x * blockDim.x + threadIdx.x;
if (dst >= num_nodes) return;
int start = row_ptr[dst];
int end = row_ptr[dst + 1];
float deg_dst = degrees[dst];
for (int i = start; i < end; ++i) {
int src = col_idx[i];
float deg_src = degrees[src];
float norm = 1.0f / sqrtf(deg_dst * deg_src);
for (int f = 0; f < feat_dim; ++f) {
out[dst * feat_dim + f] += node_feat[src * feat_dim + f] * norm;
}
}
}
torch::Tensor gcn_aggregate_cuda(
torch::Tensor row_ptr,
torch::Tensor col_idx,
torch::Tensor node_feat,
torch::Tensor degrees
) {
row_ptr = row_ptr.contiguous();
col_idx = col_idx.contiguous();
node_feat = node_feat.contiguous();
degrees = degrees.contiguous();
int num_nodes = row_ptr.size(0) - 1;
int feat_dim = node_feat.size(1);
auto out = torch::zeros({num_nodes, feat_dim}, node_feat.options()).contiguous();
int threads = 256;
int blocks = (num_nodes + threads - 1) / threads;
gcn_agg_kernel<<<blocks, threads>>>(
row_ptr.data_ptr<int>(),
col_idx.data_ptr<int>(),
node_feat.data_ptr<float>(),
degrees.data_ptr<float>(),
out.data_ptr<float>(),
num_nodes,
feat_dim
);
cudaDeviceSynchronize();
return out;
}
'''
cpp_source = '''
torch::Tensor gcn_aggregate_cuda(
torch::Tensor row_ptr,
torch::Tensor col_idx,
torch::Tensor node_feat,
torch::Tensor degrees
);
'''
module = load_inline(
name='gcn_agg',
cpp_sources=[cpp_source],
cuda_sources=[cuda_source],
functions=['gcn_aggregate_cuda'],
verbose=False,
extra_cuda_cflags=['-O3']
)
class ModelNew(nn.Module):
def __init__(self):
super().__init__()
self.module = module
def forward(self, row_ptr, col_idx, node_feat, degrees):
return self.module.gcn_aggregate_cuda(row_ptr, col_idx, node_feat, degrees)