PyBNF objective functions (pybnf.objective)

Classes defining various objective functions used for evaluating points in parameter space

class pybnf.objective.AveNormSumOfSquaresObjective(ind_var_rounding=0)[source]

Sum of squares where each point is normalized by the average value of that variable, ((y-y’)/ybar)^2

eval_point(sim_data, exp_data, sim_row, exp_row, col_name)[source]

Calculate the objective function for a single point in the data This evaluation is what differentiates the different objective functions. :param sim_data: The simulation Data object :param exp_data: The experimental Data object :param sim_row: The row number to look at in sim_data :param exp_row: The row number to look at in exp_data :param col_name: The column name to look at (same for the sim_data and the exp_data) :return:

evaluate(sim_data, exp_data, show_warnings=True, data_key=None)[source]
Parameters:
  • sim_data (Data) – A Data object containing simulated data

  • exp_data (Data) – A Data object containing experimental data

  • show_warnings (bool) – If True, print warnings about unused data

  • data_key – the (model, suffix) data key, used to resolve per-series analytic scaling (ADR-0066); None disables scaling.

Returns:

float, value of the objective function, with a lower value indicating a better fit.

class pybnf.objective.ChiSquareObjective(ind_var_rounding=0, overrides=None, noise=None, sigma_source=None, sigma_sources=None)[source]

Gaussian observation noise with sigma read per point from the data’s _SD column. Being fixed, the Gaussian normalizer is parameter-independent and dropped, leaving the chi-square data fit (ADR-0011/0021).

noise = <pybnf.noise.gaussian.Gaussian object>

The default per-observable noise model (applied to every column without an override): a NoiseModel plus its source(s). Subclasses set noise / sigma_source (a single source) as class attributes; neg_bin sets sigma_source per instance (its constant is a config value). A multi-parameter default (a whole-fit student_t line) is passed as sigma_sources (the mapping) to the constructor; _default_sources() reconciles the two (ADR-0058).

class pybnf.objective.ChiSquareObjective_Dynamic(ind_var_rounding=0, overrides=None, noise=None, sigma_source=None, sigma_sources=None)[source]

Gaussian observation noise with sigma a free parameter (sigma__FREE). Being estimated, the Gaussian normalizer +log sigma is retained (ADR-0011).

noise = <pybnf.noise.gaussian.Gaussian object>

The default per-observable noise model (applied to every column without an override): a NoiseModel plus its source(s). Subclasses set noise / sigma_source (a single source) as class attributes; neg_bin sets sigma_source per instance (its constant is a config value). A multi-parameter default (a whole-fit student_t line) is passed as sigma_sources (the mapping) to the constructor; _default_sources() reconciles the two (ADR-0058).

class pybnf.objective.ColumnSummationObjective(ind_var_rounding=0)[source]

Represents a type of objective function in which we perform some kind of summation for one column at a time. The assumption is that the independent variable is the same for each row.

eval_column(sim_data, exp_data, col_name)[source]

Calculate the objective function for a single column in the data This evaluation is what differentiates the different column based objective functions. :param sim_data: The simulation Data object :param exp_data: The experimental Data object :param col_name: The column name to look at (same for the sim_data and the exp_data) :return:

evaluate(sim_data, exp_data, show_warnings=True, data_key=None)[source]
Parameters:
  • sim_data (Data) – A Data object containing simulated data

  • exp_data (Data) – A Data object containing experimental data

  • show_warnings (bool) – If True, print warnings about unused data

  • data_key – unused (analytic scaling is refused for a column-joint objective).

Returns:

float, value of the objective function, with a lower value indicating a better fit.

classmethod from_config(config)[source]

Build this objective from the config dict (ADR-0011). The base takes no constructor args (e.g. direct_pass); subclasses that read config keys override – the uniform construction entry point replacing the registry’s per-objfunc config_args recipe.

class pybnf.objective.ConstraintCounter[source]

An objective function that just counts the numbered of failed constraints Used only in model checking

evaluate(sim_data, exp_data, show_warnings=True, data_key=None)[source]
Parameters:
  • sim_data (Data) – A Data object containing simulated data

  • exp_data (Data) – A Data object containing experimental data

  • show_warnings (bool) – If True, print warnings about unused data

  • data_key – the (model, suffix) data key of this experiment, used to resolve per-series analytic scaling (ADR-0066); None disables scaling (the default).

Returns:

float, value of the objective function, with a lower value indicating a better fit.

evaluate_multiple(sim_data_dict, exp_data_dict, constraints=(), show_warnings=True)[source]

Count the number constraints that are not satisfied by the simulation data. Experimental (quantitative) data is ignored

class pybnf.objective.DirectPassObjective[source]

Passes through the score value directly from the simulated data.

Expects the simulated data to contain a single column ‘score’ with a single row. The experimental data is ignored: the analytical / bring-your-own objective IS the score, so there is nothing to compare against (ADR-0031/0059).

evaluate(sim_data, exp_data, show_warnings=True, data_key=None)[source]
Parameters:
  • sim_data (Data) – A Data object containing simulated data

  • exp_data (Data) – A Data object containing experimental data

  • show_warnings (bool) – If True, print warnings about unused data

  • data_key – the (model, suffix) data key of this experiment, used to resolve per-series analytic scaling (ADR-0066); None disables scaling (the default).

Returns:

float, value of the objective function, with a lower value indicating a better fit.

evaluate_multiple(sim_data_dict, exp_data_dict, pset, constraints=(), show_warnings=True)[source]

Score straight off the model’s ‘score’ column, with no experimental-data pairing (ADR-0031/0059).

