code: def _find_matches(
      graph: Graph,
      modules: Dict[str, torch.nn.Module],
      patterns: Dict[Pattern, QuantizeHandler],
      root_node_getter_mapping: Dict[Pattern, Callable],
      standalone_module_names: Optional[List[str]] = None,
      standalone_module_classes: Optional[List[Type]] = None,
      custom_module_classes: Optional[List[Any]] = None,
  ) -> Dict[str, _MatchResult]:
      """
      Matches the nodes in the input graph to quantization patterns, and
      outputs the information needed to quantize them in future steps.
  
      Inputs:
        - graph: an fx.Graph object
        - modules: a mapping of fully qualified module name to instance,
            for example, {'foo': ModuleFoo, ...}
        - patterns: a mapping from a tuple of nodes in reverse order to
            uninitialized QuantizeHandler subclass.
  
      Outputs a map of
        node_name ->
          (node, matched_values, matched_pattern, QuantizeHandler instance,
           qconfig)
  
      For example, {
        'relu_1': (relu_1, [relu_1], torch.nn.functional.relu,
                   <CopyNodeQuantizeHandler instance>, QConfig(...)),
        ...
      }
      """
      if custom_module_classes is None:
          custom_module_classes = []
  
      if standalone_module_classes is None:
          standalone_module_classes = []
  
      if standalone_module_names is None:
          standalone_module_names = []
  
      match_map: Dict[str, _MatchResult] = {}
      all_matched: Set[str] = set()
  
      def _recursive_record_node_in_match_map(
          last_node,
          match_map,
          node_pattern,
          matched_node_pattern,
          pattern,
          match_value,
      ):
          if isinstance(node_pattern, Node):
              match_map[node_pattern.name] = (
                  last_node,
                  matched_node_pattern,
                  pattern,
                  match_value,
              )
          elif not isinstance(node_pattern, Iterable):
              return
          else:
              for n in node_pattern:
                  _recursive_record_node_in_match_map(
                      last_node,
                      match_map,
                      n,
                      matched_node_pattern,
                      pattern,
                      match_value,
                  )
  
      # TODO: 1. merge with fuse matcher 2. document the code
      def record_match(
          pattern, node, last_node, matched_node_pattern, match_map
      ):
          if isinstance(pattern, tuple):
              s, *args = pattern
              is_single_arg = len(args) == 1
              current_node_pattern: List[Node] = []
              record_match(
                  s, node, last_node, matched_node_pattern, match_map
              )
              if pattern[0] is not getattr:
                  for subpattern, arg in zip(args, node.args):
                      record_match(
                          subpattern,
                          arg,
                          node,
                          current_node_pattern,
                          match_map,
                      )
              if len(current_node_pattern) > 1:
                  # current_node_pattern is  the node pattern we get from matching
                  # the subpattern with arguments of the node
                  # we use is_single_arg to recover the original structure of the pattern
                  # if the original pattern has a single argument, we will have
                  # (original_op, (original_arg, ...))
                  # otherwise, we'll have a list of arguments
                  # (original_op, arg0, arg1, arg2, ...)
                  if is_single_arg:
                      matched_node_pattern.append(
                          tuple(current_node_pattern)
                      )
                  else:
                      matched_node_pattern.extend(
                          list(current_node_pattern)
                      )
              else:
                  matched_node_pattern.append(current_node_pattern[0])
          else:
              matched_node_pattern.append(node)
  
      for node in reversed(graph.nodes):
          if (
              node.name not in match_map
              and node.name not in all_matched
          ):
              for pattern, quantize_handler_cls in patterns.items():
                  root_node_getter = root_node_getter_mapping.get(
                      pattern, None
                  )
                  if (
                      _is_match(modules, node, pattern)
                      and node.name not in match_map
                  ):
                      matched_node_pattern: List[Node] = []
                      record_match(
                          pattern,
                          node,
                          node,
                          matched_node_pattern,
                          match_map,
                      )
                      quantize_handler = quantize_handler_cls(  # type: ignore[operator]
                          matched_node_pattern,
                          modules,
                          root_node_getter,
                      )
                      last_node = node
                      # record the match for all nodes in the pattern
                      _recursive_record_node_in_match_map(
                          last_node,
                          match_map,
                          # we need to record all nodes in the matched pattern in the match_map
                          matched_node_pattern,
                          # this is a part of the value corresponding to the node
                          matched_node_pattern,
                          pattern,
                          quantize_handler,
                      )
                      break
  
      # add custom module instances to the match result
      assert modules is not None
      for node in graph.nodes:
          if (
              node.op == "call_module"
              and type(modules[node.target]) in custom_module_classes
          ):
              match_map[node.name] = (
                  node,
                  node,
                  None,
                  QuantizeHandler(node, modules, is_custom_module=True),
              )
  
      def is_standalone_module(
          node_target: str, modules: Dict[str, torch.nn.Module]
      ):
          assert modules is not None
          return (
              node_target in standalone_module_names
              or type(modules[node_target])  # type: ignore[operator]
              in standalone_module_classes  # type: ignore[operator]
          )
  
      # add standalone modules to the match
      for node in graph.nodes:
          if node.op == "call_module" and (
              is_standalone_module(node.target, modules)
              or _is_observed_standalone_module(modules[node.target])
          ):
              # add node to matched nodes
              match_map[node.name] = (
                  node,
                  node,
                  None,
                  QuantizeHandler(
                      node, modules, is_standalone_module=True
                  ),
              )
  
      return match_map
  
file_path: pytorch/torch/ao/quantization/fx/match_utils.py
imports: import sys
  import torch
  from torch.fx.graph import Graph, Node
  from torch.ao.quantization.utils import Pattern
  from .quantize_handler import QuantizeHandler
  from ..qconfig import QConfigAny
  from ..utils import MatchAllNode
  from .graph_module import _is_observed_standalone_module
  from torch.nn.utils.parametrize import type_before_parametrizations
  from typing import Any, Dict, List, Callable, Optional, Tuple, Type, Set, Iterable
label_desc: Use image node_id 2 for calling a global function with example usage: _find_matches(graph, modules, patterns, root_node_getter_mapping, standalone_module_names, standalone_module_classes, custom_module_classes) and returns: match_map, unknown