Skip to content

Apache-2.0 · v0.6.5 · runs on your machine

WARM

Hybrid search, graph indexing, and vector-symbolic memory.

Search and memory that run entirely on your machine.

WARM is a local-first document search engine and a vector-symbolic associative memory in one store. Hybrid BM25 and exact-cosine retrieval, byte-offset hits, crash-safe persistence, and superposed memory you can bind, bundle and query. No service calls and no telemetry.

  • Rust
  • Python
  • Node

What are you building?

Choose a path, run an example, then inspect the evidence it returns.

Already installing? Troubleshoot setup and retrieval.

Install, ingest, search

The code below is read from the quickstart examples that CI runs on every pull request.

cargo add warm-rust --features security
// 1. Ingest. A store is a directory (`None` would use the current directory); it persists
// there and reopens with the same arguments.
let store_dir = tempfile::tempdir()?;
let warm = WarmCore::new(
    DIMENSIONS,
    Some(store_dir.path().display().to_string()),
    None,
)?;
let start = Instant::now();
let inputs: Vec<DocumentInput> = corpus.iter().map(to_input).collect();
warm.memorize_documents(inputs)?;
println!(
    "ingested {} documents in {:.1?}",
    corpus.len(),
    start.elapsed()
);

// Hits carry the passage and its byte range in the source text.
let hit = &warm.search_documents("asylum from persecution", &SearchOptions::default())?[0];
println!(
    "\n{} bytes {}..{} score {:.3}: {}",
    hit.document_id,
    hit.start_byte,
    hit.end_byte,
    hit.score,
    hit.text.as_deref().unwrap_or("")
);

The engine itself. Every other runtime is a surface over it.

uv pip install warm-sdk
# 1. Ingest. The server keeps one store per tenant; you send the text and a vector per document.
docs = [
    {"id": d["id"], "vector": toy_embed(d["text"]), "text": d["text"], "metadata": {"title": d["title"]}}
    for d in corpus
]
client = WarmClient(url=URL, api_key=API_KEY, tenant_id=tenant)
started = time.perf_counter()
client.add_documents(docs)
print(f"ingested {len(docs)} documents in {(time.perf_counter() - started) * 1000:.1f} ms")

# Hits carry the stored text, metadata and the exact cosine score.
top = client.query(toy_embed("asylum from persecution"), top_k=1)["matches"][0]
print(f"\n{top['id']} score {top['score']:.3f}: {top['text']}")

An HTTP client for warm-server. The warm-python wheel provides native vector search and phase vectors.

npm install writerslogic-warm
// 1. Ingest. A store is a directory; it persists there and reopens with the same arguments.
const memory = new DocumentMemory(new WarmMemorySystem(4096, mkdtempSync(join(tmpdir(), 'warm-quickstart-'))))
const started = performance.now()
await memory.ingest(corpus.map(toInput))
console.log(`ingested ${corpus.length} documents in ${(performance.now() - started).toFixed(1)} ms`)

// Hits carry the passage and its byte range in the source text.
const [hit] = await memory.search('asylum from persecution')
console.log(`\n${hit.documentId} bytes ${hit.startByte}..${hit.endByte} score ${hit.score.toFixed(3)}: ${hit.text}`)

The npm package installs a native binary for your platform. Building from source requires Rust.

Full quickstart with the SQLite comparison and encryption →

Measured, and traceable

Both results are read from committed files under benchmarks/results/ each time the site is built.

Hybrid retrieval on SciFact 0.729 nDCG@10, WARM hybrid search
nDCG@10 by method00.250.50.751WARM hybrid (BM25 + exact cosine)0.729WARM lexical (stemmed BM25)0.688BM25 as published in BEIR0.665WARM dense (exact cosine)0.645

BEIR SciFact test split, 300 queries over 5,183 documents, MiniLM-L6-v2 embeddings (384 dimensions). The published BM25 figure (0.665, Thakur et al. 2021) is quoted from the BEIR paper, not re-run here.

Source: benchmarks/results/public_scifact.json · Apple M4 · benchmark page

