惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

K
Kaspersky official blog
V
Visual Studio Blog
宝玉的分享
宝玉的分享
月光博客
月光博客
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Y
Y Combinator Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
大猫的无限游戏
大猫的无限游戏
H
Help Net Security
博客园_首页
Recent Announcements
Recent Announcements
小众软件
小众软件
MongoDB | Blog
MongoDB | Blog
Attack and Defense Labs
Attack and Defense Labs
The GitHub Blog
The GitHub Blog
Google DeepMind News
Google DeepMind News
Cisco Talos Blog
Cisco Talos Blog
L
LINUX DO - 最新话题
V2EX - 技术
V2EX - 技术
Simon Willison's Weblog
Simon Willison's Weblog
P
Palo Alto Networks Blog
PCI Perspectives
PCI Perspectives
T
Troy Hunt's Blog
Hacker News: Ask HN
Hacker News: Ask HN
S
Security Affairs
量子位
The Register - Security
The Register - Security
腾讯CDC
T
The Exploit Database - CXSecurity.com
P
Privacy & Cybersecurity Law Blog
V
Vulnerabilities – Threatpost
L
LINUX DO - 热门话题
N
News and Events Feed by Topic
Cloudbric
Cloudbric
Cyberwarzone
Cyberwarzone
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Recent Commits to openclaw:main
Recent Commits to openclaw:main
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
D
Darknet – Hacking Tools, Hacker News & Cyber Security
TaoSecurity Blog
TaoSecurity Blog
Scott Helme
Scott Helme
C
Cybersecurity and Infrastructure Security Agency CISA
The Last Watchdog
The Last Watchdog
W
WeLiveSecurity
H
Hacker News: Front Page
T
Tor Project blog
C
Cyber Attacks, Cyber Crime and Cyber Security
NISL@THU
NISL@THU
Know Your Adversary
Know Your Adversary
C
CXSECURITY Database RSS Feed - CXSecurity.com

Lei Mao's Log Book

2026 FIFA World Cup 备受嘲讽的会徽 Python Debugging Via VS Code In Docker Container Vargas Plateau Regional Park 徒步 Vargas Plateau Regional Park Apex 2026 FIFA World Cup 小组赛赛程 Retaining EXIF Metadata In GIMP San Francisquito Creek Joint Powers Authority 2025 Calendar Photo 麦当劳 The FIFA World Cup 套餐 Ardenwood Historic Farm 徒步 Ardenwood Historic Farm Synchronizations With TorchRec KeyedJaggedTensor Pacific Commons Linear Park 徒步 Pacific Commons Linear Park 2026 San Jose Half Marathon 竞赛 目标 Mountain View Shoreline Park 徒步 Mountain View Shoreline Park PyTorch AOTInductor Hybrid Lowering Carquinez Strait Regional Shoreline 徒步 Carquinez Strait Regional Shoreline PyTorch Triton Kernel Transparent Tracing and Compilation 脸庞 PyTorch Fake Export 2026 BRAIN Foundation 10K 竞赛 2026 Wild and Scenic Film Festival 参观 2026 Wild and Scenic Film Festival 系统工程程序员修 Bug FIFA 官方网站的语言 PyTorch Custom Operation 汉堡王 The Mandalorian and Grogu 套餐 Tilden Regional Parks Botanic Garden 参观 Tilden Regional Park Tilden Regional Parks Botanic Garden Tilden Regional Park 徒步 《寻秦记》电影版 ICML 2026 Area Chair Experience 2026 Foster City 5K Fun Run 竞赛 2026 年 3 月和 4 月该入手的模型手办 Docker Container GUI Display Using Wayland 马拉松破二 2026 Heart & Soles Run 5K 竞赛 How Is FARS, The Fully Automated Research System? 算计: 七天的死亡游戏 Lake Chabot Regional Park 徒步 Lake Chabot Regional Park 2023 年恐怖电影《感恩节》 2026 Airport Runway Run at San Carlos Airport 5K 竞赛 Page Table for Page-Locked Host Memory Don Edwards San Francisco Bay National Wildlife Refuge - Ravenswood 徒步 Don Edwards San Francisco Bay National Wildlife Refuge - Ravenswood 法外风云 Contra Costa Canal Regional Trail 徒步 Contra Costa Canal Regional Trail 娑婆诃 PyTorch Export 2026 Western Pacific 5K 竞赛 浮躁的科研和胡扯的自媒体 Connecting Logitech Devices On Linux 2026 Oakland Half Marathon 竞赛 Del Valle Regional Park 徒步 Del Valle Regional Park CUDA_LAUNCH_BLOCKING=1 踏切时间 Wildcat Canyon Regional Park 徒步 Wildcat Canyon Regional Park Credit Card Unauthorized Transaction While In Possession 莎拉的真伪人生 2026 Union City Superhero Fun Run 5K 竞赛 McLaughlin Eastshore State Park 徒步 McLaughlin Eastshore State Park Cloudflare Worker Proxy R2 Bucket Access Zorro 和 Batman Fix MacBook Pro Space Key Stuck Problem 2026 年 1 月和 2 月该入手的模型手办 2026 Brazen Victory 10K 竞赛 儿时的玩伴李峰 Marsh Creek Regional Trail 徒步 Marsh Creek Regional Trail Perfetto GPU Flow Artifacts 百万人推理 System Performance Optimizations QQ 幻想 2026 Brazen Bay Breeze 5K 竞赛 CUDA Shared Memory Bank Conflict-Free Vectorized Access Dota 闪电站出售 Mountain View Downtown 徒步 Mountain View Downtown C++ Latch and Barrier 2025 年跑步总结 2026 Rotary Mission Ten Half Marathon 竞赛 狗的素质等于人的素质 CUDA Rendezvous Stream Pleasanton Ridge Regional Park 徒步 Pleasanton Ridge Regional Park Xfinity Internet 多年来的使用感受 Randomized SVD Don Castro Regional Recreation Area 徒步 Don Castro Regional Recreation Area 拖车公司的大汉们
PyTorch Graph Symbolic Integer
Lei Mao · 2026-04-05 · via Lei Mao's Log Book

   blog 11 minutes read (About 1602 words)  visits

