pyfli.analysis.precision#

Lifetime precision: Cramér-Rao bounds, fit-free estimators and Monte Carlo studies.

crb_cv_map(tau_map, n_photons_map, irf, laser_period, mask=None, background_per_bin=0.0, fit_background=False)[source]#

Per-pixel ideal CV map.

Parameters:
  • tau_map (np.ndarray) – True lifetime per pixel, shape (H, W) (e.g. the simulator’s ground-truth maps["tau_map"]).

  • n_photons_map (np.ndarray) – Expected signal photons per pixel, shape (H, W) (e.g. the simulator’s maps["photon_count_map"], or the decay sum minus T * background).

  • irf (np.ndarray) – Shared IRF (T,) or per-pixel IRF cube (H, W, T).

  • laser_period (float) – Laser repetition period in ns.

  • mask (np.ndarray | None) – Pixels to compute; others are NaN. Defaults to tau_map > 0.

  • background_per_bin (float) – See crb_lifetime_cv().

  • fit_background (bool) – See crb_lifetime_cv().

Returns:

(H, W) ideal CV map; multiply by sqrt(n_photons_map) for the F-value.

Return type:

np.ndarray

crb_f_value(tau, irf, laser_period, n_photons=1e4, background_per_bin=0.0, fit_background=False)[source]#

Ideal F-value CV_ideal * sqrt(N). Without background it does not depend on n_photons; with background it rises as the signal-to-background ratio drops.

Parameters:
Return type:

ndarray

crb_for_cv_bins(cv_df, tau_map, photon_map, irf, laser_period, mask=None, cluster_mask=None, cluster_names=None, n_photons_map=None, background_per_bin=0.0, fit_background=False)[source]#

Ideal CV for every photon-count bin of a pyfli.data_vnp.CVPlot.compute() table, so it can be plotted against (or merged with) the measured curves.

Bin membership is rebuilt from the table’s bin_low / bin_high edges on photon_map (the same photon map given to CVPlot), per cluster when the table has clusters. Within a bin, the per-pixel CRB variances are averaged, so cv_crb = sqrt(mean((cv_i * tau_i) ** 2)) / mean(tau_i) – the CV an ideal estimator would show across those pixels, excluding the true lifetime spread.

Parameters:
  • cv_df (pd.DataFrame) – Output of CVPlot.compute (columns cluster, bin_rank, bin_low, bin_high, bin_center).

  • tau_map (np.ndarray) – True lifetime per pixel (ground truth), (H, W).

  • photon_map (np.ndarray) – Photon map used for binning in CVPlot (e.g. decay.sum(axis=-1)).

  • irf (ndarray) – See crb_lifetime_cv().

  • laser_period (float) – See crb_lifetime_cv().

  • mask (np.ndarray | None) – Pixel mask used in CVPlot.

  • cluster_mask (ndarray | None) – The same cluster labels and names given to CVPlot, if any.

  • cluster_names (dict[int, str] | None) – The same cluster labels and names given to CVPlot, if any.

  • n_photons_map (np.ndarray | None) – Expected signal photons per pixel for the bound; defaults to photon_map. Pass the ground-truth photon_count_map to exclude dark counts.

  • background_per_bin (float) – See crb_lifetime_cv().

  • fit_background (bool) – See crb_lifetime_cv().

Returns:

One row per (cluster, bin): cluster, bin_rank, bin_center, tau_true, n_signal, cv_crb and F_crb (cv_crb * sqrt(bin_center), directly comparable with a measured cv * sqrt(bin_center)). Merge with the measured table on ["cluster", "bin_rank"].

Return type:

pd.DataFrame

crb_lifetime_cv(tau, n_photons, irf, laser_period, background_per_bin=0.0, fit_background=False)[source]#

Ideal (Cramér-Rao) coefficient of variation of the lifetime, sigma_tau / tau.

The unknowns are the photon count N and tau (plus the background level when fit_background is True, which is what NLSF / MLE fits with a free offset v_shift do). The background level itself always enters the Poisson noise.

