mixle.analysis.carcinogenic_risk module¶
Carcinogenic-risk models: linear no-threshold slope-factor / unit-risk (EPA-IRIS convention).
The regulatory-toxicology answer to “how much extra cancer risk does this exposure add over a lifetime”: a chronic dose is pushed through a chemical-specific potency coefficient under the linear no-threshold (LNT) low-dose assumption.
SlopeFactor– a chemical’s potency: an oral cancer slope factor (per mg/kg-day) and/or an inhalation unit risk (per ug/m3), as published by EPA IRIS (or supplied by the caller/knowledge layer – this module carries no slope-factor database, see Non-goals).
excess_lifetime_cancer_risk()–risk = LADD * oral_csf(oral route) orrisk = conc * inhalation_iur(inhalation route), the EPA-IRIS linear low-dose form; falls back to the exact1 - exp(-dose * slope)once the linear approximation would push risk above ~0.01. Exposure may be a scalar, a plain sample array, or an IC-1Posterior– in the last case the posterior’s ownderived_quantity()pushforward is used so the honesty flagprior_dominatedpropagates untouched from the exposure posterior.
radon_wlm_risk()– the BEIR-VI radon working-level-month (WLM) coefficient,risk = wlm * risk_per_wlm.
Both return an IC-8-style DerivedQuantity (samples + credible interval + prior_dominated),
matching the construction used across the rest of mixle.analysis health/risk models. The concrete
carrier here is RiskQuantity, which satisfies the frozen
mixle.reason.posterior_protocol.DerivedQuantity protocol structurally (each health/risk module
in mixle.analysis mints its own small concrete carrier of the same shape rather than sharing one
class, so modules stay independent siblings – see analysis/health_risk.py’s equivalent).
- class SlopeFactor(oral_csf=None, inhalation_iur=None, sigma_log=0.0, source='EPA-IRIS')[source]
Bases:
objectA chemical’s cancer potency, EPA-IRIS style.
- oral_csf
oral cancer slope factor, (mg/kg-day)^-1.
Noneif the chemical has no established oral potency.- Type:
float | None
- inhalation_iur
inhalation unit risk, (ug/m3)^-1.
Noneif no established inhalation potency.- Type:
float | None
- sigma_log
log-scale standard deviation of a multiplicative log-normal uncertainty band around the point-estimate slope factor (0.0 = treat the slope factor as fixed). Must be finite and non-negative – validated at construction (a negative or NaN value has no valid meaning as a standard deviation, so it is rejected here rather than silently treated as zero/fixed uncertainty downstream).
- Type:
- source
provenance tag for the potency values (default the EPA IRIS database, the regulatory-toxicology standard; caller-supplied values should override this).
- Type:
- class RiskQuantity(samples, prior_dominated=False)[source]
Bases:
objectA pushforward risk distribution: draws + credible interval + the
prior_dominatedflag.Structurally satisfies the IC-1
DerivedQuantityprotocol (samples,prior_dominated,credible_interval) and additionally exposesmean– the point estimate callers actually read off first.Construction validates
samples: non-empty, finite, and a valid probability-like value in[0, 1](the range theexcess_lifetime_cancer_risk/radon_wlm_riskLNT pushforward always produces from validated non-negative inputs – see_lnt_risk()). This is defense-in-depth: invalid state cannot silently flow downstream to a caller even if some upstream pushforward fails to validate its own inputs.- property mean: float
the sample-mean excess risk.
- Type:
Point estimate
- credible_interval(level=0.9)[source]
Central
levelcredible interval of the risk samples (e.g.level=0.9-> 5%/95%).levelgoes through the sharedmixle.analysisinterval contract (validated_level()), so every IC-1 carrier in the package rejects the same out-of-range levels the same way (MXR-080-1580).
- excess_lifetime_cancer_risk(exposure, sf, *, route='oral', n=2000, rng=None)[source]
Excess lifetime cancer risk under the linear no-threshold model (DR-ALG K7).
route="oral"treatsexposureas an LADD (lifetime average daily dose, mg/kg-day) and multiplies throughSlopeFactor.oral_csf;route="inhalation"treatsexposureas an air concentration (ug/m3) multiplied throughSlopeFactor.inhalation_iur.- Parameters:
exposure (Posterior | ndarray | float) – the lifetime-average dose/concentration. An IC-1
Posterior(itsderived_quantitypushforward is used, soprior_dominatedpropagates from the exposure posterior), a plain array of exposure samples (already representing exposure uncertainty), or a single deterministic scalar. Every exposure draw must reduce to a single value per sample – one route/chemical/time-point/receptor already selected or aggregated by the caller – and must be finite and non-negative; this is enforced uniformly for all three forms, including aPosterior’s own draws (there is no honest way to guess, on the caller’s behalf, which route/chemical/time point/receptor a genuinely multi-dimensional draw was meant to represent, so that case is rejected with a clear error rather than silently collapsed to its first column).sf (SlopeFactor) – the chemical’s
SlopeFactor. Its potency coefficient forroute, and itssigma_log, must be finite and non-negative (sigma_logis also validated atSlopeFactorconstruction).route (str) –
"oral"or"inhalation".n (int) – number of posterior draws to take when
exposureis aPosterior, or the number of slope-factor draws to take whenexposureis a bare scalar andsf.sigma_log > 0. Must be a positive exact integer. On thePosteriorpath this is now a hard delivery receipt (MXR-080-1900): a posterior that returns fewer thanndraws is an error, not a quietly smaller risk distribution.rng (Generator | None) – numpy random Generator (a fresh default one is created if omitted).
- Returns:
A
DerivedQuantityof excess lifetime cancer risk (samples + CI +prior_dominated).- Return type:
DerivedQuantity
A Boolean
exposure(scalar ordtype=boolarray) is rejected rather than coerced:Trueis a flag, not a dose, andfloat(True) == 1.0made one indistinguishable from a measured 1 mg/kg-day LADD once it was inside the LNT product (MXR-080-1900).
- radon_wlm_risk(wlm, *, risk_per_wlm=5.38e-4, n=2000, rng=None)[source]
Radon lung-cancer risk from cumulative working-level-months (BEIR-VI linear coefficient).
risk = wlm * risk_per_wlmfor small products (the BEIR-VI committee’s linear excess-relative-risk coefficient per WLM of cumulative radon-progeny exposure, default5.38e-4per WLM), saturating via the same1 - exp(-x)low-dose-linear-no-threshold form_lnt_risk()uses once that product would exceed ~0.01 – without it, cumulative exposures large enough to be realistic over a working lifetime push the bare linear form above 1, which is not a valid probability. Bothwlmandrisk_per_wlmmust be finite and non-negative (exposure and potency are never negative).- Parameters:
wlm (Posterior | ndarray | float) – cumulative working-level-months. An IC-1
Posteriorover exposure (its ownderived_quantitypushforward is used, soprior_dominatedpropagates from the exposure posterior), a plain array of exposure samples (already representing exposure uncertainty), or a single deterministic scalar. Each draw must reduce to one value per sample, exactly asexcess_lifetime_cancer_risk()requires – a(n, d>1)matrix mixes route/receptor/time axes this function has no honest way to collapse.risk_per_wlm (float) – BEIR-VI risk coefficient per WLM.
n (int) – number of posterior draws to take when
wlmis aPosterior. Must be a positive exact integer. Ignored for the array/scalar paths, which carry their own sample count.rng (Generator | None) – numpy random Generator used to draw from a
Posterior(a fresh default one is created if omitted). Unused for the array/scalar paths, which are deterministic.
- Returns:
A
DerivedQuantityof radon-attributable lung-cancer risk.- Return type:
DerivedQuantity
n/rngwere previously accepted and never used at all, on any path – a negative, fractional or Boolean draw count was silently taken and discarded (MXR-080-1574). They now drive thePosteriorpath they were reserved for, andnis validated on every path so an impossible request is rejected rather than ignored. On that pathnis additionally a delivery receipt: a posterior returning fewer thanndraws raises instead of yielding a thinner risk distribution that reads like the full one (MXR-080-1900). A Booleanwlmis rejected for the same reason a Boolean dose is –float(True) == 1.0is not one working-level-month.