Skip to content

Commit a369cfb

Browse files
authored
feat: add aten.glu converter (#4475)
1 parent ca429dc commit a369cfb

2 files changed

Lines changed: 181 additions & 1 deletion

File tree

py/torch_tensorrt/dynamo/conversion/aten_ops_converters.py

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,18 @@
22

33
import logging
44
import operator
5-
from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union
5+
from typing import (
6+
Any,
7+
Callable,
8+
Dict,
9+
List,
10+
Mapping,
11+
Optional,
12+
Sequence,
13+
Tuple,
14+
Union,
15+
cast,
16+
)
617

718
import numpy as np
819
import torch
@@ -488,6 +499,62 @@ def aten_ops_hardtanh(
488499
)
489500

490501

502+
def _get_glu_dim(args: Tuple[Argument, ...], kwargs: Mapping[str, Argument]) -> int:
503+
return cast(int, kwargs.get("dim", args_bounds_check(args, 1, -1)))
504+
505+
506+
def glu_validator(node: Node, settings: Optional[CompilationSettings] = None) -> bool:
507+
input_meta = node.args[0].meta
508+
input_val = input_meta.get("tensor_meta")
509+
if input_val is None:
510+
input_val = input_meta.get("val")
511+
if input_val is None:
512+
_LOGGER.warning(
513+
"Meta information of input is missing. Unable to validate GLU's "
514+
"split dimension, falling back to PyTorch operation."
515+
)
516+
return False
517+
518+
input_shape = input_val.shape
519+
dim = get_positive_dim(_get_glu_dim(node.args, node.kwargs), len(input_shape))
520+
split_dim_size = input_shape[dim]
521+
522+
return (
523+
isinstance(split_dim_size, int)
524+
and split_dim_size > 0
525+
and split_dim_size % 2 == 0
526+
)
527+
528+
529+
@dynamo_tensorrt_converter(
530+
torch.ops.aten.glu.default,
531+
capability_validator=glu_validator,
532+
supports_dynamic_shapes=True,
533+
)
534+
@enforce_tensor_types(
535+
{
536+
0: (TRTTensor,),
537+
}
538+
)
539+
def aten_ops_glu(
540+
ctx: ConversionContext,
541+
target: Target,
542+
args: Tuple[Argument, ...],
543+
kwargs: Dict[str, Argument],
544+
name: str,
545+
) -> Union[TRTTensor, Sequence[TRTTensor]]:
546+
input_val = args[0]
547+
dim = get_positive_dim(_get_glu_dim(args, kwargs), len(input_val.shape))
548+
split_size = input_val.shape[dim] // 2
549+
first, second = impl.split.split(
550+
ctx, target, SourceIR.ATEN, f"{name}_split", input_val, split_size, dim
551+
)
552+
gated = impl.activation.sigmoid(
553+
ctx, target, SourceIR.ATEN, f"{name}_sigmoid", second
554+
)
555+
return impl.elementwise.mul(ctx, target, SourceIR.ATEN, f"{name}_mul", first, gated)
556+
557+
491558
@dynamo_tensorrt_converter(torch.ops.aten.sigmoid.default, supports_dynamic_shapes=True)
492559
def aten_ops_sigmoid(
493560
ctx: ConversionContext,
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import torch
2+
import torch.nn as nn
3+
from parameterized import parameterized
4+
from torch.testing._internal.common_utils import TestCase, run_tests
5+
from torch_tensorrt import Input
6+
from torch_tensorrt.dynamo.conversion import UnsupportedOperatorException
7+
from torch_tensorrt.dynamo.conversion.aten_ops_converters import glu_validator
8+
9+
from .harness import DispatchTestCase
10+
11+
12+
class TestGluConverter(DispatchTestCase):
13+
@parameterized.expand(
14+
[
15+
("last_dim_fp32", (2, 8), -1, torch.float32),
16+
("first_dim_fp32", (6, 4), 0, torch.float32),
17+
("middle_dim_fp16", (2, 4, 6), 1, torch.float16),
18+
]
19+
)
20+
def test_glu(self, _, input_shape, dim, dtype):
21+
class Glu(nn.Module):
22+
def forward(self, input):
23+
return torch.ops.aten.glu.default(input, dim)
24+
25+
inputs = [torch.randn(input_shape, dtype=dtype)]
26+
self.run_test(Glu(), inputs, use_dynamo_tracer=True)
27+
28+
def test_glu_keyword_dim(self):
29+
class Glu(nn.Module):
30+
def forward(self, input):
31+
return torch.ops.aten.glu.default(input, dim=0)
32+
33+
inputs = [torch.randn(6, 4)]
34+
self.run_test(Glu(), inputs, use_dynamo_tracer=False, propagate_shapes=True)
35+
36+
def test_glu_default_dim(self):
37+
class Glu(nn.Module):
38+
def forward(self, input):
39+
return torch.ops.aten.glu.default(input)
40+
41+
inputs = [torch.randn(2, 8)]
42+
self.run_test(Glu(), inputs, use_dynamo_tracer=False, propagate_shapes=True)
43+
44+
def test_glu_zero_sized_split_dim_rejected(self):
45+
class Glu(nn.Module):
46+
def forward(self, input):
47+
return torch.ops.aten.glu.default(input, dim=0)
48+
49+
inputs = [torch.randn(0, 4)]
50+
with self.assertRaises(UnsupportedOperatorException):
51+
self.run_test(Glu(), inputs, use_dynamo_tracer=False, propagate_shapes=True)
52+
53+
def test_glu_with_dynamic_batch(self):
54+
class Glu(nn.Module):
55+
def forward(self, input):
56+
return torch.ops.aten.glu.default(input, -1)
57+
58+
input_specs = [
59+
Input(
60+
min_shape=(2, 4, 8),
61+
opt_shape=(3, 4, 8),
62+
max_shape=(5, 4, 8),
63+
dtype=torch.float32,
64+
),
65+
]
66+
self.run_test_with_dynamic_shape(Glu(), input_specs, use_dynamo_tracer=True)
67+
68+
69+
class TestGluValidator(TestCase):
70+
@staticmethod
71+
def make_glu_node(input_shape=None, dim=None, *, keyword=False, include_meta=True):
72+
graph = torch.fx.Graph()
73+
input_node = graph.placeholder("input")
74+
args = (input_node,)
75+
kwargs = {}
76+
if dim is not None:
77+
if keyword:
78+
kwargs["dim"] = dim
79+
else:
80+
args += (dim,)
81+
glu_node = graph.call_function(
82+
torch.ops.aten.glu.default, args=args, kwargs=kwargs
83+
)
84+
graph.output(glu_node)
85+
if include_meta:
86+
input_node.meta["val"] = torch.empty(input_shape)
87+
return glu_node
88+
89+
def test_keyword_dim(self):
90+
node = self.make_glu_node((6, 3), dim=0, keyword=True)
91+
self.assertTrue(glu_validator(node))
92+
93+
def test_default_dim(self):
94+
node = self.make_glu_node((3, 8))
95+
self.assertTrue(glu_validator(node))
96+
97+
@parameterized.expand(
98+
[
99+
("zero_sized_split_dim", (0, 4), 0),
100+
("odd_split_dim", (2, 7), -1),
101+
]
102+
)
103+
def test_rejects_invalid_split_dim(self, _, input_shape, dim):
104+
node = self.make_glu_node(input_shape, dim=dim)
105+
self.assertFalse(glu_validator(node))
106+
107+
def test_rejects_missing_metadata(self):
108+
node = self.make_glu_node(include_meta=False)
109+
self.assertFalse(glu_validator(node))
110+
111+
112+
if __name__ == "__main__":
113+
run_tests()

0 commit comments

Comments
 (0)