LowRank.Decomposition

Contents

LowRank.Decomposition#

LowRank.LowRankInfo.py

This module contains the class LowRankInfo, which is used to store information about the components of a SecSaxsData, which is mathematically interpreted as a low rank approximation of a matrix.

class Decomposition(ssd, xr_icurve, xr_ccurves, uv_icurve, uv_ccurves, mapped_curve=None, paired_ranges=None, **kwargs)#

Bases: object

A class to store the result of decomposition which is a low rank approximation.

The result includes both components of X-ray and UV data and their associated information.

ssd#

The SecSaxsData object from which the decomposition was performed.

Type:

SecSaxsData

xr#

The XrData object from the SecSaxsData.

Type:

XrData

xr_icurve#

The i-curve used for the decomposition of the X-ray data.

Type:

Curve

xr_ccurves#

The component curves for the X-ray data.

Type:

list of Curve

xr_ranks#

The ranks for each component of the X-ray data. If None, default ranks are used.

Type:

list of int or None

uv#

The UvData object from the SecSaxsData.

Type:

UvData

uv_icurve#

The i-curve used for the decomposition of the UV data.

Type:

Curve

uv_ccurves#

The component curves for the UV data.

Type:

list of Curve

uv_ranks#

The ranks for each component of the UV data. If None, default ranks are used.

Type:

list of int or None

mapping#

The mapping information between the X-ray and UV data.

Type:

MappingInfo

mapped_curve#

The mapped curve from the X-ray to UV domain. If None, it can be computed when needed.

Type:

MappedCurve or None

paired_ranges#

The paired ranges for the X-ray and UV data. If None, it can be computed when needed.

Type:

list of PairedRange or None

num_components#

The number of components in the decomposition.

Type:

int

Initialize the Decomposition object.

Parameters:
  • ssd (SecSaxsData) – The SecSaxsData object from which the decomposition was performed.

  • xr_icurve (Curve) – The i-curve used for the decomposition of the X-ray data.

  • xr_ccurves (list of Curve) – The component curves for the X-ray data.

  • uv_icurve (Curve) – The i-curve used for the decomposition of the UV data.

  • uv_ccurves (list of Curve) – The component curves for the UV data.

  • mapped_curve (MappedCurve, optional) – The mapped curve from the X-ray to UV domain. If None, it can be computed when needed.

  • paired_ranges (list of PairedRange, optional) – The paired ranges for the X-ray and UV data. If None, it can be computed when needed.

  • kwargs (dict, optional) – Additional keyword arguments (not used).

copy_with_new_components(xr_ccurves, uv_ccurves, **kwargs)#

Create a new Decomposition with new component curves.

Parameters:
  • xr_ccurves (list of Curve) – The new component curves for the X-ray data.

  • uv_ccurves (list of Curve) – The new component curves for the UV data.

Returns:

A new Decomposition object with the specified component curves.

Return type:

Decomposition

property xr_components#

Alias for xr_ccurves — the XR elution-curve parameter objects.

Returns a list of ComponentCurve (one per component). Each holds the EGH parameters [H, tR, sigma, tau] of the elution curve only — these objects do not carry per-component scattering profiles P[:, i] and cannot compute Rg.

For per-component scattering profiles or Rg-capable objects, use:

property uv_components#

Alias for uv_ccurves — the UV elution-curve parameter objects.

Same caveat as xr_components: these are ComponentCurve instances carrying only EGH elution parameters, not UV spectra. For per-component UV spectra, use get_uv_matrices().

get_num_components()#

Get the number of components.

Returns:

The number of components in the decomposition.

Return type:

int

get_guinier_objects(debug=False)#

Get the list of Guinier objects for the XR components.

Returns:

The list of Guinier objects for each XR component.

Return type:

list of Guinier

get_rgs()#

Get the list of Rg values for the XR components.

Returns:

rgs – Radius of gyration in Ångströms (Å) for each XR component, in the same order as get_xr_components(). If Guinier fitting fails for a component, float('nan') is returned for that position (never None), so the result is always safe to use in numeric / numpy operations.

Guard pattern:

import math
for i, rg in enumerate(decomp.get_rgs()):
    if math.isnan(rg):
        print(f"Component {i+1}: Guinier fit failed")
    else:
        print(f"Component {i+1}: Rg = {rg:.2f} Å")

Return type:

