LowRank.NumComponentsRecommender#

LowRank.NumComponentsRecommender — recommend num_components for SDM-style decompositions by detecting degeneracy when an extra component is added.

Background#

Setting num_components one too high for the data is a common failure mode of SDM (and SDM-rigorous) optimization: the extra component’s elution profile collapses onto an existing one, cond(C) blows up, and optimize_rigorously later stalls at SV ≈ 51 (“Poor”). The diagnostic implemented here was verified across 7 datasets (SAMPLE1-4 + Apo + ATP + MY) in molass-researcher/experiments/13_rigorous_optimization/13aa_num_components_diagnostic.ipynb (see issue #116 for the full table).

class Recommendation(recommended_k, reason, metrics)#

Bases: tuple

Result of Decomposition.recommend_num_components().

recommended_k#

The recommended num_components. None if no fit succeeded.

Type:

int or None

reason#

Human-readable justification for the choice.

Type:

str

metrics#

One row per k actually fitted, with columns ['k', 'residual', 'cond_C', 'max_cos', 'amp_ratio', 'flag_count', 'status'].

Type:

pandas.DataFrame

metrics#

Alias for field number 2

reason#

Alias for field number 1

recommended_k#

Alias for field number 0

recommend_num_components(decomp, k_max=3, model='SDM', rgcurve=None, rt_dist='gamma', cond_threshold=50.0, cos_threshold=0.99, amp_threshold=0.2, quiet=True, debug=False)#

Recommend num_components by detecting degeneracy at k+1.

For each k in 1..k_max, runs a fresh ssd.quick_decomposition(num_components=k).upgrade(model, ...) on decomp.ssd, then computes four diagnostics:

  • residual = ||M - PC|| / ||M|| (P solved as M @ pinv(C))

  • cond_C  = np.linalg.cond(C)

  • max_cos = max_{i<j} |C[i] . C[j]| / (||C[i]|| ||C[j]||)

  • amp_ratio = min_i area_i / max_i area_i

Decision rule: pick the smallest k such that k+1 either (a) increases the residual or (b) trips >=2 of {cond_C > cond_threshold, max_cos > cos_threshold, amp_ratio < amp_threshold}. If neither happens up to k_max, return k_max with reason "no degeneracy detected up to k_max".

Parameters:
  • decomp (Decomposition) – Used only for decomp.ssd; the decomposition itself is not modified.

  • k_max (int, optional) – Maximum num_components to try. Default 3.

  • model (str, optional) – Model name passed to Decomposition.upgrade(). Default 'SDM'.

  • rgcurve (Curve, optional) – Rg curve to pass to upgrade. If None, computed once via decomp.ssd.xr.compute_rgcurve().

  • rt_dist (str, optional) – SDM residence-time distribution ('gamma' or 'exponential'). Forwarded as model_params={'rt_dist': rt_dist}.

  • cond_threshold (float, optional) – Degeneracy thresholds.

  • cos_threshold (float, optional) – Degeneracy thresholds.

  • amp_threshold (float, optional) – Degeneracy thresholds.

  • quiet (bool, optional) – Suppress per-fit stdout/stderr from the fitting machinery. Default True.

  • debug (bool, optional) – If True, do not suppress output and forward debug=True downstream.

Returns:

Named tuple (recommended_k, reason, metrics). metrics is a pandas.DataFrame indexed-free with one row per k actually attempted (including failed ones, marked in the status column).

Return type:

Recommendation

Notes

Verified across 7 datasets (SAMPLE1-4 + Apo + ATP + MY) - see issue #116. The diagnostic uses upgrade (cheap, ~seconds per fit), not rigorous optimization.