HeatherDB

Search documentation

Loading the documentation index…

Download
HeatherDB documentation
DocumentationReference

Encoding reference

The catalogue of ways to turn a thing into a vector.

HeatherDB stores vectors and reconstructs them. It never sees your data — the encoder decides what "similar" means, and every downstream behaviour of the memory follows from that choice. The column that matters below is What similarity means.

Every scheme writes a d-length array to POST /db/{db}/collections/{name}/write, where d is the database's dimension.

Choosing

Data kindScheme
Identity (a label, an id, a category)Hash-seeded random symbol
A set of thingsSuperposed symbol set
Structure (typed fields, a record)Role–filler bind + bundle
Continuous quantity (a price, a duration)Fractional power encoding
Continuous position (a phase, a place on a line)Fractional rotation
Order and directionPermutation, ±1 key binding
Magnitude that must not be collapsed by cosineWeighted feature block — one-hot buckets
Cycle (hour, month, heading)Weighted feature block — sin/cos pair
Text, meaningSentence embedding, Random projection, Self-formed PPMI
Text, importance-weightedIDF / norm² pooling
Signal (audio, a sensor trace)Raw signal as the vector
A small fixed attribute vocabulary with intensitiesOne dimension per attribute
Prediction (given this, what follows)Context/target concatenation
A whole collection, as one itemCollection fingerprint

Two rules cut across all of them:

  • Cosine is magnitude-blind. A $5,000 charge and a $4.50 charge point the same way unless the encoder buckets or spectrally encodes the amount.
  • Seeds must be fixed and namespaced. A symbol derived from an unseeded RNG is a different vector next process; a symbol derived from a bare label collides across attribute types ("drama" the genre with "drama" the tag).

The catalogue

Hash-seeded random symbol

ItemAn atomic label — a genre, a country code, a product id, a word
VectorSHA-256(namespace + "::" + label) → first 8 bytes → RNG seed → standard_normal(d) → L2-normalise
SimilarityIdentity only. A symbol is similar to itself and quasi-orthogonal to every other symbol. There is no partial credit for a near-miss spelling
Use whenThe value is categorical and has no internal structure you want the memory to see

The namespace prefix is load-bearing: "GENRE::drama" and "TAG::drama" must be different vectors. Cache the table — regenerating it is cheap but must be deterministic across processes.

Superposed symbol set

ItemAn unordered set — the genres of a film, the apps open in a window layout
Vectornormalize(Σ symbol(memberᵢ)), optionally weighted. Server-side equivalent: POST /vec/bundle, or repeated algebra/add across collections
SimilaritySet overlap. Cosine is roughly the fraction of members two sets share. Order is invisible; multiplicity shows up as weight
Use whenMembership is the whole signal and nothing occupies a named slot

Bounded by capacity: past roughly d/32 members the bundle stops resolving into its parts. See Capacity.

Role–filler bind + bundle

ItemA record with typed fields
Vectornormalize(Σ bind(role_symbolᵢ, filler_symbolᵢ)), where bind is circular convolution — irfft(rfft(a)·rfft(b)). Server-side: POST /vec/bind then /vec/bundle, or /algebra/bind then /algebra/add
SimilaritySame filler in the same role. Moving a filler to a different role destroys the match. Roles compose: bind(bind(PRODUCT, COUNTRY), TERM) is one key for a three-way fact
Use whenThe question is "which record has X at this field", not "which record mentions X"

Recover a filler by unbinding with the role (/vec/unbind); the result carries crosstalk from the other roles and wants cleanup against the codebook. Slot capacity is √(d/32), not d/32 — a schema of 8 slots wants d ≥ 4096. Query it with role_pairs on documents/query, and over-fetch: the pairs score but do not steer recall.

Fractional power encoding

