code: def disambiguate_candidates(
      aligner,
      text,
      spec,
      spec_len,
      confidence,
      device,
      heteronyms,
      log_file=None,
  ):
      """Retrieves and disambiguate all candidate sentences for disambiguation of a given some text.
  
      Assumes that the max number of candidates per word is a reasonable batch size.
  
      Note: This could be sped up if multiple words' candidates were batched, but this is conceptually easier.
      """
      # Grab original G2P result
      aligner_g2p = aligner.tokenizer.g2p
      base_g2p = aligner_g2p(text)
  
      # Tokenize text
      words = [word for word, _ in aligner_g2p.word_tokenize_func(text)]
  
      ### Loop Through Words ###
      result_g2p = []
      word_start_idx = 0
  
      has_heteronym = False
  
      for word in words:
          # Retrieve the length of the word in the default G2P conversion
          g2p_default_len = len(aligner_g2p(word))
  
          # Check if word needs to be disambiguated
          if word in heteronyms:
              has_heteronym = True
  
              # Add candidate for each ambiguous pronunciation
              word_candidates = []
              candidate_prons_and_lengths = []
  
              for pron in aligner_g2p.phoneme_dict[word]:
                  # Replace graphemes in the base G2P result with the current variant
                  candidate = (
                      base_g2p[:word_start_idx]
                      + pron
                      + base_g2p[word_start_idx + g2p_default_len :]
                  )
                  candidate_tokens = aligner.tokenizer.encode_from_g2p(
                      candidate
                  )
  
                  word_candidates.append(candidate_tokens)
                  candidate_prons_and_lengths.append((pron, len(pron)))
  
              ### Inference ###
              num_candidates = len(word_candidates)
  
              # If only one candidate, just convert and continue
              if num_candidates == 1:
                  has_heteronym = False
                  result_g2p.append(
                      f"<{' '.join(candidate_prons_and_lengths[0][0])}>"
                  )
                  word_start_idx += g2p_default_len
                  continue
  
              text_len = [len(toks) for toks in word_candidates]
              text_len_in = torch.tensor(text_len, device=device).long()
  
              # Have to pad text tokens in case different pronunciations have different lengths
              max_text_len = max(text_len)
              text_stack = []
              for i in range(num_candidates):
                  padded_tokens = general_padding(
                      torch.tensor(
                          word_candidates[i], device=device
                      ).long(),
                      text_len[i],
                      max_text_len,
                  )
                  text_stack.append(padded_tokens)
              text_in = torch.stack(text_stack)
  
              # Repeat spectrogram and spec_len tensors to match batch size
              spec_in = spec.repeat([num_candidates, 1, 1])
              spec_len_in = spec_len.repeat([num_candidates])
  
              with torch.no_grad():
                  soft_attn, _ = aligner(
                      spec=spec_in,
                      spec_len=spec_len_in,
                      text=text_in,
                      text_len=text_len_in,
                  )
  
              # Need embedding distances and duration preds to calculate mean distance for just the one word
              text_embeddings = aligner.embed(text_in).transpose(1, 2)
              l2_dists = aligner.alignment_encoder.get_dist(
                  keys=text_embeddings, queries=spec_in
              ).sqrt()
  
              durations = aligner.alignment_encoder.get_durations(
                  soft_attn, text_len_in, spec_len_in
              ).int()
  
              # Retrieve average embedding distances
              min_dist = float("inf")
              max_dist = 0.0
              best_candidate = None
              for i in range(num_candidates):
                  candidate_mean_dist = aligner.alignment_encoder.get_mean_distance_for_word(
                      l2_dists=l2_dists[i],
                      durs=durations[i],
                      start_token=word_start_idx
                      + (1 if aligner.tokenizer.pad_with_space else 0),
                      num_tokens=candidate_prons_and_lengths[i][1],
                  )
                  if log_file:
                      log_file.write(
                          f"{candidate_prons_and_lengths[i][0]} -- {candidate_mean_dist}\n"
                      )
  
                  if candidate_mean_dist < min_dist:
                      min_dist = candidate_mean_dist
                      best_candidate = candidate_prons_and_lengths[i][0]
                  if candidate_mean_dist > max_dist:
                      max_dist = candidate_mean_dist
  
              # Calculate confidence score. If below threshold, skip and use graphemes.
              disamb_conf = (max_dist - min_dist) / (
                  (max_dist + min_dist) / 2.0
              )
              if disamb_conf < confidence:
                  if log_file:
                      log_file.write(
                          f"Below confidence threshold: {best_candidate} ({disamb_conf})\n"
                      )
  
                  has_heteronym = False
                  result_g2p.append(f"<{' '.join(aligner_g2p(word))}>")
                  word_start_idx += g2p_default_len
                  continue
  
              # Otherwise, can write disambiguated word
              if log_file:
                  log_file.write(
                      f"best candidate: {best_candidate} (confidence: {disamb_conf})\n"
                  )
  
              result_g2p.append(f"<{' '.join(best_candidate)}>")
          else:
              if re.search("[a-zA-Z]", word) is None:
                  # Punctuation or space
                  result_g2p.append(f"<{word}>")
              elif word in aligner_g2p.phoneme_dict:
                  # Take default pronunciation for everything else in the dictionary
                  result_g2p.append(
                      f"<{' '.join(aligner_g2p.phoneme_dict[word][0])}>"
                  )
              else:
                  # OOV
                  result_g2p.append(f"<{' '.join(aligner_g2p(word))}>")
  
          # Advance to phoneme index of next word
          word_start_idx += g2p_default_len
  
      if log_file and has_heteronym:
          log_file.write(f"{text}\n")
          log_file.write(f"===\n{''.join(result_g2p)}\n===\n")
          log_file.write(f"===============================\n")
  
      return result_g2p, has_heteronym
  
file_path: NeMo/examples/tts/aligner_heteronym_disambiguation.py
imports: import argparse
  import json
  import os
  import re
  import librosa
  import soundfile
  import torch
  from nemo.collections.tts.models import AlignerModel
  from nemo.collections.tts.parts.utils.tts_dataset_utils import general_padding
label_desc: Use image node_id 3 for calling a global function with example usage: disambiguate_candidates(aligner, text, spec, spec_len, confidence, device, heteronyms, log_file) and returns: result_g2p,  has_heteronym