starling.structure.bme.BME
- class BME[source]
Bases:
objectBayesian Maximum Entropy reweighting. Refine ensembles with experimental data. Used to improve fit to experiments while minimizing bias in the ensemble.
- Parameters:
observables (list[ExperimentalObservable]) – List of experimental observables to fit.
calculated_values (np.ndarray) – Array of calculated observable values for each frame. Shape: (n_frames, n_observables)
initial_weights (np.ndarray, optional) – Initial weights for ensemble frames. If None, uniform weights are used.
Methods
Compute weighted averages of observables using optimized BME weights.
Scan a range of theta values and select the optimal regularization (θ).
Attributes
Get the most recent optimization result.
Theta value used in the most recent fit (if any).
Last theta scan run on this BME instance (if any).
- __init__(observables: list, calculated_values: ndarray, theta: float = 0.5, initial_weights: ndarray | None = None)[source]
- scan_theta(theta_range: Tuple[float, float] | ndarray = (0.01, 10.0), n_points: int = 15, log_scale: bool = True, max_iterations: int = 50000, optimizer: str = 'L-BFGS-B', verbose: bool = False, progress_callback: callable | None = None, method: str = 'perpendicular') ThetaScanResult[source]
Scan a range of theta values and select the optimal regularization (θ).
This helper runs a theta scan using this instance’s observables, calculated_values, and initial_weights, returning a ThetaScanResult that contains one BME fit per theta, summary metrics, and the index/value of the recommended theta based on an L-curve knee criterion.
- Parameters:
theta_range (tuple[float, float] | np.ndarray, optional) –
If a tuple (min_theta, max_theta) is provided, generate a grid of size n_points within this range (logarithmic if log_scale=True).
If a 1D array is provided, use it as the exact grid of theta values.
Default is (0.01, 10.0).
n_points (int, optional) – Number of theta samples used when theta_range is a tuple. Default 15.
log_scale (bool, optional) – If True and theta_range is a tuple, sample theta values on a log scale. Default True.
max_iterations (int, optional) – Maximum iterations for the internal optimizer in each single-theta fit. Default DEFAULT_MAX_ITERATIONS.
optimizer (str, optional) – Optimizer name forwarded to scipy.optimize.minimize (e.g., “L-BFGS-B”). Default DEFAULT_OPTIMIZER.
verbose (bool, optional) – If True, print scan progress messages. Default False.
progress_callback (Callable[[int, int, float], None] | None, optional) – Optional callback invoked for each theta with signature progress_callback(current_index, total, theta_value).
method (str, optional) –
Knee selection rule for the L-curve:
”perpendicular” (default): maximum perpendicular distance to the line connecting the endpoints (classic knee finding).
”curvature”: maximum Menger curvature (3-point curvature estimate).
- Returns:
Object with:
theta_values: np.ndarray of scanned θ
chi_squared_values: np.ndarray of final χ² per θ
phi_values: np.ndarray of Φ (entropy-based effective fraction) per θ
kl_divergence_values: np.ndarray of KL divergence per θ
results: list[BMEResult] for each θ
optimal_theta: selected θ value
optimal_idx: index of the selected θ
method: human-readable name of the knee selection method used
- Return type:
Notes
This method does not modify self._theta or self._result; it only stores the scan outcome in self._theta_scan_result for later access.
To run a full auto-θ fit and set the model state accordingly, call BME.fit(auto_theta=True) or use Ensemble.reweight_bme(…, theta=None).
Examples
>>> scan = bme.scan_theta(theta_range=(1e-2, 1e1), n_points=25, method="curvature") >>> scan.print_summary() >>> fig = scan.plot(show=True, figsize=(8, 4))
- fit(max_iterations: int = 50000, optimizer: str = 'L-BFGS-B', verbose: bool = True, *, theta: float | None = None, auto_theta: bool = True, theta_scan_kwargs: dict | None = None) BMEResult[source]
- property result: BMEResult | None
Get the most recent optimization result.
- Returns:
The optimization result, or None if fit() has not been called.
- Return type:
BMEResult or None
- property theta_scan_result: ThetaScanResult | None
Last theta scan run on this BME instance (if any).
- predict(calculated_values: ndarray) ndarray[source]
Compute weighted averages of observables using optimized BME weights.
This method applies the fitted BME weights to compute ensemble-averaged values for any set of observables calculated from the same frames. This is useful when you want to apply BME weights to observables that weren’t used in the fitting process, or when using BME standalone without the Ensemble class.
- Parameters:
calculated_values (np.ndarray) – Calculated observable values for each frame. Shape: (n_frames, n_observables) Note: n_frames must match the number of frames used in fit()
- Returns:
Weighted average of each observable. Shape: (n_observables,)
- Return type:
np.ndarray
- Raises:
ValueError – If fit() has not been called yet, or if the number of frames doesn’t match.
Examples
>>> # Fit BME with some observables >>> bme = BME([obs1, obs2], calculated_training, theta=1) >>> result = bme.fit() >>> >>> # Apply weights to different observables from same frames >>> rg_values = calculate_rg(frames) # Shape: (n_frames, 1) >>> weighted_rg = bme.predict(rg_values) >>> >>> # Or multiple new observables at once >>> new_observables = np.column_stack([rg, ete, contacts]) # (n_frames, 3) >>> weighted_means = bme.predict(new_observables) # Returns 3 means
Notes
When using BME through the Ensemble class, you typically don’t need this method - use ensemble.rij(use_bme_weights=True) or equivalent methods instead. This method is primarily for standalone BME usage or advanced workflows.