list of float, length n_components

get_channel_consistency()#

Check UV/XR proportion consistency across decomposition channels.

Computes the area fraction of each component in the XR and UV elution curves and reports the maximum absolute difference.

Returns:

result – A namedtuple with fields:

  • inconsistency (float): max |XR_frac_i - UV_frac_i| across components. 0.0 = perfect agreement, 1.0 = completely different. Values above ~0.1 suggest the decomposition has assigned different proportions to UV and XR channels.

  • xr_fractions (list of float): area fraction per XR component

  • uv_fractions (list of float): area fraction per UV component

Return type:

ChannelConsistency

Examples

cc = decomp.get_channel_consistency()
print(f"Inconsistency: {cc.inconsistency:.3f}")
if cc.inconsistency > 0.1:
    print("WARNING: UV/XR proportions diverged")
get_rg_curve()#

Compute the per-frame Rg curve from the raw XR data.

Runs a Guinier fit on every elution frame independently and returns the results as an RgCurve object. This is useful for assessing whether a peak is a pure single-component species (flat Rg vs. frame) or a heterogeneous mixture (varying Rg).

Note

This can be slow for large datasets because it fits one Guinier region per frame.

Returns:

rgcurve – An RgCurve with attributes:

  • .x — frame indices (integer array)

  • .y — Rg values in Å; NaN where Guinier fit failed

  • .scores — Guinier fit quality scores (0–1)

Return type:

molass.Guinier.RgCurve.RgCurve

Examples

rgcurve = decomp.get_rg_curve()
import matplotlib.pyplot as plt
plt.plot(rgcurve.x, rgcurve.y, '.')
plt.xlabel("Frame")
plt.ylabel("Rg (Å)")
plt.title("Rg vs. elution frame")
plt.show()
compute_reconstructed_rgcurve(debug=False)#

Compute the reconstructed Rg curve as a concentration-weighted average.

At each frame j, the reconstructed Rg is the weighted average of each component’s Rg, weighted by the component’s elution intensity:

Rg_recon(j) = Σ_k  [C_k(j) / Σ_k C_k(j)]  ×  Rg_k

This matches the legacy plot_rg_curves / compute_rg_curves in GuinierTools.RgCurveUtils and the GuinierDeviation scoring used by optimize_rigorously().

Returns:

rgcurve – An RgCurve with the same frame indices as the data.

Return type:

molass.Guinier.RgCurve.RgCurve

get_P_at(q_target, normalize=False)#

Return the XR scattering matrix P interpolated onto q_target.

Parameters:
  • q_target (array-like, shape (m,)) – Target q-values in Å⁻¹.

  • normalize (bool, optional) – If True, each component column is divided by its maximum so that all columns peak at 1. Default False.

Returns:

P_interp – Scattering matrix P evaluated at q_target.

Return type:

np.ndarray, shape (m, n_components)

component_quality_scores()#

Compute a per-component reliability score in [0, 1].

Scores blend Rg distinctiveness (70 %) and area proportion (30 %). A score of 0.0 means Guinier fitting failed or Rg is indistinguishable from another component’s. A score near 1.0 means the component is well-separated in Rg and carries a non-trivial fraction of the signal.

Returns:

scores – Reliability score for each component, in the same order as get_rgs() and get_proportions().

Return type:

list of float

See also

is_component_reliable

threshold-based boolean version.

Examples

scores = decomp.component_quality_scores()
for i, s in enumerate(scores):
    print(f"Component {i+1}: reliability = {s:.2f}")
is_component_reliable(index, threshold=0.5)#

Return True if component index has a quality score ≥ threshold.

Parameters:
  • index (int) – Zero-based component index.

  • threshold (float, optional) – Minimum score considered reliable. Default 0.5.

Return type:

bool

Examples

if not decomp.is_component_reliable(1):
    print("Component 2 may be a noise artifact.")
plot_components(title=None, fig=None, axes=None, **kwargs)#

Plot the components.

