@@ -422,3 +422,92 @@ def test_different_fqs_independent(self):
422422 _apply_defaults (fq_map )
423423 assert fq_linear .granularity .axis == 0
424424 assert fq_conv_t .granularity .axis == 0
425+
426+
427+ class TestConvTransposeAxisDefaultsGraph :
428+ """Graph-mode regression tests for ConvTranspose2d/3d axis default resolution.
429+
430+ ``ATEN_OP_TO_MODULE_TYPE`` maps the aten op emitted in the exported graph
431+ (e.g. ``aten.conv_transpose2d.input``) to the corresponding ``nn.Module``
432+ type so that ``_apply_defaults`` can look up the correct weight axis from
433+ ``_WEIGHT_AXIS_SPECS``. These tests verify the full coreai-opt path:
434+ Quantizer.prepare → axis-defaults pass → correct axis on weight FQ.
435+
436+ ConvTranspose weight layout is ``[in_ch, out_ch, ...]``, so:
437+ - per-channel axis (output channels) = 1
438+ - per-block axis (input channels) = 0
439+ """
440+
441+ @pytest .mark .parametrize (
442+ ("make_model" , "make_input" ),
443+ [
444+ pytest .param (
445+ lambda : nn .ConvTranspose2d (16 , 8 , 3 , padding = 1 ),
446+ lambda : torch .randn (1 , 16 , 8 , 8 ),
447+ id = "conv_transpose2d" ,
448+ ),
449+ pytest .param (
450+ lambda : nn .ConvTranspose3d (16 , 8 , 3 , padding = 1 ),
451+ lambda : torch .randn (1 , 16 , 4 , 4 , 4 ),
452+ id = "conv_transpose3d" ,
453+ ),
454+ ],
455+ )
456+ @pytest .mark .parametrize (
457+ ("granularity" , "expected_axis" ),
458+ [
459+ pytest .param (PerChannelGranularity (axis = None ), 1 , id = "per_channel_axis_1" ),
460+ pytest .param (
461+ PerBlockGranularity (axis = None , block_size = _TEST_BLOCK_SIZE ), 0 , id = "per_block_axis_0"
462+ ),
463+ ],
464+ )
465+ def test_axis_none_resolves_for_conv_transpose (
466+ self ,
467+ make_model ,
468+ make_input ,
469+ granularity ,
470+ expected_axis ,
471+ ):
472+ """ConvTranspose axis=None resolves to the correct default in graph mode.
473+
474+ Per-channel should resolve to axis 1 (output channels), per-block to
475+ axis 0 (input channels), reflecting the [in_ch, out_ch, ...] weight layout.
476+ """
477+ config = _make_config (granularity , execution_mode = "graph" )
478+ prepared = Quantizer (make_model (), config ).prepare ((make_input (),))
479+
480+ weight_fqs = _get_weight_fqs (prepared )
481+ assert len (weight_fqs ) == 1
482+ assert weight_fqs [0 ].granularity .axis == expected_axis
483+
484+ @pytest .mark .parametrize (
485+ ("make_model" , "make_input" ),
486+ [
487+ pytest .param (
488+ lambda : nn .ConvTranspose2d (16 , 8 , 3 , padding = 1 ),
489+ lambda : torch .randn (1 , 16 , 8 , 8 ),
490+ id = "conv_transpose2d" ,
491+ ),
492+ pytest .param (
493+ lambda : nn .ConvTranspose3d (16 , 8 , 3 , padding = 1 ),
494+ lambda : torch .randn (1 , 16 , 4 , 4 , 4 ),
495+ id = "conv_transpose3d" ,
496+ ),
497+ ],
498+ )
499+ def test_prepare_calibrate_finalize_conv_transpose_graph (self , make_model , make_input ):
500+ """Full graph-mode workflow succeeds for ConvTranspose with axis=None."""
501+ config = _make_config (PerChannelGranularity (axis = None ), execution_mode = "graph" )
502+ quantizer = Quantizer (make_model (), config )
503+ example_input = make_input ()
504+
505+ prepared = quantizer .prepare ((example_input ,))
506+ with quantizer .calibration_mode ():
507+ prepared (example_input )
508+ quantizer .finalize ()
509+
510+ weight_fqs = _get_weight_fqs (prepared )
511+ assert len (weight_fqs ) == 1
512+ assert weight_fqs [0 ].granularity .axis is not None
513+
0 commit comments