t-SNE & UMAP showed t-SNE's core habit: friends stay together. Before t-SNE draws anything, it writes down who counts as whose neighbour in the original data, as a table of numbers. The flat picture is then arranged so that its own neighbours match that table. In this problem you build the table.
Step 1: each point picks its neighbours. For point , every other point gets a weight that shrinks as the squared distance between them grows. How fast it shrinks is set by a width that belongs to point :
You can read as the chance that picks as its neighbour. A point never picks itself, and each point's chances add up to 1.
Step 2: choose each width by perplexity. One shared width would be unfair. A point in a dense crowd and a point out on its own need very different reach. So each point gets the width that gives it the same effective number of neighbours, called the perplexity:
Any term with counts as 0. If spread its chances evenly over 3 neighbours, its perplexity would be exactly 3. A wider spreads the chances out and raises the perplexity. A narrower one concentrates them and lowers it. For every point, find the whose perplexity matches the target to within .
Step 3: make it symmetric. Point may like more than likes . t-SNE averages the two directions, and scales the result so the whole table adds up to 1:
Here is the number of points.
Task: write neighbour_affinities(points, perplexity). It returns as a list of lists, with every entry rounded to 4 decimal places.
points is a list of points. Each point is a list of numbers, and all points have the same length.