Skip to main content

TriMap is a dimensionality reduction method that uses triplet constraints to form a low-dimensional embedding of a set of points. The triplet constraints are of the form “point i is closer to point j than point k”. The triplets are sampled from the high-dimensional representation of the points and a weighting scheme is used to reflect the importance of each triplet.

TriMap provides a significantly better global view of the data than the other dimensionality reduction methods such t-SNE, LargeVis, and UMAP. The global structure includes relative distances of the clusters, multiple scales in the data, and the existence of possible outliers. We define a global score to quantify the quality of an embedding in reflecting the global structure of the data.

CIFAR-10 dataset (test set) passed through a CNN (n = 10,000, d = 1024): Notice the semantic structure unveiled by TriMap.

Visualizations of the CIFAR-10 dataset

The following implementation is in Python. Further details and more experimental results are available in the paper. See the example colab for some analysis.

News!

[Aug 18, 2026] A GPU-parallel PyTorch implementation is now available as trimap.TorchTRIMAP, with support for CUDA, MPS, and CPU.

[Mar 16, 2022] An example colab using TriMap JAX implementation is now available at https://github.com/eamid/examples/blob/master/TriMap.ipynb. We analyze the results on S-curve, MNIST, Fashion MNIST, etc. using t-SNE, UMAP, TriMap, and PCA.

[Feb 17, 2022] A JAX implementation is now available at https://github.com/google-research/google-research/tree/master/trimap. More updates are coming soon!

How to use TriMap

TriMap has a transformer API similar to other sklearn libraries. To use TriMap with the default parameters, simply do:

import trimap
from sklearn.datasets import load_digits

digits = load_digits()

embedding = trimap.TRIMAP().fit_transform(digits.data)

To find the embedding using a precomputed pairwise distance matrix D, pass D as input and set use_dist_matrix to True:

embedding = trimap.TRIMAP(use_dist_matrix=True).fit_transform(D)

You can also pass the precomputed k-nearest neighbors and their corresponding distances as a tuple (knn_nbrs, knn_distances). Note that the rows must be in order, starting from point 0 to n-1. This feature also requires X to compute the embedding

embedding = trimap.TRIMAP(knn_tuple=(knn_nbrs, knn_distances)).fit_transform(X)

To calculate the global score, do:

gs = trimap.TRIMAP(verbose=False).global_score(digits.data, embedding)
print("global score %2.2f" % gs)

Parameters

The list of parameters is given blow:

  • n_dims: Number of dimensions of the embedding (default = 2)

  • n_inliers: Number of nearest neighbors for forming the nearest neighbor triplets (default = 12).

  • n_outliers: Number of outliers for forming the nearest neighbor triplets (default = 4).

  • n_random: Number of random triplets per point (default = 3).

  • distance: Distance measure (‘euclidean’ (default), ‘manhattan’, ‘angular’ (or ‘cosine’), ‘hamming’)

  • weight_temp: Temperature of the logarithm applied to the weights. Larger temperatures generate more compact embeddings. weight_temp=0. corresponds to no transformation (default=0.5).

  • weight_adj (deprecated): The value of gamma for the log-transformation (default = 500.0).

  • lr: Learning rate (default = 0.1).

  • n_iters: Number of iterations (default = 400).

The other parameters include:

  • knn_tuple: Use the precomputed nearest-neighbors information in form of a tuple (knn_nbrs, knn_distances) (default = None)

  • use_dist_matrix: Use the precomputed pairwise distance matrix (default = False)

  • apply_pca: Reduce the number of dimensions of the data to 100 if necessary before applying the nearest-neighbor search (default = True).

  • opt_method: Optimization method {‘sd’ (steepest descent), ‘momentum’ (GD with momentum), ‘dbd’ (delta-bar-delta, default)}.

  • verbose: Print the progress report (default = False).

  • return_seq: Store the intermediate results and return the results in a tensor (default = False).

An example of adjusting these parameters:

import trimap
from sklearn.datasets import load_digits

digits = load_digits()

embedding = trimap.TRIMAP(n_inliers=20,
                          n_outliers=10,
                          n_random=10).fit_transform(digits.data)

The nearest-neighbor calculation in the legacy implementation is performed using ANNOY.

GPU-parallel PyTorch version