Introduction

When converting a PyTorch eager model to a graph, there are graph breaks if model tracing encounters control flow that depends on input data rather than meta-data or operations whose output meta-data depend on input data rather than meta-data. The meta-data includes things like tensor shapes and data types. The former graph break problem might be solved using conditional operations such as torch.cond provided that certain constraints are satisfied, such as the output meta-data of the two branches of the conditional operation must be the same. The latter graph break problem is sometimes more difficult to solve because it usually requires re-designing the neural network model.

Symbolic integers (SymInts) are used to represent variables that can span a range. The graph can allow dynamic shapes if the graph meta-data can be described using symbolic integers when the actual input meta-data is known.

In this blog post, I would like to quickly discuss what symbolic integers are and use a simple example to show how to specify symbolic integers in the graph when exporting a PyTorch model using torch.export.

The TopK Example

Suppose my model only consists of one torch.topk layer. The output shape of the torch.topk layer depends on the input tensor meta-data and the k value. To allow dynamic shapes of the model that depends on the k value, the k value must be represented as a symbolic integer in the graph.

To specify the k value as a symbolic integer via torch.export, there are a few ways, some of which do not work.

K As An Integer

If we specify the k value as a Python integer, it will not work because the graph will treat it as a constant instead of a symbolic integer.

k_int.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import torch

class TopKModelWithInt(torch.nn.Module):
def forward(self, x, k):

values, indices = torch.topk(x, k=k, dim=-1)
return values, indices

model = TopKModelWithInt()
x = torch.randn(10, 100)
k_int = 5


exported = torch.export.export(model, args=(x, k_int))

print("Exported program: ")
print(exported)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ python k_int.py
Exported program:
ExportedProgram:
class GraphModule(torch.nn.Module):
def forward(self, x: "f32[10, 100]", k):

topk = torch.ops.aten.topk.default(x, 5); x = None
getitem: "f32[10, 5]" = topk[0]
getitem_1: "i64[10, 5]" = topk[1]; topk = None
return (getitem, getitem_1)

Graph signature:

x: USER_INPUT
k: USER_INPUT


getitem: USER_OUTPUT
getitem_1: USER_OUTPUT

Range constraints: {}

K Via Proxy Tensor

If we specify the k value as a proxy tensor, i.e., its value is encoded in the proxy tensor meta-data, it will work because integers in meta-data will be represented as symbolic integers in the graph.

k_proxy_tensor.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import torch

class TopKModelWithProxyTensor(torch.nn.Module):
def forward(self, x, k_proxy):

k = k_proxy.size(0)
values, indices = torch.topk(x, k=k, dim=-1)
return values, indices

model = TopKModelWithProxyTensor()
x = torch.randn(10, 100)
k_proxy = torch.empty(1)
k_proxy.resize_(5)


k_dim = torch.export.Dim("k_value", min=1, max=100)


exported = torch.export.export(model, args=(x, k_proxy), dynamic_shapes={"x": None, "k_proxy": {0: k_dim}})

print("Exported program: ")
print(exported)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ python k_proxy_tensor.py
Exported program:
ExportedProgram:
class GraphModule(torch.nn.Module):
def forward(self, x: "f32[10, 100]", k_proxy: "f32[s99]"):

sym_size_int_1: "Sym(s99)" = torch.ops.aten.sym_size.int(k_proxy, 0); k_proxy = None


