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 – a Segmenter cuts raw data (text, image, signal, sequence, structure) into units, committing to a decomposition but no vocabulary;

  • embed – an Embedding maps each unit into shared R^dim (a lookup table for a discrete unit, a small encoder for a continuous one), sharable/tie-able across models;

  • quantize – an optional, learned VectorQuantizer turns 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: 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]]]

exception AutoencoderFitError(message, losses, epoch)[source]

Bases: RuntimeError

Reconstruction 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 AutoencoderResult that looks trained.

Parameters:
Return type:

None

class AutoencoderResult(encoder, decoder, quantizer, losses=<factory>)[source]

Bases: object

A 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 state AutoencoderFitError exists to keep out of the shared representation space.

Two things are now bound rather than assumed:

  • losses must 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. _train refits the codebook at epoch 0 unconditionally, so every quantizer that reaches a real result has one.

losses is also converted to a tuple. fit_autoencoder used 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). Note AutoencoderFitError.losses stays a list – it is type-tested as one by represent_generative_test and is already a defensive copy.

Deliberately NOT enforced: that losses actually descends, or that the final loss is what encoder/decoder would 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:
  • encoder (FeatureEmbedding)

  • decoder (Any)

  • quantizer (VectorQuantizer | None)

  • losses (tuple[float, ...])

encode(units)[source]

Encode units through the trained autoencoder encoder.

Parameters:

units (ndarray)

Return type:

ndarray

class ByteSegmenter[source]

Bases: Segmenter

A string/bytes -> (n,) byte ids in [0, 256). The vocabulary-free text decomposition.

segment(raw)[source]

Return UTF-8 byte ids for a string or bytes-like object.

Parameters:

raw (Any)

Return type:

ndarray

class CategoricalEmbedding(num_categories, dim, *, name=None, device='cpu', dtype='float32', init_seed=0)[source]

Bases: object

A lazily-built learned embedding of shape (num_categories, dim); every consumer gets the same module.

Parameters:
  • num_categories (int)

  • dim (int)

  • name (str | None)

  • device (str)

  • dtype (str)

  • init_seed (int)

module()[source]

The underlying nn.Embedding – built on first call, the identical instance thereafter.

Return type:

Any

class Embedder(featurizer, result, kind, corpus_vectors)[source]

Bases: object

A fitted embedding of raw heterogeneous items: transform to vectors, retrieve neighbours.

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] = nan turned retrieve() 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_corpus and 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 result and featurizer remain rebindable attributes – see the note in retrieve() 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. item is 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 – see transform() for why the container alone cannot decide that.

Parameters:

item (Any)

Return type:

ndarray

transform_batch(items)[source]

Embed a SEQUENCE of items; always returns an (n, dim) block.

The unambiguous batch contract: every element of items is one whole record, never one record’s field.

Parameters:

items (Any)

Return type:

ndarray

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 for kind="record" it can: _kind_of accepts 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 items is a kind="record" sequence whose own elements are not themselves record containers, so it is indistinguishable from one list-shaped record. Call transform_one() or transform_batch() instead.

Parameters:

items (Any)

Return type:

ndarray

retrieve(query, k=5)[source]

Top-k fitted-corpus neighbours of query as (corpus index, cosine similarity).

Indices are positions in corpus_vectors; identity records which corpus that is. Note what identity does NOT cover: result and featurizer are still plain rebindable attributes, so replacing emb.result re-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 by save() and rebuilt field-by-field by load(), which is a wider change than this finding, and it is left deliberately undone rather than half-done.

Raises:

ValueError – If k is not an exact non-negative integer – see mixle.represent.identity.exact_count() for why a Boolean is refused and an integral float is not.

Parameters:
Return type:

list[tuple[int, float]]

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.

Parameters:

path (str)

Return type:

str

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 result can embed a live torch module (see AutoencoderResult) – unpickling it executes arbitrary code from the file, exactly like pickle.load on an untrusted source. Refuses by default; pass trust_code=True for a path whose source you trust (or call from inside mixle.utils.serialization.trusted_deserialization()), matching mixle.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.

Parameters:
Return type:

Embedder

class ElementSegmenter(alphabet)[source]

Bases: Segmenter

A 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 to unknown_id, a state reserved past the declared alphabet (len(alphabet)), so num_categories is len(alphabet) + 1. Unknown symbols used to collapse onto id 0, 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 inflating num_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])

segment(raw)[source]

Map sequence elements through the fixed alphabet index, unknowns to the reserved unknown_id.

Parameters:

raw (Any)

Return type:

ndarray

class FeatureEmbedding(in_features, dim, *, hidden=(), name=None)[source]

Bases: object

A continuous unit encoder: (n_units, in_features) -> (n_units, dim) via a linear or small-MLP module.

