code: def HDBSCAN_flat(
      X,
      n_clusters=None,
      cluster_selection_epsilon=0.0,
      clusterer=None,
      inplace=False,
      **kwargs,
  ):
      """
      Train a HDBSCAN clusterer by specifying n_clusters.
      Or, modify a trained clusterer to return specific n_clusters.
  
      Parameters
      ----------
      X: array-like
          Data to be passed to HDBSCAN for training.
  
      n_clusters: int, default=None
          Number of clusters to produce.
          If None, revert to default HDBSCAN
  
      cluster_selection_epsilon: float, default=0.
          core-distance below which to stop splitting clusters.
          This can indirectly impose n_clusters.
          This argument is ignored if n_clusters is supplied.
  
      clusterer: HDBSCAN, default=None
          If supplied, modify this clusterer to produce n_clusters clusters.
  
      inplace: bool, default=False
          If 'clusterer' parameter is supplied, and inplace is True,
              modify the previous clusterer inplace.
              If False, return a modified copy of the previous clusterer.
  
      **kwargs: keyword arguments
          All init arguments for HDBSCAN
  
      Returns
      -------
      new_clusterer: HDBSCAN
          New HDBSCAN instance; returned irrespective of inplace=True or False
  
      Usage
      -----
      # Extract flat clustering from HDBSCAN's hierarchy for 7 clusters
      clusterer = HDBSCAN_flat(X_train, n_clusters=7,
                               min_cluster_size=12, min_samples=8)
      labels = clusterer.labels_
      proba = clusterer.probabilities_
  
      # Use a previously initialized/trained HDBSCAN
      old_clusterer = HDBSCAN(min_cluster_size=12, min_samples=8)
      clusterer = HDBSCAN_flat(X_train, n_clusters=7,
                               clusterer=old_clusterer, inplace=True)
      labels = clusterer.labels_
      proba = clusterer.probabilities_
  
      See Also
      ---------
      :py:func:`hdbscan.HDBSCAN`
      :py:func:`re_init`
      """
      # Handle the trivial case first.
      if (n_clusters is None) and (cluster_selection_epsilon == 0.0):
          if (not isinstance(clusterer, HDBSCAN)) or (not inplace):
              # Always generate prediction_data to avoid later woes
              kwargs["prediction_data"] = True
              new_clusterer = HDBSCAN(**kwargs)
          else:
              new_clusterer = clusterer
              new_clusterer.prediction_data = True
  
          new_clusterer.fit(X)
          return new_clusterer
  
      if (n_clusters is not None) and (
          cluster_selection_epsilon != 0.0
      ):
          warn(
              f"'cluster_selection_epsilon' (={cluster_selection_epsilon})"
              f" is ignored when 'n_clusters' is supplied."
          )
          cluster_selection_epsilon = 0.0
          # This will later be chosen according to n_clusters
  
      if not isinstance(clusterer, HDBSCAN):
          # Initialize and train clusterer if one was not previously supplied.
          # Always generate prediction data
          kwargs["prediction_data"] = True
          new_clusterer = HDBSCAN(**kwargs)
          # We do not pass cluster_selection_epsilon here.
          # While this adds unnecessary computation, it makes the code
          #   easier to read and debug.
          new_clusterer.fit(X)
      else:
          if inplace:
              new_clusterer = clusterer
          else:
              new_clusterer = copy.deepcopy(clusterer)
  
          new_clusterer.prediction_data = True
  
          # Train on 'X'. Do this even if the supplied clusterer was trained,
          #   because we want to make sure it fits 'X'.
          new_clusterer.prediction_data = True
          new_clusterer.fit(X)
  
      if new_clusterer.cluster_selection_method == "eom":
          max_eom_clusters = len(
              new_clusterer.condensed_tree_._select_clusters()
          )
  
      # Pick an epsilon value right after a split produces n_clusters,
      #   and the don't split further for smaller epsilon (larger lambda)
      if n_clusters is not None:
          if (new_clusterer.cluster_selection_method == "eom") and (
              n_clusters > max_eom_clusters
          ):
              warn(
                  f"Cannot predict more than {max_eom_clusters} with cluster "
                  "selection method 'eom'. Changing to method 'leaf'..."
              )
              new_clusterer.cluster_selection_method = "leaf"
          epsilon = select_epsilon(
              new_clusterer.condensed_tree_, n_clusters
          )
      else:
          # Or use the specified cluster_selection_epsilon
          epsilon = cluster_selection_epsilon
  
      new_clusterer.cluster_selection_epsilon = float(epsilon)
  
      # Extract tree related stuff, in order to re-assign labels
      single_linkage_tree = new_clusterer.single_linkage_tree_
      single_linkage_tree = single_linkage_tree.to_numpy()
      min_cluster_size = new_clusterer.min_cluster_size
      cluster_selection_method = new_clusterer.cluster_selection_method
      allow_single_cluster = new_clusterer.allow_single_cluster
      match_reference_implementation = False
  
      # Get labels according to the required cluster_selection_epsilon
      output = _tree_to_labels(
          None,
          single_linkage_tree,
          min_cluster_size,
          cluster_selection_method,
          allow_single_cluster,
          match_reference_implementation,
          cluster_selection_epsilon=epsilon,
      )
  
      # Reflect the related changes in HDBSCAN.
      (
          new_clusterer.labels_,
          new_clusterer.probabilities_,
          new_clusterer.cluster_persistence_,
          new_clusterer._condensed_tree,
          new_clusterer._single_linkage_tree,
      ) = output
  
      # PredictionData attached to HDBSCAN should also change.
      # A function re_init is defined in this module to handle this.
      re_init(
          new_clusterer.prediction_data_,
          new_clusterer.condensed_tree_,
          cluster_selection_epsilon=epsilon,
      )
      return new_clusterer
  
imports: import copy
  from warnings import warn
  import numpy
  from ._hdbscan_tree import compute_stability, get_cluster_tree_leaves
  from .hdbscan_ import HDBSCAN, _tree_to_labels
  from .plots import _bfs_from_cluster_tree
  from .prediction import PredictionData, _find_cluster_and_probability, _find_neighbor_and_lambda
  from ._prediction_utils import get_tree_row_with_child, dist_membership_vector, outlier_membership_vector, prob_in_some_cluster, all_points_dist_membership_vector, all_points_outlier_membership_vector, all_points_prob_in_some_cluster
label_desc: Use image node_id 1 for calling a global function with example usage: HDBSCAN_flat(X, n_clusters, cluster_selection_epsilon, clusterer, inplace) and returns: new_clusterer, new_clusterer