def _x2p_torch(
    X: torch.Tensor,
    tol: float = 1e-5,
    initial_dims: int = 50,
    perplexity: float = 30.0,
    device: torch.device = torch.device("cuda" if torch.cuda.is_available() else "cpu"),
    verbose: bool = False,
):
    """
    Performs a binary search to get P-values in such a way that each
    conditional Gaussian has the same perplexity.
    """

    # Initialize some variables
    # print("Computing pairwise distances...")
    (n, d) = X.shape

    sum_X = torch.sum(X * X, 1)
    D = torch.add(torch.add(-2 * torch.mm(X, X.t()), sum_X).t(), sum_X).to(device)

    P = torch.zeros(n, n, device=device)
    beta = torch.ones(n, 1, device=device)
    logU = torch.log(torch.tensor([perplexity], device=device))
    n_list = [i for i in range(n)]

    # Loop over all datapoints
    bar = range(n)
    if verbose:
        bar = tqdm(bar)

    for i in bar:
        # Print progress
        # if i % 500 == 0:
        #     print("Computing P-values for point %d of %d..." % (i, n))

        # Compute the Gaussian kernel and entropy for the current precision
        # there may be something wrong with this setting None
        betamin = None
        betamax = None
        Di = D[i, n_list[0:i] + n_list[i + 1 : n]]

        (H, thisP) = _h_beta_torch(Di, beta[i])

        # Evaluate whether the perplexity is within tolerance
        h_diff = H - logU
        tries = 0
        while torch.abs(h_diff) > tol and tries < initial_dims:
            # If not, increase or decrease precision
            if h_diff > 0:
                betamin = beta[i].clone()
                if betamax is None:
                    beta[i] = beta[i] * 2.0
                else:
                    beta[i] = (beta[i] + betamax) / 2.0
            else:
                betamax = beta[i].clone()
                if betamin is None:
                    beta[i] = beta[i] / 2.0
                else:
                    beta[i] = (beta[i] + betamin) / 2.0

            # Recompute the values
            (H, thisP) = _h_beta_torch(Di, beta[i])

            h_diff = H - logU
            tries += 1

        # Set the final row of P
        P[i, n_list[0:i] + n_list[i + 1 : n]] = thisP

    # Return final P-matrix
    return P

    Performs a binary search to get P-values in such a way that each
    conditional Gaussian has the same perplexity.
    
smoe/utils/visualization/tsne_torch_model.py