Vector search on nytimes-256-angular Queries per second at fixed recall, one thread
Queries per second at fixed recall@10, by systemrecall@10 ≥ 0.90recall@10 ≥ 0.95WARM VGraph (4-bit codes, rerank) †13,133not reachedSymphonyQG (qb=0)5,913not reachedWARM VGraph (8-bit codes, rerank)4,409not reachedRaBitQ HNSW 1+42,066not reachedFAISS HNSW M=162,038379FAISS HNSW M=322,011509hnswlib M=161,352348Lucene HNSW M=161,108278RaBitQ IVF 1+8969803RaBitQ IVF 1+4940not reachedLucene HNSW M=32358129SymphonyQG (qb=4)not reachednot reached
  • At recall@10 of 0.90: the fastest WARM configuration (WARM VGraph (8-bit codes, rerank)) ran at 4,409 QPS, slower than the fastest other system (SymphonyQG (qb=0), 5,913 QPS).
  • Not reached at 0.90 by: SymphonyQG (qb=4).
  • At recall@10 of 0.95: no WARM configuration reached it. Reached by: FAISS HNSW M=16, FAISS HNSW M=32, Lucene HNSW M=16, Lucene HNSW M=32, RaBitQ IVF 1+8, hnswlib M=16.
  • † WARM VGraph (4-bit codes, rerank): opt-in encoding that ran at lower machine load than the other rows; the benchmark page treats it as unconfirmed.

Source: benchmarks/results/public_qgraph_nytimes-256-angular.json · Apple M4 · load gate met by 0 of 107 configurations (limit 3.0), so throughput is indicative · benchmark page

One engine, two layers

Document search is a conventional hybrid retriever. The vector-symbolic layer is the associative memory. Both sit on one crash-safe store. Module map →

WARM architecture Applications reach one Rust engine through its language surfaces. The engine has two layers, document search and vector-symbolic memory, over one persistent store. Your application WARM engine (Rust, in your process) What comes back Text and metadata plus embeddings you supply (optional) Rust Python Node warm-server (HTTP) Document search Chunk256 words Stemmed BM25 Exact cosine Blendmin-max Ranked hitspassage, byte range Vector-symbolic memory Atomshash-seeded Bind, bundlepermute Triplesmulti-hop CleanupHopfield Persistent store Memory-mapped segmented log, atomic batches, replay AES-256-GCM store (feature: security) Signed audit log, provenance (features) Passageswith source offsets Facts and answersrecalled by resonance

Why holographic

A hologram stores the whole picture in every fragment. A hypervector memory does the same: many facts are superposed into one long vector, no single bit holds any one fact, and damage is spread thinly over all of them.

  1. Bind a key to a value with XOR. The result looks like neither, and binding again with the key gives the value back.
  2. Bundle many bound pairs by majority vote into one vector that stays a little similar to each of them.
  3. Resonate: unbind with a key and compare against known values. The right one stands out from the noise, even after you corrupt the memory.

The demo uses dense binary vectors so the algebra is easy to see. The engine uses sparse binary vectors, and its document search does not rank with hypervectors; the concepts page says exactly which parts do.

A memory in one vector

4,096 bits hold every key-value pair at once. Pick how many pairs, damage the memory, and ask for one value back.

bit set flipped by damage

Similarity of the unbound memory to each known value

    What is being computed

    Each key and each value is a random 4,096-bit vector. A pair is key XOR value. The memory is the bitwise majority of all pairs, then damage flips the chosen share of its bits. To ask, XOR the memory with a key and measure the fraction of matching bits against every value. Unrelated vectors match about half their bits, with a spread near 0.8 percentage points, so a match well above 0.5 is the stored value. More pairs shrink the signal; more damage shrinks it too. Past some point the answer is wrong, and the demo says so.

    Retrieval quality on SciFact

    SystemnDCG@10Recall@100
    WARM hybrid (BM25 + exact cosine)0.7290.953
    WARM lexical (stemmed BM25)0.6880.925
    WARM dense (exact cosine)0.6450.925
    NumPy exact cosine, same embeddings0.6450.925
    WARM sparse vector path0.4700.779

    Same embeddings for every dense row. The benchmark pages carry the full comparison, including where WARM loses. Source: benchmarks/results/public_scifact.json.

    What is in the box

    How to read this site

    The document pipeline is conventional retrieval: stemmed BM25 fused with exact cosine over embeddings you supply. The vector-symbolic layer powers the meaning memory, composition and the experimental vector index. Where WARM loses to another system on a measured benchmark, the benchmark pages say so, and every number on this site traces to a committed file under benchmarks/results/.

    New here? Start with the quickstart, then read what "holographic" means in this code. Looking something up? Use the search box, the glossary or the FAQ. The version selector in the header switches between latest (the newest tagged release that carries these docs), dev (the main branch) and each tagged release. How the site is built and checked is on its own page.

    The standards and provenance page names the W3C, C2PA, CAWG, DIF, and Trust over IP features, their interoperability checks, and the limits on current claims.