Parameters:
  • tau (float | np.ndarray) – True lifetime (ns) and expected number of detected signal photons; broadcast against each other.

  • n_photons (float | np.ndarray) – True lifetime (ns) and expected number of detected signal photons; broadcast against each other.

  • irf (np.ndarray) – IRF of shape (T,), or one IRF per value with shape S + (T,).

  • laser_period (float) – Laser repetition period in ns.

  • background_per_bin (float | np.ndarray) – Expected background (dark) counts per gate, e.g. the simulator’s dcr.

  • fit_background (bool) – Treat the background level as an extra unknown parameter.

Returns:

Ideal CV with the broadcast shape of the inputs; NaN where tau <= 0, N <= 0 or an input is not finite. CV * sqrt(N) is the F-value.

Return type:

np.ndarray

crlb_covariance(info)[source]#

Cramér-Rao bound on the covariance, I^-1 per batch entry. A (near-)singular Fisher matrix – an unidentifiable parameter – falls back to the pseudo-inverse.

Parameters:

info (ndarray)

Return type:

ndarray

crlb_cv(info, value, index)[source]#

Smallest achievable coefficient of variation std / value of parameter index.

Parameters:
Return type:

ndarray

crlb_std(info, index)[source]#

Smallest achievable standard deviation of parameter index, per batch entry.

Parameters:
Return type:

ndarray

estimate_lifetimes(decays, irf, laser_period, method, freq=None, background_per_bin=0.0, max_iter=1000, n_jobs=-1)[source]#

Lifetime of every decay with one method: "NLSF", "MLE" (fits), "mean_arrival", "phasor" or "phasor_bin_corrected" (fit-free, background-subtracted).

Parameters:
Return type:

ndarray

estimator_efficiency(cv_measured, cv_bound)[source]#

Statistical efficiency (cv_bound / cv_measured) ** 2: 1 when an estimator reaches the CRLB, smaller otherwise (a biased estimator can exceed 1).

Parameters:
  • cv_measured (Any)

  • cv_bound (Any)

Return type:

ndarray

f_value(cv, n_photons)[source]#

Photon-economy F-value CV * sqrt(N): 1 for an ideal photon-counting measurement, larger for any real instrument or estimator. Applies equally to a measured CV and to a CRLB CV.

Parameters:
Return type:

ndarray

fisher_information(model, params, noise='poisson', sigma=None, jacobian=None, rel_step=1e-4, abs_step=1e-8)[source]#

Fisher matrices (P, K, K) of a batched model at params (P, K).

Parameters:
  • model (Callable) – model(params) -> channel means (P, T).

  • params (np.ndarray) – Parameter vectors (P, K).

  • noise (str) – "poisson" (counts; the means come from the model) or "gaussian" (needs sigma).

  • sigma (Any) – Noise standard deviation for noise="gaussian".

  • jacobian (np.ndarray | None) – Analytic Jacobian (P, K, T); computed numerically when None.

  • rel_step (float) – Finite-difference steps (see numerical_jacobian()).

  • abs_step (float) – Finite-difference steps (see numerical_jacobian()).

Return type:

ndarray

fit_free_lifetime_maps(decay, irf, laser_period, mask=None, background_per_bin=0.0)[source]#

All fit-free lifetime maps for an image cube, ready for CVPlot / FactorAnalysis: {"tau_mean_arrival", "tau_mean_arrival_naive", "tau_phasor", "tau_phasor_bin_corrected"}, each (H, W) with NaN outside mask.

irf may be a shared (T,) IRF or the (H, W, T) cube, in which case the mean IRF over mask is used.

Parameters:
Return type:

dict[str, ndarray]

fit_lifetimes(decays, irf, freq, method, max_iter=1000, n_jobs=-1)[source]#

Fit every decay of decays (M, T) with a mono-exponential model using FLICPUProcessor and the fitter/estimator of method ("NLSF" or "MLE"). Returns the fitted lifetimes (M,); failed fits are NaN.

Parameters:
Return type:

ndarray

gate_times(n_gates, laser_period)[source]#

Gate start times k * laser_period / n_gates in ns (endpoint excluded).

Parameters:
Return type:

ndarray