Parameters:
  • title (str, optional) – If specified, add a super title to the plot.

  • fig (matplotlib.figure.Figure, optional) – An existing Figure to draw into. If None (default), a new figure is created automatically.

  • axes (array-like of shape (2, 3), optional) –

    A 2×3 array of Axes to draw into. Must be provided together with fig when injecting into an existing subplot grid. If None (default), axes are created automatically inside fig.

    Expected layout:

    axes[0, 0]  UV elution curves
    axes[0, 1]  UV absorbance curves
    axes[0, 2]  (UV spare / unused)
    axes[1, 0]  XR elution curves
    axes[1, 1]  XR scattering curves (log scale)
    axes[1, 2]  XR Guinier plot  ← Kratky is omitted when axes are injected
    

    Note

    When both fig and axes are provided the caller is responsible for creating axes with compatible geometry.

  • rgcurve (molass.Guinier.RgCurve.RgCurve, optional) –

    If provided, overlays the per-frame Rg values as a scatter plot on the XR elution subplot (axes[1, 0]), colour-coded by Guinier fit score. Obtain via decomp.get_rg_curve().

    Example:

    rgcurve = decomp.get_rg_curve()
    decomp.plot_components(rgcurve=rgcurve)
    

  • rg_score_threshold (float, optional) – Only Rg points whose Guinier fit score exceeds this value are plotted. Default is None (all points shown).

  • rg_marker_size (float, optional) – Marker size for the Rg scatter points. Default is 12.

  • rg_cmap (str, optional) – Matplotlib colormap name used to colour Rg markers by score. Default is 'viridis'.

Returns:

result – A PlotResult object which contains the following attributes.

  • fig: The matplotlib Figure object.

  • axes: A 2×3 array of Axes objects.

Return type:

PlotResult

update_xr_ranks(ranks, debug=False)#

Update the ranks for the X-ray data.

Default ranks are one for each component which means that interparticle interactions are not considered. This method allows the user to set different ranks for each component.

Parameters:

ranks (list of int) – The ranks for each component.

Return type:

None

get_xr_matrices(debug=False)#

Get the factorized matrices for the X-ray (SAXS) data.

Parameters:

debug (bool, optional) – If True, enable debug mode.

Returns:

  • M (np.ndarray, shape (n_q, n_frames)) – Measured scattering intensity matrix. Rows are q-points; columns are elution frames.

  • C (np.ndarray, shape (n_components, n_frames)) – Elution curves (concentration profiles) for each component. Each row is one component’s elution curve over frames.

  • P (np.ndarray, shape (n_q, n_components)) – Scattering profiles (form factors) for each component. Each column is one component’s P(q) in absolute or relative intensity units (matching the scale of M).

  • Pe (np.ndarray, shape (n_q, n_components)) – Estimated error (standard deviation) on P, propagated from the measurement error matrix.

Notes

The q-values corresponding to the n_q rows are stored in decomp.xr.qv (shape (n_q,), units Å⁻¹).

Example

M, C, P, Pe = decomp.get_xr_matrices()
# P[:, 0]  →  scattering profile of component 1
# C[0, :]  →  elution curve of component 1
get_xr_components(debug=False)#

Get the per-component objects for the X-ray (SAXS) data.

Parameters:

debug (bool, optional) – If True, enable debug mode.

Returns:

components – One XrComponent per decomposed component, in component order.

Each XrComponent exposes:

  • get_guinier_object() → Guinier fit result (Rg, I0, fit range)

  • get_jcurve_array()np.ndarray shape (n_q, 3): columns are [q, P(q), Pe(q)] in Å⁻¹ and intensity units.

  • icurve_arraynp.ndarray shape (2, n_frames): rows are [frame_x, elution_y].

  • compute_area() → scalar, integrated elution area.

Return type:

list of XrComponent, length n_components

get_scattering_profiles(debug=False)#

Get the per-component scattering profiles P and their errors Pe.

This is a convenience accessor; equivalent to:

_, _, P, Pe = decomp.get_xr_matrices()
Returns:

  • qv (np.ndarray, shape (n_q,)) – q-values in Å⁻¹ (alias of decomp.xr.qv).

  • P (np.ndarray, shape (n_q, n_components)) – Scattering profiles. P[:, i] is component i’s profile.

  • Pe (np.ndarray, shape (n_q, n_components)) – Propagated standard error on P.

Notes

Use this when you only need the SAXS profiles and want to skip constructing XrComponent objects. For full per-component objects (with Guinier fitting), use get_xr_components().

get_uv_matrices(debug=False)#

Get the matrices for the UV data.

Parameters:

debug (bool, optional) – If True, enable debug mode.

Returns:

The matrices for the UV data.

Return type:

