code: def add_input_quant_params_to_op_inputs(graph):
      """
      In Torch, input quant params are not explicitly passed around
      Instead, they are stored in QTensor data structure, and retrieved
      at runtime by each quantized ops.
      However, they need to be known statically for QNN translation.
      To workaround and simplify the translation of inputs, we manually add
      input quant params to inputs of Torch quantized operators listed below.
      See _quantized_conv2d() below for example of why this is helpful.
  
      For example,
        %input : QUInt8(1, 512, 7, 7) = quantized::add(%x.8, %x.9, %434, %435)
      becomes
        %395 : float = prim::Constant[value=0.036212071776390076]()
        %396 : int = prim::Constant[value=0]()
        %430 : float = prim::Constant[value=0.16080744564533234]()
        %431 : int = prim::Constant[value=42]()
        %input : QUInt8(1, 512, 7, 7) = quantized::add(%x.8, %x.9, %434, %435,
                                                       %430, %431, %395, %396)
  
      %434, %435 are output scale and zp of quantized::add op
      %430, %431, %395, %396 are two pairs of input (scale, zp) for two tensors
      added by this function
      """
      # How many quantized tensors each op takes as inputs?
      # A pair of (scale, zp) for each input quantized tensor will be added
      # to the input nodes
      num_quantized_inputs = {
          "quantized::conv2d": 1,
          "quantized::conv2d_relu": 1,
          "quantized::linear": 1,
          "quantized::linear_relu": 1,
          "quantized::add_relu": 2,
          "quantized::add": 2,
          "quantized::mul_relu": 2,
          "quantized::mul": 2,
          "aten::dequantize": 1,
          "aten::mean": 1,
          "aten::sigmoid": 1,
          "aten::upsample_nearest2d": 1,
          "aten::upsample_bilinear2d": 1,
          "aten::relu_": 1,
          "aten::relu": 1,
          "quantized::add_scalar": 1,
          "quantized::mul_scalar": 1,
          "quantized::relu6": 1,
          "quantized::hardswish": 1,
          "aten::hardsigmoid": 1,
          "quantized::conv_transpose2d": 1,
          "quantized::leaky_relu": 1,
      }
  
      need_input_quant_param = set(num_quantized_inputs.keys())
      need_input_quant_param.add("quantized::cat")
  
      input_scales_for_bias = {}
  
      for node in graph.nodes():
          operator = node.kind()
          if operator not in need_input_quant_param:
              continue
  
          input_scales = []
          input_zero_points = []
  
          if operator == "quantized::cat":
              # the number of inputs to concat is not constant
              # so handle it separately
              inputs = node.inputsAt(0).node().inputs()
              for inp in inputs:
                  scale, zp = _get_quant_param_for_input(inp)
                  input_scales.append(scale)
                  input_zero_points.append(zp)
          else:
              for i in range(num_quantized_inputs[operator]):
                  scale, zp = _get_quant_param_for_input(
                      node.inputsAt(i)
                  )
                  if scale is not None and zp is not None:
                      input_scales.append(scale)
                      input_zero_points.append(zp)
  
          if operator in [
              "quantized::add_scalar",
              "quantized::mul_scalar",
          ]:
              scalar = node.inputsAt(1).node().f("value")
              inp_scale = input_scales[0].node().f("value")
              inp_zero_point = input_zero_points[0].node().i("value")
  
              # see the comments in this function above
              _add_output_quant_params_to_scalar_op(
                  node, graph, inp_scale, inp_zero_point, scalar
              )
  
          if operator == "aten::sigmoid":
              _add_output_quant_params_to_sigmoid_op(node, graph)
  
          for scale, zp in zip(input_scales, input_zero_points):
              node.addInput(scale)
              node.addInput(zp)
  
          if (
              "quantized::conv" in operator
              or "quantized::linear" in operator
          ):
              # This is required for quantizing the bias
              assert (
                  len(input_scales) == 1
              ), "One quantized parameter expected for qconv or qlinear."
              input_scales_for_bias[node.inputsAt(1).debugName()] = (
                  input_scales[0].node().f("value")
              )
  
      return input_scales_for_bias
  
imports: import numpy
  import tvm
  from tvm import relay
  from tvm.relay import expr
  from tvm.relay import op
  from tvm.relay.frontend.common import infer_shape
  from .common import logger
  from .pytorch_utils import is_version_greater_than
label_desc: Use image node_id 12 for calling a global function with example usage: add_input_quant_params_to_op_inputs(graph) and returns: input_scales_for_bias