pub struct JointPoissonObjective<'a> {
pub model: &'a dyn FitModel,
pub o: &'a [f64],
pub s: &'a [f64],
pub c: f64,
pub active_mask: Option<&'a [bool]>,
}Expand description
Joint-Poisson objective.
Wraps a transmission FitModel (which produces T_i = model.evaluate(θ))
together with the observed open-beam counts O_i, sample counts S_i,
and proton-charge ratio c = Q_s / Q_ob.
The caller is responsible for ensuring o, s, and model.evaluate()
output all have the same length.
Fields§
§model: &'a dyn FitModelTransmission model: evaluate(θ) → T(E).
o: &'a [f64]Open-beam counts per bin.
s: &'a [f64]Sample counts per bin.
c: f64Proton-charge ratio c = Q_s / Q_ob. Must be strictly positive.
active_mask: Option<&'a [bool]>Optional per-bin active mask (SAMMY EMIN/EMAX-equivalent
fit-energy-range restriction). When Some(m), only bins where
m[i] is true contribute to the deviance / gradient / Fisher
information; the model is still evaluated on the full grid so
resolution broadening at the boundaries is correct. When
None, all bins are active (default behaviour).
Length must equal o.len(); the GUI / pipeline dispatch builds
it from the configured [E_min, E_max] against the energy grid.
Implementations§
Source§impl<'a> JointPoissonObjective<'a>
impl<'a> JointPoissonObjective<'a>
Sourcepub fn n_active(&self) -> usize
pub fn n_active(&self) -> usize
Number of active data bins — n_data when no mask is set,
or the count of true entries in active_mask otherwise.
This is the count that should drive deviance-per-dof reporting.
Sourcepub fn profile_lambda(&self, t_i: f64, o_i: f64, s_i: f64) -> f64
pub fn profile_lambda(&self, t_i: f64, o_i: f64, s_i: f64) -> f64
Closed-form profile MLE for the per-bin flux: λ̂ = c·(O+S) / (1+c·T).
Guards: when 1 + c·T ≤ ε, returns 0 to avoid division blow-up.
Sourcepub fn profile_lambda_per_bin(
&self,
t: &[f64],
) -> Result<Vec<f64>, FittingError>
pub fn profile_lambda_per_bin( &self, t: &[f64], ) -> Result<Vec<f64>, FittingError>
Vector form of profile_lambda.
Validates t.len() == o.len() == s.len() and c > 0; returns
FittingError::LengthMismatch / InvalidConfig rather than the
previous .zip() truncate-and-pretend behaviour (which would
silently shrink the output to min(t.len(), o.len(), s.len())).
Sourcepub fn deviance_from_transmission(&self, t: &[f64]) -> Result<f64, FittingError>
pub fn deviance_from_transmission(&self, t: &[f64]) -> Result<f64, FittingError>
Conditional binomial deviance at the given transmission vector.
D = 2 · Σ [ S·ln(S/(Np)) + O·ln(O/(N(1−p))) ] with
p = cT/(1+cT), N = O+S, and x·ln(x/0) → 0.
Near invalid or numerically tiny transmission values, the per-bin
evaluation (binomial_deviance_term) uses t.max(POISSON_EPSILON)
to clamp T away from zero before entering the logarithms and the
1/(1+cT) factor. This avoids singular logs and division-by-zero
but is a piecewise clamp, not a smooth quadratic extrapolation —
D(T) is C⁰ at the clamp boundary, not C¹. In practice this is
adequate because the optimizer’s transmission values come from a
FitModel that keeps T bounded well above POISSON_EPSILON for
physically plausible density / nuisance parameter values.
Sourcepub fn deviance(&self, params: &[f64]) -> Result<f64, FittingError>
pub fn deviance(&self, params: &[f64]) -> Result<f64, FittingError>
Evaluate the deviance at parameter vector θ by calling the model.
Sourcepub fn deviance_gradient_analytical(
&self,
params: &[f64],
free_param_indices: &[usize],
) -> Result<Option<Vec<f64>>, FittingError>
pub fn deviance_gradient_analytical( &self, params: &[f64], free_param_indices: &[usize], ) -> Result<Option<Vec<f64>>, FittingError>
Analytical gradient of the deviance w.r.t. the free parameters.
Returns None if the transmission model does not provide an analytical
Jacobian — callers should fall back to deviance_gradient_fd.
Gradient derivation: with p_i = cT_i/(1+cT_i) and N_i = O_i+S_i,
d D / d T_i = −2 · (S_i − O_i·c·T_i) / (T_i · (1 + c·T_i))
then chain-rule with the transmission Jacobian J_{i,j} = ∂T_i / ∂θ_{f(j)} where f(j) is the j-th free parameter index.
Sourcepub fn fisher_information(
&self,
params: &[f64],
free_param_indices: &[usize],
) -> Result<Option<FlatMatrix>, FittingError>
pub fn fisher_information( &self, params: &[f64], free_param_indices: &[usize], ) -> Result<Option<FlatMatrix>, FittingError>
Fisher information for free parameters (Gauss-Newton curvature of D).
Uses the expected-info form
h_i ≡ ∂² D / ∂ T_i² ≈ 2 · (O_i + S_i) · c / (T_i · (1 + c·T_i)²)
(derived from logit-link binomial Var(S|N) = N p (1−p) and d logit(p) / dT = 1/T, scaled by 2 since D = −2 L). Then
I(θ){j,k} = Σ_i h_i · J{i,j} · J_{i,k}.
Returns None if the transmission model does not provide an analytical
Jacobian.
Sourcepub fn fisher_information_fd(
&self,
params: &mut ParameterSet,
fd_step: f64,
) -> Result<Option<FlatMatrix>, FittingError>
pub fn fisher_information_fd( &self, params: &mut ParameterSet, fd_step: f64, ) -> Result<Option<FlatMatrix>, FittingError>
Finite-difference Fisher information.
Fallback for callers whose transmission model does not implement
FitModel::analytical_jacobian — i.e., when
Self::fisher_information would return None. Builds the
transmission Jacobian column-by-column via central differences and
assembles
I(θ)_{j,k} = Σ_i h_i · J_{i,j} · J_{i,k}
where h_i = ∂² D / ∂ T_i² is the per-bin deviance curvature
2·(O_i + S_i)·c / (T_i·(1 + c·T_i)²) (Fisher-scoring form derived
from binomial logit-link Var(S | N) = N·p·(1−p) with d logit p / dT
= 1/T — see the module-level docstring §Model). Returns Ok(None)
only if the base model evaluation itself fails.
Sourcepub fn deviance_gradient_fd(
&self,
params: &mut ParameterSet,
fd_step: f64,
) -> Result<Vec<f64>, FittingError>
pub fn deviance_gradient_fd( &self, params: &mut ParameterSet, fd_step: f64, ) -> Result<Vec<f64>, FittingError>
Finite-difference gradient of the deviance.
Central differences on each free parameter. Used as a fallback when
the model has no analytical Jacobian. params is a mutable
ParameterSet so we can respect bounds via clamp().
Auto Trait Implementations§
impl<'a> Freeze for JointPoissonObjective<'a>
impl<'a> !RefUnwindSafe for JointPoissonObjective<'a>
impl<'a> !Send for JointPoissonObjective<'a>
impl<'a> !Sync for JointPoissonObjective<'a>
impl<'a> Unpin for JointPoissonObjective<'a>
impl<'a> UnsafeUnpin for JointPoissonObjective<'a>
impl<'a> !UnwindSafe for JointPoissonObjective<'a>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more