tuple of (np.ndarray, np.ndarray, np.ndarray, np.ndarray)

get_uv_components(debug=False)#

Get the components for the UV data.

Return type:

List of UvComponent objects.

get_pairedranges(mapped_curve=None, area_ratio=0.7, concentration_datatype=2, debug=False)#

Get the paired ranges.

Parameters:
  • mapped_curve (MappedCurve, optional) – If specified, use this mapped curve instead of computing a new one.

  • area_ratio (float, optional) – The area ratio for the range computation.

  • concentration_datatype (int, optional) – The concentration datatype for the range computation.

  • debug (bool, optional) – If True, enable debug mode.

Returns:

The list of PairedRange objects.

Return type:

list of PairedRange

get_proportions()#

Get the relative area fractions of the XR components.

Returns:

proportions – Normalised elution-curve area fraction for each component, summing to 1.0. Values are in the range [0, 1]. Proportional to the amount (concentration × volume) of each species in the SEC peak region.

Return type:

np.ndarray, shape (n_components,)

compute_scds(debug=False)#

Get the list of SCDs (Score of Concentration Dependence) for the decomposition.

Returns:

The list of SCD values for each component.

Return type:

list of float

get_cd_color_info()#

Get the color information for the concentration dependence.

Returns:

  • peak_top_xes (list of float) – The list of peak top x values for each component.

  • scd_colors (list of str) – The list of colors for each component based on their ranks.

upgrade(model, *, rgcurve=None, model_params=None, debug=False, **kwargs)#

Upgrade the decomposition to a physics-aware column model (SDM or EDM).

Replaces the EGH elution model with a more physically realistic column model, using the EGH shape parameters as a starting point for column-parameter estimation.

Parameters:
  • model (str) –

    The name of the column model to use.

    Supported models:

  • rgcurve (Curve, optional) – The Rg curve to use for the optimization.

  • model_params (dict, optional) – The parameters for the model.

  • pore_dist (str, optional) – For SDM only: pore-size distribution to use. 'mono' (default) uses a single pore size (G1200, gamma residence time); 'lognormal' uses a lognormal pore distribution (G1300). Takes precedence over model_params['pore_dist'] when both are given.

  • debug (bool, optional) – If True, enable debug mode.

  • **kwargs – Additional keyword arguments forwarded to the model’s optimize_decomposition and downstream estimators (e.g. poresize_bounds, N0, include_M3 for SDM — see molass.SEC.Models.SdmEstimator.estimate_sdm_column_params()).

Returns:

result – A new Decomposition object with optimized components.

Return type:

Decomposition

optimize_with_model(model_name, rgcurve=None, model_params=None, debug=False, **kwargs)#

Deprecated. Use upgrade() instead.

