Closed-form PK and observation error

Closed-form helpers calculate concentrations for linear one- and two-compartment models from supplied parameters. They are useful for simulation, unit tests, and cross-checking event-aware ODE implementations.

One-compartment routes

import numpy as np
from pymixef.pharmacometrics import (
    one_compartment_infusion,
    one_compartment_iv_bolus,
    one_compartment_oral,
)

time = np.linspace(0.0, 24.0, 97)

bolus = one_compartment_iv_bolus(
    time, dose=100.0, clearance=5.0, volume=20.0
)
infusion = one_compartment_infusion(
    time,
    dose=100.0,
    duration=2.0,
    clearance=5.0,
    volume=20.0,
)
oral = one_compartment_oral(
    time,
    dose=100.0,
    clearance=5.0,
    volume=20.0,
    absorption_rate=1.2,
)

Bolus accepts bioavailability and lag. Infusion accepts a consistent dose/rate/duration declaration plus start time. Oral accepts first-order absorption, bioavailability, and lag.

With \(k=CL/V\), the one-compartment bolus and first-order oral solutions are

\[ C_\mathrm{bolus}(t)=\frac{FD}{V}e^{-k(t-t_\mathrm{lag})}, \]
\[ C_\mathrm{oral}(t) =\frac{FDk_a}{V(k_a-k)} \left[e^{-k(t-t_\mathrm{lag})}-e^{-k_a(t-t_\mathrm{lag})}\right], \qquad t\ge t_\mathrm{lag}. \]

pymixef.pharmacometrics.pk.OneCompartmentPK stores the structural parameters for an object-oriented form. pymixef.pharmacometrics.pk.one_compartment_bolus() and pymixef.pharmacometrics.pk.one_compartment_iv_infusion() are aliases.

Two-compartment routes

pymixef.pharmacometrics.pk.two_compartment_iv_bolus(), pymixef.pharmacometrics.pk.two_compartment_infusion(), and pymixef.pharmacometrics.pk.two_compartment_oral() accept clearance, central volume, intercompartmental clearance, and peripheral volume. two_compartment_rates returns the derived micro/rate constants k10, k12, k21, alpha, and beta.

The microconstants and hybrid exponents satisfy

\[ k_{10}=\frac{CL}{V_c},\qquad k_{12}=\frac{Q}{V_c},\qquad k_{21}=\frac{Q}{V_p}, \]
\[ \alpha,\beta =\frac12\left[ k_{10}+k_{12}+k_{21} \pm\sqrt{(k_{10}+k_{12}+k_{21})^2-4k_{10}k_{21}} \right]. \]
from pymixef.pharmacometrics import two_compartment_rates

rates = two_compartment_rates(
    clearance=5.0,
    central_volume=20.0,
    intercompartmental_clearance=3.0,
    peripheral_volume=35.0,
)
print(rates)

pymixef.pharmacometrics.pk.TwoCompartmentPK bundles the four structural parameters. Bolus/infusion aliases are indexed in the API alias page.

Parameter and unit contracts

  • clearances, volumes, doses, absorption rates, and required durations/rates are validated for their mathematical domains;

  • scalar or array-like time is accepted;

  • units are not converted—time, rate, dose, volume, and clearance must be mutually consistent;

  • functions return structural concentration, not noisy observations;

  • a same-package closed-form/ODE agreement test is regression evidence, not an independent implementation comparison.

Invalid parameters raise pymixef.pharmacometrics.pk.PKValidationError.

Observation-error models

Let \(f\) be structural prediction. Error objects define conditional variance and likelihood behavior:

Factory/class

Variance form

additive() / AdditiveError

\(\sigma_a^2\)

proportional() / ProportionalError

\((\sigma_p f)^2\)

power() / PowerError

$\sigma_p^2

combined() / CombinedError

$\sigma_a^2+\sigma_p^2

lognormal() / LogNormalError

error on log concentration scale

from pymixef.pharmacometrics import combined

error = combined(0.10, 0.15)
variance = error.variance(bolus)

The parameters are supplied values; calling variance does not estimate them. Error objects can also compute log likelihood and generate random observations through their shared protocol.

Censoring helpers

pymixef.pharmacometrics.pk.left_censored_loglikelihood(), pymixef.pharmacometrics.pk.right_censored_loglikelihood(), and pymixef.pharmacometrics.pk.interval_censored_loglikelihood() combine a prediction with an pymixef.pharmacometrics.pk.ObservationError and censoring limits. They use CDF/survival/interval probability rather than replacing censored observations with a fixed fraction of the limit.

from pymixef.pharmacometrics import left_censored_loglikelihood

log_likelihood = left_censored_loglikelihood(
    limit=[0.1, 0.1],
    prediction=[0.08, 0.15],
    error=error,
)

API and tutorial