public Array <float> x2p(Array <float> X, float tol = 1e-5f, float perplexity = 30f, bool sym = true, bool normalize = true) { //"""Performs a binary search to get P-values in such a way that each conditional Gaussian has the same perplexity.""" // Initialize some variables Console.WriteLine("Computing pairwise distances..."); int n = X.Shape[0], d = X.Shape[1]; var sum_X = NN.Sum(X * X, axis: 1); var D = (-2 * X.Dot(X.T) + sum_X).T + sum_X; var P = NN.Zeros(n, n); var beta = NN.Ones(n); var logU = (float)Math.Log(perplexity); var Di = NN.Zeros(n, n - 1); // Loop over all datapoints for (int i = 0; i < n; ++i) { // Print progress if (i % 500 == 0) { Console.WriteLine("Computing P-values for point {0} of {1} ...", i, n); } // Compute the Gaussian kernel and entropy for the current precision var betamin = float.NegativeInfinity; var betamax = float.PositiveInfinity; Di[i, Until(i)] = D[Until(i)]; if (i + 1 < n) { Di[i, From(i + 1)] = D[From(i)]; } var H_thisP = Hbeta(Di, beta.Item[i]); var H = H_thisP.Item1; var thisP = H_thisP.Item2; // Evaluate whether the perplexity is within tolerance var Hdiff = H - logU; var tries = 0; while (Math.Abs(Hdiff) > tol && tries < 50) { // If not, increase or decrease precision if (Hdiff > 0) { betamin = beta.Item[i]; if (float.IsInfinity(betamax)) { beta.Item[i] = beta.Item[i] * 2; } else { beta.Item[i] = (beta.Item[i] + betamax) / 2; } } else { betamax = beta.Item[i]; if (float.IsInfinity(betamin)) { beta.Item[i] = beta.Item[i] / 2; } else { beta.Item[i] = (beta.Item[i] + betamin) / 2; } } // Recompute the values H_thisP = Hbeta(Di, beta.Item[i]); H = H_thisP.Item1; thisP = H_thisP.Item2; Hdiff = H - logU; tries = tries + 1; } // Set the final row of P P[i, Until(i)] = thisP[Until(i)]; if (i + 1 < n) { P[i, From(i + 1)] = thisP[From(i)]; } } var sigma = NN.Mean(NN.Sqrt(1 / beta)); Console.WriteLine("Mean value of sigma: {0}", sigma); // Return final P-matrix if (sym) { P += P.T; } if (normalize) { P /= NN.Sum(P); } return(P); }