gated_bin_probabilities(tau, irf, laser_period)[source]#

Expected per-gate photon distribution of PhotonCountSimulator for lifetime(s) tau: the wrapped, gate-integrated exponential (wrapped_gate_exponential()) circularly convolved with the IRF.

Parameters:
  • tau (float | np.ndarray) – Lifetime(s) in ns; any shape S.

  • irf (np.ndarray) – IRF of shape (T,) (shared) or S + (T,) (one per lifetime); normalised to unit sum here.

  • laser_period (float) – Laser repetition period in ns; the T gates cover [0, laser_period).

Returns:

Array of shape S + (T,) whose last axis sums to 1.

Return type:

np.ndarray

gated_decay_model(params, irf, laser_period)[source]#

Expected counts per gate mu_k = N * p_k(tau) + b for parameter vectors params (P, 3) ordered as PARAMS (N, tau, b); irf is (T,) or (P, T). This is the model handed to the generic fisher_crlb.fisher_information().

Parameters:
Return type:

ndarray

gaussian_fisher_information(sigma, jacobian)[source]#

Fisher matrices (P, K, K) for independent Gaussian channels with known noise standard deviation sigma (scalar, (T,) or (P, T)).

Parameters:
Return type:

ndarray

mean_arrival_lifetime(decay, irf, laser_period, background_per_bin=0.0, tau_grid=None)[source]#

Lifetime whose exact gated-model first moment equals the measured mean arrival time (method of moments with the wrap-around / gate / IRF model).

Parameters:
  • decay (np.ndarray) – Photon histograms, time axis last.

  • irf (np.ndarray) – Shared IRF (T,).

  • laser_period (float) – Laser repetition period in ns.

  • background_per_bin (float) – Expected background counts per gate, subtracted before the moment.

  • tau_grid (np.ndarray | None) – Lifetimes (ns) of the lookup table; defaults to 2000 log-spaced values in [0.01, 5 * laser_period].

Returns:

Lifetime per decay (ns); NaN for empty decays or means outside the table.

Return type:

np.ndarray

mean_arrival_time_naive(decay, irf, laser_period, background_per_bin=0.0)[source]#

<t>_decay - <t>_IRF per decay (ns) – the textbook first-moment lifetime, exact only for an unlimited window without wrap-around or gating.

Parameters:
Return type:

ndarray

numerical_jacobian(model, params, rel_step=1e-4, abs_step=1e-8)[source]#

Central-difference Jacobian dmu/dtheta of a batched model.

Parameters:
  • model (Callable) – model(params) with params (P, K) returning channel means (P, T).

  • params (np.ndarray) – Parameter vectors (P, K) (or (K,) for a single one).

  • rel_step (float) – Step for parameter k is max(rel_step * |theta_k|, abs_step).

  • abs_step (float) – Step for parameter k is max(rel_step * |theta_k|, abs_step).

Returns:

Jacobian of shape (P, K, T).

Return type:

np.ndarray

phasor_lifetime(decay, irf, laser_period, background_per_bin=0.0, bin_corrected=False, device='cpu')[source]#

First-harmonic phasor lifetime at the laser repetition frequency, calibrated with the IRF, via PhasorAnalyzer.

Parameters:
  • decay (np.ndarray) – Photon histograms, time axis last.

  • irf (np.ndarray) – Shared IRF (T,).

  • laser_period (float) – Laser repetition period in ns (the phasor frequency is 1 / laser_period).

  • background_per_bin (float) – Expected background counts per gate, subtracted before the phasor.

  • bin_corrected (bool) – False (default): the standard phase lifetime S / (omega * G) from PhasorAnalyzer.compute_lifetime. True: invert the calibrated phasor z = (1 - q) / (1 - q * exp(i*omega*dt)) of the exact wrapped, gated exponential for q = exp(-dt / tau), which removes the gate-width bias.

  • device (Any | None) – Torch device for PhasorAnalyzer (calibration runs in torch).

Returns:

Lifetime per decay (ns), shape decay.shape[:-1].

Return type:

np.ndarray

plot_monte_carlo(df, figsize=(13, 4.5))[source]#

