Skip to content

Commit 2d26e99

Browse files
authored
Fix complex_graph_rewrite crashing on a None-valued placeholder (#4401)
1 parent 2aac435 commit 2d26e99

2 files changed

Lines changed: 37 additions & 16 deletions

File tree

py/torch_tensorrt/dynamo/lowering/passes/complex_graph_rewrite.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import logging
22
import math
33
import operator
4-
from typing import Callable, List, Optional, Tuple
4+
from typing import Any, Callable, Dict, FrozenSet, List, Optional, Set, Tuple
55

66
import torch
77
from torch._subclasses.fake_tensor import FakeTensorMode
@@ -24,7 +24,7 @@
2424
# NOTE: add.Scalar / sub.Scalar are NOT in this set. (a+bi)+s = (a+s)+bi
2525
# adds the scalar only to the real part, but on the [...,2] layout
2626
# add.Scalar would add to both parts. Those need explicit rewrites.
27-
_ELEMENTWISE_SAFE: frozenset = frozenset(
27+
_ELEMENTWISE_SAFE: FrozenSet[Any] = frozenset(
2828
{
2929
# Arithmetic — component-wise operations are correct by construction
3030
torch.ops.aten.add.Tensor,
@@ -72,7 +72,7 @@
7272
)
7373

7474

75-
def _complex_unpacker(*ops: object) -> Callable:
75+
def _complex_unpacker(*ops: object) -> Callable[..., Any]:
7676
"""Decorator that registers a rewrite method for a complex aten op into a real value subgraph.
7777
7878
Usage::
@@ -84,7 +84,7 @@ def _rewrite_sin_cos(self, node): ...
8484
``@_register_unpackers`` when the class is fully defined.
8585
"""
8686

87-
def decorator(fn: Callable) -> Callable:
87+
def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
8888
fn._complex_unpacker_ops = ops
8989
return fn
9090

@@ -94,7 +94,7 @@ def decorator(fn: Callable) -> Callable:
9494
def _register_unpackers(cls: type) -> type:
9595
"""Class decorator that builds ``cls._DISPATCH`` from all methods tagged
9696
with ``@_complex_unpacker``. Applied once at class-definition time."""
97-
dispatch: dict = {}
97+
dispatch: Dict[Any, Any] = {}
9898
for attr in vars(cls).values():
9999
for op in getattr(attr, "_complex_unpacker_ops", ()):
100100
dispatch[op] = attr
@@ -276,7 +276,7 @@ def replace_input_node(
276276
elif input_node.op == "get_attr":
277277
# Sanitize dots from nested-module targets (e.g. "block1.freq")
278278
# so register_buffer does not raise KeyError on dotted names.
279-
sanitized = input_node.target.replace(".", "__") # type: ignore
279+
sanitized = input_node.target.replace(".", "__")
280280
new_attr_name = sanitized + "_unpacked_complex"
281281
with unset_fake_temporarily():
282282
original_tensor = self.get_attr_tensor(input_node.target) # type: ignore
@@ -1277,7 +1277,7 @@ def _rewrite_scalar_tensor(self, node: Node) -> bool:
12771277
return False
12781278
with SubgraphBuilder(self.gm.graph, node) as b:
12791279
out = b(torch.ops.aten.scalar_tensor.default, 0.0)
1280-
out.kwargs = {"dtype": torch.float32} # type: ignore[assignment]
1280+
out.kwargs = {"dtype": torch.float32}
12811281
node.replace_all_uses_with(out)
12821282
self.gm.graph.erase_node(node)
12831283
return True
@@ -1557,7 +1557,7 @@ def _is_complex_layout_node(self, n: Node) -> bool:
15571557
during the detection phase (or by each rewrite handler as it emits new
15581558
nodes), so this is a direct metadata lookup — no shape heuristics needed.
15591559
"""
1560-
return n.meta.get("is_complex_layout", False)
1560+
return bool(n.meta.get("is_complex_layout", False))
15611561

15621562
@_complex_unpacker(torch.ops.aten.mm.default)
15631563
def _rewrite_mm(self, node: Node) -> bool:
@@ -1809,16 +1809,19 @@ def propagate_metadata(
18091809
"""
18101810
from torch.fx.passes.fake_tensor_prop import FakeTensorProp
18111811

1812-
fake_inputs = []
1812+
fake_inputs: List[Optional[torch.Tensor]] = []
18131813
for node in self.gm.graph.nodes:
18141814
if node.op == "placeholder":
18151815
if "val" in node.meta:
18161816
fake_val = node.meta["val"]
1817-
fake_inputs.append(
1818-
fake_val.to("cuda")
1819-
if fake_val.device.type == "cuda"
1820-
else fake_val
1821-
)
1817+
if fake_val is None:
1818+
# present-but-None placeholder = a real None input
1819+
# (e.g. an optional/None transformer input, common in diffusers)
1820+
fake_inputs.append(None)
1821+
elif fake_val.device.type == "cuda":
1822+
fake_inputs.append(fake_val.to("cuda"))
1823+
else:
1824+
fake_inputs.append(fake_val)
18221825
else:
18231826
fake_tensor = torch.empty(
18241827
[s if s != 0 else 1 for s in node.meta["tensor_meta"].shape],
@@ -1987,7 +1990,7 @@ def _get_complex_input_names(gm: GraphModule) -> List[str]:
19871990
return names
19881991

19891992

1990-
def _get_complex_input_dtypes(gm: GraphModule) -> dict:
1993+
def _get_complex_input_dtypes(gm: GraphModule) -> Dict[str, Any]:
19911994
"""Return a mapping of placeholder name -> complex dtype for complex-dtype inputs.
19921995
19931996
Used by the post-partition boundary pass to know which inputs were complex128

tests/py/dynamo/lowering/test_complex_rewrite.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
import pytest
3535
import torch
3636
import torch.nn as nn
37-
3837
from torch_tensorrt.dynamo._settings import CompilationSettings
3938
from torch_tensorrt.dynamo.lowering.passes.complex_graph_rewrite import (
4039
complex_graph_detection,
@@ -1230,3 +1229,22 @@ def forward(self, z):
12301229
return torch.cat([a * b, b * a], dim=1)
12311230

12321231
_check_op(M(), (torch.randn(3, 4, dtype=torch.complex64),), "split_mul_cat")
1232+
1233+
1234+
# ===========================================================================
1235+
# 11. None-valued placeholder (optional/None input)
1236+
# ===========================================================================
1237+
1238+
1239+
@pytest.mark.unit
1240+
def test_none_placeholder():
1241+
"""opt=None — a present-but-None placeholder must survive metadata re-prop."""
1242+
1243+
class M(nn.Module):
1244+
def forward(self, z, opt):
1245+
out = z * z # complex op triggers the rewrite + metadata re-prop
1246+
if opt is not None:
1247+
out = out + opt
1248+
return out
1249+
1250+
_check_op(M(), (_z(), None), "none_placeholder")

0 commit comments

Comments
 (0)