CLI tool
NGT-labs/NGT avatar
NGT-labs/NGT

NGT: a graph-and-tree ANN index you build with CMake, not pip

Nearest Neighbor Search with Neighborhood Graph and Tree for High-dimensional Data

1,373 stars130 forksC++Apache-2.0

At a glance

What is it?
NGT (Neighborhood Graph and Tree) is a C++ library and command set for approximate nearest neighbor search over high-dimensional vectors, distributed by Yahoo Japan and now maintained under NGT-labs. Its build-time switches decide whether the index fits in RAM, whether it can be shared between processes, and whether quantization is compiled in at all.
Who is it for?
NGT fits teams that can compile C++ and want control over how an ANN index is built: the shared memory allocator, the large-dataset switch and the QG/QBG quantized methods are all decided at cmake time. It does not fit anyone who wants a managed vector service, or who needs concurrent writes to one shared index, since the documentation states there is no lock function and the index should be used only for reference when multiple processes share it.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 15 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

What NGT actually indexes, and who ends up using it

NGT stands for Neighborhood Graph and Tree. The README describes it as a graph and tree-based method for approximate nearest neighbor search over high-dimensional vector spaces, from several tens to several thousand dimensions. The repository ships three methods: NGT itself, QG (quantized graph-based), and QBG (quantized blob graph-based). The quantized methods are the newer line, added to address the memory cost of holding a full graph index in memory.

The target user is not someone assembling a retrieval pipeline from Python packages alone. NGT is a C++ project with a CMake build, a command-line utility, and bindings for Python, Ruby, PHP, Rust, Go, C and C++. The Python package exists, and the README notes it is installable via pip from PyPI as of v1.5.1, but the primary artifact is the library. If your workload is a few hundred thousand vectors and you want a hosted endpoint, this is a heavier tool than you need. If you have tens of millions of vectors, a fixed distance function, and a preference for running the index inside your own process, the build-time switches described below are the reason to look at it.

One structural detail matters early: QG and QBG require BLAS and LAPACK. If you only want the graph and tree method, the README offers NGT_QBG_DISABLED=ON so you can skip those dependencies entirely. That is a real fork in the road, and it happens before you write any code.

How the graph and tree method is put together

The index combines a neighborhood graph with a tree structure. The repository layout reflects this: lib/ holds the core, bin/ holds the command-line utilities (ngt and qbg), python/ holds the bindings, samples/ holds per-distance example projects, and tests/ holds the test suite. The samples directory is organized by distance function and data type rather than by API surface, which tells you something about how the project expects you to start: pick the sample closest to your data, then adapt it.

The supported distance functions are L1, L2, cosine similarity, angular, Hamming, Jaccard, Poincare, Lorentz, and inner product. Inner product arrived in v2.1.0. Data types are 4-byte float, 2-byte float (FP16, added in v1.14.0), and 1-byte unsigned integer. Scalar quantization as a graph index data type arrived in v2.3.0, product quantization in v2.4.0.

Two capabilities separate NGT from a static index library. First, objects can be added and removed after the index is built, per the key features list. Second, the index can be placed in shared memory via memory mapped files, which the README says reduces memory when multiple processes use the same index and speeds up opening it. The catch is stated plainly: there is no lock function, so a shared index should be treated as read-only when several processes touch it. That is a design boundary, not a bug, but it rules out a multi-writer service pattern on top of one shared index.

Installing NGT from source and running a first search

There is no source tarball on PyPI for the C++ library. The README points to the Releases page for downloads, and the build steps assume you have unzipped a release archive. On Ubuntu, the documented sequence installs BLAS and LAPACK first, then configures with CMake and installs to /usr/local.

bash
apt install libblas-dev liblapack-dev
unzip NGT-x.x.x.zip
cd NGT-x.x.x
mkdir build
cd build
cmake ..
make
make install
ldconfig /usr/local/lib

After this, the shared library is registered with the loader and the ngt command should be on your path. If you do not want BLAS and LAPACK on the machine and you do not need QG or QBG, the README gives a shorter path that disables those methods at configure time.

bash
cmake -DNGT_QBG_DISABLED=ON ..
make
make install
ldconfig /usr/local/lib

On macOS, the README documents two routes: Homebrew for a pre-built package (brew install ngt), or a source build that requires cmake and libomp, with OpenMP_ROOT exported from the Homebrew prefix before configuring.

bash
export OpenMP_ROOT=$(brew --prefix)/opt/libomp
cmake ..
make
make install

For a first real use, the samples directory is the intended entry point. It contains per-distance example projects such as samples/l2-uint8, samples/cosine-float, samples/hamming-uint8, samples/jaccard-sparse, samples/l2-uint8-range-search, and C API examples for qbg and qg. Each has its own CMakeLists.txt, so the workflow is to build the sample whose distance function matches your data, read how it creates the index and inserts vectors, then port that call sequence into your own code. The README does not walk through a search call end to end in the main file; that detail lives in the linked tutorial wiki and the per-sample sources.

The build flags that decide your memory profile

NGT pushes several consequential decisions into CMake, which means they cannot be changed after the fact without rebuilding. That is the most important operational fact about the project.

Shared memory is the clearest example. Passing -DNGT_SHARED_MEMORY_ALLOCATOR=ON places the index in shared memory backed by memory mapped files. The documented benefits are reduced aggregate memory when several processes read the same index, the ability to handle an index larger than RAM, and faster open time. The documented cost is the absence of locking, so the index is for reference use when shared. If your architecture assumed you could point several writer processes at one index file, this flag will not give you that.

