(self, X)
return inv_transformed_points
Transform X in the existing embedded space back into the input
        data space and return that transformed output.

        Parameters
        ----------
        X : array, shape (n_samples, n_components)
            New points to be inverse transformed.

        Returns
        -------
        X_new : array, shape (n_samples, n_features)
            Generated data points new data in data space.
Transform X in the existing embedded space back into the input
        data space and return that transformed output.
Transform X in the existing embedded space back into the input data space and return that transformed output .
def inverse_transform(self, X):
        """Transform X in the existing embedded space back into the input
        data space and return that transformed output.

        Parameters
        ----------
        X : array, shape (n_samples, n_components)
            New points to be inverse transformed.

        Returns
        -------
        X_new : array, shape (n_samples, n_features)
            Generated data points new data in data space.
        """

        if self._sparse_data:
            raise ValueError("Inverse transform not available for sparse input.")
        elif self._inverse_distance_func is None:
            raise ValueError("Inverse transform not available for given metric.")
        elif self.densmap:
            raise ValueError("Inverse transform not available for densMAP.")
        elif self.n_components >= 8:
            warn(
                "Inverse transform works best with low dimensional embeddings."
                " Results may be poor, or this approach to inverse transform"
                " may fail altogether! If you need a high dimensional latent"
                " space and inverse transform operations consider using an"
                " autoencoder."
            )
        elif self.transform_mode == "graph":
            raise ValueError(
                "Inverse transform not available for transform_mode = 'graph'"
            )

        X = check_array(X, dtype=np.float32, order="C")
        random_state = check_random_state(self.transform_seed)
        rng_state = random_state.randint(INT32_MIN, INT32_MAX, 3).astype(np.int64)

        # build Delaunay complex (Does this not assume a roughly euclidean output metric)?
        deltri = scipy.spatial.Delaunay(
            self.embedding_, incremental=True, qhull_options="QJ"
        )
        neighbors = deltri.simplices[deltri.find_simplex(X)]
        adjmat = scipy.sparse.lil_matrix(
            (self.embedding_.shape[0], self.embedding_.shape[0]), dtype=int
        )
        for i in np.arange(0, deltri.simplices.shape[0]):
            for j in deltri.simplices[i]:
                if j < self.embedding_.shape[0]:
                    idx = deltri.simplices[i][
                        deltri.simplices[i] < self.embedding_.shape[0]
                    ]
                    adjmat[j, idx] = 1
                    adjmat[idx, j] = 1

        adjmat = scipy.sparse.csr_matrix(adjmat)

        min_vertices = min(self._raw_data.shape[-1], self._raw_data.shape[0])

        neighborhood = [
            breadth_first_search(adjmat, v[0], min_vertices=min_vertices)
            for v in neighbors
        ]
        if callable(self.output_metric):
            # need to create another numba.jit-able wrapper for callable
            # output_metrics that return a tuple (already checked that it does
            # during param validation in `fit` method)
            _out_m = self.output_metric

            @numba.njit(fastmath=True)
            def _output_dist_only(x, y, *kwds):
                return _out_m(x, y, *kwds)[0]

            dist_only_func = _output_dist_only
        elif self.output_metric in dist.named_distances.keys():
            dist_only_func = dist.named_distances[self.output_metric]
        else:
            # shouldn't really ever get here because of checks already performed,
            # but works as a failsafe in case attr was altered manually after fitting
            raise ValueError(
                "Unrecognized output metric: {}".format(self.output_metric)
            )

        dist_args = tuple(self._output_metric_kwds.values())
        distances = [
            np.array(
                [
                    dist_only_func(X[i], self.embedding_[nb], *dist_args)
                    for nb in neighborhood[i]
                ]
            )
            for i in range(X.shape[0])
        ]
        idx = np.array([np.argsort(e)[:min_vertices] for e in distances])

        dists_output_space = np.array(
            [distances[i][idx[i]] for i in range(len(distances))]
        )
        indices = np.array([neighborhood[i][idx[i]] for i in range(len(neighborhood))])

        rows, cols, distances = np.array(
            [
                [i, indices[i, j], dists_output_space[i, j]]
                for i in range(indices.shape[0])
                for j in range(min_vertices)
            ]
        ).T

        # calculate membership strength of each edge
        weights = 1 / (1 + self._a * distances ** (2 * self._b))

        # compute 1-skeleton
        # convert 1-skeleton into coo_matrix adjacency matrix
        graph = scipy.sparse.coo_matrix(
            (weights, (rows, cols)), shape=(X.shape[0], self._raw_data.shape[0])
        )

        # That lets us do fancy unpacking by reshaping the csr matrix indices
        # and data. Doing so relies on the constant degree assumption!
        # csr_graph = graph.tocsr()
        csr_graph = normalize(graph.tocsr(), norm="l1")
        inds = csr_graph.indices.reshape(X.shape[0], min_vertices)
        weights = csr_graph.data.reshape(X.shape[0], min_vertices)
        inv_transformed_points = init_transform(inds, weights, self._raw_data)

        if self.n_epochs is None:
            # For smaller datasets we can use more epochs
            if graph.shape[0] <= 10000:
                n_epochs = 100
            else:
                n_epochs = 30
        else:
            n_epochs = int(self.n_epochs // 3.0)

        # graph.data[graph.data < (graph.data.max() / float(n_epochs))] = 0.0
        # graph.eliminate_zeros()

        epochs_per_sample = make_epochs_per_sample(graph.data, n_epochs)

        head = graph.row
        tail = graph.col
        weight = graph.data

        inv_transformed_points = optimize_layout_inverse(
            inv_transformed_points,
            self._raw_data,
            head,
            tail,
            weight,
            self._sigmas,
            self._rhos,
            n_epochs,
            graph.shape[1],
            epochs_per_sample,
            self._a,
            self._b,
            rng_state,
            self.repulsion_strength,
            self._initial_alpha / 4.0,
            self.negative_sample_rate,
            self._inverse_distance_func,
            tuple(self._metric_kwds.values()),
            verbose=self.verbose,
            tqdm_kwds=self.tqdm_kwds,
        )

        return inv_transformed_points
def inverse_transform ( self , X ) : if self . _sparse_data : raise ValueError ( "Inverse transform not available for sparse input." ) elif self . _inverse_distance_func is None : raise ValueError ( "Inverse transform not available for given metric." ) elif self . densmap : raise ValueError ( "Inverse transform not available for densMAP." ) elif self . n_components >= 8 : warn ( "Inverse transform works best with low dimensional embeddings." " Results may be poor, or this approach to inverse transform" " may fail altogether! If you need a high dimensional latent" " space and inverse transform operations consider using an" " autoencoder." ) elif self . transform_mode == "graph" : raise ValueError ( "Inverse transform not available for transform_mode = 'graph'" ) X = check_array ( X , dtype = np . float32 , order = "C" ) random_state = check_random_state ( self . transform_seed ) rng_state = random_state . randint ( INT32_MIN , INT32_MAX , 3 ) . astype ( np . int64 ) # build Delaunay complex (Does this not assume a roughly euclidean output metric)? deltri = scipy . spatial . Delaunay ( self . embedding_ , incremental = True , qhull_options = "QJ" ) neighbors = deltri . simplices [ deltri . find_simplex ( X ) ] adjmat = scipy . sparse . lil_matrix ( ( self . embedding_ . shape [ 0 ] , self . embedding_ . shape [ 0 ] ) , dtype = int ) for i in np . arange ( 0 , deltri . simplices . shape [ 0 ] ) : for j in deltri . simplices [ i ] : if j < self . embedding_ . shape [ 0 ] : idx = deltri . simplices [ i ] [ deltri . simplices [ i ] < self . embedding_ . shape [ 0 ] ] adjmat [ j , idx ] = 1 adjmat [ idx , j ] = 1 adjmat = scipy . sparse . csr_matrix ( adjmat ) min_vertices = min ( self . _raw_data . shape [ - 1 ] , self . _raw_data . shape [ 0 ] ) neighborhood = [ breadth_first_search ( adjmat , v [ 0 ] , min_vertices = min_vertices ) for v in neighbors ] if callable ( self . output_metric ) : # need to create another numba.jit-able wrapper for callable # output_metrics that return a tuple (already checked that it does # during param validation in `fit` method) _out_m = self . output_metric @ numba . njit ( fastmath = True ) def _output_dist_only ( x , y , * kwds ) : return _out_m ( x , y , * kwds ) [ 0 ] dist_only_func = _output_dist_only elif self . output_metric in dist . named_distances . keys ( ) : dist_only_func = dist . named_distances [ self . output_metric ] else : # shouldn't really ever get here because of checks already performed, # but works as a failsafe in case attr was altered manually after fitting raise ValueError ( "Unrecognized output metric: {}" . format ( self . output_metric ) ) dist_args = tuple ( self . _output_metric_kwds . values ( ) ) distances = [ np . array ( [ dist_only_func ( X [ i ] , self . embedding_ [ nb ] , * dist_args ) for nb in neighborhood [ i ] ] ) for i in range ( X . shape [ 0 ] ) ] idx = np . array ( [ np . argsort ( e ) [ : min_vertices ] for e in distances ] ) dists_output_space = np . array ( [ distances [ i ] [ idx [ i ] ] for i in range ( len ( distances ) ) ] ) indices = np . array ( [ neighborhood [ i ] [ idx [ i ] ] for i in range ( len ( neighborhood ) ) ] ) rows , cols , distances = np . array ( [ [ i , indices [ i , j ] , dists_output_space [ i , j ] ] for i in range ( indices . shape [ 0 ] ) for j in range ( min_vertices ) ] ) . T # calculate membership strength of each edge weights = 1 / ( 1 + self . _a * distances ** ( 2 * self . _b ) ) # compute 1-skeleton # convert 1-skeleton into coo_matrix adjacency matrix graph = scipy . sparse . coo_matrix ( ( weights , ( rows , cols ) ) , shape = ( X . shape [ 0 ] , self . _raw_data . shape [ 0 ] ) ) # That lets us do fancy unpacking by reshaping the csr matrix indices # and data. Doing so relies on the constant degree assumption! # csr_graph = graph.tocsr() csr_graph = normalize ( graph . tocsr ( ) , norm = "l1" ) inds = csr_graph . indices . reshape ( X . shape [ 0 ] , min_vertices ) weights = csr_graph . data . reshape ( X . shape [ 0 ] , min_vertices ) inv_transformed_points = init_transform ( inds , weights , self . _raw_data ) if self . n_epochs is None : # For smaller datasets we can use more epochs if graph . shape [ 0 ] <= 10000 : n_epochs = 100 else : n_epochs = 30 else : n_epochs = int ( self . n_epochs // 3.0 ) # graph.data[graph.data < (graph.data.max() / float(n_epochs))] = 0.0 # graph.eliminate_zeros() epochs_per_sample = make_epochs_per_sample ( graph . data , n_epochs ) head = graph . row tail = graph . col weight = graph . data inv_transformed_points = optimize_layout_inverse ( inv_transformed_points , self . _raw_data , head , tail , weight , self . _sigmas , self . _rhos , n_epochs , graph . shape [ 1 ] , epochs_per_sample , self . _a , self . _b , rng_state , self . repulsion_strength , self . _initial_alpha / 4.0 , self . negative_sample_rate , self . _inverse_distance_func , tuple ( self . _metric_kwds . values ( ) ) , verbose = self . verbose , tqdm_kwds = self . tqdm_kwds , ) return inv_transformed_points