mixle.utils.hvis package

Model-based (hierarchical) t-SNE and UMAP for heterogeneous data.

Pairwise affinities are derived from a fitted mixture model rather than from Euclidean distances, so anything mixle can model (tuples, sequences, sets, variable-length data, …) can be embedded. Six affinity definitions are supported (the affinity argument):

  • ‘local’ (the ‘auto’ default whenever raw data is available): the model is flattened into leaf fields and each field contributes a local statistical affinity. Discrete fields use the per-field posterior Bhattacharyya geometry; continuous/count fields additionally use a component-local Mahalanobis metric in sufficient-statistic-like coordinates learned from the realized data. Thus the same component is no longer a zero-distance quotient: within-component neighborhoods are resolved when the field has actual local structure.

  • ‘balanced’: the model is flattened into its leaf fields (nested composites, sequence element/length models, and optional wrappers all decompose), a field-restricted posterior z^f is computed from each field’s likelihoods alone, and the pair distance is the sum over fields of per-field Bhattacharyya distances -log sum_k sqrt(z^f_ik z^f_jk), each Winsorized at evidence_cap nats. The per-field posteriors keep every field’s structure visible regardless of its likelihood scale (by default, a 15-token sequence field contributes summed sequence evidence while length is a separate field; if the sequence model was explicitly fit with len_normalized=True, the sequence field instead contributes a per-token composition quotient), and the cap bounds each field’s influence so one spuriously sharp field cannot veto a pair’s similarity that every other field supports.

  • ‘fisher’: each observation is mapped through the model’s to_fisher() view to posterior-expected sufficient statistics and, by default, whitened by the empirical observed Fisher covariance of those score vectors. Pair affinities are Gaussian in that Fisher-vector space, so htsne can use the same sufficient-statistic geometry exposed to downstream tools.

  • ‘bhattacharyya’: the Bhattacharyya coefficient between joint posteriors, s_ij = sum_k sqrt(z_ik z_jk); -log s_ij is the Bhattacharyya distance on the posterior simplex. The square root amplifies shared low-probability components, so affinities stay graded even when hard assignments coincide - which is what gives the embedding within-cluster geometry. Like ‘coassign’, it depends on the data only through posteriors, so variable-length observations need no adjustments.

  • ‘coassign’: the co-assignment probability

    s_ij = P(z_i = z_j | x_i, x_j) = sum_k z_ik z_jk,

    the posterior similarity matrix of Bayesian clustering - an exact probability under the fitted model. The principled choice when the affinity itself must be a probability, but near-deterministic posteriors make it almost binary: every same-component pair ties at ~1, and t-SNE renders tied groups as rings/blobs with no internal structure.

  • ‘likelihood’: the predictive affinity s_ij = sum_k p(x_i | theta_k) z_jk (likelihood of x_i under the posterior mixture of x_j). Retains within- component likelihood detail, but for variable-length data the evidence in x_i grows with its length, so long observations reduce to their single best component while short ones stay blended.

For t-SNE the affinities are converted to input probabilities by row-conditional normalization p_{j|i} = softmax_j(log s_ij), optionally calibrated to a target perplexity per row, and symmetrized P = (P + P^T) / (2n).

Two t-SNE engines are provided:

  • ‘exact’: a full-matrix gradient descent supporting a heavy-tailed student-t kernel q_ij ~ (1 + d_ij^2 / alpha)^{-(alpha+1)/2} whose tail parameter alpha can be optimized along with the embedding. O(n^2) per iteration.

  • ‘barnes_hut’: scalable O(n log n) t-SNE run by an internal Barnes-Hut optimizer on a sparse model-neighbor probability matrix. The dense affinity matrix is never materialized; neighbor search can be exact blockwise or approximate via a random-projection candidate forest.

humap embeds the same model-based kNN graph with UMAP (umap-learn).

This package preserves the public API of the former single-module mixle.utils.hvis: every name below remains importable from mixle.utils.hvis. The implementation is split into:

  • affinity - factor/affinity computation and probability calibration

  • neighbors - sparse model-distance graphs, RP-trees, and kNN

  • tsne - the t-SNE embedding cores (exact and Barnes-Hut)

  • embed - the htsne/humap/dpmsne entry points