The second flag is for scale. The README states that when you insert more than about 5 million objects into the graph-based method, you should add -DNGT_LARGE_DATASET=ON to improve search time. That is a documented threshold, and it implies the default build is tuned for a working set below it. Building without the flag and then discovering the index is slow at 20 million vectors means a rebuild and a reindex.

The third is -DNGT_QBG_DISABLED=ON, which removes the BLAS and LAPACK dependency. Choose it deliberately: it also removes QG and QBG, and QG is described as higher performance than the graph and tree method. If you disable it for convenience and later want quantized search, you are back to a full rebuild.

Where NGT is the wrong choice

The shared memory limitation is the sharpest one. Any deployment that needs concurrent inserts or deletes against a single index shared across processes is outside what the documentation supports, because the index has no lock function. You can have shared read access, or you can have one writer per index, not both.

The second limitation is platform. Supported operating systems are Linux and macOS. There is no Windows build documented in the README, and the installation section covers Linux distributions and Homebrew only.

The third is distance-function coverage in the quantized methods. NGT itself supports nine distance functions, but QG supports only L2 and cosine similarity. If your workload depends on Jaccard, Hamming, or Poincare distance, the higher-performance quantized path is not available to you, and you are back to the graph and tree method with its larger memory footprint. That is a genuine restriction, and it is easy to miss if you read the QG performance claim before the QG feature list.

Finally, there is a documentation shape problem. The main README is an index of links: installation, command reference, tutorial wiki, publications. The search API details are not in the main file. Anyone evaluating NGT from the README alone will know what it supports but not how a query call looks, which makes the samples directory the real documentation for first use.

NGT against a purpose-built vector database

The natural comparison is with a vector database server rather than another library. NGT ships as a library plus a command-line tool; the README lists ngtd and vald as separate distributed server projects built on top of it. That separation is the difference in approach. A vector database bundles the index, the server, replication and a query API into one artifact you run. NGT gives you the index and expects you to supply the process, the concurrency model, and the network layer, either yourself or through ngtd or vald.

The trade-off is control against operational surface. Building NGT with NGT_SHARED_MEMORY_ALLOCATOR=ON and pointing several read-only processes at one memory-mapped index is a deployment you can reason about precisely, and it avoids a network hop per query. A server-based system gives you writes from multiple clients and a stable query endpoint without you writing C++ or managing memory-mapped files. If your team has no C++ build pipeline, the server route is the shorter path regardless of index quality.

Within the project, the choice between NGT and QG is a second axis. QG is documented as higher performance with a narrower distance-function set. QBG, the quantized blob graph, is the third option and also requires BLAS and LAPACK. Picking among them is a build-time decision, so it is worth prototyping with the matching sample before committing to one.

Maintenance, licence, and the cost of upgrading

The repository is not archived, and the last push was on 2026-09-01, the same day v2.8.0 was released. Recent releases show steady work: v2.8.0 improved Forest-based QG, v2.7.4 added PrimitiveComparatorArm.h, and v2.7.3 added ARM NEON support. ARM support arriving in 2026 is worth noting if you are deploying on ARM hardware, because it means earlier versions did not have it.

The licence is Apache-2.0, which is a permissive licence with an explicit patent grant. That matters for a library you link into a product, but it is not legal advice, and you should have your own counsel review how you distribute binaries that include it.

The upgrade cost is dominated by the build-time configuration model. Because shared memory, large-dataset tuning, and QG/QBG availability are compile-time flags, moving to a new NGT version means re-running cmake with the same flag set and rebuilding. If you changed any flag between versions, you are comparing different index behaviour, not just different code. Version 2.0.0 also removed the ngtq and ngtqg commands in favour of qbg, so any script calling the old command names needs updating before you take a newer release. The README does not document a rollback path for an index built by one version and read by another.

Editorial conclusion

NGT fits teams that can compile C++ and want control over how an ANN index is built: the shared memory allocator, the large-dataset switch and the QG/QBG quantized methods are all decided at cmake time. It does not fit anyone who wants a managed vector service, or who needs concurrent writes to one shared index, since the documentation states there is no lock function and the index should be used only for reference when multiple processes share it. Before committing, verify the distance function you need is in the supported list (L1, L2, cosine, angular, Hamming, Jaccard, Poincare, Lorentz, inner product), confirm whether you need to disable QG and QBG to avoid installing BLAS and LAPACK, and check that your data volume justifies NGT_LARGE_DATASET=ON.

Frequently asked questions

What is NGT used for?

NGT performs approximate nearest neighbor search against large volumes of high-dimensional vector data, from several tens to several thousand dimensions. It provides a graph and tree-based method plus the QG and QBG quantized methods, with a command-line tool and bindings for several languages.

How do I install NGT on Ubuntu?

The README documents installing libblas-dev and liblapack-dev with apt, unzipping a release archive, then running cmake, make, make install and ldconfig /usr/local/lib from a build directory. If you do not need QG or QBG, you can configure with -DNGT_QBG_DISABLED=ON and skip the BLAS and LAPACK packages.

Can multiple processes write to the same NGT index in shared memory?

No. The README states that the shared memory option has no lock function, so the index should be used only for reference when multiple processes are using the same index.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. NGT-labs/NGT on GitHub
  4. README
  5. Releases
Community notes

Community notes