@@ -82,6 +82,74 @@ def _run(model, inputs, transform_passes=None):
8282 return counts , delegates , (out - ref ).abs ().max ().item ()
8383
8484
85+ class TestMLXPartitionerConstantInputs (unittest .TestCase ):
86+ class OptionalInput (nn .Module ):
87+ def forward (self , x , optional , y ):
88+ return x + y
89+
90+ def test_unused_constants_preserve_signature (self ):
91+ for value in (None , True , 3 , 1.5 , "unused" ):
92+ with self .subTest (value = value ):
93+ ep = export (
94+ self .OptionalInput (),
95+ (torch .randn (4 ), value , torch .randn (4 )),
96+ strict = False ,
97+ )
98+ signature = str (ep .graph_signature )
99+ placeholder = next (n for n in ep .graph .nodes if n .name == "optional" )
100+ self .assertFalse (placeholder .users )
101+
102+ builder = MLXProgramBuilder (ep )
103+ builder .check_support_only ()
104+ self .assertTrue (
105+ all (info .supported for info in builder .node_info .values ())
106+ )
107+ built = MLXProgramBuilder (ep ).build ()
108+ self .assertEqual (built .num_input_tensors , 2 )
109+ self .assertEqual (len (built .input_map ), 2 )
110+ self .assertEqual (str (ep .graph_signature ), signature )
111+
112+ def test_unused_constants_lower_and_execute (self ):
113+ # The Python runtime does not accept string inputs; cover those above.
114+ for value in (None , True , 3 , 1.5 ):
115+ with self .subTest (value = value ):
116+ _ , delegates , error = _run (
117+ self .OptionalInput (),
118+ (torch .randn (4 ), value , torch .randn (4 )),
119+ )
120+ self .assertGreater (delegates , 0 )
121+ self .assertLess (error , 1e-6 )
122+
123+ def test_string_constant_lowers (self ):
124+ program = _lower (
125+ self .OptionalInput (),
126+ (torch .randn (4 ), "unused" , torch .randn (4 )),
127+ )
128+ self .assertGreater (_delegate_count (program ), 0 )
129+
130+ def test_consumed_constant_still_raises (self ):
131+ ep = export (
132+ self .OptionalInput (),
133+ (torch .randn (4 ), 3 , torch .randn (4 )),
134+ strict = False ,
135+ )
136+ placeholder = next (n for n in ep .graph .nodes if n .name == "optional" )
137+ add = next (n for n in ep .graph .nodes if n .op == "call_function" )
138+ add .target = torch .ops .aten .add .Scalar
139+ add .args = (add .args [0 ], placeholder )
140+ ep .graph_module .recompile ()
141+ ep .validate ()
142+
143+ for check_only in (True , False ):
144+ with self .subTest (check_only = check_only ):
145+ builder = MLXProgramBuilder (ep )
146+ with self .assertRaisesRegex (NotImplementedError , "ConstantArgument" ):
147+ if check_only :
148+ builder .check_support_only ()
149+ else :
150+ builder .build ()
151+
152+
85153class Sdpa (nn .Module ):
86154 def __init__ (self , is_causal : bool = False ):
87155 super ().__init__ ()
0 commit comments