class_docstr: Base QuantizationTestCase for PT2E QAT with some helper methods.
class_docstr_tok: ["Base","QuantizationTestCase","for","PT2E","QAT","with","some","helper","methods","."]
docstr: Verify that the graph module matches the fused QAT [conv - bn (- relu)] pattern
  with fake quantizes inserted into the correct places.
  # TODO: also verify that metadata is copied over to the new nodes.
docstr_tok: ["Verify","that","the","graph","module","matches","the","fused","QAT","[","conv","-","bn","(","-","relu",")","]","pattern","with","fake","quantizes","inserted","into","the","correct","places",".","#","TODO",":","also","verify","that","metadata","is","copied","over","to","the","new","nodes","."]
code: def _verify_symmetric_xnnpack_qat_graph_helper(
      self,
      m: torch.fx.GraphModule,
      example_inputs: Tuple[Any, ...],
      is_per_channel: bool,
      has_relu: bool,
      has_bias: bool = True,
      is_cuda: bool = False,
      expected_conv_literal_args: Optional[Tuple[Any, ...]] = None,
  ):
      """
      Verify that the graph module matches the fused QAT [conv - bn (- relu)] pattern
      with fake quantizes inserted into the correct places.
      # TODO: also verify that metadata is copied over to the new nodes.
      """
      m = copy.deepcopy(m)
      quantizer = XNNPACKQuantizer()
      quantizer.set_global(
          get_symmetric_quantization_config(is_per_channel, is_qat=True)
      )
      m = capture_pre_autograd_graph(
          m,
          example_inputs,
      )
      m = prepare_qat_pt2e(m, quantizer)
      m(*example_inputs)
  
      # Verify: getitem output activation fake quantize
      output_node = list(m.graph.nodes)[-1]
      output_fq_node = output_node.args[0][0]
      self.assertTrue(
          output_fq_node.target.startswith("activation_post_process_")
      )
      output_fq_mod = getattr(m, output_fq_node.target)
      self.assertEqual(
          type(output_fq_mod), FusedMovingAvgObsFakeQuantize
      )
      self.assertEqual(
          type(output_fq_mod.activation_post_process),
          MovingAverageMinMaxObserver,
      )
      self.assertEqual(output_fq_mod.dtype, torch.int8)
      self.assertEqual(output_fq_mod.quant_min, -128)
      self.assertEqual(output_fq_mod.quant_max, 127)
  
      # Verify: getitem(bn, 0) or relu(getitem(bn, 0))
      if has_relu:
          relu_node = output_fq_node.args[0]
          getitem_node = relu_node.args[0]
          self.assertEqual(
              relu_node.target, torch.ops.aten.relu.default
          )
      else:
          relu_node = None
          getitem_node = output_fq_node.args[0]
      bn_node = getitem_node.args[0]
      if is_cuda:
          if torch.version.cuda is not None:
              expected_bn_op = torch.ops.aten.cudnn_batch_norm.default
          elif torch.version.hip is not None:
              expected_bn_op = torch.ops.aten.miopen_batch_norm.default
      else:
          expected_bn_op = (
              torch.ops.aten._native_batch_norm_legit.default
          )
      self.assertEqual(getitem_node.target, operator.getitem)
      self.assertEqual(bn_node.target, expected_bn_op)
  
      # Verify: conv / scale_factor.reshape [+ bias.reshape]
      if has_bias:
          add_bias_node = bn_node.args[0]
          (
              div_scale_factor_node,
              bias_reshape_node,
          ) = add_bias_node.args
          self.assertEqual(
              add_bias_node.target, torch.ops.aten.add.Tensor
          )
          self.assertEqual(
              bias_reshape_node.target, torch.ops.aten.reshape.default
          )
      else:
          div_scale_factor_node = bn_node.args[0]
      (
          conv_node,
          scale_factor_reshape_node,
      ) = div_scale_factor_node.args
      self.assertEqual(
          div_scale_factor_node.target, torch.ops.aten.div.Tensor
      )
      self.assertTrue(_is_conv_node(conv_node))
      self.assertEqual(
          scale_factor_reshape_node.target,
          torch.ops.aten.reshape.default,
      )
  
      # Verify: conv literal args
      if expected_conv_literal_args is not None:
          assert (
              len(expected_conv_literal_args) == 6
          ), "wrong num conv args, bad test setup"
          for i in range(6):
              if i + 3 < len(conv_node.args):
                  self.assertEqual(
                      conv_node.args[i + 3],
                      expected_conv_literal_args[i],
                  )
  
      # Verify: conv input activation fake quantize
      conv_input_fq_node = conv_node.args[0]
      conv_input_node = conv_input_fq_node.args[0]
      self.assertTrue(
          conv_input_fq_node.target.startswith(
              "activation_post_process_"
          )
      )
      conv_input_fq_mod = getattr(m, conv_input_fq_node.target)
      self.assertEqual(
          type(conv_input_fq_mod), FusedMovingAvgObsFakeQuantize
      )
      self.assertEqual(
          type(conv_input_fq_mod.activation_post_process),
          MovingAverageMinMaxObserver,
      )
      self.assertEqual(conv_input_fq_mod.dtype, torch.int8)
      self.assertEqual(conv_input_fq_mod.quant_min, -128)
      self.assertEqual(conv_input_fq_mod.quant_max, 127)
      self.assertTrue(conv_input_node.op, "placeholder")
  
      # Verify: conv weight fake quantize
      conv_weight_fq_node = conv_node.args[1]
      self.assertTrue(
          conv_weight_fq_node.target.startswith(
              "activation_post_process_"
          )
      )
      conv_weight_fq_mod = getattr(m, conv_weight_fq_node.target)
      if is_per_channel:
          expected_weight_observer_type = (
              MovingAveragePerChannelMinMaxObserver
          )
      else:
          expected_weight_observer_type = MovingAverageMinMaxObserver
      self.assertEqual(
          type(conv_weight_fq_mod), FusedMovingAvgObsFakeQuantize
      )
      self.assertEqual(
          type(conv_weight_fq_mod.activation_post_process),
          expected_weight_observer_type,
      )
      self.assertEqual(conv_weight_fq_mod.dtype, torch.int8)
      self.assertEqual(conv_weight_fq_mod.quant_min, -127)
      self.assertEqual(conv_weight_fq_mod.quant_max, 127)
  
      # Verify: conv(fq(input), fq(weight * scale_factor.reshape), zero_bias)
      zero_bias_node = (
          conv_node.args[2] if len(conv_node.args) > 2 else None
      )
      mul_weight_scale_factor_node = conv_weight_fq_node.args[0]
      (
          conv_weight_fq_node,
          scale_factor_reshape_node,
      ) = mul_weight_scale_factor_node.args
      if has_bias:
          self.assertEqual(
              zero_bias_node.target, torch.ops.aten.zeros_like.default
          )
      else:
          self.assertTrue(zero_bias_node is None)
      self.assertEqual(
          mul_weight_scale_factor_node.target, torch.ops.aten.mul.Tensor
      )
      self.assertEqual(
          scale_factor_reshape_node.target,
          torch.ops.aten.reshape.default,
      )
  
      # Verify: scale_factor = bn_weight / sqrt(bn_running_var + eps)
      scale_factor_node = scale_factor_reshape_node.args[0]
      (bn_weight_node, sqrt_node) = scale_factor_node.args
      bn_running_var_add_node = sqrt_node.args[0]
      (bn_running_var_node, eps) = bn_running_var_add_node.args
      self.assertEqual(
          scale_factor_node.target, torch.ops.aten.div.Tensor
      )
      self.assertTrue("bn_weight" in bn_weight_node.target)
      self.assertEqual(sqrt_node.target, torch.ops.aten.sqrt.default)
      self.assertEqual(
          bn_running_var_add_node.target, torch.ops.aten.add.Tensor
      )
      self.assertTrue("bn_running_var" in bn_running_var_node.target)
      self.assertEqual(eps, 1e-5)
  
