Adaptive-Dimension (Matryoshka) Retrieval

A Matryoshka model promises that the leading coordinates of an embedding are themselves a usable embedding at a lower dimension. pgContext uses that promise for one thing only: making candidate generation cheaper.

The stored vector is never truncated or rewritten, and final ranking always rechecks the full authoritative dimensions under MVCC and RLS. Before reading a prefix, pgContext proves that the request’s cardinality and Rust-owned-memory budgets can admit every visible candidate and rerank it exactly. If they cannot, it selects full-vector exact search before doing prefix work. The returned ordered answer is therefore the same as the full-vector exact oracle.

Maturity: Experimental. The mechanism is correctness-preserving and bounded, but the current scan-based implementation is a performance no-go: its small-corpus PG17/PG18 gate performs more work than full-vector exact search, and larger corpora safely select exact fallback under the 10,000-candidate execution ceiling. Do not enable it for latency improvement.

Certifying prefixes on a profile

Prefixes are declared on an embedding profile, not per query:

SELECT pgcontext.register_embedding_profile(
    'docs', 'mrl', 'embedding', 'public.docs_hnsw',
    jsonb_build_object(
        'representation', 'dense', 'dimensions', 768,
        'normalization', 'unit_l2', 'metric', 'cosine',
        'provider', 'acme', 'model', 'mrl-768', 'revision', '1',
        'input_template', '{text}', 'output_template', '{vector}',
        'bit_order', NULL, 'byte_order', NULL, 'scale', NULL, 'zero_point', NULL,
        'configuration_hash', '0123456789abcdef',
        'matryoshka_prefixes', jsonb_build_array(128, 256, 512)
    )
);

matryoshka_prefixes accepts 1..=8 strictly ascending positive dimensions, each strictly below the profile’s dimensions.

What is eligible, and why

A policy is accepted only for a dense or half representation under the l2, inner_product, or cosine metric. Those are the representations and metrics whose coordinates stay independently interpretable at an arbitrary cut point.

Everything else is rejected at registration:

Rejected Reason
bit Packed bits have no coordinate boundary at an arbitrary prefix
sparse Coordinates are indexed, not positional
int8 / uint8 with a scale An affine interpretation is not preserved by truncation
hamming / jaccard Set metrics are not defined coordinate-wise
l1 Not certified here; add it only with evidence

Descending or duplicated prefixes, a zero prefix, and a prefix that reaches the full dimension are also rejected.

Choosing a prefix at query time

SET pgcontext.adaptive_prefix_dimensions = 0;    -- automatic (default)
SET pgcontext.adaptive_prefix_dimensions = 128;  -- pin a declared prefix
SET pgcontext.adaptive_prefix_dimensions = -1;   -- read the full dimensions
Setting Behavior
0 Selects the narrowest certified prefix when the complete visible corpus can be covered within the cardinality and memory budgets
positive Pins that prefix. An undeclared value is rejected and the query falls back to the full dimensions rather than reading an uncertified cut point
-1 Disables prefix candidate generation entirely

Every one of these falls back to full-dimension candidate generation when the collection has no certified policy, when the profile is ineligible or absent, or when candidate, comparison, recheck, transient-memory, or expansion budgets cannot fund exhaustive prefix coverage. A collection that never declares a policy behaves exactly as it did before this feature existed.

How it works

  1. The candidate probe scores pgcontext.vector_prefix(column, N) against a query prefix. The stored value is untouched. When the profile promises unit_l2, the query prefix is renormalized under cosine and inner_product, where rescaling one side cannot reorder results. Under l2 it is not: |q - s|^2 reweights the dot-product term against a per-row |s|^2, so scaling only the query would produce an order that is neither “truncate both” nor “renormalize both”. l2 prefixes are truncated plainly on both sides.
  2. A bounded preflight counts the invoker-visible corpus only up to the point needed to prove whether exhaustive prefix coverage fits. If it does not fit, pgContext runs full-vector exact search immediately.
  3. The first prefix probe admits 4 × the requested limit. When that page is not already exhaustive, one wider step admits the complete visible corpus and uses the next declared prefix when one exists. Both pages, both scans, the expansion, the exact rechecks, and Rust-owned candidate memory are charged before the first prefix query runs.
  4. The executor’s source recheck rereads every admitted current row under MVCC and RLS, recomputes the exact full-dimension distance, then ranks and truncates.

Nothing downstream trusts the prefix score, and a non-exhaustive prefix page is never reported as complete. That is what makes the ordered answer identical to full-vector search.

pgcontext.vector_prefix(vector, dimensions) is immutable and parallel-safe. Note that pgContext’s own probe passes the width as a bind parameter, and PostgreSQL matches expression indexes structurally, so an index built over vector_prefix(col, 128) will not be matched by the probe. Treat the function as a projection you can use in your own SQL, not as an index-acceleration hook.

Scope: exact candidate generation only

Adaptive dimensions apply to the exact dense candidate path. A query served through an attached HNSW index (pgcontext.hnsw_search, or any path that selects the HNSW adapter) ignores certified prefixes entirely: the index is built over the full vector, so a prefix cut point has no meaning inside its graph. Subvector or prefix-quantized index support is a separate, unshipped piece of work.

If your collection is served by HNSW today, this feature will do nothing for it.

Observing it

An exhaustive prefix schedule appears in query telemetry as dense_adaptive_prefix_exhaustive. A budget rejection appears as a dense_exact_adaptive_*_budget strategy, and a disabled or uncertified path uses the ordinary exact strategy. adaptive_prefix_dimensions records the last prefix used, adaptive_termination records why the schedule completed or fell back, and total_expansions records widening work:

SELECT strategy, query_count, total_candidates, total_rechecks,
       total_expansions, adaptive_prefix_dimensions,
       adaptive_termination, avg_latency_ms
  FROM pgcontext.query_execution_stats()
 WHERE collection_name = 'docs';

Limits

Bound Value
Declared prefixes per profile 8
Prefix dimension 1..=16000, strictly below the full dimension
Initial candidate width 4× the requested limit
Widening steps At most 1 after the initial probe
Single-leaf candidate ceiling 10,000 total candidate admissions across all prefix steps

The executor also applies the request’s comparison, recheck, stage, expansion, elapsed-time, and transient-memory limits. Composite query branches retain their smaller per-leaf allocation and commonly select exact fallback.

Elapsed time is enforced by PostgreSQL’s current-statement timeout across the preflight, prefix scans, and exact recheck; it is not estimated during the cardinality preflight. A timeout can therefore cancel an in-flight schedule, but cancellation is fail-closed and never returns a partial prefix answer.

The frozen Phase 10 manifest records 1M and 10M rows as no_go with full_vector_exact_fallback: both exceed the 10,000-row authoritative recheck ceiling, so the pure preflight returns recheck_budget with zero prefix steps. The pure manifest pins the 1,000,000-comparison allowance and the zero-prefix recheck_budget decision. On the development harness, the frozen 500 ms global elapsed limit is tighter than a 1M-row exact scan, so the live PG17 and PG18 gate requires fail-closed cancellation with zero prefix expansion. The preserved 10M command requires the same fail-closed behavior and records whether elapsed or comparison budget terminates first. Exact fallback is not a promise that an over-budget or timed-out exact scan will return results.