docstr: Constructs the basis set from the differences of positive and negative
  pairs from the triplets constraints.
  
  The basis set is constructed iteratively by taking n_features triplets,
  then adding and substracting respectively all the outerproducts of the
  positive and negative pairs, and finally selecting the eigenvectors
  of this matrix with positive eigenvalue. This is done until n_basis are
  selected.
docstr_tok: ["Constructs","the","basis","set","from","the","differences","of","positive","and","negative","pairs","from","the","triplets","constraints",".","The","basis","set","is","constructed","iteratively","by","taking","n_features","triplets",",","then","adding","and","substracting","respectively","all","the","outerproducts","of","the","positive","and","negative","pairs",",","and","finally","selecting","the","eigenvectors","of","this","matrix","with","positive","eigenvalue",".","This","is","done","until","n_basis","are","selected","."]
code: def _generate_bases_dist_diff(self, triplets, X):
      """Constructs the basis set from the differences of positive and negative
      pairs from the triplets constraints.
  
      The basis set is constructed iteratively by taking n_features triplets,
      then adding and substracting respectively all the outerproducts of the
      positive and negative pairs, and finally selecting the eigenvectors
      of this matrix with positive eigenvalue. This is done until n_basis are
      selected.
      """
      n_features = X.shape[1]
      n_triplets = triplets.shape[0]
  
      if self.n_basis is None:
          # TODO: Get a good default n_basis directive
          n_basis = n_features * 80
          warnings.warn(
              "As no value for `n_basis` was selected, the number of "
              "basis will be set to n_basis= %d" % n_basis
          )
      elif isinstance(self.n_basis, int):
          n_basis = self.n_basis
      else:
          raise ValueError(
              "n_basis should be an integer, instead it is of type %s"
              % type(self.n_basis)
          )
  
      if n_features > n_triplets:
          raise ValueError(
              "Number of features (%s) is greater than the number of triplets(%s).\n"
              "Consider using dimensionality reduction or using another basis "
              "generation scheme." % (n_features, n_triplets)
          )
  
      basis = np.zeros((n_basis, n_features))
  
      # get all positive and negative pairs with lowest index first
      # np.array (2*n_triplets,2)
      triplets_pairs_sorted = np.sort(
          np.vstack((triplets[:, [0, 1]], triplets[:, [0, 2]])),
          kind="stable",
      )
      # calculate all unique pairs and their indices
      uniqPairs, indices = np.unique(
          triplets_pairs_sorted, return_inverse=True, axis=0
      )
      # calculate differences only for unique pairs
      diff = X[uniqPairs[:, 0], :] - X[uniqPairs[:, 1], :]
  
      diff_pos = diff[indices[:n_triplets], :]
      diff_neg = diff[indices[n_triplets:], :]
  
      rng = check_random_state(self.random_state)
  
      start = 0
      finish = 0
      while finish != n_basis:
          # Select triplets to yield diff
          select_triplet = rng.choice(
              n_triplets, size=n_features, replace=False
          )
  
          # select n_features positive differences
          d_pos = diff_pos[select_triplet, :]
  
          # select n_features negative differences
          d_neg = diff_neg[select_triplet, :]
  
          # Yield matrix
          diff_sum = d_pos.T.dot(d_pos) - d_neg.T.dot(d_neg)
  
          # Calculate eigenvalue and eigenvectors
          w, v = np.linalg.eigh(diff_sum.T.dot(diff_sum))
  
          # Add eigenvectors with positive eigenvalue to basis set
          pos_eig_mask = w > 0
          start = finish
          finish += pos_eig_mask.sum()
  
          try:
              basis[start:finish, :] = v[pos_eig_mask]
          except ValueError:
              # if finish is greater than n_basis
              basis[start:, :] = v[pos_eig_mask][: n_basis - start]
              break
  
          # TODO: maybe add a warning in case there are no added bases, this could
          # be caused by a bad triplet set. This would cause an infinite loop
  
      return basis, n_basis
  
imports: from __future__ import print_function, absolute_import, division
  import numpy
  from .base_metric import _TripletsClassifierMixin, MahalanobisMixin
  from ._util import components_from_metric
  from sklearn.base import TransformerMixin
  from .constraints import Constraints
  from sklearn.preprocessing import normalize
  from sklearn.neighbors import NearestNeighbors
  from sklearn.cluster import KMeans
  from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
  from sklearn.utils import check_array, check_random_state
  import warnings
label_desc: Use image node_id 7 for calling the _BaseSCML obj's underlying member method code with example usage: obj._generate_bases_dist_diff(triplets, X) and returns: basis,  n_basis