htsne(data, emb_dim=2, alpha=1.0, max_components=50, Y=None, perplexity=30.0, max_its=1000, print_iter=100, eta=None, momentum=0.8, min_gain=0.01, min_value=1.0e-128, optimize_alpha=False, min_alpha=1.0e-6, max_alpha_its=3, seed=None, mix_model=None, enc_data=None, method='auto', early_exaggeration=12.0, tol=1.0e-7, dpm_max_its=200, affinity='auto', field_weights=None, evidence_cap=1.0, fisher_metric='diagonal', fisher_ridge=1.0e-8, fisher_information='observed', out=None, variable_length=False, barnes_hut_theta=0.5, barnes_hut_leaf_size=16, neighbor_method='auto', neighbor_threshold=5000, neighbor_trees=8, neighbor_leaf_size=None, candidate_multiplier=8, repulsion_method='auto', exact_repulsion_threshold=5000)[source]

Embed heterogeneous data with model-based t-SNE.

A mixture model is fit to the data (a Dirichlet process mixture with automatically typed components by default, or pass mix_model), pairwise affinities are computed from the model, and the affinities are embedded with t-SNE. Passing affinity=’fisher’ with any model that exposes to_fisher(), or passing a pre-built affinity factor list, bypasses the mixture-posterior affinity path and does not require a DPM/mixture model.

method:

‘exact’ - full-matrix gradient descent (supports optimize_alpha) ‘barnes_hut’ - sparse model probabilities + internal Barnes-Hut t-SNE ‘auto’ - barnes_hut for n > 10 unless optimize_alpha is set

affinity:
‘auto’ (default) - ‘local’ whenever raw data is available and the

model decomposes into leaf fields, else ‘bhattacharyya’

‘local’ - per-field posterior overlap plus component-local

Mahalanobis geometry for continuous/count fields, estimated from the realized data; discrete fields fall back to posterior overlap

‘balanced’ - per-field posteriors (the model’s flattened leaves:

nested composites, sequence element/length models, and optional wrappers all decompose) combined by per-field Bhattacharyya, so a sharp discrete field cannot drown an overlapping continuous one (or vice versa); optional field_weights sets exponents on whole field-level Bhattacharyya coefficients

‘fisher’ - posterior-expected sufficient statistics from

mix_model.to_fisher(), whitened by an observed Fisher metric; fisher_information=’observed’ uses the empirical covariance of observed score vectors, while ‘model’ uses the view’s model metric; fisher_metric is ‘diagonal’ by default, with ‘identity’ and ‘full’ also accepted

‘bhattacharyya’ - Bhattacharyya coefficient between joint posteriors;

graded even under hard assignments, so embeddings retain within-cluster geometry

‘coassign’ - co-assignment probability P(z_i = z_j | x); exact but

near-binary when posteriors are sharp

‘likelihood’ - predictive affinity sum_k p(x_i|theta_k) z_jk

variable_length is retained for backward compatibility and does not rescale densities. Variable-length behavior is determined by the fitted sequence model: ordinary SequenceDistribution leaves use summed element log-likelihood with length as a separate field, while SequenceDistribution(len_normalized=True) intentionally uses a per-token composition quotient for the element field.

evidence_cap (default 1.0 nats) bounds the dissimilarity evidence any single field may contribute to a pair’s distance under multi-field affinities: without it, one spuriously sharp field (a serial-number-like categorical the model micro-clustered) drives its per-field affinity to zero and vetoes the pair’s similarity no matter what every other field says. None disables the cap; single-field affinities ignore it.

barnes_hut_theta controls the Barnes-Hut opening angle for method=’barnes_hut’; 0.0 gives exact repulsive forces and larger values are faster/coarser.

repulsion_method controls repulsive forces for method=’barnes_hut’: ‘exact’ uses a vectorized all-pairs calculation, ‘barnes_hut’ uses the tree approximation, and ‘auto’ uses exact repulsion when n is at most exact_repulsion_threshold.

neighbor_method controls graph construction for method=’barnes_hut’: ‘exact’ uses blockwise all-pairs top-k, ‘approx’ uses a random-projection candidate forest, and ‘auto’ switches to ‘approx’ when n >= neighbor_threshold.

