mixle.analysis.developmental_risk module

Teratogenic / reproductive / developmental risk: benchmark-dose (BMD/BMDL) analysis (K8, work-plan §7-K).

Fits a quantal log-logistic (or Hill) dose-response curve to a developmental-toxicity cohort (dose, n_affected out of n_total per dose group) by maximum likelihood, following the EPA BMDS convention: the benchmark dose (BMD) is the dose giving a specified benchmark response (default 10% extra risk over background); the BMDL is a one-sided lower confidence bound on the BMD. rfd_exceedance divides the BMDL by an uncertainty factor to get a reference dose (RfD), then pushes an exposure Posterior (IC-1) through the RfD threshold into an IC-8 DerivedQuantity.

class BMDResult(bmd, bmdl, bmr, model, dof, status='ok', bmd_se=nan, _coef=<factory>)[source]

Bases: object

A fitted benchmark-dose analysis: the BMD, its lower confidence bound (BMDL), and fit metadata.

status is one of:

  • "ok": the curve fit converged, the BMD was found and bracketed, and the BMDL lower bound was established. bmd and bmdl are real doses.

  • "unidentifiable": the curve fit itself did not converge, or the fitted curve never reaches the benchmark target within a bounded search (flat, wrong-signed, or requiring an implausibly large dose). bmd and bmdl are nan – never a search boundary or other placeholder dressed up as a real dose.

  • "bmdl_unavailable": the BMD itself was identified (bmd is real), but the BMDL computation did not converge to a valid bound. bmdl is nan.

bmd_se is the delta-method standard error of the BMD (nan unless status == "ok"); bmdl = bmd - z * bmd_se clipped at 0, z the one-sided normal quantile for the requested confidence level.

Parameters:
property converged: bool

True only when both the BMD and the BMDL were genuinely established.

benchmark_dose(dose, n_affected, n_total, *, bmr=0.10, model='loglogistic', risk='extra', ci_level=0.95)[source]

Fit a quantal dose-response and report the benchmark dose (BMD) and its lower bound (BMDL).

dose/n_affected/n_total are per-dose-group arrays (n_affected <= n_total). The curve is fit by maximum likelihood (DR-ALG K8); the BMD solves for the dose giving bmr extra (or added) risk over the fitted background rate. The BMDL is the one-sided ci_level lower confidence bound on the BMD by the delta method: the gradient of the BMD with respect to the fitted coefficients is obtained by implicit differentiation of the BMD-defining equation, the coefficient covariance by inverting the observed Fisher information at the MLE, and Var(BMD) by propagating one through the other (grad @ Cov @ grad); BMDL = BMD - z * SE(BMD) with z the one-sided normal quantile for ci_level, clipped at 0 (dose cannot be negative). See BMDResult.status for what happens when any step of that chain fails to converge.

Parameters:
Return type:

BMDResult

rfd_exceedance(exposure, bmd, *, uf=100.0, n=2000, rng=None)[source]

P(exposure > RfD) as an IC-8 DerivedQuantity, where RfD = BMDL / uf (EPA convention).

exposure may be an IC-1 Posterior (the pushforward runs through its own derived_quantity so prior_dominated propagates), an array of exposure draws (resampled to n if its length differs), or a bare scalar (a degenerate point mass).

uf must be finite and strictly positive, n a positive exact sample count, and exposure draws finite and nonnegative – all rejected up front, before any division or resampling. bmd.bmdl must also be finite: a BMDResult with status != "ok" (see BMDResult) has no real BMDL to divide by an uncertainty factor, and a NaN silently compared with draws > nan would quietly evaluate to all-False – a fabricated-looking “0% exceedance” result – rather than surfacing the underlying unidentifiability. The finite/nonnegative exposure check applies uniformly to a Posterior’s own draws too, not only to a plain array/scalar: it lives inside fn below (the pushforward handed to Posterior.derived_quantity), not in a separate top-level check on the raw exposure – otherwise a mis-specified exposure posterior could emit a negative or NaN draw that flowed straight through draws > rfd (silently False for NaN) into a confident-looking “not exceeding” result with no warning.

Parameters:
  • exposure (Posterior | np.ndarray)

  • bmd (BMDResult)

  • uf (float)

  • n (int)

  • rng (np.random.Generator | None)

Return type:

DerivedQuantity