ItemA continuous quantity — a price, an amount, a time to maturity
VectorBuild a unit-spectrum base kernel once (exp(i·θ) on the interior bins with a fixed seed; force DC and Nyquist to 1 so negative exponents do not blow up), then enc(x) = base^⊗x via POST /vec/pow with t = x
SimilarityCloseness of value. cos(enc(x), enc(y)) is a smooth kernel in `
Use whenMagnitude carries meaning and you want the memory to generalise across nearby values

/vec/pow never normalises. Decoding is a peak search over the value axis against the kernel. Phase wraps past π at large exponents, so bound the range you encode.

Fractional rotation

ItemA continuous position — a phase, a place along an axis
VectorPOST /vec/rotate with a seed or name identifying the permutation and a real t. t=0 is the identity, t=1 reproduces the discrete permutation, integer t reproduces algebra/permute's power
SimilarityCloseness of position. A dimmer dial between adjacent discrete permutation states; smooth in t
Use whenPosition is continuous but you want the exact discrete states to remain exactly recoverable

Exact isometry on odd-length permutation cycles; even-length cycles have a boundary condition. /vec/pow is the alternative continuous-quantity dial and is the one in wider use.

Permutation

ItemOrder, sequence position, direction of a relation
Vectorρ^k applied to a vector — POST /db/{db}/algebra/permute with a seed or name and an integer power. Negative power applies ρ⁻¹
SimilaritySame content at the same offset. Unlike bind, permutation is non-commutative: ρ(a) ⊗ b ≠ a ⊗ ρ(b), which is exactly what lets a bundle distinguish "A then B" from "B then A"
Use whenBind alone cannot express what you need, because bind is commutative and order would collapse

The inverse must use the negated power, or the vector stays permuted — no error, just no match.

Sentence embedding, native dimension

ItemA sentence, a paragraph, a document
VectorA sentence-transformer output written straight through, normalised. Set the database's dimension to the model's width (384 for MiniLM-class models)
SimilaritySemantic proximity as the model defines it. Inherits the model's biases and its notion of relatedness wholesale
Use whenThe database exists to serve text meaning and you can pick its dimension to fit the model

Cache embeddings keyed by a hash of the text; encoding dominates write latency.

Embedding + fixed-seed random projection

ItemThe same, when the database's dimension is not the model's
VectorBuild Q once from a fixed seed: RandomState(seed).randn(native, target) → QR → take the orthonormal factor. Then normalize(v @ Q)
SimilarityApproximately the embedding's cosine (Johnson–Lindenstrauss). Distances contract slightly; the ordering largely survives
Use whenThe database dimension is fixed by other collections, or you are deliberately running narrower than the model for capacity reasons

The fixed seed is not a detail. A projection matrix regenerated with a different seed makes every previously written vector meaningless, and nothing in the engine will tell you.

Weighted feature block

ItemA structured event with mixed field types — a transaction, a sensor frame, a market window
VectorLay out named dimension blocks by hand: one-hot magnitude buckets for amounts (cosine cannot see magnitude), a sin/cos pair for each cyclic field (hour, heading, month), one-hot for categories, a scaled dim for each continuous field. Multiply each block by a per-feature weight to set its influence, L2-normalise the whole thing, then zero-pad to the database dimension
SimilarityExactly what you weighted. Two events are alike in proportion to the weighted blocks they share. Fully auditable — you can name the dimension that moved a score
Use whenYou need to defend why two things matched, or the fields are heterogeneous enough that no learned encoder fits

Zero-padding preserves unit norm and costs nothing but width. The bucket edges and the weights are the model; version them with the data.

Raw signal as the vector

ItemAn audio frame, a waveform window, any fixed-length sampled signal
VectorTake d FFT magnitude bins (an fftSize of 2d yields d bins), L2-normalise. Or, when phase matters and the window is already the right length, use the d raw samples directly
SimilaritySpectral shape, independent of loudness once normalised. Two frames match when their energy sits in the same bins
Use whenThe signal's native width can be made to equal the database dimension — then no projection step exists to go wrong

Choose the database dimension to match the analyser (128 bins ↔︎ d = 128). For speech, a cepstral variant (log-mel → DCT, c0 zeroed, mean-subtracted) separates content from speaker.

±1 key binding

ItemA payload that must carry a position or an identity alongside its content
VectorDerive a deterministic ±1 mask from the position (RandomState(f(position)).choice([-1, 1], d)), then multiply the normalised payload elementwise
SimilaritySame content at the same key. Same-position copies stay similar and their noise cancels under superposition; different positions become near-orthogonal, so nothing bleeds between slots
Use whenYou need bind's slot separation but want an involution — multiplying by the same key again unbinds exactly, with no crosstalk and no FFT

Cheaper and exactly invertible where circular convolution is approximate; correspondingly, it composes less richly.

One dimension per attribute

ItemA profile over a small fixed vocabulary — symptoms and severities, a grid, a checklist
Vector`d =
SimilarityThe same attributes lit at proportional intensity. No generalisation between related attributes — two clinically adjacent symptoms are orthogonal unless something else in the profile links them
Use whenThe vocabulary is small, closed and meaningful, and you want a reconstruction you can read back as a profile