Returns the n x emb_dim embedding.

Parameters:
  • emb_dim (int)

  • alpha (float)

  • max_components (int)

  • Y (ndarray | None)

  • perplexity (float | None)

  • max_its (int)

  • print_iter (int)

  • eta (float | None)

  • momentum (float)

  • min_gain (float)

  • min_value (float)

  • optimize_alpha (bool)

  • min_alpha (float)

  • max_alpha_its (int)

  • seed (int | None)

  • method (str)

  • early_exaggeration (float)

  • tol (float)

  • dpm_max_its (int)

  • evidence_cap (float | None)

  • fisher_metric (str)

  • fisher_ridge (float)

  • fisher_information (str)

  • variable_length (bool)

  • barnes_hut_theta (float)

  • barnes_hut_leaf_size (int)

  • neighbor_method (str)

  • neighbor_threshold (int)

  • neighbor_trees (int)

  • neighbor_leaf_size (int | None)

  • candidate_multiplier (int)

  • repulsion_method (str)

  • exact_repulsion_threshold (int)

humap(data, emb_dim=2, n_neighbors=15, min_dist=0.1, max_components=50, seed=None, mix_model=None, enc_data=None, dpm_max_its=200, print_iter=100, affinity='auto', field_weights=None, evidence_cap=1.0, fisher_metric='diagonal', fisher_ridge=1.0e-8, fisher_information='observed', n_epochs=None, out=None, **umap_kwargs)[source]

Embed heterogeneous data with model-based UMAP.

The same mixture-model affinities as htsne (see the affinity and evidence_cap arguments there), but the k-nearest-neighbor graph of model distances -log s_ij is handed to UMAP’s fuzzy simplicial set construction and layout (umap-learn) instead of t-SNE. Scales like UMAP: the dense affinity matrix is never built.

Extra keyword arguments are passed to umap.UMAP. Returns the n x emb_dim embedding.

Parameters:
  • emb_dim (int)

  • n_neighbors (int)

  • min_dist (float)

  • max_components (int)

  • seed (int | None)

  • dpm_max_its (int)

  • print_iter (int)

  • evidence_cap (float | None)

  • fisher_metric (str)

  • fisher_ridge (float)

  • fisher_information (str)

  • n_epochs (int | None)

dpmsne(P=None, emb_dim=2, alpha=1.0, Y=None, max_its=1000, print_iter=100, eta=None, momentum=0.8, min_gain=0.01, min_value=1.0e-128, optimize_alpha=False, min_alpha=1.0e-6, max_alpha_its=3, seed=None, early_exaggeration=12.0, tol=1.0e-7, out=None, **_compat_kwargs)[source]

Embed a precomputed (symmetric, non-negative) affinity matrix P with exact t-SNE.

Parameters:
model_log_affinity(posterior_mat, ll_mat=None, affinity='bhattacharyya', evidence_cap=None)[source]

Dense n x n matrix of log affinities (see module docstring) with -inf diagonal.

Rows are comparable up to a per-row shift, which both the row-conditional normalization and per-row perplexity calibration are invariant to.

evidence_cap bounds the dissimilarity evidence any single factor (field) may contribute: each factor’s log affinity is floored at -evidence_cap nats before the factors are summed. Without the cap a single sharp field with (near-)disjoint per-field posteriors drives its log affinity to -inf and vetoes the pair no matter what every other field says; with it, a field can at most testify “these differ by evidence_cap nats”. The cap is only applied to multi-factor (per-field) affinities - for a single factor it could only create ties.

Parameters:
Return type:

ndarray

sparse_model_distances(posterior_mat, ll_mat=None, k=90, block_size=1024, affinity='bhattacharyya', evidence_cap=None)[source]

Sparse n x n matrix of model distances d_ij = -log s_ij.

Keeps the k nearest neighbors (largest affinity) per row. Built blockwise so the dense n x n affinity matrix is never materialized. Distances are non-negative. evidence_cap as in model_log_affinity.

Parameters:
Return type:

csr_matrix

