equilibrator-pathway-core
Max-min driving force (MDF) and enzyme cost minimization (ECM) for fully specified pathway models, with no compound database.
This is the lower layer of equilibrator-pathway. It analyses model SBtab files that already carry everything the optimisation needs -- stoichiometry, concentration bounds, fluxes and standard Gibbs energies -- and it never predicts or looks anything up. That is what lets it install in a few megabytes of Python on top of numpy, including in the browser via Pyodide.
| this package | equilibrator-pathway | |
|---|---|---|
| MDF, ECM on a model SBtab | yes | yes (re-exported) |
| network SBtabs, formula search, identifier resolution | -- | yes |
| dG'0 prediction (Component Contribution) | -- | yes |
| dG'0 uncertainty, MDMC | -- | yes |
pint Q_ public API |
-- | yes |
| compound database, sqlalchemy, pint | never | yes |
Usage
pip install equilibrator-pathway-core
A model SBtab names its analysis in the algorithm option of its
Configuration table. load_model reads the file, applies any Configuration
edits, and returns the document, the model, the algorithm and a list of the
edits it made:
import numpy as np
from equilibrator_pathway_core import mdf_analysis
from equilibrator_pathway_core.ecm_model import EnzymeCostModel
from equilibrator_pathway_core.model import load_model
from equilibrator_pathway_core.solution import EcmSolutionReport, MdfSolutionReport
with open("model.tsv") as f:
document, model, algorithm, _ = load_model(f.read(), "model.tsv")
if algorithm == "MDF":
result = mdf_analysis(
model.S,
model.standard_dg_primes, # kJ/mol
np.diag(model.I_dir), # the flux directions
model.ln_conc_mu,
model.ln_conc_sigma,
model.ln_conc_confidence,
)
report = MdfSolutionReport(
model,
result.score,
result.ln_conc,
result.reaction_prices,
result.compound_prices,
)
print(f"MDF = {result.score:.2f} kJ/mol")
else:
ecm = EnzymeCostModel.from_sbtab(document, model)
score, ln_conc = ecm.optimize_ecm()
report = EcmSolutionReport(model, ecm.ecf, score, ln_conc)
print(f"enzyme cost = {score:.4g}")
with open("report.tsv", "w") as f:
f.write(report.to_sbtab().to_str())
The report is the SBtab eQuilibrator's pathway page offers for download, byte
for byte. mdf_analysis takes plain arrays, so it also runs on a model built
some other way; load_model(text, filename, overrides={"version": "2"}) edits
the Configuration before anything is read (see configuration.OPTION_SCHEMA
for the editable options).
Errors in the model file raise ModelError (a ValueError); a missing table
or column raises sbtabpy's SBtabError, and an unsupported unit
units.UnitError (also a ValueError). A solver failure raises
SolverFailure; an infeasible model (no concentrations within the bounds let
every reaction run in the direction of its flux) raises a plain
RuntimeError.
For the network-only SBtabs of eQuilibrator's "Build pathway model" step, or to predict dG'0 with Component Contribution, use equilibrator-pathway.
Units
Everything here works on plain floats in one canonical set of units -- M,
kJ/mol, K, 1/s, Da -- documented in equilibrator_pathway_core.constants.
Units are attached at the boundary, by equilibrator-pathway's pint adapter or by
the browser client's string parser.
Staying small
Importing this package must not pull in the equilibrator database stack. That is
tested directly, in a fresh interpreter, by tests/test_isolation.py, which
fails if equilibrator_api, equilibrator_cache, component_contribution,
sqlalchemy, pint or matplotlib appear in sys.modules. A dependency added
to pyproject.toml without the code that needs it is equally unwelcome.
How this reader differs from eQuilibrator's
eQuilibrator's server reads model SBtabs with equilibrator-api's
StoichiometricModel (via equilibrator-pathway), which resolves every compound
against the compound database. This package reads the same files on its own.
The two agree on the example models to the last bit -- the golden files in the
ECM webapp repository hold them to it -- but they are separate code, and on
other files they can differ. The known differences, checked on 2026-09-24
against equilibrator-api 0.8 by feeding both readers the same broken models:
Standard Gibbs energies. Both take dG'0 from the Thermodynamics table
(reaction gibbs energy or equilibrium constant rows). Without that table the
server predicts dG'0 with Component Contribution; this package cannot, so it
falls back on equilibrium constant rows in the Parameter table, and refuses
the model if there are none. The server never reads energies from the
Parameter table.
Water. Water is held at activity 1 and left out of the physiological dG'
correction, so misidentifying it shifts every dG' involving it. Both readers
honour a Compound table IsWater column first (a column that marks nothing
means "no water"). Without it, the server asks the compound database, which
knows water under 61 accessions; this package matches a short list of
accessions (model.WATER_ACCESSIONS) and then the names in model.WATER_NAMES.
Tests on both sides pin this.
The physiological dG' correction (every reactant at 1 mM). The server
computes it from each compound's phase, as the database records it: liquid
water and solid sulfur contribute nothing, and a compound in the gas phase
would count at 1 mbar rather than 1 mM (O2, CO2 and the other gases default to
the aqueous phase, so this needs a model that asks for the gas). This package
counts every compound at 1 mM except water and compounds with a
RedoxPotential. So a model with elemental sulfur gets a different correction;
how the server treats RedoxPotential compounds here was not compared.
Units. The server parses units with pint, which accepts anything pint
understands. This package accepts a fixed list per dimension (see units.py),
matched case-insensitively -- so mM and MM both mean millimolar -- and
raises UnitError for anything else, e.g. mol/m^3.
Missing concentration bounds. A compound with no row in the
ConcentrationConstraint table gets the server's default bounds (1 µM to
10 mM) silently; this package refuses the model and names the compound.
A Thermodynamics table without a Compound column cannot be read by the
server (KeyError: 'Compound'), although reaction energies never use that
column. This package reads it.
Error types. For the same mistakes the server raises a mix --
ValueError for a missing table, AssertionError for a missing Flux table,
unknown compounds or reactions, inverted bounds or a standard concentration
other than 1 M, and pyparsing's ParseException for a reaction formula with no
arrow. This package raises ModelError for all of these, and SBtabError for
a missing table, with messages that mostly match the server's word for word.
Status
In place:
| module | what | needs sbtabpy |
|---|---|---|
constants.py |
R, T, standard concentrations | -- |
ecm.py |
the enzyme cost function and ECM | -- |
mdf.py |
the MDF linear program | -- |
errors.py |
ModelError, SolverFailure, ConfigurationError |
-- |
units.py |
unit strings to canonical floats | -- |
model.py |
model SBtab to arrays: S, bounds, fluxes, dG'0, water | yes |
ecm_model.py |
an ECM model's Parameter table | yes |
solution.py |
result tables and the SBtab report | yes |
configuration.py |
the editable Configuration options, their schema, and applying edits | yes |
Both equilibrator-pathway and the browser client run MDF and ECM through this
package; the browser also reads models (model.load_model), edits their
Configuration (configuration.py) and writes reports with it.
The SBtab modules use sbtabpy directly, and need sbtabpy 1.1.1 or later.
1.1.0 was the first release with no required dependencies (earlier ones
declared pandas, pyarrow, python-libsbml and openpyxl, which its parser never
imports); 1.1.1 added the dictionary rows, attribute defaults and pandas-free
SBtabTable.from_rows that replaced the core's own adapter module.
tests/test_isolation.py still forbids all four: nothing installs them by
default now, but the test is what guarantees the core never imports them, and
so stays loadable in the browser.
The extraction plan is in
enzyme-cost-minimization-webapp/CORE_EXTRACTION_PLAN.txt. Correctness is held
to the golden results frozen in that repository (tests/golden/), which record
what equilibrator-pathway and the browser port each produced while they were
still independent implementations. After the move, both consumers reproduce
every one of those 482 numbers bit for bit, not merely within the checker's
1e-6 tolerance.
Where the two replaced copies disagreed, see the docstring of ecm.py for
which behaviour was kept and why. In short: the port's get_volumes (the
server's zeroed the last metabolite when a model had no water), and working
versions of get_fluxes and is_feasible, which raised on every call in
equilibrator-pathway without any test noticing.
PYTHONPATH=src pytest tests/
Changelog
0.1.1 (2026-09-25)
- A Compound table can declare its water compound in an
IsWatercolumn, which then decides on its own; a column that marks nothing means the model has no water. Before, water was recognised only by accession or name. sabiork.compound:34is no longer taken for water -- it is ATP; water issabiork.compound:40. A model identifying ATP by that accession had ATP pinned to 1 M.- The Configuration schema's notes no longer assume the browser (the
solveranddg_confidenceoptions). - The version is taken from the git tag at build time, as in the other
equilibrator packages;
__version__reads it from the installed metadata.
0.1.0 (2026-09-24)
First release: the database-free layer of equilibrator-pathway, shared with the in-browser ECM app.
- MDF (
mdf.py) and ECM (ecm.py) on plain arrays in canonical units. - The model SBtab reader (
model.py), the ECM Parameter table (ecm_model.py), the result tables and SBtab report (solution.py), and the editable Configuration options (configuration.py). - Requires numpy, scipy, cvxpy and sbtabpy >= 1.1.1; importing it loads no pandas, pint or compound database.
Release files for equilibrator-pathway-core 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| equilibrator_pathway_core-0.1.1.tar.gz | 49.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| equilibrator_pathway_core-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 91.6 kB
Release files / equilibrator_pathway_core-0.1.1.tar.gz
| Download URL | equilibrator_pathway_core-0.1.1.tar.gz |
|---|---|
| Size | 49.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3d32e625131bdb40198ebc08a814ac0fbcf1d87ba0380201898ff6759bfd1822
|
|
BLAKE2b-256 checksum How to use checksums |
38f16dcdf81dbbe8af456e2907bcd80d09ca301a9eb54c8848d214807f533b23
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Release files / equilibrator_pathway_core-0.1.1-py3-none-any.whl
| Download URL | equilibrator_pathway_core-0.1.1-py3-none-any.whl |
|---|---|
| Size | 41.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
50b5c0afc749dc98aa6485c0646ee3257ff8492ba1b98f1ba74da260e6dae8f1
|
|
BLAKE2b-256 checksum How to use checksums |
2fdc62840bddefe29e43094a99ac6f83bbeae17c942b6712e8431957db53e342
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|