Why the Computational Environment Is Part of a QSP Result
A QSP model is not run by equations alone. The executable result also depends on the software that interprets the model, the libraries that implement numerical operations, and settings that affect how those operations are carried out. Two analysts can use the same model code and input values yet obtain different outputs if their computational environments differ. A reproducible workflow therefore records and, where practical, locks that environment alongside the model.
Here, an environment means the software and configuration needed to execute a specified workflow: the language runtime, direct and indirect dependencies, solver libraries, operating-system details, and relevant environment settings. Locking an environment means recording a specific resolved set of these components, rather than relying only on broad version ranges or an analyst’s current workstation. This is a complement to version control for mechanistic model changes: the model’s history and the software used to run it answer different questions.
A list such as “Python 3, SciPy, and the QSP package” is not a lock: the names do not uniquely identify versions or the dependencies those packages require. Even a list of direct dependencies can be insufficient, because each may depend on additional packages. Reproducibility requires the resolved dependency set, not just the visible top level of the software stack.
What to Record and What to Lock
Before creating a lock, define the execution scope: which model implementation and workflow are to be recreated, and on what class of machine or operating system. The record should be specific enough to identify software that can change execution, but it need not attempt to describe every incidental property of an analyst’s computer.
- Runtime: language and exact runtime version, plus relevant runtime implementation.
- Dependencies: direct and transitive package versions, package source where relevant, and integrity hashes when the package manager supports them.
- Numerical libraries: solver and linear-algebra libraries, including relevant native-library or backend versions when they are not fully captured by the package lock.
- Platform: operating system and architecture; include compiler or system-library details if native extensions depend on them.
- Execution settings: relevant environment variables, thread limits, random-number seed policy, and solver configuration used by the run. Recording solver configuration here identifies the execution context; it does not replace scientific justification of that configuration.
The assumptions behind a lock are important. It assumes that the package manager can resolve the declared constraints, that the locked packages remain obtainable, and that the target platform is compatible with the resolution. A lock generated on one platform may not fully specify another platform if packages differ by operating system or architecture. A lock also cannot, by itself, guarantee identical numerical output across different processors, native libraries, or nondeterministic operations.
A useful way to understand dependency resolution is to treat each package version as a choice constrained by all packages that require it. If two packages impose constraints \(C_1\) and \(C_2\) on a shared dependency, a valid version must lie in their intersection:
If that intersection is empty, no version of the shared dependency can satisfy the declared requirements. If it is nonempty, the resolver selects a compatible version, together with compatible versions of the other dependencies. The lock records that particular solution. This is why copying a package list from another analyst is weaker than resolving the full dependency graph in a clean environment: the other analyst’s machine may contain undeclared packages that make an otherwise incomplete setup appear to work.
Worked Example: Resolving a Dependency Mismatch
Suppose a hypothetical QSP workflow uses a package called qsp-solver version 3.2 and qsp-analysis version 1.8. Their declared requirements are: qsp-solver 3.2 requires NumPy at least 1.24 but below 2.0, and SciPy at least 1.10 but below 1.13; qsp-analysis 1.8 requires NumPy at least 1.26 but below 2.0. An analyst’s existing environment has NumPy 2.0.0 and SciPy 1.13.1. The workflow fails while importing the solver extension.
Check the constraints. Both packages require NumPy in a range that includes versions from 1.26 up to, but not including, 2.0. Thus the shared feasible range is \( [1.26,2.0) \). The solver package also requires SciPy in \( [1.10,1.13) \). The installed NumPy 2.0.0 and SciPy 1.13.1 are each outside their respective allowed ranges.
Resolve rather than patch the existing environment. In a clean environment, the package manager could select NumPy 1.26.4 and SciPy 1.12.0. Check each value: \(1.26 \leq 1.26.4 < 2.0\), and \(1.10 \leq 1.12.0 < 1.13\). Both direct packages’ constraints are satisfied. The analyst should also let the resolver check the complete transitive dependency graph; satisfying these two visible ranges alone does not prove every indirect requirement is compatible.
Lock and verify. Record the resolved versions and package integrity information in a lock file, then recreate the environment from that file and run the intended workflow. If the import now succeeds and the run completes, this supports the conclusion that the original mismatch was in the software environment. It does not prove that the model is biologically valid, nor does it establish that all numerical outputs will be bit-for-bit identical on every platform.
From a Lock File to a Recreated Run
A practical process separates environment definition, resolution, and verification. Each step answers a different question: what is required, which compatible versions were selected, and whether another setup can reproduce the execution.
Specify the runtime version and the packages the workflow directly uses. Avoid broad, undocumented assumptions about what happens to be installed globally.
Use a package manager to solve the direct and transitive constraints together. If resolution fails, inspect the incompatible requirements rather than forcing an arbitrary version.
Keep the lock file with the project and record platform, solver-library versions, and relevant settings. Where platform-specific resolutions are needed, state the supported platform rather than implying universal portability.
Build a fresh environment from the recorded resolution, then run the intended workflow. A successful recreation tests whether the environment record is operational rather than merely descriptive.
This process assumes the lock file is authoritative for the intended environment and is not silently replaced by newer package versions during installation. It also assumes the packages can be obtained from the recorded or otherwise controlled sources. A lock file that names exact versions but points to unavailable or changed artifacts may not be enough for long-term recreation; retaining package artifacts or using a suitable container can reduce that risk.
Worked Example: Recording the Environment for a QSP Run
Consider a hypothetical run of a mechanistic cytokine model using a Python implementation. The analyst records Python 3.11.8, the exact resolved package set in a lock file, the SciPy solver-library version, a Linux x86-64 platform, and the environment variable that limits numerical-library threads to one. The simulation workflow uses a fixed random seed of 741 for its stochastic component.
A compact run record would identify the lock-file name and checksum, the platform, the relevant solver-library version, the thread setting, and the seed policy and value. The package versions should come from the resolved lock, not from memory or a manually typed list. Together, these entries let another analyst recreate the declared software stack and check that the intended settings were applied.
Suppose the workflow produces a hypothetical peak cytokine concentration of 18.42 ng/mL on the original machine and 18.43 ng/mL after recreation on a different processor. The absolute difference is \( |18.43-18.42|=0.01\) ng/mL. Relative to the original value, it is \(0.01/18.42 \times 100 \approx 0.0543\%\). This small difference may be consistent with floating-point variation, but its acceptability depends on the model and the purpose of the run. The environment record makes the comparison interpretable; it does not supply a universal tolerance or establish that the difference is negligible for every decision.
Choosing Between Lock Files and Containers
A lock file and a container address overlapping but different parts of the reproducibility problem. A lock file is usually the more direct record of application-level package versions and is convenient for updating dependencies deliberately. A container can capture a broader runtime image, including operating-system libraries and system configuration, but it can be larger and may still depend on host hardware, container runtime, or external resources.
| Approach | What it helps preserve | Important limitation |
|---|---|---|
| Dependency lock file | Resolved application packages and their versions; sometimes artifact hashes | May not capture system libraries, hardware, or all platform-specific details |
| Container image | A packaged runtime and many operating-system-level dependencies | Does not guarantee identical arithmetic across hosts or preserve unavailable external resources automatically |
| Both together | Application-level resolution plus a broader execution image | Requires maintaining, identifying, and retaining both records |
For a workflow with compiled solver extensions or important native-library dependencies, a lock file plus a container may provide a stronger reconstruction path than either alone. The choice should match the risk and intended lifespan of the work. A small exploratory analysis may need less infrastructure than a model intended for long-term reuse, but even a lightweight setup should state its runtime and resolved dependencies.
Worked Example: Deciding Whether a Container Adds Value
An analyst’s QSP package lock records all Python packages, but the model uses a compiled extension that links to a system linear-algebra library. A collaborator recreates the lock on another operating system and installs the same Python package versions, yet the native library differs.
The lock establishes that the application-level package resolution matches, but it does not establish that the linked system library matches. The analyst should record the relevant library and platform details. If the workflow depends on those details and needs to be shared across analysts, a container that includes the compatible system library may reduce ambiguity. The collaborator must still use a compatible host and container runtime, and numerical equality must be assessed from the results rather than assumed.
Diagnostics, Failure Modes, and Limits
Environment problems often appear as installation failures, import errors, missing shared libraries, or output changes after a package update. These symptoms are clues, not diagnoses. Compare the declared constraints, resolved versions, platform, and relevant settings. Rebuild in isolation to distinguish a reproducible dependency problem from accidental packages or configuration left on a workstation.
- Resolution fails: inspect which constraints have no common version. Do not solve the error by removing a constraint without understanding what depends on it.
- Imports fail after installation: check runtime compatibility, compiled-extension compatibility, and native libraries, not only the top-level package version.
- The lock recreates on one platform but not another: determine whether the lock is platform-specific or whether it omits system-level dependencies. Record the supported platform scope.
- Outputs differ despite matching versions: compare relevant settings, random-number handling, thread behavior, and hardware or numerical-library context. Then determine whether the difference matters for the intended use.
A locked environment improves repeatability, but it is not the same as proof of correctness. It does not show that model equations were implemented correctly, that the model is appropriate for a scientific question, or that a predicted effect is robust. Nor does it guarantee bitwise identity: floating-point computations can vary with operation ordering, hardware, or numerical-library behavior. A more practical goal is often a clearly specified environment and results that agree within a justified, context-dependent tolerance.
Common Mistake
A common mistake is to write “all packages are up to date” or list only the direct dependencies and assume another analyst can recreate the run. That reasoning overlooks transitive requirements, exact resolved versions, platform-specific native components, and execution settings. The correction is to save the resolved environment, identify relevant platform and solver-library details, and verify the setup by recreating it in a clean environment. Another mistake is to treat a matching lock file as proof that a model result is scientifically sound. Environment reproducibility supports repeatable execution; model credibility requires separate evidence.
Check Your Understanding
Answer each question, then compare your reasoning with the solutions.
- Why is a list of direct package names not necessarily enough to reproduce a QSP workflow?
- A package requires a numerical library version at least 4.2 but below 5.0, while another requires it at least 4.5 but below 4.9. What is their shared feasible range?
- A lock file recreates successfully, but peak concentrations differ slightly on another processor. Does the lock guarantee bitwise equality? What should be checked?
- When might a container add value beyond a package lock file?
- Does successful recreation of an environment establish that the QSP model is scientifically valid? Explain.
Solutions. (1) Direct packages may depend on additional packages with their own version constraints, and package names alone do not identify exact versions. A resolved lock records the complete selected dependency set. (2) The intersection is \( [4.5,4.9) \): the version must be at least 4.5 and strictly below 4.9. (3) No. Check relevant settings, random-number handling, hardware, and numerical-library context, then assess whether the difference is acceptable for the intended use. (4) A container can capture operating-system-level libraries or other system dependencies that a package lock does not fully specify. (5) No. Recreation demonstrates that the recorded environment can support repeatable execution; it does not establish correct implementation, model validity, or scientific suitability.