approx_sparse_model_distances(posterior_mat, ll_mat=None, k=90, affinity='bhattacharyya', evidence_cap=None, n_trees=8, leaf_size=None, candidate_multiplier=8, seed=None)[source]

Approximate sparse model distances without all-pairs graph construction.

A random-projection forest proposes candidate neighbors in normalized model-factor coordinates. Candidate pairs are then rescored with the exact model affinity used by sparse_model_distances, so approximation only enters through candidate recall. This is local/non-distributed today, but the proposal/evaluation split is the intended boundary for future distributed graph construction.

Parameters:
Return type:

csr_matrix

model_knn(posterior_mat, ll_mat=None, k=15, block_size=1024, affinity='bhattacharyya', evidence_cap=None)[source]

k-nearest-neighbor arrays under the model distance d_ij = -log s_ij.

Returns (indices, distances), each n x k, sorted ascending per row with each point as its own first neighbor at distance 0 (the convention expected by umap-learn, where self counts toward n_neighbors). Built blockwise; the dense affinity matrix is never materialized. evidence_cap as in model_log_affinity.

Parameters:
Return type:

tuple[ndarray, ndarray]

get_pmat(posterior_mat, ll_mat=None, targ_perplexity=None, vlen=False, affinity='bhattacharyya', evidence_cap=None)[source]

Symmetrized t-SNE input probabilities from model posteriors (and optionally component log-likelihoods, for affinity=’likelihood’).

The vlen flag is kept for backward compatibility and ignored.

Parameters:
  • affinity (str)

  • evidence_cap (float | None)

balanced_factors(mix_model, data, field_weights=None)[source]

Per-field Bhattacharyya affinity factors for heterogeneous models.

The joint posterior is dominated by whichever field has the largest log-likelihood contrast across components - sharp categorical fields, long token-sequence fields, or collapsed continuous components can contribute many nats of contrast while overlapping continuous fields contribute fractions of one. The drowned fields’ relationships then become invisible to any affinity computed from the joint posterior.

‘balanced’ fixes the scale problem at the affinity level: a field- restricted posterior z^f is computed from each field’s likelihoods alone (fields are the model’s flattened leaves - nested composites, sequence element/length models, and optional wrappers all decompose; see _field_log_densities), and the affinity combines per-field Bhattacharyya coefficients, so every field contributes comparably regardless of its likelihood scale. field_weights apply as exponents on whole field coefficients, i.e. weights on log field-affinities. Combined with an evidence cap (see model_log_affinity) no single field can veto a pair’s similarity either.

local_factors(mix_model, data, field_weights=None)[source]

Per-field local statistical affinity factors.

Each leaf field is first represented by its field-restricted component posterior. If the leaf has meaningful local coordinates (continuous/count leaves, and averages of such leaves inside sequences), the factor also carries component-local inverse covariances estimated from the realized data. Pair affinities then use

sum_k sqrt(z_ik z_jk) exp(-delta_ijk / 8),

where delta_ijk is the component-local Mahalanobis distance in that field’s coordinates. This is the local Fisher quadratic in the plug-in model, with posterior overlap handling component uncertainty.

fisher_factors(model, data=None, enc_data=None, metric='diagonal', ridge=1.0e-8, weight=1.0, information='observed')[source]

Fisher-vector affinity factor for a model and observations.

The model supplies posterior-expected sufficient statistics through to_fisher(). By default those statistics are treated as observed score vectors and whitened by their empirical observed Fisher covariance. Set information=’model’ to use the view’s model Fisher metric directly. Pair affinities are s_ij = exp(-0.5 ||v_i - v_j||^2).

Parameters:
tsne_barnes_hut(P, emb_dim=2, max_its=1000, eta=None, momentum=0.8, early_exaggeration=12.0, min_gain=0.01, tol=1.0e-7, print_iter=100, theta=0.5, leaf_size=16, repulsion_method='auto', exact_repulsion_threshold=5000, seed=None, Y=None, out=None)[source]

Embed a precomputed sparse t-SNE probability matrix with Barnes-Hut.

P must be a symmetric, non-negative affinity/probability matrix. It is normalized internally. This function is self-contained and does not call sklearn.

Parameters:
Return type:

ndarray

Submodules