mixle.represent.posterior module

Posterior retrieval by fitted-model affinity rather than raw-feature cosine.

Fit a mixture to heterogeneous records and retrieval similarity becomes posterior affinity: two records are close when the model’s field-restricted latent posteriors agree. The implementation uses the per-field Bhattacharyya-style balanced affinity from mixle.utils.hvis, with an evidence cap so one inconsistent field can contribute negative evidence without dominating every other field. Raw-feature cosine has neither property: it weights fields by numeric scale, and one high-variance field can dominate the dot product:

m = mixle.propose(records, fit=True)
r = PosteriorRetriever(m.fitted, records)          # any mixture over the records works
r.retrieve(query, k=5)                             # [(corpus index, log-affinity), ...]

Cost note: affinities are computed jointly over corpus + queries through the model’s per-field likelihoods. Model passes are linear in rows, while the affinity block is quadratic, so this is intended for moderate corpora. For large-corpus first-stage recall, use mixle.represent.fit_embedder() and rerank the shortlist here.

class PosteriorRetriever(model, corpus, *, evidence_cap=1.0, field_weights=None)[source]

Bases: object

Retrieve over raw heterogeneous records by the fitted mixture’s posterior affinity.

Parameters:
  • model (Any)

  • corpus (Any)

  • evidence_cap (float | None)

  • field_weights (Any)

property model: Any

The fitted mixture whose posterior defines affinity (read-only; see identity).

property corpus: tuple

The fixed corpus every returned index refers to, as an immutable tuple.

The elements are the caller’s own record objects and are deliberately NOT deep-copied: records here are arbitrary heterogeneous payloads, copying them could be arbitrarily expensive, and a mixture’s records are routinely shared with the model that was fitted on them. What is owned is the sequence – its length and order, which is what an index means.

property evidence_cap: float | None

The per-field negative-evidence cap (read-only).

property field_weights: ndarray | None

The per-field affinity weights as a privately owned read-only array, or None.

property identity: RetrievalIdentity

What this retriever’s (index, score) pairs are relative to (MXR-080-1906).

A bare corpus index is meaningless without the corpus it indexes and the model that scored it, and neither used to be recorded anywhere. This is stable for the retriever’s lifetime – model, corpus and weights are all fixed at construction – so it identifies every result the instance returns, and two retrievers can be compared for whether they were even ranking the same thing.

affinity_matrix()[source]

The corpus’s dense (n, n) log-affinity matrix (diagonal -inf).

Return type:

ndarray

retrieve(query, k=5)[source]

Top-k corpus records for one query: [(corpus_index, log_affinity), ...] best first.

Parameters:
Return type:

list[tuple[int, float]]

retrieve_batch(queries, k=5)[source]

Top-k per query, computed in one joint pass over corpus + queries.

Indices are positions in corpus; identity records which corpus that is.

Raises:

ValueError – If k is not an exact non-negative integer. A negative k is never silently accepted here: Python’s [:k] slicing treats a negative k as “all but the last |k| items”, not empty/error, which is never what a caller passing a negative retrieval count intended. A Boolean is refused for the same reason num_codes=True is: float(True) == 1.0, so k=True used to be accepted and silently return exactly one neighbour (MXR-080-1906).

Parameters:
Return type:

list[list[tuple[int, float]]]