topk = torch.ops.aten.topk.default(x, sym_size_int_1); x = sym_size_int_1 = None
getitem: "f32[10, s99]" = topk[0]
getitem_1: "i64[10, s99]" = topk[1]; topk = None
return (getitem, getitem_1)

Graph signature:

x: USER_INPUT
k_proxy: USER_INPUT


getitem: USER_OUTPUT
getitem_1: USER_OUTPUT

Range constraints: {s99: VR[1, 100]}

K Via Tensor

The aforementioned approach of using a proxy tensor and dynamic shapes to specify the k value looks somewhat hacky. Consequently, PyTorch allows symbolic integers to be directly specified using tensors only if the tensor is the input of the graph.

k_tensor.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import torch

class TopKModelWithTensor(torch.nn.Module):
def forward(self, x, k_tensor):

k = k_tensor.item()
values, indices = torch.topk(x, k=k, dim=-1)
return values, indices

model = TopKModelWithTensor()
x = torch.randn(10, 100)
k_tensor = torch.tensor(5)


exported = torch.export.export(model, args=(x, k_tensor))

print("Exported program: ")
print(exported)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ python k_tensor.py
Exported program:
ExportedProgram:
class GraphModule(torch.nn.Module):
def forward(self, x: "f32[10, 100]", k_tensor: "i64[]"):

item: "Sym(u0)" = torch.ops.aten.item.default(k_tensor); k_tensor = None
ge: "Sym(u0 >= 0)" = item >= 0
_assert_scalar_default = torch.ops.aten._assert_scalar.default(ge, "Runtime assertion failed for expression u0 >= 0 on node 'ge'"); ge = _assert_scalar_default = None
le: "Sym(u0 <= 100)" = item <= 100
_assert_scalar_default_1 = torch.ops.aten._assert_scalar.default(le, "Runtime assertion failed for expression u0 <= 100 on node 'le'"); le = _assert_scalar_default_1 = None


topk = torch.ops.aten.topk.default(x, item); x = item = None
getitem: "f32[10, u0]" = topk[0]
getitem_1: "i64[10, u0]" = topk[1]; topk = None
return (getitem, getitem_1)

Graph signature:

x: USER_INPUT
k_tensor: USER_INPUT


getitem: USER_OUTPUT
getitem_1: USER_OUTPUT

Range constraints: {u0: VR[0, 100]}

Note that item() is used to extract the integer value from the tensor and typically will cause graph breaks. This is because in many cases, the output meta-data of some operations that depends on it will be data-dependent. The torch.topk operation is one of such operations because its output shape depends on the k_tensor value. PyTorch made an exception for this case to allow the k_tensor value to be specified as a tensor input of the graph, by evaluating the k_tensor value at runtime so that the k value is known before graph execution. The downside is that if k_tensor is a tensor on GPU, it will cause a GPU to CPU copy and also notice that we could not specify the range of k values via the standard dynamic shapes mechanism as used in the previous proxy tensor approach. Other graph frameworks and runtimes, such as ONNX, usually do not allow this kind of behaviors and might evaluate the k_tensor value at export time and treat it as a constant.

If the k_tensor is not the immediate input of the graph, rather than being an intermediate tensor in the graph, and somehow it is not bounded, then it is a graph break and PyTorch could not tolerate it.

Conclusions

There are pros and cons for the “K Via Proxy Tensor” approach and the “K Via Tensor” approach.

The “K Via Proxy Tensor” approach looks more grammatically correct to me and the graph can be wrong on pure device without GPU to CPU copy. However, if k is large, the user would have to construct a large proxy tensor whose only useful information is its meta-data and copy it to the device before graph execution, which can be inefficient. Of course, if the proxy tensor can just be constructed as a meta tensor without any actual data, then it will be very efficient.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import torch

class TopKModelWithProxyTensor(torch.nn.Module):
def forward(self, x, k_proxy):

k = k_proxy.size(0)
values, indices = torch.topk(x, k=k, dim=-1)
return values, indices

model = TopKModelWithProxyTensor()
x = torch.randn(10, 100)
k_proxy = torch.empty(1, device='meta')
k_proxy.resize_(5)


k_dim = torch.export.Dim("k_value", min=1, max=100)


exported = torch.export.export(model, args=(x, k_proxy), dynamic_shapes={"x": None, "k_proxy": {0: k_dim}})


package_path = torch._inductor.aoti_compile_and_package(
exported,
package_path="model.pt2"
)


lowered = torch._inductor.aoti_load_package(package_path)


selected_from_exported = exported.module()(x, k_proxy)
selected_from_lowered = lowered(x, k_proxy)

The “K Via Tensor” approach looks weird because it appears that PyTorch can allow graph breaks in the graph, which is against the general principle of graph representation. However, it is usually more efficient because there is no need to construct a proxy tensor and copy it to the device before graph execution sometimes will not introduce too much overhead.

References