imports: import copy
  import operator
  import unittest
  from typing import Any, Optional, Tuple, Type
  import torch
  from torch._export import capture_pre_autograd_graph
  from torch.ao.quantization import default_fake_quant, FusedMovingAvgObsFakeQuantize, MovingAverageMinMaxObserver, MovingAveragePerChannelMinMaxObserver, QConfigMapping
  from torch.ao.quantization.backend_config import get_qnnpack_backend_config
  from torch.ao.quantization.qconfig import default_per_channel_symmetric_qnnpack_qat_qconfig, default_symmetric_qnnpack_qat_qconfig
  from torch.ao.quantization.quantize_fx import prepare_qat_fx
  from torch.ao.quantization.quantize_pt2e import _convert_to_reference_decomposed_fx, convert_pt2e, prepare_pt2e, prepare_qat_pt2e
  from torch.ao.quantization.quantizer import DerivedQuantizationSpec, QuantizationAnnotation, QuantizationSpec, Quantizer
  from torch.ao.quantization.quantizer.xnnpack_quantizer import get_symmetric_quantization_config, XNNPACKQuantizer
  from torch.testing._internal.common_cuda import TEST_CUDA
  from torch.testing._internal.common_quantization import NodeSpec, QuantizationTestCase, skip_if_no_torchvision, skipIfNoQNNPACK
  from torch.testing._internal.common_quantized import override_quantized_engine
label_desc: Use image node_id 5 for calling the PT2EQATTestCase obj's underlying member method code with example usage: obj._verify_symmetric_xnnpack_qat_graph_helper(m, example_inputs, is_per_channel, has_relu, has_bias, is_cuda, expected_conv_literal_args) without return types