Plot a run_monte_carlo() table: CV vs. photon count (log-log) with the Cramér-Rao bound, and the F-value vs. photon count with the bound and the ideal F = 1.

Parameters:
Return type:

tuple[Any, Any]

poisson_fisher_information(mu, jacobian)[source]#

Fisher matrices (P, K, K) for independent Poisson channels with means mu (P, T) and Jacobian (P, K, T). Channels with mu <= 0 carry no information and are skipped.

Parameters:
Return type:

ndarray

run_monte_carlo(irf, tau, photon_counts, n_repeats=200, methods=('NLSF', 'MLE', 'mean_arrival', 'phasor'), laser_feq=80.0, dcr=0.0, max_iter=1000, n_jobs=-1, seed=None, crb_fit_background=None)[source]#

Monte Carlo precision of each method against photon count, for one fixed lifetime.

For every photon count N in photon_counts, n_repeats decays are simulated, every method estimates the lifetime of every decay, and the spread of the estimates is summarized. The Cramér-Rao bound for the same tau, N, IRF, window and background is added for reference.

Parameters:
  • irf (np.ndarray) – IRF trace (T,).

  • tau (float) – True lifetime (ns) shared by all copies.

  • photon_counts (iterable of float) – Expected signal photons per decay (each >= 10).

  • n_repeats (int) – Copies per photon count.

  • methods (iterable of str) – Any of "NLSF", "MLE", "mean_arrival", "phasor", "phasor_bin_corrected".

  • laser_feq (float) – Laser repetition rate in MHz.

  • dcr (float) – Dark counts per gate (0 disables them).

  • max_iter (int) – Optimizer iteration budget for the fits.

  • n_jobs (int) – Parallel fitting jobs.

  • seed (int | None) – Base seed; photon count i uses seed + i.

  • crb_fit_background (bool | None) – Whether the bound treats the background as unknown (as fits with a free offset do). Defaults to dcr > 0.

Returns:

One row per (method, N): method, n_photons, n_detected (mean total counts), n_valid, tau_true, tau_mean, tau_std, bias, rel_bias, cv (std / mean), F (cv * sqrt(N)), cv_crb, F_crb and efficiency ((cv_crb / cv) ** 2, see fisher_crlb.estimator_efficiency(); 1 means the method reaches the bound).

Return type:

pd.DataFrame

select_parameters(info, indices)[source]#

Fisher sub-matrices for the parameters in indices, i.e. the information when every other parameter is known exactly and not estimated.

Parameters:
Return type:

ndarray

simulate_fixed_lifetime_decays(irf, tau, n_photons, n_repeats, laser_feq=80.0, dcr=0.0, seed=None)[source]#

Simulate n_repeats independent photon histograms of one pixel with lifetime tau and on average n_photons detected signal photons, using PhotonCountSimulator.

Parameters:
  • irf (np.ndarray) – IRF trace (T,) (or a cube, from which the simulator picks one pixel).

  • tau (float) – Fixed lifetime in ns.

  • n_photons (float) – Expected signal photons per decay; must be >= 10 (the simulator needs at least 1000 laser cycles).

  • n_repeats (int) – Number of independent copies.

  • laser_feq (float) – Laser repetition rate in MHz (period 1000 / laser_feq ns).

  • dcr (float) – Dark counts per gate added by the simulator (0 disables them).

  • seed (int | None) – Seeds the simulator engine and NumPy’s global RNG (used for dark counts).

Returns:

(decays, irf_used): decays (n_repeats, T) and the normalized IRF the simulator convolved with (T,).

Return type:

tuple[np.ndarray, np.ndarray]

Modules

crb_precision

Cramér-Rao lower bound on mono-exponential lifetime precision for gated / TCSPC data.

fisher_crlb

Generic Fisher information and Cramér-Rao lower bound (CRLB) tools.

fit_free_lifetime

Fit-free mono-exponential lifetimes from the photon histogram: mean arrival time and phasor lifetime.

monte_carlo_precision

Monte Carlo lifetime precision: repeated simulation of one pixel across photon counts.