TorchTRIMAP is a tensor-native implementation of the complete pipeline. It keeps preprocessing, nearest-neighbor results, triplets, weights, the loss, gradient, optimizer state, and embedding on the selected accelerator. The native nearest-neighbor search is exact and blocked, so it does not allocate an n x n distance matrix.

A single import trimap exposes both estimator APIs:

import trimap

legacy_model = trimap.TRIMAP()
torch_model = trimap.TorchTRIMAP(device="cuda")

trimap.TRIMAP is the legacy NumPy/Numba implementation, while trimap.TorchTRIMAP runs the tensor-native pipeline. Both provide fit_transform. A NumPy array can be passed directly to the PyTorch version; it is moved to the selected device before preprocessing:

import numpy as np
import trimap
from sklearn.datasets import load_digits

X = np.asarray(load_digits().data, dtype=np.float32)
model = trimap.TorchTRIMAP(
    device="cuda",
    knn_backend="auto",
    n_iters=400,
    random_state=42,
    triplet_batch_size=1_000_000,
)

embedding = model.fit_transform(X)
embedding_numpy = embedding.detach().cpu().numpy()

The returned embedding is a PyTorch tensor on the input/selected device. Use embedding.detach().cpu().numpy() only when a NumPy consumer needs it. The pipeline runs preprocessing/PCA, nearest-neighbor search, triplet and weight construction, initialization, and gradient-descent optimization on that device.

For a large CUDA data set such as Covertype, after removing its target column:

model = trimap.TorchTRIMAP(
    device="cuda",
    knn_backend="cuvs-cagra",
    gradient="explicit",
    n_iters=400,
    random_state=42,
    triplet_batch_size=30_000_000,
)
embedding = model.fit_transform(X[:, :54])

Nearest-neighbor backends

The PyTorch implementation does not use Annoy. It supports these open-source alternatives:

  • torch: exact batched search with torch.cdist/matrix multiplication; supports CUDA, MPS, CPU, and every TriMap distance.

  • faiss-flat (or faiss): exact Faiss Flat search for Euclidean/cosine data. A CUDA-enabled Faiss build accepts resident PyTorch tensors directly.

  • faiss-ivf: approximate inverted-file search for large data sets.

  • cuvs-cagra: NVIDIA cuVS CAGRA, a GPU-native graph ANN intended for very large CUDA data sets.

knn_backend="auto" chooses cuVS CAGRA for large CUDA inputs when installed, then Faiss, and otherwise uses exact PyTorch. The cutoff is controlled by ann_threshold. Install the optional CPU Faiss dependency with pip install -e '.[faiss]'. CUDA Faiss is normally installed with conda. Install cuvs-cu12 or cuvs-cu13 to match the machine’s CUDA runtime; it is intentionally not a universal dependency because CUDA wheels are platform-specific. On macOS use the native torch backend: current CPU Faiss and PyTorch wheels can load conflicting OpenMP runtimes in one process.

The memory/speed controls are query_batch_size and database_batch_size for native k-NN, distance_batch_size for high- dimensional indexed distances, and triplet_batch_size for optimization. Larger values use more accelerator memory and launch fewer kernels.

Gradient implementations

gradient="explicit" is the default and uses parallel index_add_ reductions for the fastest, lower-memory optimization path. Set gradient="autograd" to backpropagate through independent triplet batches; the graph is released after each batch. trimap.trimap_loss and trimap.trimap_explicit_grad are public for custom training loops and gradient checks. Both PyTorch gradient paths include the mathematical factor of two omitted by the historical Numba kernel; TorchTRIMAP compensates internally so its public lr has the same step-size meaning as legacy TRIMAP.

When supplying knn_tuple, pass features in the same coordinate system used to calculate the supplied neighbor distances. As in legacy TRIMAP, that precomputed-neighbor path does not normalize the features again.

Parity benchmark

The parity benchmark gives both implementations identical inputs, initialization, triplets, weights, iteration count, and optimizer settings. It reports runtime, speedup, and absolute/relative embedding error:

python benchmarks/benchmark_legacy_parity.py --device mps --n 10000

This isolates optimization parity from the intentionally independent random triplet samples used by the two full pipelines.

Examples

The following are some of the results on real-world datasets. The values of nearest-neighbor accuracy and global score are shown as a pair (NN, GS) on top of each figure. For more results, please refer to our paper.

USPS Handwritten Digits (n = 11,000, d = 256)

Visualizations of the USPS dataset

