pyfli.bayes_utils.param_combinations#
Classes
|
Evaluate a stack of posterior-sample parameter combinations against measured decay data, and select the best-fitting combination per pixel by a chosen goodness-of-fit metric. |
- class ParamSelector(freq_acq, irf, decay, model_type='bi-exponential', backend='compute_detailed_results', bool_mask=None)[source]#
Bases:
objectEvaluate a stack of posterior-sample parameter combinations against measured decay data, and select the best-fitting combination per pixel by a chosen goodness-of-fit metric.
- Parameters:
freq_acq (
float) – Acquisition frequency (e.g. 80 MHz), passed straight through to compute_detailed_results’ freq_acq argument.irf (
np.ndarray) – Passed through to compute_detailed_results unchanged for every sample and for the final best-combination re-fit.decay (
np.ndarray) – Passed through to compute_detailed_results unchanged for every sample and for the final best-combination re-fit.bool_mask (
np.ndarray | None) – Optional (H, W) boolean mask – e.g. the same ROI mask passed to BiPipeline.run_inference. Not used during sample evaluation/selection itself (compute_detailed_results’ own pixel_health_map is derived from decay, not this mask, so a pixel with background counts outside the real ROI can otherwise look “healthy” even though its params are meaningless placeholders). Stored as the default for compute_best_model_fit_result’s own bool_mask argument, which uses it to NaN-out excluded pixels in the final result’s output maps.model_type (
str) – “bi-exponential” or “mono-exponential”.backend (
str) – Which implementation reconstructs each sample’s fit/residual/ goodness-of-fit maps:"compute_detailed_results"(default, the rescale-to-measured-totals implementation inpyfli.reconstruction.detailed_results) or"reconstructor"(pyfli.reconstruction.ParamToDecay’s vectorized path). Both return the same{"name", "method", "results": {"maps", "error_maps", "TR_maps"}}shape, so switching backends doesn’t change any downstream code.
- METRICS: ClassVar[dict[str, tuple[str, str]]] = {'R2': ('r2_stack', 'max'), 'RMSE': ('rmse_stack', 'min'), 'chi2': ('chi2_stack', 'min'), 'reduced_chi2': ('reduced_chi2_stack', 'min')}#
(stack_key, “min” | “max”)} – whether the metric should be minimized (chi2/reduced_chi2/RMSE) or maximized (R2) to find the best-fitting sample per pixel.
- Type:
Registry of {metric
- BACKENDS: tuple[str, ...] = ('compute_detailed_results', 'reconstructor')#
Valid values for the
backendconstructor argument.
- REDUCERS: ClassVar[dict[str, Callable]] = {'mean': <function mean>, 'median': <function median>}#
numpy function} for compute_aggregate_model_fit_result’s non-“best” methods – collapses the NUM_SAMPLES axis of each output_combination array to a single per-pixel value. Add an entry here to support another reduction (e.g. “mode”) without touching the method itself.
- Type:
Registry of {reducer
- evaluate_all_samples(output_combination, keep_per_sample_results=False, progress=True)[source]#
Run compute_detailed_results once per posterior sample.
- Parameters:
output_combination (
dict[str,np.ndarray]) – e.g.{'tau1': (H,W,NUM_SAMPLES), 'tau2': (H,W,NUM_SAMPLES), 'alpha1': (H,W,NUM_SAMPLES)}for bi-exponential, or{'tau': (H,W,NUM_SAMPLES)}for mono-exponential.keep_per_sample_results (
bool) – If True, also returns the full compute_detailed_results() dict for every sample (memory-heavy for large images/NUM_SAMPLES). Default False – only the scalar metric stacks are kept.progress (
bool) – Show a tqdm progress bar over the NUM_SAMPLES loop. Default True; pass False for quiet use (e.g. a 1x1-pixel crop). The per-sample backend log line is suppressed regardless, so it never competes with the bar.
- Returns:
Dictionary with keys:
chi2_stack,reduced_chi2_stack,rmse_stack,r2_stack: (H, W, NUM_SAMPLES)per_sample_results: list[dict] or None
- Return type:
- select_best_combination(output_combination, stacks, metric='RMSE')[source]#
For each pixel independently, pick the sample index that optimizes the chosen metric (minimizes chi2/reduced_chi2/RMSE, maximizes R2), and build the corresponding best-per-pixel parameter maps.
- Parameters:
- Returns:
Dictionary with keys:
best_params: dict[str, np.ndarray] – best (H, W) map per key in output_combination (e.g. tau1/tau2/alpha1)best_sample_idx: (H, W) int array – which sample index won per pixelbest_score: (H, W) float array – the winning metric value per pixel
- Return type:
- compute_best_model_fit_result(output_combination, metric='RMSE', data_name='BI_MODEL_bi_best', bool_mask=None, stacks=None)[source]#
Full pipeline: evaluate every sample, pick the best per-pixel combination by
metric, then re-run compute_detailed_results once more on that best combination.- Parameters:
output_combination (
dict[str,np.ndarray]) – Same dict passed to evaluate_all_samples.metric (
str) – One of “chi2”, “reduced_chi2”, “RMSE”, “R2”.data_name (
str) – Dataset name recorded in the returned result dict.stacks (
dict | None) – Precomputedevaluate_all_samples()output for this exactoutput_combination. When given, the per-sample evaluation loop (NUM_SAMPLES reconstructions) is skipped and these stacks are used directly – so a caller that already ranevaluate_all_samples(e.g. to inspect the raw stacks or try several metrics) doesn’t pay for it twice. When None (default) it is computed internally. The stacks must come from the sameoutput_combination; passing mismatched stacks yields wrong selections.bool_mask (
np.ndarray | None) – Optional (H, W) boolean mask; defaults toself.bool_mask(set at construction) when not given. Pixels where the mask is False are NaN’d out in every array under result[‘results’][‘maps’], [‘TR_maps’], and [‘error_maps’] – so excluded pixels (e.g. outside the real ROI) can’t be mistaken for ordinary fits downstream, since compute_detailed_results’ own pixel_health_map is derived from decay, not this mask. No masking is applied if both this argument andself.bool_maskare None.
Notes
The return value is exactly the dict compute_detailed_results itself returns –
{"name", "method", "results": {"maps", "error_maps", "TR_maps"}}– so it plugs directly into the rest of the workflow (e.g.result['results']['maps'].keys(),result['results']['TR_maps']['fit_map'],saver.save_npy(...), DataViewer, etc.) exactly like any other compute_detailed_results output.Two extra maps are folded into
result['results']['maps']:best_sample_idx_map: (H, W) – which posterior sample (0..NUM_SAMPLES-1) won at each pixel<metric>_selection_map: (H, W) – the winning metric value at each pixel (e.g.reduced_chi2_selection_map)
Per-sample diagnostics (the full metric stacks across all samples, and the chosen metric name) are attached as an additional top-level key,
sample_selection, without disturbing the primary"name"/"method"/"results"structure.- Returns:
Same shape as compute_detailed_results()’s return value, plus a top-level
sample_selectionkey holding the raw per-sample stacks and best_params used to produce the final maps.- Return type:
- compute_aggregate_model_fit_result(output_combination, method='best', metric='RMSE', data_name='BI_MODEL_bi_aggregate', bool_mask=None, stacks=None)[source]#
Collapse the stack of posterior-sample parameter combinations down to a single per-pixel parameter map via
method, then run the configured backend once on that combination.- Parameters:
output_combination (
dict[str,np.ndarray]) – Same dict passed to evaluate_all_samples/compute_best_model_fit_result (each array (H, W, NUM_SAMPLES)).method (
str) –- How to reduce the NUM_SAMPLES axis to a single per-pixel value:
- ”best”per-pixel sample that optimizes
metric (delegates to compute_best_model_fit_result; see that method for the extra ‘best_sample_idx_map’/’sample_selection’ diagnostics only this option adds).
- ”best”per-pixel sample that optimizes
- any key in
REDUCERS(default “mean”, “median”) : per-pixel reduction across samples, e.g. the per-pixel mean or median tau/alpha1.
- any key in
metric (
str) – Only used when method=”best”; one of “chi2”, “reduced_chi2”, “RMSE”, “R2” (seeMETRICS).data_name (
str) – Dataset name recorded in the returned result dict.bool_mask (
np.ndarray | None) – Optional (H, W) boolean mask; defaults toself.bool_mask(set at construction) when not given. Forwarded to compute_best_model_fit_result for method=”best”; for the reducer methods, applied the same way directly here – see compute_best_model_fit_result’s bool_mask parameter for what it does.stacks (
dict | None) – Only used when method=”best”; forwarded to compute_best_model_fit_result to skip the per-sample evaluation loop when the caller already has its output. Ignored by the reducer methods, which never score individual samples.
- Returns:
dict – compute_best_model_fit_result for the exact structure notes). For method=”best” this is exactly compute_best_model_fit_result’s return value, including its extra ‘sample_selection’ key; the other methods have no per-sample selection to report, so that key is simply absent.
- Return type:
same shape as compute_detailed_results()'s return value (see