The continuous analogue of CategoricalEmbedding – same dim / .module() contract, so it shares and trains identically. hidden=() is a single linear projection (a learned patch/ window/element embedding); non-empty hidden inserts ReLU layers.

Parameters:
  • in_features (int)

  • dim (int)

  • hidden (Sequence[int])

  • name (str | None)

module()[source]

Build or return the Torch feature-embedding module.

Return type:

Any

class GraphEmbedding(in_features, dim, *, layers=2, name=None)[source]

Bases: object

A message-passing embedding: (node_features, adjacency) -> (n_nodes, dim) with layers GCN rounds.

Parameters:
  • in_features (int)

  • dim (int)

  • layers (int)

  • name (str | None)

module()[source]

Build or return the Torch graph-embedding module.

Return type:

Any

class GraphEncoder(embedding)[source]

Bases: ModalityEncoder

A structure modality: raw = (node_features (n, f), adjacency (n, n)) -> per-node vectors in shared R^dim.

Parameters:

embedding (GraphEmbedding)

encode(raw)[source]

Encode graph nodes and adjacency into Torch embeddings.

Parameters:

raw (Any)

Return type:

Any

encode_numpy(raw)[source]

Encode a graph and return NumPy embeddings.

Parameters:

raw (Any)

Return type:

ndarray

class HeterogeneousEncoder(dim)[source]

Bases: object

A registry of per-modality encoders sharing one dim space, plus a learned modality-type embedding.

Parameters:

dim (int)

register(modality, segmenter, embedding, *, rebind=False)[source]

Add a modality’s (segmenter, embedding); the embedding’s dim must match the shared space.

Parameters:
Return type:

HeterogeneousEncoder

register_encoder(modality, encoder, *, rebind=False)[source]

Add a pre-built ModalityEncoder (e.g. a GraphEncoder that 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 passes rebind=True, which performs it and invalidates the export, so the optimizer must be rebuilt from a fresh parameters() call before training resumes.

Parameters:
  • modality (str)

  • encoder (ModalityEncoder)

  • rebind (bool)

Return type:

HeterogeneousEncoder

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. record must carry exactly the registered modalities: a key naming no registered modality, or a registered modality missing from record, raises KeyError.

Parameters:

record (dict[str, Any])

Return type:

tuple[Any, ndarray]

encode_numpy(record)[source]

Encode a heterogeneous record and return NumPy token stream plus modality ids.

Parameters:

record (dict[str, Any])

Return type:

tuple[ndarray, ndarray]

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:

list

class LearnedSegmenter(atomic, n_states=4, *, max_its=30, seed=0)[source]

Bases: Segmenter

Cut a raw object into variable-length tokens at HMM state changes over its atomic units (pooled to features).

Parameters:
  • atomic (Segmenter)

  • n_states (int)

  • max_its (int)

  • seed (int)

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 feat fallback below), but it is recorded: the resulting HMM saw zero emissions, so n_fit_atoms is 0 and segment() refuses to invent boundaries from it rather than returning fabricated tokens.

Parameters:

raws (Sequence[Any])

Return type:

LearnedSegmenter

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:

ndarray

class ModalityEncoder(segmenter, embedding)[source]

Bases: object

One 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).

Parameters:

raw (Any)

Return type:

Any

encode_numpy(raw)[source]

Detached (n_units, dim) array – for eval, quantization, or feeding a non-torch model.

Parameters:

raw (Any)

Return type:

ndarray

class PatchSegmenter(patch=8)[source]

Bases: Segmenter

An 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.

Parameters:

raw (Any)

Return type:

ndarray

unit_features(channels=1)[source]

Return feature width of one flattened patch.

Parameters:

channels (int)

Return type:

int

class Segmenter[source]

Bases: object

Base: segment(raw) -> np.ndarray. discrete says whether units are ids (vs. float features).

segment(raw)[source]

Segment raw input into model units.

Parameters:

raw (Any)

Return type:

ndarray

class SetSegmenter[source]

Bases: Segmenter

A 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).

segment(raw)[source]

Return set elements as rows of a feature matrix.

Parameters:

raw (Any)

Return type:

ndarray

class VectorQuantizer(num_codes, dim, *, seed=0)[source]

Bases: object

A learned codebook over R^dim: nearest-centroid quantization of embedding vectors into discrete ids.

Parameters:
property codebook: ndarray | None

The fitted (num_codes, dim) centroids – read-only, or None before fit().

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), setting vq.codebook[0, 0] = nan collapsed quantize from [0, 2, 0, 0, 2] to [0, 0, 0, 0, 0] – every vector assigned to code 0, because a NaN distance never wins an argmin – and turned reconstruction_error into nan rather than an error. Rebinding was just as open: vq.codebook = np.zeros((99, 7)) was accepted beside an unchanged dim=2 and num_codes=3.

