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, so the two can disagree on a model without a Thermodynamics table. PathwayModel.thermodynamics_fallback is set (to a warning string explaining this) whenever that fallback was used, so a consumer can warn about it distinctly rather than missing it among routine notes.

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. Both readers refuse a model in which a compound has no row in the ConcentrationConstraint table, naming the compound -- except water, whose bounds both pin to 1 M anyway. (Before equilibrator-api's next release, the server gave such a compound its default bounds, 1 µM to 10 mM, without a word.) Which compound counts as water follows each reader's own rule, above.

A Thermodynamics table without a Compound column cannot be read by equilibrator-api 0.8.1 and earlier (KeyError: 'Compound'), although reaction energies never use that column. This package reads it; equilibrator-api does too from its next release on.

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

Unreleased

0.1.3 (2026-10-07)

  • PathwayModel has a new thermodynamics_fallback attribute: None normally, or a warning string when standard_dg_primes came from the Parameter table's equilibrium constants rather than a Thermodynamics table (a fallback kept for backwards compatibility, since this package has no access to the compound database to predict them instead). It no longer appears in warnings, so a consumer can warn about it distinctly -- equilibrator-api's own model reader has no such fallback (a model without a Thermodynamics table goes through Component Contribution there instead), so the two can give different standard Gibbs energies for the same model.

0.1.2 (2026-10-07)

  • Water needs no row in the ConcentrationConstraint table: its bounds are pinned to 1 M anyway, and requiring them refused real model files, such as equilibrator-api's ccm_*.tsv examples. Every other compound still needs one, and equilibrator-api now applies the same rule.

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.

Metadata

Release files for equilibrator-pathway-core 0.1.3

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.3
File Size Uploaded
equilibrator_pathway_core-0.1.3.tar.gz 52.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for equilibrator-pathway-core 0.1.3
File Interpreter ABI Platform
equilibrator_pathway_core-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 95.0 kB

Release files / equilibrator_pathway_core-0.1.3.tar.gz

Download URL equilibrator_pathway_core-0.1.3.tar.gz
Size 52.4 kB
Tags Source
SHA-256 checksum
How to use checksums
2818ba7ffb5cf167f35241d7b8295d8df94eb13bab7782575349a1734cd156a2
BLAKE2b-256 checksum
How to use checksums
e14ba31046a4fb7d2b8849ddd15a664c065206a1f32083e52269b1ba2222f4d6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / equilibrator_pathway_core-0.1.3-py3-none-any.whl

Download URL equilibrator_pathway_core-0.1.3-py3-none-any.whl
Size 42.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
03df32056022af3f18c9a30372635009dec55bcb1bf1ed3a39a5bcfcfdc7fbfa
BLAKE2b-256 checksum
How to use checksums
cbab684b2dcaaf82f9a25e091ff36441111722c790f9d3baf6fbe8385062ecd5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.14.8

Release history Release notifications | RSS feed

This release

0.1.3 This release

2 release files

0.1.2

2 release files

0.1.1

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