Typed pharmacometric model authoring

The pharmacometric DSL executes ordinary Python declarations to build typed symbolic objects. It does not parse arbitrary model text. The output is a validated pymixef.pharmacometrics.dsl.CompiledModel and then the same versioned pymixef.ir.ModelIR used by the rest of PyMixEF.

Declare a model

from pymixef.pharmacometrics import (
    Dose,
    Eta,
    Param,
    State,
    additive,
    covariate,
    d,
    exp,
    model,
    observe,
)

@model(name="one_compartment_population_pk")
def population_pk():
    tvcl = Param.positive("tvcl", init=5.0, unit="L/h")
    volume = Param.positive("volume", init=25.0, unit="L")
    sigma = Param.positive("sigma", init=0.2, unit="mg/L")

    eta_cl = Eta.independent("eta_cl", block="omega_cl", level="subject")
    weight = covariate("weight", unit="kg", reference=70.0)
    central = State("central", unit="mg", initial=0.0)

    clearance = tvcl * (weight / 70.0) ** 0.75 * exp(eta_cl)
    equation = d(central, -(clearance / volume) * central)
    dose = Dose.into(central, amount="AMT", rate="RATE", duration="DUR")
    observation = observe(
        "DV",
        mean=central / volume,
        error=additive(sigma),
    )
    return equation, dose, observation

compiled = population_pk()
print(compiled.explain())

The pymixef.pharmacometrics.dsl.model() decorator returns a pymixef.pharmacometrics.dsl.ModelDefinition. Calling it executes the declaration, collects returned components, validates them, and creates a pymixef.pharmacometrics.dsl.CompiledModel.

The example declares the symbolic equations

\[ CL_i=TVCL\left(\frac{WT_i}{70}\right)^{0.75}e^{\eta_{CL,i}}, \qquad \frac{dA_i}{dt}=-\frac{CL_i}{V}A_i, \qquad f_i=\frac{A_i}{V}. \]

Symbolic building blocks

Object/helper

Role

real(), positive(), bounded()

declared fixed/model parameters and constraints

independent(), correlated()

random effects, covariance block, and hierarchy level

symbol(), covariate()

predictors with role, unit, and reference

State

dynamic state and initial value

d() / derivative()

differential equation for a state

into()

event fields mapped into a state

observe()

endpoint, structural mean, error, and optional censoring

DSL exp(), log(), log1p(), sqrt()

typed symbolic transforms

Expr.format() supports review, Expr.to_dict() supports serialization, and Expr.evaluate() evaluates against an explicit symbol environment.

Constraints and transforms

Parameter declarations preserve support:

  • real → identity transform;

  • positive → log transform;

  • bounded → bounded transform with explicit lower/upper values.

Constraints become pymixef.ir.ParameterIR and pymixef.ir.TransformIR nodes. Units are metadata and are not automatically converted.

Random-effect blocks

An pymixef.pharmacometrics.dsl.Eta names the random effect, block, covariance mode (diagonal or correlated), and level such as subject. The DSL can represent richer blocks than the integrated estimator currently supports; check validation compatibility rather than assuming representability implies estimability.

Validate capability

validation = compiled.validate()
print(validation.valid)
print(validation.dimensions)
print(validation.estimator_compatibility)

for message in validation.messages:
    print(message.to_dict())

Compatibility entries currently distinguish simulation, conditional-mode, FOCEI, and SAEM support. A syntactically valid model can still be incompatible with an estimator.

Compile to ModelIR

ir = compiled.to_ir()
print(ir.schema_version)
print(ir.semantic_hash)
print(ir.to_json(indent=2))

The IR contains typed parameter, random-effect, predictor, state-equation, event, likelihood, transform, and output nodes with explicit dependencies. Round-trip with pymixef.ir.ModelIR.from_json(); semantic equality and hash preservation are testable.

Node counts and a valid dependency graph describe the model contract. They do not establish identifiability, estimator readiness, biological plausibility, or adequate data.

Programmatic construction

pymixef.pharmacometrics.dsl.compiled_model() constructs a pymixef.pharmacometrics.dsl.CompiledModel directly from component sequences. pymixef.pharmacometrics.dsl.ModelDefinition.compile(), validate(), explain(), to_dict(), to_ir(), and declaration_signature expose each stage without requiring an implicit fit.

API and tutorial