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-truthmaps["tau_map"]).n_photons_map (
np.ndarray) – Expected signal photons per pixel, shape(H, W)(e.g. the simulator’smaps["photon_count_map"], or the decay sum minusT * 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 totau_map > 0.background_per_bin (float) – See
crb_lifetime_cv().fit_background (bool) – See
crb_lifetime_cv().
- Returns:
(H, W)ideal CV map; multiply bysqrt(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.
- 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_highedges on photon_map (the same photon map given toCVPlot), perclusterwhen the table has clusters. Within a bin, the per-pixel CRB variances are averaged, socv_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 ofCVPlot.compute(columnscluster,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 inCVPlot(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 inCVPlot.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-truthphoton_count_mapto 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_crbandF_crb(cv_crb * sqrt(bin_center), directly comparable with a measuredcv * 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
Nandtau(plus the background level when fit_background is True, which is what NLSF / MLE fits with a free offsetv_shiftdo). 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 shapeS + (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’sdcr.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 <= 0or 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^-1per batch entry. A (near-)singular Fisher matrix – an unidentifiable parameter – falls back to the pseudo-inverse.
- crlb_cv(info, value, index)[source]#
Smallest achievable coefficient of variation
std / valueof parameter index.
- crlb_std(info, index)[source]#
Smallest achievable standard deviation of parameter index, per batch entry.
- 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).
- 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).
- 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.
- 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 fornoise="gaussian".jacobian (
np.ndarray | None) – Analytic Jacobian(P, K, T); computed numerically when None.rel_step (
float) – Finite-difference steps (seenumerical_jacobian()).abs_step (
float) – Finite-difference steps (seenumerical_jacobian()).
- Return type:
- 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.
- 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 usingFLICPUProcessorand the fitter/estimator of method ("NLSF"or"MLE"). Returns the fitted lifetimes(M,); failed fits are NaN.
- gate_times(n_gates, laser_period)[source]#
Gate start times
k * laser_period / n_gatesin ns (endpoint excluded).
- gated_bin_probabilities(tau, irf, laser_period)[source]#
Expected per-gate photon distribution of
PhotonCountSimulatorfor 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 shapeS.irf (
np.ndarray) – IRF of shape(T,)(shared) orS + (T,)(one per lifetime); normalised to unit sum here.laser_period (
float) – Laser repetition period in ns; theTgates 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) + bfor parameter vectorsparams(P, 3)ordered asPARAMS(N,tau,b); irf is(T,)or(P, T). This is the model handed to the genericfisher_crlb.fisher_information().
- 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)).
- 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>_IRFper decay (ns) – the textbook first-moment lifetime, exact only for an unlimited window without wrap-around or gating.
- numerical_jacobian(model, params, rel_step=1e-4, abs_step=1e-8)[source]#
Central-difference Jacobian
dmu/dthetaof 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 parameterkismax(rel_step * |theta_k|, abs_step).abs_step (
float) – Step for parameterkismax(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 is1 / laser_period).background_per_bin (
float) – Expected background counts per gate, subtracted before the phasor.bin_corrected (
bool) – False (default): the standard phase lifetimeS / (omega * G)fromPhasorAnalyzer.compute_lifetime. True: invert the calibrated phasorz = (1 - q) / (1 - q * exp(i*omega*dt))of the exact wrapped, gated exponential forq = exp(-dt / tau), which removes the gate-width bias.device (
Any | None) – Torch device forPhasorAnalyzer(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 idealF = 1.
- 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 withmu <= 0carry no information and are skipped.
- 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
Nin 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 sametau,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 (
iterableoffloat) – Expected signal photons per decay (each >= 10).n_repeats (
int) – Copies per photon count.methods (
iterableofstr) – 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 countiusesseed + i.crb_fit_background (
bool | None) – Whether the bound treats the background as unknown (as fits with a free offset do). Defaults todcr > 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_crbandefficiency((cv_crb / cv) ** 2, seefisher_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.
- 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 (period1000 / laser_feqns).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
Cramér-Rao lower bound on mono-exponential lifetime precision for gated / TCSPC data. |
|
Generic Fisher information and Cramér-Rao lower bound (CRLB) tools. |
|
Fit-free mono-exponential lifetimes from the photon histogram: mean arrival time and phasor lifetime. |
|
Monte Carlo lifetime precision: repeated simulation of one pixel across photon counts. |