class_docstr: MMI loss with custom CTC topologies.
  Available topologies:
      -   `default`, with or without self-loops
      -   `compact`, with or without self-loops
      -   `shared_blank`, with or without self-loops
      -   `minimal`, without self-loops
  
  cfg takes precedence over all optional parameters
  We keep explicit parameter setting to be able to create an instance without the need of a config.
code: def update_graph(self, graph: "k2.Fsa"):
      self.lm_graph = graph
      lm_graph = self.lm_graph.clone()
      if hasattr(lm_graph, "aux_labels"):
          delattr(lm_graph, "aux_labels")
      labels = lm_graph.labels
      if labels.max() != self.num_classes - 1:
          raise ValueError(
              f"lm_graph is not compatible with the num_classes: {labels.unique()}, {self.num_classes}"
          )
      from nemo.collections.asr.parts.k2.graph_compilers import (
          MmiGraphCompiler as compiler,
      )
  
      self.graph_compiler = compiler(
          self.num_classes,
          self.blank,
          self.topo_type,
          self.topo_with_self_loops,
          aux_graph=lm_graph,
      )
  
imports: from abc import abstractmethod
  from typing import Any, Optional, Tuple, Union
  import torch
  from omegaconf import DictConfig
  from nemo.collections.asr.parts.k2.classes import GraphIntersectDenseConfig
  from nemo.collections.asr.parts.k2.loss_mixins import CtcK2Mixin
  from nemo.collections.asr.parts.k2.ml_loss import MLLoss
  from nemo.collections.asr.parts.k2.utils import create_sparse_wrapped, get_tot_objf_and_finite_mask, invert_permutation, load_graph
  from nemo.core.utils.k2_guard import k2
  from nemo.utils import logging
label_desc: Use image node_id 2 for calling the CtcMmiLoss obj's underlying member method code with example usage: obj.update_graph(graph) without return types