Skip to content

Commit 46d65ae

Browse files
Merge pull request #217 from dkobak/precomputed_affinity
Implement precomputed affinity class
2 parents f79f37c + 1268714 commit 46d65ae

File tree

3 files changed

+55
-0
lines changed

3 files changed

+55
-0
lines changed

openTSNE/affinity.py

+23
Original file line numberDiff line numberDiff line change
@@ -1264,3 +1264,26 @@ def to_new(self, data, k_neighbors=None, return_distances=False):
12641264
return P, neighbors, distances
12651265

12661266
return P
1267+
1268+
1269+
class PrecomputedAffinities(Affinities):
1270+
"""Use a precomputed affinity matrix.
1271+
1272+
Parameters
1273+
----------
1274+
affinities: scipy.sparse.csr_matrix, np.ndarray
1275+
A N x N matrix containing the affinities.
1276+
normalize: bool
1277+
Normalize the affinity matrix to sum to 1. Default is True.
1278+
1279+
"""
1280+
1281+
def __init__(self, affinities, normalize=True):
1282+
if not isinstance(affinities, sp.csr_matrix):
1283+
affinities = sp.csr_matrix(affinities)
1284+
if normalize:
1285+
affinities /= np.sum(affinities)
1286+
self.P = affinities
1287+
1288+
def to_new(self, data, return_distances=False):
1289+
raise RuntimeError("Precomputed affinity matrices cannot be queried.")

tests/test_affinities.py

+23
Original file line numberDiff line numberDiff line change
@@ -340,3 +340,26 @@ def test_to_new(self):
340340
for method_name, cls in AFFINITY_CLASSES:
341341
aff = cls(knn_index=knn_index)
342342
aff.to_new(self.iris)
343+
344+
class TestPrecomputedAffinity(unittest.TestCase):
345+
@classmethod
346+
def setUpClass(cls):
347+
cls.iris = datasets.load_iris().data
348+
349+
def test_precomputed_affinity_matches(self):
350+
aff1 = PerplexityBasedNN(self.iris)
351+
aff2 = affinity.PrecomputedAffinities(aff1.P)
352+
np.testing.assert_allclose(
353+
aff1.P.toarray(),
354+
aff2.P.toarray(),
355+
err_msg="Precomputed affinities are not passed in correctly.",
356+
)
357+
358+
def test_precomputed_affinity_normalization(self):
359+
aff1 = PerplexityBasedNN(self.iris)
360+
aff2 = affinity.PrecomputedAffinities(aff1.P * 2)
361+
np.testing.assert_allclose(
362+
aff1.P.toarray(),
363+
aff2.P.toarray(),
364+
err_msg="Precomputed affinities are not normalized to sum to 1.",
365+
)

tests/test_different_usages.py

+9
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,15 @@ def test_affinity(self):
109109
new_embedding.optimize(10, learning_rate=0.1, inplace=True)
110110
self.eval_embedding(new_embedding, self.y, f"transform::{aff.__class__.__name__}")
111111

112+
def test_precomputed_affinity(self):
113+
# Setup precomputed affinity
114+
aff = affinity.Uniform(self.x).P
115+
aff *= 10 # rescale
116+
precomputed_affinity = affinity.PrecomputedAffinities(aff, normalize=True)
117+
118+
embedding = TSNE().fit(affinities=precomputed_affinity, initialization="spectral")
119+
self.eval_embedding(embedding, self.y, aff.__class__.__name__)
120+
112121

113122
class TestUsageWithCustomAffinityAndCustomNeighbors(TestUsage):
114123
def test_affinity_with_queryable_knn_index(self):

0 commit comments

Comments
 (0)