code: def fit(self, X, y=None, **fit_params):
      if "relations" not in fit_params:
          raise ValueError(
              "Aligned UMAP requires relations between data to be "
              "specified"
          )
  
      self.dict_relations_ = fit_params["relations"]
      assert type(self.dict_relations_) in (list, tuple)
      assert type(X) in (list, tuple, np.ndarray)
      assert (len(X) - 1) == (len(self.dict_relations_))
  
      if y is not None:
          assert type(y) in (list, tuple, np.ndarray)
          assert (len(y) - 1) == (len(self.dict_relations_))
      else:
          y = [None] * len(X)
  
      # We need n_components to be constant or this won't work
      if type(self.n_components) in (list, tuple, np.ndarray):
          raise ValueError(
              "n_components must be a single integer, and cannot vary"
          )
  
      self.n_models_ = len(X)
  
      if self.n_epochs is None:
          self.n_epochs = 200
  
      n_epochs = self.n_epochs
  
      self.mappers_ = [
          UMAP(
              n_neighbors=get_nth_item_or_val(self.n_neighbors, n),
              min_dist=get_nth_item_or_val(self.min_dist, n),
              n_epochs=get_nth_item_or_val(self.n_epochs, n),
              repulsion_strength=get_nth_item_or_val(
                  self.repulsion_strength, n
              ),
              learning_rate=get_nth_item_or_val(self.learning_rate, n),
              init=self.init,
              spread=get_nth_item_or_val(self.spread, n),
              negative_sample_rate=get_nth_item_or_val(
                  self.negative_sample_rate, n
              ),
              local_connectivity=get_nth_item_or_val(
                  self.local_connectivity, n
              ),
              set_op_mix_ratio=get_nth_item_or_val(
                  self.set_op_mix_ratio, n
              ),
              unique=get_nth_item_or_val(self.unique, n),
              n_components=self.n_components,
              metric=self.metric,
              metric_kwds=self.metric_kwds,
              low_memory=self.low_memory,
              random_state=self.random_state,
              angular_rp_forest=self.angular_rp_forest,
              transform_queue_size=self.transform_queue_size,
              target_n_neighbors=self.target_n_neighbors,
              target_metric=self.target_metric,
              target_metric_kwds=self.target_metric_kwds,
              target_weight=self.target_weight,
              transform_seed=self.transform_seed,
              force_approximation_algorithm=self.force_approximation_algorithm,
              verbose=self.verbose,
              a=self.a,
              b=self.b,
          ).fit(X[n], y[n])
          for n in range(self.n_models_)
      ]
  
      window_size = fit_params.get(
          "window_size", self.alignment_window_size
      )
      relations = expand_relations(self.dict_relations_, window_size)
  
      indptr_list = numba.typed.List.empty_list(numba.types.int32[::1])
      indices_list = numba.typed.List.empty_list(numba.types.int32[::1])
      heads = numba.typed.List.empty_list(numba.types.int32[::1])
      tails = numba.typed.List.empty_list(numba.types.int32[::1])
      epochs_per_samples = numba.typed.List.empty_list(
          numba.types.float64[::1]
      )
  
      for mapper in self.mappers_:
          indptr_list.append(mapper.graph_.indptr)
          indices_list.append(mapper.graph_.indices)
          heads.append(mapper.graph_.tocoo().row)
          tails.append(mapper.graph_.tocoo().col)
          epochs_per_samples.append(
              make_epochs_per_sample(
                  mapper.graph_.tocoo().data, n_epochs
              )
          )
  
      rng_state_transform = np.random.RandomState(self.transform_seed)
      regularisation_weights = build_neighborhood_similarities(
          indptr_list,
          indices_list,
          relations,
      )
      first_init = spectral_layout(
          self.mappers_[0]._raw_data,
          self.mappers_[0].graph_,
          self.n_components,
          rng_state_transform,
      )
      expansion = 10.0 / np.abs(first_init).max()
      first_embedding = (first_init * expansion).astype(
          np.float32,
          order="C",
      )
  
      embeddings = numba.typed.List.empty_list(
          numba.types.float32[:, ::1]
      )
      embeddings.append(first_embedding)
      for i in range(1, self.n_models_):
          next_init = spectral_layout(
              self.mappers_[i]._raw_data,
              self.mappers_[i].graph_,
              self.n_components,
              rng_state_transform,
          )
          expansion = 10.0 / np.abs(next_init).max()
          next_embedding = (next_init * expansion).astype(
              np.float32,
              order="C",
          )
          anchor_data = relations[i][window_size - 1]
          left_anchors = anchor_data[anchor_data >= 0]
          right_anchors = np.where(anchor_data >= 0)[0]
          embeddings.append(
              procrustes_align(
                  embeddings[-1],
                  next_embedding,
                  np.vstack([left_anchors, right_anchors]),
              )
          )
  
      seed_triplet = rng_state_transform.randint(
          INT32_MIN, INT32_MAX, 3
      ).astype(np.int64)
      self.embeddings_ = optimize_layout_aligned_euclidean(
          embeddings,
          embeddings,
          heads,
          tails,
          n_epochs,
          epochs_per_samples,
          regularisation_weights,
          relations,
          seed_triplet,
          lambda_=self.alignment_regularisation,
          move_other=True,
      )
  
      for i, embedding in enumerate(self.embeddings_):
          disconnected_vertices = (
              np.array(self.mappers_[i].graph_.sum(axis=1)).flatten()
              == 0
          )
          embedding[disconnected_vertices] = np.full(
              self.n_components, np.nan
          )
  
      return self
  
imports: import numpy
  import numba
  from sklearn.base import BaseEstimator
  from sklearn.utils import check_array
  from umap.sparse import arr_intersect
  from umap.sparse import arr_union
  from umap.umap_ import UMAP, make_epochs_per_sample
  from umap.spectral import spectral_layout
  from umap.layouts import optimize_layout_aligned_euclidean
label_desc: Use image node_id 2 for calling the AlignedUMAP obj's underlying member method code with example usage: obj.fit(X, y) and returns: self