The array is frozen with writeable = False and reachable only through this property, which matches mixle.engines.formats.CodebookFormat, whose codebook is copied and sealed the same way. dequantize returns codebook[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_codes is a cap, not a guarantee: fitting on fewer than num_codes samples yields one center per sample, and num_codes is 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.

iters must be a positive integer: iters=0 used to publish the random initialization centers as a fitted codec, with no Lloyd step ever run.

Parameters:
Return type:

VectorQuantizer

quantize(vectors)[source]

Nearest-code id for each vector – the discrete token stream (n,).

Parameters:

vectors (ndarray)

Return type:

ndarray

dequantize(ids)[source]

Codebook vectors for token ids (n,) -> (n, dim) (the reconstruction / de-tokenization).

Ids must already be integral. The cast to int64 used to happen before the range check, so a fractional id such as 0.9 truncated to the ordinary in-range token 0 and decoded as if it had been produced by quantize().

Parameters:

ids (ndarray)

Return type:

ndarray

reconstruction_error(vectors)[source]

Mean squared quantization error – the codebook’s fidelity (a codebook-size / bitrate knob).

Parameters:

vectors (ndarray)

Return type:

float

straight_through(vectors)[source]

VQ-VAE straight-through estimator: return quantized vectors but pass gradients to vectors unchanged.

Lets the encoders and (with a codebook-commitment loss) the codebook train end to end through the discrete bottleneck. vectors is a torch tensor (n, dim).

Parameters:

vectors (Any)

Return type:

Any

class WholeSegmenter[source]

Bases: Segmenter

A single feature vector -> (1, feat): the object is one unit (a pooled structure descriptor, a record).

segment(raw)[source]

Treat the whole input as one feature-vector segment.

Parameters:

raw (Any)

Return type:

ndarray

class WindowSegmenter(window=64, hop=None)[source]

Bases: Segmenter

A 1-D signal (T,) -> (n_frames, window) float units by a sliding window (seismic/audio/time-series).

Parameters:
  • window (int)

  • hop (int | None)

segment(raw)[source]

Split a one-dimensional signal into fixed windows.

A signal shorter than window still 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.

Parameters:

raw (Any)

Return type:

ndarray

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 quantizer this 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 every refit_codebook_every epochs on the current embeddings. commitment weights the VQ codebook-commitment term.

Architecture and training controls are validated rather than truncated: epochs=0, -2, and .9 each 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. retrieve works out of the box over the fitted data; transform embeds anything of the same kind.

kind is one explicit input schema for the whole corpus, resolved and validated BEFORE any featurizing or fitting happens (MXR-080-1784). An explicit kind must 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. When kind is 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 through str() as text (or the reverse) is a wrong representation, not a usable one. Declare kind explicitly if that coercion is what you want.

Raises:

ValueError – If kind names no supported schema, or if it is left to inference over a corpus whose items are not all the same kind.

Parameters:
Return type:

Embedder

vectorize(item, kind, *, dim=16, embedder=None)[source]

Map a raw item of modality kind to a fixed dim vector (see module docstring).

Parameters:
  • item (Any) – the raw item (a string, a record, an image array, a signal array).

  • kind (str) – 'text' | 'record' | 'image' | 'signal'.

  • dim (int) – output vector dimension.

  • embedder (Any) – required for text/record: a fitted Embedder defining the shared coordinate system the vector lives in.

Raises:

ValueError – for text/record without an embedder. 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 with fit_embedder() and pass it here, or vectorize the whole corpus at once with vectorize_all(), which fits and applies a single shared embedder.

Return type:

ndarray

vectorize_all(items, kind, *, dim=16)[source]

Vectorize a sequence of same-modality items to an (n, dim) array (one shared embedder for text).

Parameters:
Return type:

ndarray

image_features(img, dim=16, *, grid=None)[source]

A fixed dim descriptor of an image: mean intensity over a rows x cols partition of cells.

img is (H, W) or (H, W, C); channels are averaged. The cell grid is the most nearly square factorization rows * 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 to dim. For any dim that is not a perfect square that is not pooling to a coarser resolution – it permanently deletes the bottom/right cells. At the default grid for dim=2, changing the entire bottom half of a 4x4 image from 0 to 100 left the descriptor exactly [0, 0]; at dim=3 the whole bottom-right quadrant was invisible. A descriptor presented as capturing spatial layout must not have location-dependent blind spots.

grid overrides the automatic choice with an explicit square side and must satisfy grid * grid == dim, so an explicit grid partitions the image too.

The image must be non-empty and finite (see _require_measured()).

Parameters:
Return type:

ndarray

signal_features(sig, dim=16, *, windows=None)[source]

A fixed dim descriptor 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 to dim.

Parameters:
Return type:

ndarray

Submodules