code: def prepare_n_shadows_model(
      model: torch.nn.Module,
      example_inputs: Any,
      qconfig_multi_mapping: QConfigMultiMapping,
      backend_config: BackendConfig,
      custom_prepare_fn: Optional[Callable] = None,
      custom_prepare_kwargs: Optional[Dict[str, Any]] = None,
      custom_tracer: Any = None,
  ) -> GraphModule:
      """
      Given a model with a graph with M ops such as
  
  
        args_kwargs_m -> op_m -> output_m
  
  
      And a set of N qconfigs for each op, creates a new model, with
      each of the subgraph of `op_m` transformed into
  
      .. code::
  
             |---------> op_m_n -> log_m_n
             |                     /
        args_kwargs_m ---------> op_m -> log_m_0
  
      Where op_m_n is op_m wrapped in a submodule and transformed with
      qconfig_n, and its inner graph looks like
  
      .. code::
  
        args_m -------- op_m_prepared_with_qconfig_n -> out_m_n
                    /
        kwargs_m ---
  
      This is useful for testing different quantization of multiple layers in
      a single pass through the model.
  
      High level TODOs for future PRs:
      * figure out a better way to name the output structure
      * return a results data structure instead of printing it out
      * add examples to docblocks
      """
  
      if custom_tracer is None:
          tracer = quantize_fx.QuantizationTracer([], [])
      else:
          tracer = custom_tracer
      mt = torch.fx.GraphModule(model, tracer.trace(model))
      # this is necessary to ensure logger FQNs get populated
      mt._node_name_to_scope = tracer.node_name_to_scope
  
      # run example input propagation, we need this to call prepare_fx on
      # individual subgraphs
      output_prop = OutputProp(mt)
      output_prop.propagate(*example_inputs)
  
      # Find the set of subgraphs in the original graph which we need to
      # consider.
      modules = dict(mt.named_modules(remove_duplicate=False))
      patterns = _get_pattern_to_quantize_handlers(backend_config)
      root_node_getter_mapping = get_fusion_pattern_to_root_node_getter(
          backend_config
      )
      standalone_module_names: List[str] = []
      standalone_module_classes: List[Type] = []
      custom_module_classes: List[Type] = []
      matches = _find_matches(
          mt.graph,
          modules,
          patterns,
          root_node_getter_mapping,
          standalone_module_names,
          standalone_module_classes,
          custom_module_classes,
      )
      subgraphs_dedup: Dict[str, List[Node]] = _get_dedup_subgraphs(
          matches
      )
  
      # generate node to qconfig for each subgraph
      # TODO(future PR): deduplicate repeating entries
      list_of_node_name_to_qconfig: List[Dict[str, QConfigAny]] = []
      for (
          qconfig_mapping
      ) in qconfig_multi_mapping.qconfig_mappings_list:
          node_name_to_qconfig = _generate_node_name_to_qconfig(
              mt,
              modules,
              mt.graph,
              qconfig_mapping,
              tracer.node_name_to_scope,
          )
          list_of_node_name_to_qconfig.append(node_name_to_qconfig)
  
      # For each region in the model, do the following:
      #   For each qconfig for that region, do the following:
      #     1. create a copy of the region wrapped in a module
      #     2. pass original args, original kwargs, and expected output to module
      #     3. add an output comparison logger and hook it up to compare
      #        actual output to expected output
      #     4. run `prepare_fx` on the module
      for subgraph_idx, (
          match_name,
          nodes_in_this_subgraph,
      ) in enumerate(subgraphs_dedup.items()):
          create_n_transformed_and_logged_copies_of_subgraph(
              mt,
              subgraph_idx,
              match_name,
              nodes_in_this_subgraph,
              qconfig_multi_mapping.qconfig_mappings_list,
              list_of_node_name_to_qconfig,
              custom_prepare_fn,
              custom_prepare_kwargs,  # type: ignore[arg-type]
          )
  
      return mt
  
imports: import collections
  import torch
  import torch.nn
  import torch.ao.quantization.quantize_fx
  from torch.fx import GraphModule
  from torch.fx.graph import Node
  from torch.ao.ns.fx.mappings import get_base_name_to_sets_of_related_ops
  from torch.ao.ns.fx.graph_matcher import get_matching_subgraph_pairs, get_type_a_related_to_b
  from .fx.weight_utils import extract_weight_from_node
  from .fx.graph_passes import add_loggers_to_model, create_a_shadows_b
  from .fx.utils import rekey_logger_info_on_node_name_of_model, maybe_add_missing_fqns, get_target_type_str
  from .fx.ns_types import NSSingleResultValuesType, NSResultsType, NSNodeTargetType
  from torch.ao.quantization.backend_config.utils import get_fusion_pattern_to_root_node_getter
  from torch.ao.quantization.backend_config import BackendConfig
  from torch.ao.quantization.fx.match_utils import _find_matches
  from torch.ao.quantization.fx.graph_module import _get_observed_graph_module_attr
  from torch.ao.quantization.fx.qconfig_mapping_utils import _generate_node_name_to_qconfig
  from torch.ao.quantization.fx.quantize_handler import _get_pattern_to_quantize_handlers
  from torch.ao.quantization.qconfig import QConfigAny
  from torch.ao.quantization import QConfigMapping
  from torch.ao.ns.fx.n_shadows_utils import OutputProp, _get_dedup_subgraphs, SHADOW_WRAPPER_NODE_NAME_PREFIX, group_results_by_subgraph, create_results_comparison, print_n_shadows_summary, create_n_transformed_and_logged_copies_of_subgraph, create_add_loggers_graph, extract_weight_comparison
  from torch.ao.ns.fx.qconfig_multi_mapping import QConfigMultiMapping
  from typing import Dict, Tuple, Callable, List, Optional, Set, Any, Type
label_desc: Use image node_id 13 for calling a global function with example usage: prepare_n_shadows_model(model, example_inputs, qconfig_multi_mapping, backend_config, custom_prepare_fn, custom_prepare_kwargs, custom_tracer) and returns: mt