20 News Groups (n = 18,846, d = 100)

Visualizations of the 20 News Groups dataset

Tabula Muris (n = 53,760, d = 23,433)

Visualizations of the Tabula Muris Mouse Tissues dataset

MNIST Handwritten Digits (n = 70,000, d = 784)

Visualizations of the MNIST dataset

Fashion MNIST (n = 70,000, d = 784)

Visualizations of the  Fashion MNIST dataset

TV News (n = 129,685, d = 100)

Visualizations of the  TV News dataset

Runtime of t-SNE, LargeVis, UMAP, and TriMap in the hh:mm:ss format on a single machine with 2.6 GHz Intel Core i5 CPU and 16 GB of memory is given in the following table. We limit the runtime of each method to 12 hours. Also, UMAP runs out of memory on datasets larger than ~4M points.

Runtime of TriMap compared to other methods

Installing

Requirements:

  • numpy

  • scikit-learn

  • numba

  • annoy

Installing annoy

If you are having trouble with installing annoy on macOS using the command:

pip3 install annoy

you can alternatively try:

pip3 install git+https://github.com/sutao/annoy.git@master

Install Options

If you have all the requirements installed, you can use pip:

sudo pip install trimap

Please regularly check for updates and make sure you are using the most recent version. If you have TriMap installed and would like to upgrade to the newer version, you can use the command:

sudo pip install --upgrade --force-reinstall trimap

An alternative is to install the dependencies manually using anaconda and using pip to install TriMap:

conda install numpy
conda install scikit-learn
conda install numba
conda install annoy
pip install trimap

For a manual install get this package:

wget https://github.com/eamid/trimap/archive/master.zip
unzip master.zip
rm master.zip
cd trimap-master

Install the requirements

sudo pip install -r requirements.txt

or

conda install scikit-learn numba annoy

Install the package

python setup.py install

Support and Contribution

This implementation is still a work in progress. Any comments/suggestions/bug-reports are highly appreciated. Please feel free contact me at: eamid@ucsc.edu. If you would like to contribute to the code, please fork the project and send me a pull request.

Citation

If you use TriMap in your publications, please cite our current reference on arXiv:

@article{2019TRIMAP,
     author = {{Amid}, Ehsan and {Warmuth}, Manfred K.},
     title = "{TriMap: Large-scale Dimensionality Reduction Using Triplets}",
     journal = {arXiv preprint arXiv:1910.00204},
     archivePrefix = "arXiv",
     eprint = {1910.00204},
     year = 2019,
}

License

Please see the LICENSE file.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

trimap-1.2.0.tar.gz (39.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

trimap-1.2.0-py3-none-any.whl (32.3 kB view details)

Uploaded Python 3

File details

Details for the file trimap-1.2.0.tar.gz.

File metadata

  • Download URL: trimap-1.2.0.tar.gz
  • Upload date:
  • Size: 39.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for trimap-1.2.0.tar.gz
Algorithm Hash digest
SHA256 909b9652a5bee76c7fbf6b65d6c9c32b3041207f89e1280a6793ad84938002c9
MD5 36644bcfca40e0c28d865d928d66b7c3
BLAKE2b-256 8aa74c75f4973f939115e4076b8a77ceaf17fe3f8278f97d30b3ee3135b93f0f

See more details on using hashes here.

File details

Details for the file trimap-1.2.0-py3-none-any.whl.

File metadata

  • Download URL: trimap-1.2.0-py3-none-any.whl
  • Upload date:
  • Size: 32.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for trimap-1.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 dc2006d420514f9b46028e6a30784467fe63e68d240d6709bbd3410b853e6535
MD5 47de625d8f7a44052b151048515c3793
BLAKE2b-256 f741a4a0ddab6328f42cf3168b4a2469e43f2d96c06629f53f026b371cd9ce55

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

1.2.0 This release

2 files

1.1.5

1 file

1.1.4

1 file

1.1.3

1 file

1.1.2

1 file

1.1.1

1 file

1.1.0

1 file

1.0.15

1 file

1.0.14

1 file

1.0.13

1 file

1.0.12

1 file

1.0.11

1 file

1.0.10

1 file

1.0.9

1 file

1.0.8

1 file

1.0.7

1 file

1.0.6

1 file

1.0.5

1 file

1.0.4

1 file

1.0.3

1 file

1.0.2

1 file

1.0.1

1 file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page