From 3d303c01126d3cd02cdfa1c747027357f711095f Mon Sep 17 00:00:00 2001 From: Rahul Chandra Date: Thu, 11 Dec 2025 10:56:10 -0800 Subject: [PATCH 1/2] Adding test for CadenceWith16BitConvActivationsQuantizer (#16205) Summary: Add annotation tests for CadenceWith16BitConvActivationsQuantizer covering both conv1d and conv2d operations. https://www.internalfb.com/code/fbsource/[01c566b03c670b1869136cbb64f25d16d730c8d4]/fbcode/executorch/backends/cadence/aot/quantizer/quantizer.py?lines=384-396 Reviewed By: hsharma35 Differential Revision: D88895865 --- .../cadence/aot/tests/test_quantizer_ops.py | 67 ++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/backends/cadence/aot/tests/test_quantizer_ops.py b/backends/cadence/aot/tests/test_quantizer_ops.py index 99953346b05..871857196a6 100644 --- a/backends/cadence/aot/tests/test_quantizer_ops.py +++ b/backends/cadence/aot/tests/test_quantizer_ops.py @@ -56,7 +56,6 @@ CadenceW8A32MixedQuantizer, # TODO: T247438158 Add test coverage CadenceRmsNormNopQuantizer, # No-op quantizer, doesn't annotate anything, preserves rms_norm from decomposition CadenceWakeWordQuantizer, # TODO: T247438162 Add test coverage - CadenceWith16BitConvActivationsQuantizer, # TODO: T247438221 Add test coverage CadenceWithLayerNormQuantizer, # TODO: T247438410 Add test coverage CadenceWithSoftmaxQuantizer, # TODO: T247438418 Add test coverage } @@ -93,6 +92,24 @@ # For linear: [input_activation, weight] [qconfig_A16.input_activation, qconfig_A16.weight], ), + ( + "conv1d_A16", + lambda self: self._build_conv1d_graph(), + CadenceWith16BitConvActivationsQuantizer(), + torch.ops.aten.conv1d.default, + qconfig_A16.output_activation, + # For conv1d: [input_activation, weight] + [qconfig_A16.input_activation, qconfig_A16.weight], + ), + ( + "conv2d_A16", + lambda self: self._build_conv2d_graph(), + CadenceWith16BitConvActivationsQuantizer(), + torch.ops.aten.conv2d.default, + qconfig_A16.output_activation, + # For conv2d: [input_activation, weight] + [qconfig_A16.input_activation, qconfig_A16.weight], + ), ] # Derive the set of tested quantizer classes from the test cases. @@ -149,6 +166,54 @@ def _build_linear_graph(self) -> tuple[torch.fx.GraphModule, torch.fx.Node]: self.assertEqual(len(linear_nodes), 1, "Should find exactly one linear node") return gm, linear_nodes[0] + def _build_conv1d_graph(self) -> tuple[torch.fx.GraphModule, torch.fx.Node]: + """Build a simple graph with a conv1d operation (no bias).""" + builder = GraphBuilder() + # Input shape: (batch, in_channels, length) + x = builder.placeholder("x", torch.randn(1, 3, 10)) + # Weight shape: (out_channels, in_channels, kernel_size) + weight = builder.placeholder("weight", torch.randn(6, 3, 3)) + conv1d = builder.call_operator( + op=torch.ops.aten.conv1d.default, + args=(x, weight), + meta=NodeMetadata( + {"source_fn_stack": [("conv1d", torch.ops.aten.conv1d.default)]} + ), + ) + builder.output([conv1d]) + gm = builder.get_graph_module() + + conv1d_nodes = gm.graph.find_nodes( + op="call_function", + target=torch.ops.aten.conv1d.default, + ) + self.assertEqual(len(conv1d_nodes), 1, "Should find exactly one conv1d node") + return gm, conv1d_nodes[0] + + def _build_conv2d_graph(self) -> tuple[torch.fx.GraphModule, torch.fx.Node]: + """Build a simple graph with a conv2d operation (no bias).""" + builder = GraphBuilder() + # Input shape: (batch, in_channels, height, width) + x = builder.placeholder("x", torch.randn(1, 3, 8, 8)) + # Weight shape: (out_channels, in_channels, kernel_h, kernel_w) + weight = builder.placeholder("weight", torch.randn(6, 3, 3, 3)) + conv2d = builder.call_operator( + op=torch.ops.aten.conv2d.default, + args=(x, weight), + meta=NodeMetadata( + {"source_fn_stack": [("conv2d", torch.ops.aten.conv2d.default)]} + ), + ) + builder.output([conv2d]) + gm = builder.get_graph_module() + + conv2d_nodes = gm.graph.find_nodes( + op="call_function", + target=torch.ops.aten.conv2d.default, + ) + self.assertEqual(len(conv2d_nodes), 1, "Should find exactly one conv2d node") + return gm, conv2d_nodes[0] + @parameterized.expand(QUANTIZER_ANNOTATION_TEST_CASES) def test_quantizer_annotation( self, From de467ff4e33c9b1659610e75837a77f657c4edb2 Mon Sep 17 00:00:00 2001 From: Rahul Chandra Date: Thu, 11 Dec 2025 10:56:10 -0800 Subject: [PATCH 2/2] Adding test for CadenceWithSoftmaxQuantizer Summary: Add annotation tests for CadenceWithSoftmaxQuantizer. https://www.internalfb.com/code/fbsource/[01c566b03c670b1869136cbb64f25d16d730c8d4]/fbcode/executorch/backends/cadence/aot/quantizer/quantizer.py?lines=360-369 Reviewed By: hsharma35 Differential Revision: D88896712 --- .../cadence/aot/tests/test_quantizer_ops.py | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/backends/cadence/aot/tests/test_quantizer_ops.py b/backends/cadence/aot/tests/test_quantizer_ops.py index 871857196a6..9c4bca00965 100644 --- a/backends/cadence/aot/tests/test_quantizer_ops.py +++ b/backends/cadence/aot/tests/test_quantizer_ops.py @@ -57,7 +57,6 @@ CadenceRmsNormNopQuantizer, # No-op quantizer, doesn't annotate anything, preserves rms_norm from decomposition CadenceWakeWordQuantizer, # TODO: T247438162 Add test coverage CadenceWithLayerNormQuantizer, # TODO: T247438410 Add test coverage - CadenceWithSoftmaxQuantizer, # TODO: T247438418 Add test coverage } @@ -110,6 +109,15 @@ # For conv2d: [input_activation, weight] [qconfig_A16.input_activation, qconfig_A16.weight], ), + ( + "softmax_A16", + lambda self: self._build_softmax_graph(), + CadenceWithSoftmaxQuantizer(), + torch.ops.aten._softmax.default, + qconfig_A16.output_activation, + # For softmax: only input_activation + [qconfig_A16.input_activation], + ), ] # Derive the set of tested quantizer classes from the test cases. @@ -214,6 +222,27 @@ def _build_conv2d_graph(self) -> tuple[torch.fx.GraphModule, torch.fx.Node]: self.assertEqual(len(conv2d_nodes), 1, "Should find exactly one conv2d node") return gm, conv2d_nodes[0] + def _build_softmax_graph(self) -> tuple[torch.fx.GraphModule, torch.fx.Node]: + """Build a simple graph with a softmax operation.""" + builder = GraphBuilder() + x = builder.placeholder("x", torch.randn(1, 10)) + softmax = builder.call_operator( + op=torch.ops.aten._softmax.default, + args=(x, -1, False), # dim=-1, half_to_float=False + meta=NodeMetadata( + {"source_fn_stack": [("softmax", torch.ops.aten._softmax.default)]} + ), + ) + builder.output([softmax]) + gm = builder.get_graph_module() + + softmax_nodes = gm.graph.find_nodes( + op="call_function", + target=torch.ops.aten._softmax.default, + ) + self.assertEqual(len(softmax_nodes), 1, "Should find exactly one softmax node") + return gm, softmax_nodes[0] + @parameterized.expand(QUANTIZER_ANNOTATION_TEST_CASES) def test_quantizer_annotation( self,