The base ObjectiveFunction.evaluate_multiple() only scores a model-output suffix that has a matching experimental-data suffix – the right rule for a per-point objective, but for direct_pass there is no exp column to compare to, so that rule forced an empty placeholder .exp file to exist purely to make the suffix match (the #425 discoverability footgun). This override drops the exp requirement: it sums the score of every output suffix the model emits (each read by evaluate(), which still requires a score column), so a closed-form / analytical target scores with no data file at all. Constraint penalties still apply, unchanged.

class pybnf.objective.InformationCriteria(k, n, log_likelihood, aic, bic, aicc)

AIC/BIC/AICc for a fit, alongside the inputs (k free params, n data points, log_likelihood) so a report can show the derivation. aicc is None when the small-sample correction is undefined (n <= k + 1).

aic

Alias for field number 3

aicc

Alias for field number 5

bic

Alias for field number 4

k

Alias for field number 0

log_likelihood

Alias for field number 2

n

Alias for field number 1

class pybnf.objective.KLLikelihood(ind_var_rounding=0)[source]

The Kullback-Leibler likelihood. It should be more efficient in parameter fitting as numerical experiments suggest

eval_column(sim_data, exp_data, col_name)[source]

Calculate the objective function for a single column in the data This evaluation is what differentiates the different column based objective functions. :param sim_data: The simulation Data object :param exp_data: The experimental Data object :param col_name: The column name to look at (same for the sim_data and the exp_data) :return:

class pybnf.objective.LaplaceObjective(ind_var_rounding=0, overrides=None, noise=None, sigma_source=None, sigma_sources=None)[source]

Laplace observation noise with the scale b a free parameter (b__FREE) – the heavy-tailed, outlier-robust likelihood behind least-absolute-deviation fitting (ADR-0021). Being estimated, the log(2 b) normalizer is retained, which is what keeps the fit from driving b -> inf. PEtab v2’s noiseDistribution = laplace; a fixed-scale Laplace is reachable per observable via read_exp_file / fix_at.

noise = <pybnf.noise.laplace.Laplace object>

The default per-observable noise model (applied to every column without an override): a NoiseModel plus its source(s). Subclasses set noise / sigma_source (a single source) as class attributes; neg_bin sets sigma_source per instance (its constant is a config value). A multi-parameter default (a whole-fit student_t line) is passed as sigma_sources (the mapping) to the constructor; _default_sources() reconciles the two (ADR-0058).

class pybnf.objective.LikelihoodObjective(ind_var_rounding=0, overrides=None, noise=None, sigma_source=None, sigma_sources=None)[source]

A per-point likelihood: a distribution-family NoiseModel scored against the data with its noise parameter drawn from a SigmaSource, summed over points (ADR-0011, ADR-0021). The (family, sigma_source) pair is selected per observable – the class-level default applies to every column, overridden for named observables by self.overrides. The five legacy likelihood objfuncs are exactly this object with a fixed default pair (chi_sq = Gaussian x the _SD data column, chi_sq_dynamic = Gaussian x a free sigma, neg_bin = NegBinomial x a constant, …); per-observable selection is the new capability they all inherit.

The whole family/normalizer choice collapses to one per-point expression: the family’s data_fit always, plus each noise parameter’s param_normalizers entry iff that parameter’s source is estimated. That single rule reproduces every legacy objfunc – the data-fit-vs-nll split that used to be hard-coded per subclass now follows from whether each noise parameter is estimated (ADR-0011), and a two-parameter family (student_t’s sigma + df, ADR-0058) gates each independently.

aligned_prediction_data(sim_data_dict, exp_data_dict, pset)[source]

Aligned (prediction, observation, variance) for the Kalman proposal (ADR-0067 Stage 3; overrides ObjectiveFunction.aligned_prediction_data()).

Walks the same scored points as evaluate_pointwise() in the same deterministic order (_pointwise_suffix’s model -> suffix -> row -> sorted(compare_cols) walk, skipping only NaN observations), so prediction (the model output f(theta)), observation (the data d), and variance (the Gaussian sigma**2, the R diagonal) are index-aligned and stable across draws. Returns three np.ndarray s, or None unless every scored point is an ordinary additive-error Gaussian observable (linear scale): the Kalman gain’s R = diag(variance) is the Gaussian measurement covariance, so a log-scale (lognormal) or non-Gaussian (Laplace / Student-t / count) family has no R to form and the proposal is unsupported for that objective.

data_fit_grad_point(sim_data, exp_data, sim_row, exp_row, col_name)[source]

d(data_fit)/d(prediction) for one scored point – the scalar-gradient seam an asymmetric family with no clean least-squares residual routes through (layer G, #454/#385): Laplace (its L1 data fit |z|/b is a cusp, sqrt(2 data_fit) ~ sqrt|z| has infinite slope at z=0, so it is inherently not least-squares, #459) and the count family (NegBinomial). A Gaussian or Student-t observable – whose data fit does reformulate as a smooth half-square – is routed through residual_point instead (has_least_squares_residual() decides).

The assembly accumulates sum_i w_i * data_fit_grad_point_i * d(prediction_i)/d theta directly into the scalar gradient and flags the result not least_squares_exact – the universal form a quasi-Newton (L-BFGS) step consumes. Reads the prediction through the same _prediction seam and the noise through the same _noise_values mapping eval_point uses, so it differentiates exactly the data fit PyBNF scores; the per-family slope is d_data_fit_d_prediction(). The per-point bootstrap weight is applied by the assembly, not here – mirroring residual_point.

eval_point(sim_data, exp_data, sim_row, exp_row, col_name)[source]

Calculate the objective function for a single point in the data This evaluation is what differentiates the different objective functions. :param sim_data: The simulation Data object :param exp_data: The experimental Data object :param sim_row: The row number to look at in sim_data :param exp_row: The row number to look at in exp_data :param col_name: The column name to look at (same for the sim_data and the exp_data) :return:

evaluate_pointwise(sim_data_dict, exp_data_dict, pset)[source]

The per-observation log-likelihoods this fit’s noise model assigns the data under pset – the pointwise decomposition LOO/WAIC consume (ADR-0056).

Returns (ids, values): ids a list of stable per-point labels (model/suffix/observable@indvar=value), values the matching np.ndarray of genuine, unweighted log-densities. Each value is the family’s complete normalized log_density (the constant Gaussian/Jacobian terms eval_point drops restored), NOT -eval_point: a predictive density needs the full normalization, and PyBNF’s per-point weights are a fitting device, not part of the generative model. So these do not sum to -score; they are the honest densities az.loo / az.waic need.

Mirrors evaluate_multiple’s per-evaluation setup – the {name: value} map a FreeParameterSigma reads, and the measurement-model observation layer – so the densities are scored against exactly the data evaluate saw. The emitted observation set is fixed by the experimental data (a point is skipped only on a NaN observation, never on anything draw-dependent), so every draw yields the same ids in the same order – the rectangular chain x draw x obs array the bridge needs.

classmethod from_config(config)[source]

Build this objective from the config dict (ADR-0011). The base takes no constructor args (e.g. direct_pass); subclasses that read config keys override – the uniform construction entry point replacing the registry’s per-objfunc config_args recipe.

has_least_squares_residual(col_name)[source]

Whether this observable contributes an exact least-squares residual/Jacobian (layer G, #454/#385; #459). True for a family whose data fit reformulates as a smooth half-square – the Gaussian (data_fit = 1/2 rho**2) and, #459, the Student-t (the exact square-root-loss residual sign(z) sqrt(2 data_fit), smooth through z=0) – on any scale and either location, so the assembly routes it through residual_point and a fixed-scale fit is least_squares_exact (#386’s LM/TRF consumes the residual form). False for an asymmetric family with no clean residual – Laplace (its L1 data fit |z|/b gives the cusp sqrt|z|, infinite slope at z=0) and the count family – whose gradient goes through the scalar data_fit_grad_point path, so the result is not least_squares_exact.

is_linear_gaussian()[source]

True iff the default family and every per-observable override are a linear-scale Gaussian (ADR-0067 Stage 3; overrides ObjectiveFunction.is_linear_gaussian()).

The config-time twin of aligned_prediction_data()’s per-point gate (isinstance(family, Gaussian) and family.additive_on.ln_base == 0.0), checked against the declared families rather than a walked point set so proposal = kalman can reject a non-Gaussian objfunc before the run starts. Both chi_sq (fixed _SD sigma) and chi_sq_dynamic (an estimated free sigma) pass – the family is Gaussian either way, and the Kalman R = diag(sigma**2) is formed per accept from the current state’s sourced sigma. A log-scale (lognormal) or non-Gaussian (laplace / student_t / neg_bin) default or override makes it False.

location_fisher_point(sim_data, exp_data, sim_row, exp_row, col_name)[source]

The expected per-point Fisher information of the location kappa – the Gauss-Newton curvature the EFIM trust-region Hessian weights d(prediction)/d(theta) by (fit_type = gntr, #481). Read through the same _prediction / _noise_values seams as residual_point / data_fit_grad_point, so it is the curvature of exactly the loss PyBNF scores.

A residual-bearing family (Gaussian, Student-t) has kappa == (d rho/d pred)**2 identically – the assembly’s residual-Jacobian already carries it – so this returns that square directly (no new family math). This is the correct location Fisher for a prediction-dependent σ too (ADR-0080): the location block (1/σ²)outer(s_i, s_i) uses the partial ∂ρ/∂pred = 1/σ holding σ fixed, exactly as here – the scale’s own θ-dependence rides the noise block (noise_fisher_point()), not this one. A non-residual family (Laplace, count) delegates to the family’s location_fisher(). Any configuration the family refuses (a MEDIAN-centered count, …) raises GradientNotSupported, so the fit falls back to the scalar-gradient (L-BFGS-B) path (a MEAN-on-log estimated scale, whose location↔scale coupling this block does not model, is refused by noise_fisher_point() – reached in the same assembly point loop – before the Hessian is returned).

noise = None

The default per-observable noise model (applied to every column without an override): a NoiseModel plus its source(s). Subclasses set noise / sigma_source (a single source) as class attributes; neg_bin sets sigma_source per instance (its constant is a config value). A multi-parameter default (a whole-fit student_t line) is passed as sigma_sources (the mapping) to the constructor; _default_sources() reconciles the two (ADR-0058).

noise_fisher_point(sim_data, exp_data, sim_row, exp_row, col_name, raw_sens, index)[source]

The per-point expected Fisher noise block of the EFIM Hessian (#481; ADR-0080) – the full (len(index), len(index)) matrix Σ_p I_scale_p · outer(g_i^p, g_i^p) over the estimated noise parameters p, or None for a fixed-noise point. The curvature twin of noise_grad_point() (which returns the noise gradient vector the same way): each estimated parameter’s expected Fisher I_scale_p (noise_param_fisher()) weights the outer product of its scale sensitivity g_i^p = ∂p/∂θ (sigma_sensitivity()) – the same ∂σ/∂θ vector noise_grad_point() chains ∂L/∂p through.

For a FreeParameterSigma (a bare free scale) g_i^p is the unit vector e_p (the free parameter is the noise parameter, model-unbound), so I_scale_p · outer(e_p, e_p) places I_scale_p on that parameter’s own coordinate – byte- identical to the historical diagonal noise block (#481). For a PredictionFormulaSigma (sigma = sigma_abs + sigma_rel*y, ADR-0075) g_i^p also carries entries on the model-parameter columns (its ∂σ/∂prediction chains the same forward sensitivity the residual rides), so outer(g_i^p, g_i^p) produces the genuine location↔scale coupling off the diagonal – the block ADR-0079 deferred and ADR-0080 assembles. Both terms are PSD (I_scale >= 0), so the block is PSD by construction.

Empty (None) for a fixed-noise point (no estimated parameter, no normalizer, no scale column). A coupled corner the family refuses – a MEAN-on-log estimated scale (a nonzero location↔scale cross-Fisher the moment offset introduces), the count family’s free dispersion, the Student-t 2-parameter block – raises GradientNotSupported from noise_param_fisher(), so the fit falls back to the scalar-gradient (L-BFGS-B) path. raw_sens/index are the assembly’s forward- sensitivity accessor and free-parameter column map, and exp_data/exp_row locate the scored experimental point (read only by a per-measurement composite σ, #505) – all threaded to sigma_sensitivity exactly as noise_grad_point() threads them (unused by a bare free scale).

noise_grad_point(sim_data, exp_data, sim_row, exp_row, col_name, raw_sens, index)[source]

The estimated noise scale’s contribution to ∂(loss)/∂θ at one scored point – the full (len(index),) native-space vector Σ_p (∂L/∂p)·(∂p/∂θ) over the estimated noise parameters p, or None for a fixed-noise point (layer D/G, #451/#454/#385; ADR-0079).

None for a fixed-noise point (chi_sq’s data column, a constant, a relative scale): no noise parameter is estimated, so the loss carries no normalizer and the noise scale adds no gradient column. Otherwise each estimated noise parameter contributes ∂(data_fit + its own normalizer)/∂θ, the column that keeps a free scale from running away. The per-parameter loss derivative ∂L/∂p is the family’s own (d_nll_d_noise_params()):

  • Gaussian’s single sigma: (1 - rho**2)/sigma (the +log sigma normalizer, layer D) – rho the additive-space residual, so it composes with a log scale (#452); on the linear scale it is the historical closed form byte-for-byte.

  • Laplace’s scale b: -|residual|/b**2 + 1/b (the log(2 b) normalizer).

  • Student-t’s sigma and df – the first multi-parameter estimated-noise gradient (ADR-0058); each independently sourced and gated.

Each ∂p/∂θ comes from the source’s sigma_sensitivity(): a FreeParameterSigma is the unit vector e_p (the scale is the free parameter, factor 1, model-unbound), so its column reduces to the historical scalar derivative on that parameter’s coordinate – byte-identical to the pre-ADR-0079 dict form. A PredictionFormulaSigma (sigma = sigma_abs + sigma_rel*y, ADR-0075) instead returns the σ formula’s chain rule: ∂σ/∂coeff on each coefficient’s column plus ∂σ/∂prediction chained through the same raw_sens forward sensitivity the residual rides – so a prediction-dependent scale perturbs the model-parameter columns too (and the fit is not least_squares_exact). A PSet-only composite scale (FormulaSigma / PerMeasurementFormulaSigma, #505) returns the coefficient-only chain rule (no prediction term), the per-measurement variant also binding a row-varying token from exp_data/exp_row (a numeric token contributes nothing, a parameter id lands ∂σ/∂token on that parameter’s column).

Read through the same _prediction / _noise_values seams eval_point and residual_point use, so the noise gradient differentiates exactly the loss PyBNF reports. Lives on the scalar-gradient (L-BFGS) path only: a normalizer like +log sigma is not a sum of squares, so the trust-region residual form cannot represent it. The assembly adds this straight to the scalar gradient (and flags the residual form’s least-squares model inexact); the per-point bootstrap weight is applied there, not here – mirroring residual_point. raw_sens/index are the assembly’s forward-sensitivity accessor and free-parameter column map, and exp_data/exp_row locate the scored point on the experimental data – all threaded to sigma_sensitivity (a free-parameter scale reads none of them; a prediction σ reads raw_sens; a per-measurement composite σ reads exp_data/exp_row).

overrides

{col_name: (NoiseModel, {param: SigmaSource})} overriding the default per observable; empty -> every column uses the default, byte-identical to the pre-#410 single global objfunc.

required_free_noise_params()[source]

The free-parameter names this objective’s noise sources estimate (default spec + every override) – what _load_variables checks have matching FreeParameters (ADR-0021). Iterates each spec’s per-parameter source mapping (one source for the single-parameter families, two for student_t; ADR-0058).

residual_point(sim_data, exp_data, sim_row, exp_row, col_name)[source]

The least-squares residual r and its derivative d r/d pred for one scored point (#449/#385/#452/#459).

The residual a family whose data fit reformulates as a smooth half-square carries – the Gaussian (data_fit = 1/2 rho**2, so r is the standardized residual rho) and, layer-G follow-up #459, the Student-t (the exact square-root-loss residual r = sign(z) sqrt(2 data_fit), smooth through z=0). The assembly routes such a column here, an asymmetric family with no clean residual (Laplace, whose L1 data fit is a cusp; the count family) through data_fit_grad_point() instead (has_least_squares_residual() decides). The pure family math lives on the noise model (residual() / d_residual_d_prediction()); this seam sources the prediction and the noise parameters and delegates, so r is defined for the prediction as the MEDIAN or the MEAN (layer G, #454), additive on any scale (LINEAR, or a log scale: LOG10 / LN, #452). The residual lives in the additive (log) space, mu = forward(pred) - location offset, so its derivative picks up the scale’s chain factor forward'(pred) (1 on the linear scale, 1/(pred*ln10) for log10). The location offset is prediction-independent, so a MEDIAN (any scale) / a MEAN on the linear scale collapses to the historical Gaussian (pred-obs)/sigma / 1/sigma byte-for-byte; a MEAN on a log scale subtracts the family’s moment correction. Either way the family’s residual satisfies 1/2 r**2 == data_fit, so this returns the same data-fit loss eval_point does in residual form, and r * d_residual_d_prediction == d_data_fit_d_prediction, so the residual-Jacobian reproduces the objective gradient.

sigma (and Student-t’s df) may be fixed (chi_sq / lognormal / student_t) or an estimated free parameter (chi_sq_dynamic); the residual is identical either way – an estimated scale’s own gradient column (its retained +log sigma / df-block normalizer, which is not a square) is emitted separately by noise_grad_point() (layer D/G, #451/#454). Any other per-observable configuration raises GradientNotSupported (the capability gate); later layers extend it.

Out-of-support points (log scale): match ``evaluate``. On a log scale forward = log10 requires pred > 0 and obs > 0. PyBNF does not raise on a non-positive point here; it propagates a non-finite r (so the assembled gradient goes non-finite), exactly as evaluate returns a non-finite score for the same point rather than raising – the optimizer’s existing signal to reject the step. Raising would diverge the gradient from the scalar objective it differentiates (a point the objective merely scores inf/nan would instead abort assembly); propagating keeps the two paths consistent.

Reads the prediction through the same _prediction seam and the noise through the same _noise_values mapping eval_point uses, so the residual is the exact derivative of the loss PyBNF reports – same noise-weighting, same column selection. The per-point bootstrap weight is not applied here (the assembly folds sqrt(weight) into both r and the Jacobian, exactly as evaluate multiplies eval_point by the weight).

set_default_location(location)[source]

Apply a whole-fit default location interpretation (the global noise_location key, ADR-0024; the modern-edition median default, ADR-0031) to the class-default noise model – the one used for every observable without a per-observable noise_model override (those already carry their own location). Mirrors the per-observable location field via _apply_location; every family supports both mean and median (the neg_bin median is the per-point CDF inversion of issue #419).

supports_pointwise_log_likelihood = True

A per-point likelihood can be left-one-out, so it supports LOO/WAIC (ADR-0056).

class pybnf.objective.LogNormalObjective(ind_var_rounding=0, overrides=None, noise=None, sigma_source=None, sigma_sources=None)[source]

Lognormal observation noise: the Gaussian family additive on the log10 scale with the prediction interpreted as the median (ADR-0011, ADR-0022). sigma (the log10-scale standard deviation) comes from the data’s _SD column exactly as in chi_sq – being fixed, the Gaussian normalizer and the lognormal Jacobian are parameter-independent and dropped, leaving the log10-space squared residual (log10 sim - log10 exp)^2 / (2 sigma^2). log10 (not natural log) so sigma is a log10-scale standard deviation consistently with every other PyBNF log (ADR-0022); the natural-log lognormal density is Gaussian(LN, MEDIAN). Only the noise family differs from chi_sq – the seam proof that the scale and location axes compose. Observations and predictions must be positive (the lognormal support).

noise = <pybnf.noise.gaussian.Gaussian object>

The default per-observable noise model (applied to every column without an override): a NoiseModel plus its source(s). Subclasses set noise / sigma_source (a single source) as class attributes; neg_bin sets sigma_source per instance (its constant is a config value). A multi-parameter default (a whole-fit student_t line) is passed as sigma_sources (the mapping) to the constructor; _default_sources() reconciles the two (ADR-0058).

class pybnf.objective.NegBinLikelihood(r, ind_var_rounding=0, overrides=None)[source]

Negative-binomial likelihood with the dispersion r a fixed config constant (neg_bin_r). Fixed and self-normalizing, so the objective is the NegBinomial data fit (ADR-0011).

classmethod from_config(config)[source]

Build this objective from the config dict (ADR-0011). The base takes no constructor args (e.g. direct_pass); subclasses that read config keys override – the uniform construction entry point replacing the registry’s per-objfunc config_args recipe.

noise = <pybnf.noise.negative_binomial.NegBinomial object>

The default per-observable noise model (applied to every column without an override): a NoiseModel plus its source(s). Subclasses set noise / sigma_source (a single source) as class attributes; neg_bin sets sigma_source per instance (its constant is a config value). A multi-parameter default (a whole-fit student_t line) is passed as sigma_sources (the mapping) to the constructor; _default_sources() reconciles the two (ADR-0058).

class pybnf.objective.NegBinLikelihood_Dynamic(ind_var_rounding=0, overrides=None, noise=None, sigma_source=None, sigma_sources=None)[source]

Negative-binomial likelihood with the dispersion r a free parameter (r__FREE). NegBinomial’s PMF is self-normalizing, so nll == data_fit; the source is estimated but its normalizer is 0 (ADR-0011).

noise = <pybnf.noise.negative_binomial.NegBinomial object>

The default per-observable noise model (applied to every column without an override): a NoiseModel plus its source(s). Subclasses set noise / sigma_source (a single source) as class attributes; neg_bin sets sigma_source per instance (its constant is a config value). A multi-parameter default (a whole-fit student_t line) is passed as sigma_sources (the mapping) to the constructor; _default_sources() reconciles the two (ADR-0058).

class pybnf.objective.NormSumOfSquaresObjective(ind_var_rounding=0)[source]

Sum of squares where each point is normalized by the y value at that point, ((y-y’)/y)^2

eval_point(sim_data, exp_data, sim_row, exp_row, col_name)[source]

Calculate the objective function for a single point in the data This evaluation is what differentiates the different objective functions. :param sim_data: The simulation Data object :param exp_data: The experimental Data object :param sim_row: The row number to look at in sim_data :param exp_row: The row number to look at in exp_data :param col_name: The column name to look at (same for the sim_data and the exp_data) :return:

class pybnf.objective.ObjectiveCalculator(objective, exp_data_dict, constraints)[source]

Wrapper for all of the objects needed for the workers to calculate the objective function value. Contains the objective function, exp_data_dict, and constraint tuple

evaluate_objective(sim_data_dict, pset, show_warnings=True)[source]

Evaluate the objective using the input simulation data and the info contained in this object :param sim_data_dict: Dictionary of the form {modelname: {suffix1: Data1}} containing the simulated data objects :param show_warnings: If True, print warnings about unused data :type show_warnings: bool :return:

class pybnf.objective.ObjectiveFunction[source]

Abstract class representing an objective function Subclasses customize how the objective value is calculated from the quantitative exp data The base class includes all the support we need for constraints.

aligned_prediction_data(sim_data_dict, exp_data_dict, pset)[source]

The aligned (prediction, observation, variance) vectors for one parameter set – the raw per-point model output f(theta), the matching observed data d, and the Gaussian measurement variance sigma**2 – or None when the objective is not a per-point Gaussian likelihood (ADR-0067 Stage 3, DREAM(KZS)).

This is the seam the Kalman-inspired proposal reads: it needs the model output vector (not the scalar score) to build the parameter/output cross-covariance, plus the observed data and the measurement covariance R = diag(variance) for the Kalman gain. The base is a no-op (a least-squares / distance / pass-through objective has no residual/output vector, and a non-Gaussian likelihood has no variance R); LikelihoodObjective overrides it for the Gaussian family.

data_fit_grad_point(sim_data, exp_data, sim_row, exp_row, col_name)[source]

d(data_fit)/d(prediction) for one scored point – the scalar-gradient seam for an asymmetric family whose data fit is not a sum of squares (layer G, #454/#385). The base raises GradientNotSupported (it is reached only when has_least_squares_residual() is False, which the base never reports). Only LikelihoodObjective overrides it.

evaluate(sim_data, exp_data, show_warnings=True, data_key=None)[source]
Parameters:
  • sim_data (Data) – A Data object containing simulated data

  • exp_data (Data) – A Data object containing experimental data

  • show_warnings (bool) – If True, print warnings about unused data

  • data_key – the (model, suffix) data key of this experiment, used to resolve per-series analytic scaling (ADR-0066); None disables scaling (the default).

Returns:

float, value of the objective function, with a lower value indicating a better fit.

evaluate_multiple(sim_data_dict, exp_data_dict, pset, constraints=(), show_warnings=True)[source]

Compute the value of the objective function on several data sets, and return the total. Optionally may pass an iterable of ConstraintSets whose penalties will be added to the total :param sim_data_dict: Dictionary of the form {modelname: {suffix1: Data1}} containing the simulated data objects :type sim_data_dict: dict :param exp_data_dict: Dictionary of the form {modelname: {suffix1: Data1}} containing experimental Data objects :type exp_data_dict: dict :param constraints: Iterable of ConstraintSet objects containing the constraints that we should evaluate using the simulated data :type constraints: Iterable of ConstraintSet :param show_warnings: If True, print warnings about unused data :type show_warnings: bool :return:

evaluate_pointwise(sim_data_dict, exp_data_dict, pset)[source]

Per-observation log-likelihoods for one parameter set – the pointwise decomposition LOO/WAIC consume, as opposed to evaluate_multiple’s scalar total (ADR-0056). Returns (ids, values)ids a list of stable per-point labels, values the matching np.ndarray of genuine, unweighted log-densities – or None for an objective that is not a per-point likelihood. The base is that no-op; LikelihoodObjective overrides it (and flips supports_pointwise_log_likelihood).

classmethod from_config(config)[source]

Build this objective from the config dict (ADR-0011). The base takes no constructor args (e.g. direct_pass); subclasses that read config keys override – the uniform construction entry point replacing the registry’s per-objfunc config_args recipe.

has_least_squares_residual(col_name)[source]

Whether one scored column contributes an exact least-squares residual/Jacobian (a Gaussian, whose data_fit is 1/2 rho**2) vs only the scalar data-fit gradient an asymmetric family carries (Laplace / Student-t, layer G of #385). The assembly routes a True column through residual_point() (the residual-Jacobian form) and a False one through data_fit_grad_point() (the scalar accumulator).

True on the base, where residual_point() itself refuses an unsupported objective – so a non-likelihood / unsupported objective still raises GradientNotSupported there rather than being silently routed elsewhere. Only LikelihoodObjective overrides this to distinguish its families.

is_linear_gaussian()[source]

Whether every observable this objective scores uses a linear-scale Gaussian noise family – the config-time precondition for the Kalman-inspired DREAM proposal (ADR-0067 Stage 3, DREAM(KZS)), which forms R = diag(sigma**2) from a Gaussian measurement covariance and so has no R for a least-squares / distance / pass-through objective or a non-Gaussian / log-scale likelihood. The base is False (only a linear-Gaussian LikelihoodObjective overrides it); it lets proposal = kalman reject an unsupported objfunc before the run starts, rather than silently degenerating to de because aligned_prediction_data() returns None at every accept.

location_fisher_point(sim_data, exp_data, sim_row, exp_row, col_name)[source]

The expected per-point Fisher information of the location kappa for one scored point – the Gauss-Newton curvature the EFIM trust-region path (fit_type = gntr, #481) weights d(prediction)/d(theta) by (H = sum_i w_i * kappa_i * outer(s_i, s_i)). The twin of residual_point() on the curvature side; the base raises GradientNotSupported, since only a per-point likelihood defines a location Fisher (a non-likelihood / unsupported objective already refuses at residual_point). Only LikelihoodObjective overrides it.

measurement = None

The measurement-model observation layer (ADR-0036): a MeasurementLayer that materializes each expression observableFormula’s column into the simulated data (using the PSet) before the by-name objective match below. None (the default for every job without an expression measurement model) is an exact no-op, so the objective is byte-identical to its pre-#407 behavior. New-era PEtab/SBML (and the retrofitted new-era BNGL-expression) path attaches a populated layer in config.py.

noise_fisher_point(sim_data, exp_data, sim_row, exp_row, col_name, raw_sens, index)[source]

The per-point expected Fisher noise block of the EFIM Hessian – the full (len(index), len(index)) matrix Σ_p I_scale_p · outer(∂p/∂θ, ∂p/∂θ) over the estimated noise parameters, or None for a fixed-noise point (#481; ADR-0080). The twin of noise_grad_point() on the curvature side. None on the base (a non-likelihood objective estimates no noise parameter); only LikelihoodObjective overrides it.

noise_grad_point(sim_data, exp_data, sim_row, exp_row, col_name, raw_sens, index)[source]

The estimated noise scale’s contribution to ∂(loss)/∂θ at one scored point – a full (len(index),) native-space vector, or None for a fixed-noise point (layer D, #451; ADR-0079).

None on the base: a non-likelihood objective (least-squares, distance, pass-through) estimates no noise parameter, so its scale contributes no gradient column. Only LikelihoodObjective overrides it – the noise twin of the per-point residual_point seam (whose base refuses, since the assembly reaches that first and gates the whole configuration there).

required_free_noise_params()[source]

The free-parameter names this objective requires the fit to declare for its estimated noise sources – empty unless it is a likelihood with a free-parameter noise source (ADR-0021). _load_variables checks these against the declared free parameters, generalizing the old per-objfunc sigma__FREE / r__FREE hard-coded checks.

residual_point(sim_data, exp_data, sim_row, exp_row, col_name)[source]

The standardized residual rho and its derivative d rho/d prediction for one scored point – the per-point seam the gradient path differentiates (#449/#385).

rho and d rho/d pred are defined so that this point’s contribution to the loss is 1/2 rho**2 and d(loss)/d pred = rho * d rho/d pred – the residual form scipy.least_squares minimizes, agreeing with the scalar gradient by construction. The assembly multiplies d rho/d pred by the forward sensitivity d pred/d theta (routed through #448’s chain-rule factor) to build the residual-Jacobian column.

The base raises GradientNotSupported: only a per-point likelihood (LikelihoodObjective) defines a residual, and only for the cut-1 Gaussian/MEDIAN configuration (the noise scale – linear or log – and a fixed-or-single-free sigma are admitted; #452/#451). Every other objective (least-squares, distance, pass-through) and every later layer raises until its support lands – so #386 can fall back to a gradient-free step.

supports_pointwise_log_likelihood = False

Whether this objective can decompose its value into genuine per-observation log-likelihoods (ADR-0056) – the precondition for LOO/WAIC. True only for a per-point likelihood (LikelihoodObjective); a least-squares / distance / pass-through objective has no normalized density to leave one out of, so it stays False and evaluate_pointwise returns None (the LOO/WAIC no-op gate).

class pybnf.objective.SumOfDiffsObjective(ind_var_rounding=0)[source]
eval_point(sim_data, exp_data, sim_row, exp_row, col_name)[source]

Calculate the objective function for a single point in the data This evaluation is what differentiates the different objective functions. :param sim_data: The simulation Data object :param exp_data: The experimental Data object :param sim_row: The row number to look at in sim_data :param exp_row: The row number to look at in exp_data :param col_name: The column name to look at (same for the sim_data and the exp_data) :return:

class pybnf.objective.SumOfSquaresObjective(ind_var_rounding=0)[source]
eval_point(sim_data, exp_data, sim_row, exp_row, col_name)[source]

Calculate the objective function for a single point in the data This evaluation is what differentiates the different objective functions. :param sim_data: The simulation Data object :param exp_data: The experimental Data object :param sim_row: The row number to look at in sim_data :param exp_row: The row number to look at in exp_data :param col_name: The column name to look at (same for the sim_data and the exp_data) :return:

class pybnf.objective.SummationObjective(ind_var_rounding=0)[source]

Represents a type of objective function in which we perform some kind of summation over all available experimental data points individually.

eval_point(sim_data, exp_data, sim_row, exp_row, col_name)[source]

Calculate the objective function for a single point in the data This evaluation is what differentiates the different objective functions. :param sim_data: The simulation Data object :param exp_data: The experimental Data object :param sim_row: The row number to look at in sim_data :param exp_row: The row number to look at in exp_data :param col_name: The column name to look at (same for the sim_data and the exp_data) :return:

evaluate(sim_data, exp_data, show_warnings=True, data_key=None)[source]
Parameters:
  • sim_data (Data) – A Data object containing simulated data

  • exp_data (Data) – A Data object containing experimental data

  • show_warnings (bool) – If True, print warnings about unused data

  • data_key – the (model, suffix) data key, used to resolve per-series analytic scaling (ADR-0066); None disables scaling.

Returns:

float, value of the objective function, with a lower value indicating a better fit.

classmethod from_config(config)[source]

Build this objective from the config dict (ADR-0011). The base takes no constructor args (e.g. direct_pass); subclasses that read config keys override – the uniform construction entry point replacing the registry’s per-objfunc config_args recipe.

prediction_sensitivity(sim_data, sim_row, col_name, exp_data, exp_row, raw_sens, index)[source]

∂pred/∂θ (a native-space (n_param,) vector) for one matched point – the gradient seam mirroring _prediction() (#453/#385), branch for branch.

_prediction forms the scored value from the simulation through up to one trajectory transform; this returns that value’s parameter sensitivity, so the assembly differentiates exactly what it scores. raw_sens(column_name, row) is the sensitivity of a sim column as ``_prediction`` reads it – the #447 tensor with any Data-level normalization (ADR-0053) already folded in (so a normalized fit threads the normalizer’s own derivative, and the transforms below compose on top of it exactly as scoring applies normalize -> _prediction). index maps a free-parameter name to its column.

  • Plain (the default, byte-identical to the raw observable): raw_sens(col, row).

  • Cumulative -> incident (ADR-0051): the prediction is raw_i - raw_{i-1} (row 0 keeps its raw value), so ∂pred_i/∂θ = raw_sens(col, i) - raw_sens(col, i-1) – a difference of sensitivity rows (row 0 keeps the plain value).

  • Per-measurement scale/offset (ADR-0045): a general PEtab formula, so its sensitivity is the formula’s symbolic gradient chained through each referenced column’s raw_sens plus any estimated placeholder/parameter it names (prediction_sensitivity()).

A per-measurement model takes priority and the two are mutually exclusive, exactly as in _prediction(); both default off, so a plain job returns the raw observable’s sensitivity unchanged.

Analytic per-series scaling (ADR-0066, #479) has a deliberately deferred gradient: its profiled c* depends on θ through the whole series (an implicit-function derivative not yet implemented), so a scaled column raises GradientNotSupported here and the fit falls back to a gradient-free step (ADR-0475). The much simpler floor gradient is deferred the same way in pybnf.gradient.assembly.

class pybnf.objective.WassersteinObjective(ind_var_rounding=0)[source]

The 1-Wasserstein (earth-mover) distance between the simulated and experimental profiles, summed over observables (ADR-0031). Like kl it is a column-joint objective – it compares the shape of a whole column at once, not point by point – but where kl is the multinomial cross-entropy (a likelihood), this is a geometric distance: the minimal total mass-displacement to morph one normalized profile into the other. The two span the profile_objective family’s ends (statistical vs geometric), which is why the family is an objective, not a model (ADR-0031).

Each column is normalized to a probability distribution over its row index, and the 1-Wasserstein distance on the (unit-spaced) index line is sum_i |CDF_sim_i - CDF_exp_i| – the integral of the absolute CDF gap, which is the closed form of the 1-D earth-mover distance. The configurable support and spacing (placing the profile on real coordinates rather than the index) are the deferred profile_objective value grammar (ADR-0031); the default here is unit index spacing. A non-positive or negative simulated column cannot be normalized, so it scores inf (the worst fit), mirroring kl’s degenerate-profile guard. Oracle: scipy.stats.wasserstein_distance over the index with the normalized columns as weights.

eval_column(sim_data, exp_data, col_name)[source]

Calculate the objective function for a single column in the data This evaluation is what differentiates the different column based objective functions. :param sim_data: The simulation Data object :param exp_data: The experimental Data object :param col_name: The column name to look at (same for the sim_data and the exp_data) :return:

pybnf.objective.build_named_objective(config, token)[source]

Build the objective for a modern objective = <token> key (ADR-0031): the bare score passthrough, or a legacy token desugared to the per-point engine. A profile-objective token (kl / wasserstein) is redirected to its own key rather than silently desugared, keeping one home per objective.

pybnf.objective.build_profile_objective(config, token)[source]

Build the objective for a modern profile_objective = <token> key – a column-joint (shape-comparison) objective (ADR-0031). A per-point token is redirected to objective / noise_model.

pybnf.objective.build_whole_fit_noise_objective(config)[source]

Build the objective for a whole-fit noise_model = <family>, ... line – the no-observable ('noise_model', None) key (ADR-0031). The named per-point noise model becomes the fit-wide default, with per-observable noise_model lines overriding it.

pybnf.objective.information_criteria(log_likelihood, k, n)[source]

AIC / BIC / AICc from a fit’s maximized log-likelihood (pure arithmetic).

Parameters:
  • log_likelihood – the maximized log-likelihood lnL at the best fit – the sum of the noise model’s complete per-point log_density, NOT the (constant-dropped) -score the optimizer minimizes.

  • k – number of free (fitted) parameters, INCLUDING any estimated noise parameter – a fitted sigma / b / r is itself a free parameter (ADR-0021), so counting the declared free parameters already includes it.

  • n – number of scored data points.

Returns:

an InformationCriteria. aicc is None when n <= k + 1 (the correction 2k(k+1)/(n-k-1) has a non-positive denominator there and is conventionally reported as undefined).

AIC = 2k - 2 lnL; BIC = k ln(n) - 2 lnL; AICc = AIC + 2k(k+1)/(n-k-1).

pybnf.objective.likelihood_information_criteria(objective, sim_data_dict, exp_data_dict, pset, k)[source]

Information criteria for objective scored at pset, or None.

Returns None – rather than a misleading number – when the objective is not a per-point likelihood (sos / sod / norm_sos / kl / wasserstein / direct_pass: no normalized density, so no AIC; the same gate LOO/WAIC use), when no point is scored, or when the log-likelihood is not finite. Otherwise the log-likelihood is the sum of the objective’s complete per-point log_density over the scored data (evaluate_pointwise, ADR-0056), n is that point count, and k the free-parameter count.