recommend_num_components(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.

Sweeps k in 1..k_max on this decomposition’s ssd, runs upgrade() for each k, and applies a 4-metric diagnostic (residual, cond(C), max cos(C[i],C[j]), amp ratio) plus the decision rule from issue #116. See molass.LowRank.NumComponentsRecommender.recommend_num_components() for full details.

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

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

  • rgcurve (Curve, optional) – Rg curve. If None, computed via self.ssd.xr.compute_rgcurve().

  • rt_dist (str, optional) – SDM residence-time distribution ('gamma' or 'exponential').

  • 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. Default True.

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

Returns:

Named tuple (recommended_k, reason, metrics) where metrics is a pandas.DataFrame with one row per k.

Return type:

Recommendation

Examples

rec = decomp.recommend_num_components(k_max=3)
print(rec.recommended_k, '-', rec.reason)
print(rec.metrics)
make_rigorous_initparams(baseparams, debug=False)#

Make initial parameters for rigorous optimization.

Parameters:

debug (bool, optional) – If True, enable debug mode.

Returns:

The initial parameters for rigorous optimization.

Return type:

np.ndarray

get_rigorous_param_count()#

Return the number of parameters for rigorous optimization.

Convenience wrapper around make_rigorous_initparams() that handles the baseparams setup internally. Useful for a quick sanity-check before committing to a long run:

decomp_lkm = LKM().optimize_decomposition(decomp_egh)
print(decomp_lkm.get_rigorous_param_count())  # e.g. 30

Works for all supported models (EGH, SDM, EDM, CEDM, LKM).

Returns:

Number of parameters in the flat init-params vector that will be passed to the legacy objective function (G1100 / G1200 / G1300 / G1400 / G2020).

Return type:

int

optimize_rigorously(rgcurve=None, analysis_folder=None, method='BH', niter=20, frozen_components=None, free_components=None, frozen_param_groups=None, trimmed_ssd=None, clear_jobs=True, function_code=None, in_process=True, monitor=True, async_=True, progress='dashboard', max_trials=0, debug=False, _dry_run=False, ns_narrow_bounds=True, ns_adaptive_nsteps=False, ns_nsteps=None, **kwargs)#

Perform a rigorous decomposition.

Parameters:
  • rgcurve (Curve) – The Rg curve to use for the decomposition.

  • analysis_folder (str, required) –

    The folder to save analysis results. Must be provided explicitly — passing None raises ValueError. Example: analysis_folder='temp_analysis_apo_bh'. Optimization creates the following layout on disk:

    <analysis_folder>/
        optimized/
            jobs/
                000/callback.txt   # job 0
                001/callback.txt   # job 1 (e.g. after Resume)
                ...
    

    Each callback.txt records per-iteration objective values and parameter vectors. Use list_rigorous_jobs() to inspect existing jobs, or load_rigorous_result() to reconstruct a Decomposition from a completed job.

  • method (str, optional) –

    The optimization algorithm to use. Default is 'BH'.

    Valid values:

    • 'BH'Basin-Hopping (default). Nelder-Mead local minimization with stochastic perturbation between basins. Good general-purpose choice.

    • 'NS'Nested Sampling (UltraNest). Explores the full parameter space; useful when the objective landscape has multiple well-separated minima.

  • niter (int, optional) –

    Iteration budget. Meaning depends on method:

    • 'BH': literal number of Basin-Hopping outer steps (default 20).

    • 'NS': multiplied by 7 000 to form max_ncalls for UltraNest (niter=20 → 140 000 likelihood evaluations).

    Default is 20.

  • frozen_components (list of int, optional) – 0-based indices of protein components to freeze during optimization. Their EGH shape parameters (H, mu, sigma, tau), Rg, and UV scale will be held constant at the values from the initial decomposition. Mutually exclusive with free_components.

  • free_components (list of int, optional) – 0-based indices of protein components to optimize. All other components will be frozen. This is the complement of frozen_components — use whichever is shorter. E.g., free_components=[4] to optimize only the main peak. Mutually exclusive with frozen_components.

  • trimmed_ssd (SecSaxsData, optional) –

    The trimmed but not baseline-corrected SSD — i.e., the output of ssd.trimmed_copy() before corrected_copy(). When provided, the optimizer fits its model (EGH components + linear baseline) directly to this data, while using the corrected decomposition for EGH initialization. This is the recommended two-stage approach: baseline correction helps peak initialization in the quick stage, but the rigorous stage should fit baseline as a free parameter on uncorrected data.

    Deprecated since version The: old name uncorrected_ssd is accepted as an alias but will be removed in a future release.

  • clear_jobs (bool, optional) – If True (default), existing job folders are cleared before starting. Set to False after a kernel restart to preserve previous job results and reconstruct RunInfo without losing optimization history. When False and previous jobs exist, the best params found so far are automatically used as init_params for the new trial (resuming from the best known point rather than the original decomp params, see issue #169).

  • in_process (bool, optional) – If True (default), run the optimizer in this Python process instead of spawning a subprocess. Avoids the parent/subprocess data-derivation divergence (see issues #117 / #119) and keeps the optimizer running against the same library-prepared data the parent already holds in memory. Set False to use the legacy subprocess path (required by the tkinter GUI; available as an escape hatch for notebook users who need process isolation).

  • monitor (bool, optional) – Controls the MplMonitor ipywidgets dashboard. When True (default), a live dashboard is shown whether the run is in-process or subprocess. When False, no dashboard is created — the run proceeds silently. Use monitor=False for batch / comparison runs (e.g. compare_optimization_paths) where the widget is not needed.

  • async (bool, optional) – Only meaningful when in_process=True. If True (default), the optimizer runs in a background daemon thread and this call returns a RunInfo object immediately, before the run completes. Poll RunInfo.is_alive to check whether the run is still in progress, then call RunInfo.wait() (instant if already done) to join the thread before accessing results. Set False for blocking (batch) runs.

  • progress (str or None, optional) – Deprecated and ignored — use monitor=True/False instead. Kept in the signature only for backward compatibility.

  • max_trials (int, optional) –

    Maximum number of automatic sequential re-trials after each trial completes. Default 0 means no automatic re-start — the user decides manually via the Resume button.

    Only meaningful when in_process=True and monitor=True. The subprocess path (in_process=False) always uses its own default of 30. Set max_trials=30 here to match that behaviour for unattended in-process runs.

  • debug (bool, optional) – If True, enable debug mode.

Returns:

A RunInfo object that tracks the optimization run. For blocking runs (async_=False), the run is complete when this returns. For non-blocking runs (async_=True), call run_info.load_best() to wait for the first result and load it, or run_info.wait() then run_info.load_best().

Return type:

RunInfo

Notes

The elution model (EGH, SDM, EDM) is not a parameter of this method — it is determined by the decomposition object itself (self.xr_ccurves[0].model). Passing model= raises TypeError: Unexpected keyword arguments.

To upgrade to a different column model, use upgrade() instead. That method handles EGH → SDM/EDM parameter conversion internally, using the EGH shape parameters as a starting point for column-parameter estimation.

Typical staged workflows:

# EGH → refine with G1100
decomp = corrected.quick_decomposition()
run_info = decomp.optimize_rigorously(
    analysis_folder='temp_analysis_apo_egh', ...)
result_egh = run_info.load_best()

# EGH → SDM upgrade → refine with G1200/G1300
run_info_sdm = result_egh.upgrade(
    'SDM', analysis_folder='temp_analysis_apo_sdm', ...)

# EGH → LKM upgrade → refine with G1400 (auto-detected)
decomp_lkm = decomp.upgrade('LKM')                 # moment-matching
run_lkm = decomp_lkm.optimize_rigorously(          # G1400 auto-selected
    analysis_folder='temp_analysis_apo_lkm', ...)

See also

RunInfo.get_score_breakdown

Inspect the individual score and penalty components that make up the objective value (fv).

RunInfo.is_alive

Check whether an async run is still in progress.

RunInfo.wait

Block until the run completes.

RunInfo.load_best

Wait for the first result then load it (recommended for async_=True runs instead of wait() + load_best()).

score_initial(trimmed_ssd=None, analysis_folder=None, function_code=None, debug=False)#

Evaluate the rigorous objective function once at initial parameters.

A lightweight alternative to optimize_rigorously() when you only need to know the starting score before launching full BH/NS. Equivalent to the final plot of the legacy PeakEditor — UV/XR decompositions with individual score components — but callable from a notebook without triggering any optimization loop.

For models other than EGH, call upgrade() first:

decomp_sdm = decomp.upgrade(model='SDM')
result = decomp_sdm.score_initial(trimmed_ssd=trimmed)
Parameters:
  • trimmed_ssd (SecSaxsData, optional) – Trimmed (uncorrected) SSD (Pattern B, recommended).

  • analysis_folder (str, optional) – Where to write optimizer setup artefacts. Defaults to a temporary directory that is deleted after the call.

  • function_code (str, optional) – Override auto-detected function code.

  • debug (bool, optional)

Returns:

Has .sv, .fv, .breakdown, .plot(), .diagnose(), .print_summary().

Return type:

InitialScoreResult

Examples

result_auto = decomp_auto.score_initial(trimmed_ssd=trimmed)
result_prop = decomp_prop.score_initial(trimmed_ssd=trimmed)
result_auto.plot(title="Auto EGH")
result_prop.plot(title="Proportional 1:1:1:1")
result_auto.print_summary()

See also

optimize_rigorously

Full BH/NS optimization.

RunInfo.get_score_breakdown

Score breakdown after optimization.

load_best_rigorous_result(analysis_folder, rgcurve=None, debug=False)#

Load the best rigorous optimization result from disk.

Convenience method that combines list_rigorous_jobs() and load_rigorous_result() into a single call: finds the job with the lowest objective function value and reconstructs the Decomposition from it.

Parameters:
  • analysis_folder (str) – The same analysis_folder passed to optimize_rigorously().

  • rgcurve (RgCurve, optional) – Pre-computed Rg curve. Avoids redundant per-frame Guinier fitting when loading results.

  • debug (bool, optional) – If True, reload modules from disk.

Returns:

A new Decomposition with the best optimized components.

Return type:

Decomposition

Raises:

FileNotFoundError – If no completed jobs are found.

See also

RunInfo.get_score_breakdown

Inspect the individual score and penalty components that make up the objective value (fv).

Examples

result = decomp.load_best_rigorous_result("temp_analysis")
result.plot_components()

# Fast: skip redundant Guinier fitting by passing rgcurve
result = decomp.load_best_rigorous_result("temp_analysis", rgcurve=rgcurve)
load_rigorous_result(analysis_folder, jobid=None, rgcurve=None, debug=False)#

Load a completed rigorous optimization result from disk.

This reads saved parameters without launching a new subprocess. Use it to view results from a previous session after a kernel or VS Code restart.

Parameters:
  • analysis_folder (str) – The same analysis_folder passed to optimize_rigorously(). See optimize_rigorously() for the folder layout.

  • jobid (str, optional) – Specific job id (subfolder name, e.g. '001'). If None, loads the latest job. Use list_rigorous_jobs() to see available jobs.

  • rgcurve (RgCurve, optional) – Pre-computed Rg curve. Avoids redundant per-frame Guinier fitting when loading results.

  • debug (bool, optional) – If True, reload modules from disk.

Returns:

A new Decomposition with the optimized components.

Return type:

Decomposition

Examples

After kernel restart, re-run data loading and quick decomposition, then:

result = decomp.load_rigorous_result("temp_analysis_scaffolded", rgcurve=rgcurve)
result.plot_components(rgcurve=rgcurve)
static list_rigorous_jobs(analysis_folder)#

List completed rigorous optimization jobs on disk.

Parameters:

analysis_folder (str) – The same analysis_folder passed to optimize_rigorously().

Returns:

Each entry is a JobInfo(id, iterations, best_fv, timestamp) namedtuple. Sorted by job id.

Return type:

list of JobInfo

Examples

jobs = Decomposition.list_rigorous_jobs("temp_analysis_scaffolded")
for job in jobs:
    print(f"Job {job.id}: {job.iterations} iters, best fv={job.best_fv:.4f}")

# Then load a specific job
result = decomp.load_rigorous_result("temp_analysis_scaffolded", jobid=jobs[0].id)
static has_rigorous_results(analysis_folder)#

Check whether any rigorous optimization results are available.

Lightweight filesystem check — does not parse results. Use this to poll readiness before calling load_rigorous_result() or list_rigorous_jobs().

Parameters:

analysis_folder (str) – The same analysis_folder passed to optimize_rigorously().

Returns:

True if at least one job has a callback.txt file.

Return type:

bool

Examples

if Decomposition.has_rigorous_results("temp_analysis"):
    jobs = Decomposition.list_rigorous_jobs("temp_analysis")
static wait_for_rigorous_results(analysis_folder, timeout=600, poll_interval=5)#

Block until rigorous optimization results become available.

Polls the filesystem until at least one job has written a callback.txt, or the timeout is reached.

Parameters:
  • analysis_folder (str) – The same analysis_folder passed to optimize_rigorously().

  • timeout (float, optional) – Maximum seconds to wait (default 600). Use 0 for no limit.

  • poll_interval (float, optional) – Seconds between checks (default 5).

Returns:

True if results appeared, False if timed out.

Return type:

bool

Examples

decomp.optimize_rigorously(analysis_folder="temp", ...)
if Decomposition.wait_for_rigorous_results("temp"):
    result = decomp.load_rigorous_result("temp")
static plot_convergence(analysis_folder, ax=None, title=None)#

Plot and return convergence data across rigorous optimization jobs.

Shows two subplots: (1) best fv per job, (2) per-job fv trajectory. Returns a ConvergenceInfo namedtuple for programmatic assessment.

Parameters:
  • analysis_folder (str) – The same analysis_folder passed to optimize_rigorously().

  • ax (matplotlib Axes or array of Axes, optional) – If provided, plot into these axes (expects 2).

  • title (str, optional) – Figure title.

Returns:

Namedtuple with fields: jobs, best_fv, best_job_id, spread, trend ('improving'/'worsening'/'stable'), n_jobs.

Return type:

ConvergenceInfo

Examples

info = Decomposition.plot_convergence("temp_analysis")
print(f"Best: {info.best_fv:.4f}, trend: {info.trend}")