mixle.represent package¶
The representation layer – embed any modality into one shared space; discretize it only if (and how) you want.
The tokenizer question, resolved by not fixing a vocabulary. Three separable pieces:
segment– aSegmentercuts raw data (text, image, signal, sequence, structure) into units, committing to a decomposition but no vocabulary;
embed– anEmbeddingmaps each unit into sharedR^dim(a lookup table for a discrete unit, a small encoder for a continuous one), sharable/tie-able across models;
quantize– an optional, learnedVectorQuantizerturns the shared vectors into discrete ids in the embedding space, so a codebook (a “vocabulary”) is fit to the data – and can be shared across modalities – rather than guessed.
HeterogeneousEncoder composes per-modality (segmenter, embedding)
pairs into one (N, dim) stream a single downstream model consumes – trainable to a generative or a downstream
objective, so “the right tokenization” is inferred under the objective rather than fixed upfront.
- class PosteriorRetriever(model, corpus, *, evidence_cap=1.0, field_weights=None)[source]
Bases:
objectRetrieve 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 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:
- retrieve(query, k=5)[source]
Top-
kcorpus records for one query:[(corpus_index, log_affinity), ...]best first.
- retrieve_batch(queries, k=5)[source]
Top-
kper query, computed in one joint pass overcorpus + queries.Indices are positions in
corpus;identityrecords which corpus that is.- Raises:
ValueError – If
kis not an exact non-negative integer. A negativekis never silently accepted here: Python’s[:k]slicing treats a negativekas “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 reasonnum_codes=Trueis:float(True) == 1.0, sok=Trueused to be accepted and silently return exactly one neighbour (MXR-080-1906).- Parameters:
- Return type:
- exception AutoencoderFitError(message, losses, epoch)[source]
Bases:
RuntimeErrorReconstruction training did not complete, so no representation is returned.
Carries the losses recorded before the failure and the epoch it happened on, so a caller can see where the fit diverged instead of receiving an
AutoencoderResultthat looks trained.
- class AutoencoderResult(encoder, decoder, quantizer, losses=<factory>)[source]
Bases:
objectA reconstruction-trained representation with encoder, decoder, optional codebook, and loss curve.
Only returned for a fit that ran every requested epoch to a finite loss: an untrained or diverged encoder is an
AutoencoderFitError, never a result. That sentence was previously a claim the class did not enforce (MXR-080-1906) – with no__post_init__,AutoencoderResult(encoder=FeatureEmbedding(6, 3), decoder=None, quantizer=None, losses=[])was accepted and is indistinguishable from a fitted result, which is the exact stateAutoencoderFitErrorexists to keep out of the shared representation space.Two things are now bound rather than assumed:
lossesmust be a non-empty run of finite numbers. It is the only evidence that training happened at all, so a result carrying none is not a trained representation.a
quantizer, when present, must be fitted._trainrefits the codebook at epoch 0 unconditionally, so every quantizer that reaches a real result has one.
lossesis also converted to a tuple.fit_autoencoderused to hand over the SAME list object the training loop was appending to, so the fitted loss curve stayed writable through a reference the result did not know about:res.losses.append(-999.0)rewrote recorded evidence (verified). NoteAutoencoderFitError.lossesstays alist– it is type-tested as one byrepresent_generative_testand is already a defensive copy.Deliberately NOT enforced: that
lossesactually descends, or that the final loss is whatencoder/decoderwould reproduce on the training units. A legitimate fit may plateau or bounce, and re-running the forward pass to check would make constructing a result cost a full inference pass over data the result does not retain.- Parameters:
- class ByteSegmenter[source]
Bases:
SegmenterA string/bytes ->
(n,)byte ids in[0, 256). The vocabulary-free text decomposition.
- class CategoricalEmbedding(num_categories, dim, *, name=None, device='cpu', dtype='float32', init_seed=0)[source]
Bases:
objectA lazily-built learned embedding of shape
(num_categories, dim); every consumer gets the same module.- Parameters:
- class Embedder(featurizer, result, kind, corpus_vectors)[source]
Bases:
objectA fitted embedding of raw heterogeneous items:
transformto vectors,retrieveneighbours.- Parameters:
featurizer (Any)
result (AutoencoderResult)
kind (str)
corpus_vectors (np.ndarray)
- property corpus_vectors: ndarray
The fitted corpus’s
(N, dim)unit embeddings, as a privately owned read-only array.Retrieval ranks against these vectors, so they are the embedder’s evidence, not a scratch buffer: they are validated, normalized and frozen at construction, and exposed only through this property. Handing back the caller’s own writable array (or letting the attribute be rebound) meant a post-construction
corpus_vectors[0, 0] = nanturnedretrieve()into an ordinary-looking ranked result whose similarities were all NaN (MXR-080-1785).
- property dim: int
Return embedding dimensionality.
- property identity: RetrievalIdentity
What this embedder’s
(index, similarity)pairs are relative to (MXR-080-1906).A bare corpus index names nothing without the corpus it indexes, and a cosine similarity is only interpretable against the model that produced both sides of it. Neither was recorded: the save manifest carried
kind/dim/n_corpusand no fitted-state identity at all, so two embedders over different corpora returned indistinguishable results.The digest covers the fitted corpus vectors, which are frozen at construction, so this is stable for the instance’s lifetime and identifies every result it returns. It is computed on each access rather than cached because
resultandfeaturizerremain rebindable attributes – see the note inretrieve()about what that does and does not cover.
- transform_one(item)[source]
Embed exactly ONE item; always returns a single
(dim,)vector.The unambiguous single-record contract.
itemis one whole record no matter which container it happens to be, so a list-valued record ([1, 2]) embeds to one vector here exactly as the equivalent tuple or dict does – seetransform()for why the container alone cannot decide that.
- transform_batch(items)[source]
Embed a SEQUENCE of items; always returns an
(n, dim)block.The unambiguous batch contract: every element of
itemsis one whole record, never one record’s field.
- transform(items)[source]
Embed one item or a batch of them, unit-normalized (so dot = cosine similarity).
A convenience wrapper over
transform_one()/transform_batch()whose one-vs-batch decision is a guess from the outer container. That guess is only sound while a record cannot itself be list-shaped, and forkind="record"it can:_kind_ofaccepts a list as one record at fit time, so[1, 2]is a legitimate single record while[{...}, {...}]is just as legitimately a batch of two. Rather than silently returning two embedding rows for a single declared record – training and serving disagreeing about the same record type – the genuinely ambiguous shape raises and names the two explicit methods that carry no ambiguity at all.- Raises:
ValueError – If
itemsis akind="record"sequence whose own elements are not themselves record containers, so it is indistinguishable from one list-shaped record. Calltransform_one()ortransform_batch()instead.- Parameters:
items (Any)
- Return type:
- retrieve(query, k=5)[source]
Top-
kfitted-corpus neighbours ofqueryas(corpus index, cosine similarity).Indices are positions in
corpus_vectors;identityrecords which corpus that is. Note what identity does NOT cover:resultandfeaturizerare still plain rebindable attributes, so replacingemb.resultre-points the query encoder while the frozen corpus vectors still come from the old one. Fixing that is a constructor-ownership change to a class that is pickled bysave()and rebuilt field-by-field byload(), which is a wider change than this finding, and it is left deliberately undone rather than half-done.
- save(path)[source]
Persist the embedder and fitted corpus vectors as one atomic, digest-bound envelope.
The artifact is a single file: magic, a canonical JSON manifest, then the pickled state. The manifest’s digest covers the manifest fields AND the body together, and the file is written to a temporary sibling and
os.replace-d into position, so a reader can never see a half-written artifact or a manifest describing a different payload than the one beside it.
- classmethod load(path, *, trust_code=False)[source]
Load an embedder previously saved with
save(), verifying its manifest.The saved artifact is a pickle of the fitted state, and
resultcan embed a live torch module (seeAutoencoderResult) – unpickling it executes arbitrary code from the file, exactly likepickle.loadon an untrusted source. Refuses by default; passtrust_code=Truefor a path whose source you trust (or call from insidemixle.utils.serialization.trusted_deserialization()), matchingmixle.inference.production.registry.Registry.get().Beyond that trust gate, the manifest is now actually enforced rather than ignored (MXR-080-1786). The envelope digest – manifest fields and body together – is checked BEFORE the body is unpickled, so a truncated, corrupted, swapped or manifest-tampered artifact is rejected before any deserialization runs; and the decoded payload’s own type, kind, and corpus shape are checked against what the manifest declared, so state that does not match its description can never load under it. The digest is corruption/mismatch detection, not authentication: it lives in the same file it covers, so it cannot prove the artifact was not replaced wholesale by whoever could already write to
path.
- class ElementSegmenter(alphabet)[source]
Bases:
SegmenterA sequence of hashable symbols (chars, amino acids, k-mers, categories) ->
(n,)ids via a fixed alphabet.Given
alphabet(the ordered symbols), each element maps to its index. Out-of-vocabulary symbols map tounknown_id, a state reserved past the declared alphabet (len(alphabet)), sonum_categoriesislen(alphabet) + 1. Unknown symbols used to collapse onto id0, which is already the first declared real category – an unseen residue/character/class then became positive evidence for a genuinely observed symbol. The alphabet must also be unique: a repeated entry used to overwrite its own index while still inflatingnum_categories, leaving an id no symbol could ever produce.The natural decomposition for proteins/genomes/any categorical sequence, and for characters (
alphabet=list(...)).- Parameters:
alphabet (list[Any])
- class FeatureEmbedding(in_features, dim, *, hidden=(), name=None)[source]
Bases:
objectA continuous unit encoder:
(n_units, in_features) -> (n_units, dim)via a linear or small-MLP module.The continuous analogue of
CategoricalEmbedding– samedim/.module()contract, so it shares and trains identically.hidden=()is a single linear projection (a learned patch/ window/element embedding); non-emptyhiddeninserts ReLU layers.
- class GraphEmbedding(in_features, dim, *, layers=2, name=None)[source]
Bases:
objectA message-passing embedding:
(node_features, adjacency) -> (n_nodes, dim)withlayersGCN rounds.
- class GraphEncoder(embedding)[source]
Bases:
ModalityEncoderA structure modality:
raw = (node_features (n, f), adjacency (n, n))-> per-node vectors in sharedR^dim.- Parameters:
embedding (GraphEmbedding)
- encode(raw)[source]
Encode graph nodes and adjacency into Torch embeddings.
- class HeterogeneousEncoder(dim)[source]
Bases:
objectA registry of per-modality encoders sharing one
dimspace, plus a learned modality-type embedding.- Parameters:
dim (int)
- register(modality, segmenter, embedding, *, rebind=False)[source]
Add a modality’s
(segmenter, embedding); the embedding’sdimmust match the shared space.
- register_encoder(modality, encoder, *, rebind=False)[source]
Add a pre-built
ModalityEncoder(e.g. aGraphEncoderthat owns its own segment+embed path).Re-registering an already-registered modality (same name – e.g. swapping its encoder) leaves any already-learned modality-tag embedding untouched, since the modality set did not change. Registering a genuinely new modality grows the tag embedding by one row, preserving already-learned rows for the modalities that were already registered.
Registration is a change to the trainable parameter set. Once
parameters()has handed that set to an optimizer, a later registration introduces parameter objects the optimizer does not own – the grown tag embedding is a new module, so its weight receives gradients but is never stepped, and the same holds for the newly registered encoder’s own parameters. That looked exactly like successful dynamic registration while silently freezing every new and replaced tensor. Such a registration now raises unless the caller passesrebind=True, which performs it and invalidates the export, so the optimizer must be rebuilt from a freshparameters()call before training resumes.
- encode(record)[source]
A record
{modality: raw}->(stream, modality_ids): one(N, dim)tensor + each unit’s source id.Each modality’s units are embedded and the modality-tag vector is added, then all are concatenated in registration order – the order modalities were registered, independent of
record’s own key order – the unified token stream for a downstream model.recordmust carry exactly the registered modalities: a key naming no registered modality, or a registered modality missing fromrecord, raisesKeyError.
- encode_numpy(record)[source]
Encode a heterogeneous record and return NumPy token stream plus modality ids.
- parameters()[source]
Every trainable parameter across all modality encoders + the modality-tag embedding (for an optimizer).
Calling this marks the parameter set as exported: a later
register_encoder()that would add trainable state outside the optimizer built from this list is refused (see that method).- Return type:
- class LearnedSegmenter(atomic, n_states=4, *, max_its=30, seed=0)[source]
Bases:
SegmenterCut a raw object into variable-length tokens at HMM state changes over its atomic units (pooled to features).
- fit(raws)[source]
Fit the boundary HMM on example raws – the segmentation is learned to maximize their likelihood.
An all-empty batch stays non-fatal (see the
featfallback below), but it is recorded: the resulting HMM saw zero emissions, son_fit_atomsis 0 andsegment()refuses to invent boundaries from it rather than returning fabricated tokens.
- segment(raw)[source]
Segment raw input using fitted HMM state assignments.
An empty input yields ZERO tokens, not one all-zero token. Returning a fabricated unit meant an empty modality handed downstream encoders a full-width feature vector backed by no atoms, no state path, and no inferred boundary – evidence that was never observed.
- Raises:
RuntimeError – if
fit()was never called, or was called on a batch containing no atomic units at all – an HMM fitted without data cannot locate boundaries, so the no-evidence state is explicit rather than silently productive.- Parameters:
raw (Any)
- Return type:
- class ModalityEncoder(segmenter, embedding)[source]
Bases:
objectOne modality’s
raw -> (n_units, dim)path: a segmenter feeding an embedding into the shared space.- Parameters:
segmenter (Any)
embedding (Any)
- encode(raw)[source]
Torch tensor
(n_units, dim)– gradients flow to the embedding (and, via it, train the encoder).
- class PatchSegmenter(patch=8)[source]
Bases:
SegmenterAn image
(H, W)or(C, H, W)->(n_patches, patch_features)float units (ViT-style, no vocab).- Parameters:
patch (int)
- segment(raw)[source]
Split an image tensor into flattened non-overlapping patches.
- class Segmenter[source]
Bases:
objectBase:
segment(raw) -> np.ndarray.discretesays whether units are ids (vs. float features).
- class SetSegmenter[source]
Bases:
SegmenterA set/list of feature vectors ->
(n_elements, feat): nodes of a graph, atoms of a molecule, taxa of a section.The general structured-object decomposition – a scientific structure becomes its set of element features (which a downstream model can further couple with a structure/message-passing embedding).
- class VectorQuantizer(num_codes, dim, *, seed=0)[source]
Bases:
objectA learned codebook over
R^dim: nearest-centroid quantization of embedding vectors into discrete ids.- property codebook: ndarray | None
The fitted
(num_codes, dim)centroids – read-only, orNonebeforefit().The codebook IS the learned vocabulary: every token id this codec emits or decodes is defined by it, so it is fitted state, not a scratch buffer. It used to be a plain public attribute, and a single post-fit write silently redefined the vocabulary (MXR-080-1906). Verified: after
vq.fit(x), settingvq.codebook[0, 0] = nancollapsedquantizefrom[0, 2, 0, 0, 2]to[0, 0, 0, 0, 0]– every vector assigned to code 0, because a NaN distance never wins anargmin– and turnedreconstruction_errorintonanrather than an error. Rebinding was just as open:vq.codebook = np.zeros((99, 7))was accepted beside an unchangeddim=2andnum_codes=3.The array is frozen with
writeable = Falseand reachable only through this property, which matchesmixle.engines.formats.CodebookFormat, whose codebook is copied and sealed the same way.dequantizereturnscodebook[ids], and NumPy fancy indexing copies, so decoded vectors are still ordinary writable arrays.
- fit(vectors, *, iters=25)[source]
Fit the codebook by k-means (Lloyd) on
vectors(n, dim)– the vocabulary is learned, not assumed.num_codesis a cap, not a guarantee: fitting on fewer thannum_codessamples yields one center per sample, andnum_codesis updated in place to that actual count so it – and every bounds check derived from it (dequantize, a caller building a one-hot over the vocabulary) – stays honest about the codebook’s real capacity instead of quietly overstating it.itersmust be a positive integer:iters=0used to publish the random initialization centers as a fitted codec, with no Lloyd step ever run.
- quantize(vectors)[source]
Nearest-code id for each vector – the discrete token stream
(n,).
- dequantize(ids)[source]
Codebook vectors for token ids
(n,)->(n, dim)(the reconstruction / de-tokenization).Ids must already be integral. The cast to
int64used to happen before the range check, so a fractional id such as0.9truncated to the ordinary in-range token0and decoded as if it had been produced byquantize().
- reconstruction_error(vectors)[source]
Mean squared quantization error – the codebook’s fidelity (a codebook-size / bitrate knob).
- straight_through(vectors)[source]
VQ-VAE straight-through estimator: return quantized vectors but pass gradients to
vectorsunchanged.Lets the encoders and (with a codebook-commitment loss) the codebook train end to end through the discrete bottleneck.
vectorsis a torch tensor(n, dim).
- class WholeSegmenter[source]
Bases:
SegmenterA single feature vector ->
(1, feat): the object is one unit (a pooled structure descriptor, a record).
- class WindowSegmenter(window=64, hop=None)[source]
Bases:
SegmenterA 1-D signal
(T,)->(n_frames, window)float units by a sliding window (seismic/audio/time-series).- segment(raw)[source]
Split a one-dimensional signal into fixed windows.
A signal shorter than
windowstill yields exactly one window (WindowSegmenter’s contract is to always return at least one unit, unlike PatchSegmenter which has no such contract and rejects a too-small image outright): the real samples fill[:len(x)]and only the remaining slots are zero-padded, so the padding never overwrites real data.
- fit_autoencoder(units, dim, *, hidden=(), quantizer=None, epochs=200, lr=1e-2, refit_codebook_every=25, commitment=0.25, seed=0)[source]
Train an encoder+decoder to reconstruct
units(N, in_features)with an optional VQ bottleneck.Without
quantizerthis is a standard autoencoder. With one, it is a VQ-VAE: the encoder’s vectors are quantized (straight-through) before decoding and the codebook is refit everyrefit_codebook_everyepochs on the current embeddings.commitmentweights the VQ codebook-commitment term.Architecture and training controls are validated rather than truncated:
epochs=0,-2, and.9each used to return an ordinary result carrying a randomly initialized encoder and an empty loss history – an untrained representation indistinguishable from a trained one. The global Torch RNG is seeded for reproducibility and restored on the way out, so fitting a representation does not silently reseed the caller’s other Torch randomness.- Raises:
ValueError – for non-finite/ragged/empty units or a non-exact-positive control.
AutoencoderFitError – if the loss goes non-finite – training did not complete, so there is no trained representation to return.
- Parameters:
- Return type:
AutoencoderResult
- fit_embedder(data, dim=32, *, kind=None, feature_dim=256, hidden=(64,), epochs=200, lr=1e-2, seed=0)[source]
Fit a learned embedding of raw text or record items and return an
Embedder.Items featurize deterministically (hashing trick; no fitted vocabulary), then an autoencoder learns a
dim-dimensional generative representation of the corpus.retrieveworks out of the box over the fitted data;transformembeds anything of the same kind.kindis one explicit input schema for the whole corpus, resolved and validated BEFORE any featurizing or fitting happens (MXR-080-1784). An explicitkindmust name one of the two supported schemas; an unrecognized value used to select the record featurizer and only surface at the very end of an otherwise complete autoencoder fit. Whenkindis left to inference, EVERY item is inspected rather than only the first: a corpus mixing text and record items has no single schema to infer, and silently featurizing dict records throughstr()as text (or the reverse) is a wrong representation, not a usable one. Declarekindexplicitly if that coercion is what you want.
- vectorize(item, kind, *, dim=16, embedder=None)[source]
Map a raw
itemof modalitykindto a fixeddimvector (see module docstring).- Parameters:
- Raises:
ValueError – for
text/recordwithout anembedder. There is no safe default. The fallback used to fit a fresh autoencoder on four copies of the single item being transformed, so every call returned coordinates from a different learned basis while presenting them as a common fixed-length vector. Independently vectorized"alpha alpha"and"beta beta"came out at cosine+0.81; fitting the same two items in one shared corpus space gave-0.77. Neither sign nor magnitude survived, so any downstream correlation/graph/distance built from such vectors measured the fitting noise of single-item autoencoders. Fit one space withfit_embedder()and pass it here, or vectorize the whole corpus at once withvectorize_all(), which fits and applies a single shared embedder.- Return type:
- vectorize_all(items, kind, *, dim=16)[source]
Vectorize a sequence of same-modality items to an
(n, dim)array (one shared embedder for text).
- image_features(img, dim=16, *, grid=None)[source]
A fixed
dimdescriptor of an image: mean intensity over arows x colspartition of cells.imgis(H, W)or(H, W, C); channels are averaged. The cell grid is the most nearly square factorizationrows * cols == dim(_cell_grid()), so the cells tile the image: every pixel lies in exactly one cell and therefore influences exactly one output coordinate. That is what makes this a spatial-layout vector – enough for an image field to correlate with structured fields in a discovered graph.This used to choose a square grid with
ceil(sqrt(dim))cells per side and truncate the row-major cell list todim. For anydimthat is not a perfect square that is not pooling to a coarser resolution – it permanently deletes the bottom/right cells. At the default grid fordim=2, changing the entire bottom half of a 4x4 image from 0 to 100 left the descriptor exactly[0, 0]; atdim=3the whole bottom-right quadrant was invisible. A descriptor presented as capturing spatial layout must not have location-dependent blind spots.gridoverrides the automatic choice with an explicit square side and must satisfygrid * grid == dim, so an explicit grid partitions the image too.The image must be non-empty and finite (see
_require_measured()).
- signal_features(sig, dim=16, *, windows=None)[source]
A fixed
dimdescriptor of a 1-D signal: (mean, energy, range) over evenly-spaced windows.The trace must be non-empty and finite (see
_require_measured()): an empty signal used to return the all-zero vector, which is indistinguishable from a measured zero-energy trace, and NaN samples used to travel through as NaN features padded out todim.
Submodules¶
- mixle.represent.api module
- mixle.represent.embed module
- mixle.represent.generative module
- mixle.represent.graph module
- mixle.represent.heterogeneous module
- mixle.represent.identity module
- mixle.represent.learned_segment module
- mixle.represent.modality module
- mixle.represent.posterior module
- mixle.represent.quantize module
- mixle.represent.segment module