Skip to main content

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 IsWater column, 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:34 is no longer taken for water -- it is ATP; water is sabiork.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 solver and dg_confidence options).
  • 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)

Source distribution for equilibrator-pathway-core 0.1.1
File Size Uploaded
equilibrator_pathway_core-0.1.1.tar.gz 49.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for equilibrator-pathway-core 0.1.1
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page