The one scheme where a read's output is directly interpretable: divide the reconstruction by its max and you have intensities again. Group related indices into contiguous blocks so a reconstruction is legible by eye.

Context/target concatenation

ItemA prediction pair — a context and what followed it
VectorSplit the dimension in half. normalize(concat(encode(context), encode(target))). Write the pair; at query time send normalize(concat(encode(context), zeros(d/2))) and read the target half of the reconstruction
SimilarityShared context, with the target supplied by the memory. The read reconstructs the missing half from the half you gave it — the associative completion the engine exists for
Use whenThe task is "given this, what comes next": next-token, next-action, next-state

Renormalise the recovered half before comparing it to candidate targets, and blend multiple context widths (bigram, trigram) rather than picking one.

IDF or norm²-weighted pooling

ItemA span of text, pooled from per-token vectors
Vectornormalize(Σ wᵢ · tokenᵢ). Weight either by IDF — w = max(ln(N/df), 1.0), floored so nothing vanishes — or by norm² of the token's row in a static embedding table
SimilarityShared content words. Both weightings suppress function words, so a query's rare terms dominate the match instead of washing out in a mean
Use whenPooling token vectors into a span vector, in any scheme where a plain mean lets stopwords dominate

An unweighted mean over a long span converges toward the corpus centroid and every span starts looking alike. Discount structural context (a heading path, a title) rather than dropping it.

Self-formed PPMI

ItemA vocabulary, learned from your corpus alone — no pretrained model
VectorTake the top-N non-stop tokens; count symmetric co-occurrence in a window; ppmi = max(log(p(a,b) / (p(a)·p(b))), 0). Then filler(w) = atom(w) + Σ_c ppmi[w,c] · atom(c) over hash-seeded atoms. Mean-centre across the vocabulary (the anisotropy fix) and unit-normalise each row. A span is the weighted pool of its word fillers
SimilarityKeeping the same company in this corpus. Two words are close when they co-occur with the same neighbours here — a domain-specific semantics, not a general one
Use whenThe corpus has its own vocabulary, no model covers it, or an external encoder is not acceptable

Skipping the mean-centring leaves every vector pointed at a shared dominant direction and cosine loses its resolution. Because the semantics are local, this pairs naturally with an abstention check on out-of-corpus queries.

Collection fingerprint as an item

ItemA whole collection — a user's history, a genre, a persona
VectorGET /db/{db}/collections/{name}/fingerprint: the write-count-weighted centroid of the hard locations, Hopfield-refined. null when the collection has no writes
SimilarityWhole-collection resemblance. A fingerprint is dimensionally an ordinary vector, so it cosines against items, against other fingerprints, and against algebra results interchangeably
Use whenYou want to compare aggregates: user-to-catalogue affinity, user-to-user similarity, or a negated fingerprint for anti-recommendation

It is not the naive centroid — the Hopfield refinement pulls it onto the codebook's own attractors, so it differs from a plain superposition of the members and from their mean. Feed it back through /algebra/add and /sub to build blended or contrastive profiles server-side.

  • Vector endpoints — /vec/bind, unbind, bundle, pow, rotate.
  • Algebra — the collection-level equivalents.
  • Vector algebra — why bind, bundle and permute compose.
  • Capacity — how many symbols a bundle holds, how many slots a schema holds.
  • Parameters — choosing dimension for the interference you want.