mixle.inference.structure_embedded module

Structure learning over embedded heterogeneous fields.

mixle.inference.learn_bayesian_network() can model flat records containing categorical, count, real, and fixed-length vector fields. This module adapts free-text fields into that workflow by embedding each text value with mixle.represent.fit_embedder() and representing the field as a multivariate vector node.

The learned graph relates the embedding vector to other fields through multivariate conditional-linear-Gaussian factors or by using vector components as parent design features. Representative examples per embedding cluster remain available for interpretation, but the model itself uses the full vector.

class EmbeddedFieldCodec(field, embedder, centroids, representatives)[source]

Bases: object

One text field’s bridge into the graph: embed to a vector; centroids are for interpretability.

Parameters:
vector(value)[source]

Embed one field value as a single (dim,) vector.

Uses transform_one() rather than transform. transform decides one-vs-batch by guessing from the outer container, and its own docstring says so; handed a string it can read the string as the container and answer with a block of rows, one per character. That block then went into a single field slot, which is how two unrelated texts ended up scoring identically – nothing downstream was looking at the field’s own content by then.

Parameters:

value (Any)

Return type:

ndarray

vectors(values)[source]

Embed a sequence of field values as an (n, dim) matrix, one row per value.

Parameters:

values (Sequence[Any])

Return type:

ndarray

class EmbeddedStructureModel(net, codecs)[source]

Bases: object

A discovered dependency graph over records whose text fields ride in as embedding VECTORS.

Parameters:
  • net (Any)

  • codecs (dict[int, EmbeddedFieldCodec])

encode_record(x)[source]

The record with each text field replaced by its embedding vector.

Parameters:

x (tuple)

Return type:

tuple

encode_records(rows)[source]

Replace configured text fields with embedding vectors for each record.

EmbeddedFieldCodec.vectors() guarantees one row per value, so the pairing below is total. It used to be a bare zip over an unchecked embedder result: an embedder that returned fewer vectors than it was given left the surplus records holding raw text in a slot the graph reads as numeric, and the batch silently shrank downstream instead of reporting that the embedder had underproduced.

Parameters:

rows (Sequence[tuple])

Return type:

list[tuple]

edges()[source]

Return discovered graph edges.

Return type:

list[tuple[int, int]]

density_semantics()[source]

Always LIKELIHOOD_FACTOR.

The score is exact for what it computes, but it is not a normalized law over records: text fields are replaced by embedding vectors before scoring, embedding is not a measure-preserving change of variables, and no Jacobian relates the two measures. Saying so in the docstring was not enough – the machine-readable contract (the ExactDensity capability, describe(), and every combinator that folds child semantics through join_density_semantics()) still read this as an exact density, so a model composed over one of these silently inherited an exactness claim.

log_density(x)[source]

Log density of x in the embedded space, not of the original record.

Worth being exact about, because the name invites the stronger reading. Each configured text field is replaced by its embedding vector before scoring, and the graph then evaluates a density over that representation. Embedding is not a measure-preserving change of variables and no Jacobian relates the two, so this number is not the log density of the text record and does not integrate to one over any space of text.

What it is good for is comparison: between records whose non-text fields differ, and between records whose text embeds to genuinely different vectors. What it cannot support is an absolute probability claim about a record, or a comparison against a density from a model that scores text some other way. Two different texts that embed close together will score close together – that is the representation working as designed, not the model failing to tell them apart.

Parameters:

x (tuple)

Return type:

float

seq_log_density(rows)[source]

Per-record log densities in the embedded space; see log_density() for what that means.

Parameters:

rows (Sequence[tuple])

Return type:

ndarray

describe()[source]

The discovered structure with per-cluster representative examples for each text field.

Return type:

dict[str, Any]

learn_structure_embedded(data, *, text_fields='auto', n_clusters=8, embed_dim=16, seed=0, max_parents=2, **embed_kw)[source]

Discover cross-field structure where some fields are free text (see module docstring).

Parameters:
  • data (Sequence[tuple]) – flat tuple records; text fields may hold arbitrary strings.

  • text_fields (Sequence[int] | str) – which field indices to embed, or "auto" – every string-valued field with too many distinct values to be a categorical.

  • n_clusters (int) – number of representative clusters surfaced by describe() (interpretability only; the model uses the full embedding vector, not a cluster code).

  • embed_dim (int) – embedding dimension for mixle.represent.fit_embedder() – the vector node’s dim.

  • **embed_kw (Any) – forwarded to fit_embedder (epochs, hidden, feature_dim, …).

  • seed (int)

  • max_parents (int)

  • **embed_kw

Return type:

EmbeddedStructureModel