equilibrator-pathway-core 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- equilibrator_pathway_core-0.1.0/LICENSE +24 -0
- equilibrator_pathway_core-0.1.0/PKG-INFO +107 -0
- equilibrator_pathway_core-0.1.0/README.md +83 -0
- equilibrator_pathway_core-0.1.0/pyproject.toml +71 -0
- equilibrator_pathway_core-0.1.0/setup.cfg +4 -0
- equilibrator_pathway_core-0.1.0/src/equilibrator_pathway_core/__init__.py +41 -0
- equilibrator_pathway_core-0.1.0/src/equilibrator_pathway_core/configuration.py +293 -0
- equilibrator_pathway_core-0.1.0/src/equilibrator_pathway_core/constants.py +52 -0
- equilibrator_pathway_core-0.1.0/src/equilibrator_pathway_core/ecm.py +586 -0
- equilibrator_pathway_core-0.1.0/src/equilibrator_pathway_core/ecm_model.py +205 -0
- equilibrator_pathway_core-0.1.0/src/equilibrator_pathway_core/errors.py +62 -0
- equilibrator_pathway_core-0.1.0/src/equilibrator_pathway_core/mdf.py +248 -0
- equilibrator_pathway_core-0.1.0/src/equilibrator_pathway_core/model.py +709 -0
- equilibrator_pathway_core-0.1.0/src/equilibrator_pathway_core/solution.py +340 -0
- equilibrator_pathway_core-0.1.0/src/equilibrator_pathway_core/units.py +224 -0
- equilibrator_pathway_core-0.1.0/src/equilibrator_pathway_core.egg-info/PKG-INFO +107 -0
- equilibrator_pathway_core-0.1.0/src/equilibrator_pathway_core.egg-info/SOURCES.txt +25 -0
- equilibrator_pathway_core-0.1.0/src/equilibrator_pathway_core.egg-info/dependency_links.txt +1 -0
- equilibrator_pathway_core-0.1.0/src/equilibrator_pathway_core.egg-info/requires.txt +4 -0
- equilibrator_pathway_core-0.1.0/src/equilibrator_pathway_core.egg-info/top_level.txt +1 -0
- equilibrator_pathway_core-0.1.0/tests/test_configuration.py +132 -0
- equilibrator_pathway_core-0.1.0/tests/test_constants.py +49 -0
- equilibrator_pathway_core-0.1.0/tests/test_ecm.py +110 -0
- equilibrator_pathway_core-0.1.0/tests/test_ecm_model.py +79 -0
- equilibrator_pathway_core-0.1.0/tests/test_isolation.py +104 -0
- equilibrator_pathway_core-0.1.0/tests/test_mdf.py +136 -0
- equilibrator_pathway_core-0.1.0/tests/test_model.py +161 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2013 Weizmann Institute of Science
|
|
4
|
+
Copyright (c) 2018-2020 Institute for Molecular Systems Biology, ETH Zurich
|
|
5
|
+
Copyright (c) 2018-2020 Novo Nordisk Foundation Center for Biosustainability,
|
|
6
|
+
Technical University of Denmark
|
|
7
|
+
|
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
10
|
+
in the Software without restriction, including without limitation the rights
|
|
11
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
13
|
+
furnished to do so, subject to the following conditions:
|
|
14
|
+
|
|
15
|
+
The above copyright notice and this permission notice shall be included in
|
|
16
|
+
all copies or substantial portions of the Software.
|
|
17
|
+
|
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
24
|
+
THE SOFTWARE.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: equilibrator-pathway-core
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MDF and enzyme cost minimization for fully specified pathway models, with no compound database
|
|
5
|
+
Author-email: Elad Noor <elad.noor@weizmann.ac.il>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: repository, https://gitlab.com/equilibrator/equilibrator-pathway-core/
|
|
8
|
+
Keywords: eQuilibrator,pathway analysis,max-min driving force,enzyme cost minimization
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
12
|
+
Classifier: Topic :: Scientific/Engineering :: Chemistry
|
|
13
|
+
Classifier: Natural Language :: English
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Requires-Python: >=3.11
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: numpy>=1.26
|
|
20
|
+
Requires-Dist: scipy>=1.13
|
|
21
|
+
Requires-Dist: cvxpy>=1.6
|
|
22
|
+
Requires-Dist: sbtabpy>=1.1.1
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
|
|
25
|
+
# equilibrator-pathway-core
|
|
26
|
+
|
|
27
|
+
Max-min driving force (MDF) and enzyme cost minimization (ECM) for **fully
|
|
28
|
+
specified** pathway models, with no compound database.
|
|
29
|
+
|
|
30
|
+
This is the lower layer of
|
|
31
|
+
[equilibrator-pathway](https://gitlab.com/equilibrator/equilibrator-pathway).
|
|
32
|
+
It analyses model SBtab files that already carry everything the optimisation
|
|
33
|
+
needs -- stoichiometry, concentration bounds, fluxes and standard Gibbs energies
|
|
34
|
+
-- and it never predicts or looks anything up. That is what lets it install in
|
|
35
|
+
a few megabytes of Python on top of numpy, including in the browser via
|
|
36
|
+
[Pyodide](https://pyodide.org).
|
|
37
|
+
|
|
38
|
+
| | this package | equilibrator-pathway |
|
|
39
|
+
| --- | --- | --- |
|
|
40
|
+
| MDF, ECM on a model SBtab | yes | yes (re-exported) |
|
|
41
|
+
| network SBtabs, formula search, identifier resolution | -- | yes |
|
|
42
|
+
| dG'0 prediction (Component Contribution) | -- | yes |
|
|
43
|
+
| dG'0 uncertainty, MDMC | -- | yes |
|
|
44
|
+
| pint `Q_` public API | -- | yes |
|
|
45
|
+
| compound database, sqlalchemy, pint | never | yes |
|
|
46
|
+
|
|
47
|
+
## Units
|
|
48
|
+
|
|
49
|
+
Everything here works on plain floats in one canonical set of units -- M,
|
|
50
|
+
kJ/mol, K, 1/s, Da -- documented in `equilibrator_pathway_core.constants`.
|
|
51
|
+
Units are attached at the boundary, by equilibrator-pathway's pint adapter or by
|
|
52
|
+
the browser client's string parser.
|
|
53
|
+
|
|
54
|
+
## Staying small
|
|
55
|
+
|
|
56
|
+
Importing this package must not pull in the equilibrator database stack. That is
|
|
57
|
+
tested directly, in a fresh interpreter, by `tests/test_isolation.py`, which
|
|
58
|
+
fails if `equilibrator_api`, `equilibrator_cache`, `component_contribution`,
|
|
59
|
+
`sqlalchemy`, `pint` or `matplotlib` appear in `sys.modules`. A dependency added
|
|
60
|
+
to `pyproject.toml` without the code that needs it is equally unwelcome.
|
|
61
|
+
|
|
62
|
+
## Status
|
|
63
|
+
|
|
64
|
+
In place:
|
|
65
|
+
|
|
66
|
+
| module | what | needs sbtabpy |
|
|
67
|
+
| --- | --- | --- |
|
|
68
|
+
| `constants.py` | R, T, standard concentrations | -- |
|
|
69
|
+
| `ecm.py` | the enzyme cost function and ECM | -- |
|
|
70
|
+
| `mdf.py` | the MDF linear program | -- |
|
|
71
|
+
| `errors.py` | `ModelError`, `SolverFailure`, `ConfigurationError` | -- |
|
|
72
|
+
| `units.py` | unit strings to canonical floats | -- |
|
|
73
|
+
| `model.py` | model SBtab to arrays: S, bounds, fluxes, dG'0, water | yes |
|
|
74
|
+
| `ecm_model.py` | an ECM model's Parameter table | yes |
|
|
75
|
+
| `solution.py` | result tables and the SBtab report | yes |
|
|
76
|
+
| `configuration.py` | the editable Configuration options, their schema, and applying edits | yes |
|
|
77
|
+
|
|
78
|
+
Both equilibrator-pathway and the browser client run MDF and ECM through this
|
|
79
|
+
package; the browser also reads models (`model.load_model`), edits their
|
|
80
|
+
Configuration (`configuration.py`) and writes reports with it.
|
|
81
|
+
|
|
82
|
+
The SBtab modules use sbtabpy directly, and need sbtabpy **1.1.1 or later**.
|
|
83
|
+
1.1.0 was the first release with no required dependencies (earlier ones
|
|
84
|
+
declared pandas, pyarrow, python-libsbml and openpyxl, which its parser never
|
|
85
|
+
imports); 1.1.1 added the dictionary rows, attribute defaults and pandas-free
|
|
86
|
+
`SBtabTable.from_rows` that replaced the core's own adapter module.
|
|
87
|
+
`tests/test_isolation.py` still forbids all four: nothing installs them by
|
|
88
|
+
default now, but the test is what guarantees the core never imports them, and
|
|
89
|
+
so stays loadable in the browser.
|
|
90
|
+
|
|
91
|
+
The extraction plan is in
|
|
92
|
+
`enzyme-cost-minimization-webapp/CORE_EXTRACTION_PLAN.txt`. Correctness is held
|
|
93
|
+
to the golden results frozen in that repository (`tests/golden/`), which record
|
|
94
|
+
what equilibrator-pathway and the browser port each produced while they were
|
|
95
|
+
still independent implementations. After the move, both consumers reproduce
|
|
96
|
+
every one of those 482 numbers **bit for bit**, not merely within the checker's
|
|
97
|
+
1e-6 tolerance.
|
|
98
|
+
|
|
99
|
+
Where the two replaced copies disagreed, see the docstring of `ecm.py` for
|
|
100
|
+
which behaviour was kept and why. In short: the port's `get_volumes` (the
|
|
101
|
+
server's zeroed the last metabolite when a model had no water), and working
|
|
102
|
+
versions of `get_fluxes` and `is_feasible`, which raised on every call in
|
|
103
|
+
equilibrator-pathway without any test noticing.
|
|
104
|
+
|
|
105
|
+
```sh
|
|
106
|
+
PYTHONPATH=src pytest tests/
|
|
107
|
+
```
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# equilibrator-pathway-core
|
|
2
|
+
|
|
3
|
+
Max-min driving force (MDF) and enzyme cost minimization (ECM) for **fully
|
|
4
|
+
specified** pathway models, with no compound database.
|
|
5
|
+
|
|
6
|
+
This is the lower layer of
|
|
7
|
+
[equilibrator-pathway](https://gitlab.com/equilibrator/equilibrator-pathway).
|
|
8
|
+
It analyses model SBtab files that already carry everything the optimisation
|
|
9
|
+
needs -- stoichiometry, concentration bounds, fluxes and standard Gibbs energies
|
|
10
|
+
-- and it never predicts or looks anything up. That is what lets it install in
|
|
11
|
+
a few megabytes of Python on top of numpy, including in the browser via
|
|
12
|
+
[Pyodide](https://pyodide.org).
|
|
13
|
+
|
|
14
|
+
| | this package | equilibrator-pathway |
|
|
15
|
+
| --- | --- | --- |
|
|
16
|
+
| MDF, ECM on a model SBtab | yes | yes (re-exported) |
|
|
17
|
+
| network SBtabs, formula search, identifier resolution | -- | yes |
|
|
18
|
+
| dG'0 prediction (Component Contribution) | -- | yes |
|
|
19
|
+
| dG'0 uncertainty, MDMC | -- | yes |
|
|
20
|
+
| pint `Q_` public API | -- | yes |
|
|
21
|
+
| compound database, sqlalchemy, pint | never | yes |
|
|
22
|
+
|
|
23
|
+
## Units
|
|
24
|
+
|
|
25
|
+
Everything here works on plain floats in one canonical set of units -- M,
|
|
26
|
+
kJ/mol, K, 1/s, Da -- documented in `equilibrator_pathway_core.constants`.
|
|
27
|
+
Units are attached at the boundary, by equilibrator-pathway's pint adapter or by
|
|
28
|
+
the browser client's string parser.
|
|
29
|
+
|
|
30
|
+
## Staying small
|
|
31
|
+
|
|
32
|
+
Importing this package must not pull in the equilibrator database stack. That is
|
|
33
|
+
tested directly, in a fresh interpreter, by `tests/test_isolation.py`, which
|
|
34
|
+
fails if `equilibrator_api`, `equilibrator_cache`, `component_contribution`,
|
|
35
|
+
`sqlalchemy`, `pint` or `matplotlib` appear in `sys.modules`. A dependency added
|
|
36
|
+
to `pyproject.toml` without the code that needs it is equally unwelcome.
|
|
37
|
+
|
|
38
|
+
## Status
|
|
39
|
+
|
|
40
|
+
In place:
|
|
41
|
+
|
|
42
|
+
| module | what | needs sbtabpy |
|
|
43
|
+
| --- | --- | --- |
|
|
44
|
+
| `constants.py` | R, T, standard concentrations | -- |
|
|
45
|
+
| `ecm.py` | the enzyme cost function and ECM | -- |
|
|
46
|
+
| `mdf.py` | the MDF linear program | -- |
|
|
47
|
+
| `errors.py` | `ModelError`, `SolverFailure`, `ConfigurationError` | -- |
|
|
48
|
+
| `units.py` | unit strings to canonical floats | -- |
|
|
49
|
+
| `model.py` | model SBtab to arrays: S, bounds, fluxes, dG'0, water | yes |
|
|
50
|
+
| `ecm_model.py` | an ECM model's Parameter table | yes |
|
|
51
|
+
| `solution.py` | result tables and the SBtab report | yes |
|
|
52
|
+
| `configuration.py` | the editable Configuration options, their schema, and applying edits | yes |
|
|
53
|
+
|
|
54
|
+
Both equilibrator-pathway and the browser client run MDF and ECM through this
|
|
55
|
+
package; the browser also reads models (`model.load_model`), edits their
|
|
56
|
+
Configuration (`configuration.py`) and writes reports with it.
|
|
57
|
+
|
|
58
|
+
The SBtab modules use sbtabpy directly, and need sbtabpy **1.1.1 or later**.
|
|
59
|
+
1.1.0 was the first release with no required dependencies (earlier ones
|
|
60
|
+
declared pandas, pyarrow, python-libsbml and openpyxl, which its parser never
|
|
61
|
+
imports); 1.1.1 added the dictionary rows, attribute defaults and pandas-free
|
|
62
|
+
`SBtabTable.from_rows` that replaced the core's own adapter module.
|
|
63
|
+
`tests/test_isolation.py` still forbids all four: nothing installs them by
|
|
64
|
+
default now, but the test is what guarantees the core never imports them, and
|
|
65
|
+
so stays loadable in the browser.
|
|
66
|
+
|
|
67
|
+
The extraction plan is in
|
|
68
|
+
`enzyme-cost-minimization-webapp/CORE_EXTRACTION_PLAN.txt`. Correctness is held
|
|
69
|
+
to the golden results frozen in that repository (`tests/golden/`), which record
|
|
70
|
+
what equilibrator-pathway and the browser port each produced while they were
|
|
71
|
+
still independent implementations. After the move, both consumers reproduce
|
|
72
|
+
every one of those 482 numbers **bit for bit**, not merely within the checker's
|
|
73
|
+
1e-6 tolerance.
|
|
74
|
+
|
|
75
|
+
Where the two replaced copies disagreed, see the docstring of `ecm.py` for
|
|
76
|
+
which behaviour was kept and why. In short: the port's `get_volumes` (the
|
|
77
|
+
server's zeroed the last metabolite when a model had no water), and working
|
|
78
|
+
versions of `get_fluxes` and `is_feasible`, which raised on every call in
|
|
79
|
+
equilibrator-pathway without any test noticing.
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
PYTHONPATH=src pytest tests/
|
|
83
|
+
```
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "equilibrator-pathway-core"
|
|
3
|
+
description = "MDF and enzyme cost minimization for fully specified pathway models, with no compound database"
|
|
4
|
+
readme = {file = "README.md", content-type = "text/markdown"}
|
|
5
|
+
license = "MIT"
|
|
6
|
+
license-files = ["LICENSE"]
|
|
7
|
+
authors = [
|
|
8
|
+
{name = "Elad Noor", email = "elad.noor@weizmann.ac.il"}
|
|
9
|
+
]
|
|
10
|
+
keywords = [
|
|
11
|
+
"eQuilibrator",
|
|
12
|
+
"pathway analysis",
|
|
13
|
+
"max-min driving force",
|
|
14
|
+
"enzyme cost minimization",
|
|
15
|
+
]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Science/Research",
|
|
19
|
+
"Topic :: Scientific/Engineering :: Bio-Informatics",
|
|
20
|
+
"Topic :: Scientific/Engineering :: Chemistry",
|
|
21
|
+
"Natural Language :: English",
|
|
22
|
+
"Operating System :: OS Independent",
|
|
23
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
24
|
+
]
|
|
25
|
+
requires-python = ">=3.11"
|
|
26
|
+
version = "0.1.0"
|
|
27
|
+
|
|
28
|
+
# Deliberately short, and it must stay that way: this distribution exists so
|
|
29
|
+
# that a pathway analysis can be installed -- including in Pyodide -- without
|
|
30
|
+
# the compound database, the Component Contribution parameters, sqlalchemy or
|
|
31
|
+
# pint. tests/test_isolation.py enforces it on every run.
|
|
32
|
+
#
|
|
33
|
+
# Add a dependency only when a module that genuinely needs it moves in, never
|
|
34
|
+
# speculatively.
|
|
35
|
+
#
|
|
36
|
+
# The floors are what the browser actually runs (Pyodide 0.28: cvxpy-base 1.6.3,
|
|
37
|
+
# numpy 2.2.5, scipy 1.14.1), not equilibrator-pathway's tighter pins -- this
|
|
38
|
+
# exact code is exercised there, so they are known to work. In Pyodide the
|
|
39
|
+
# wheel is installed with deps=False and these go unenforced; the worker loads
|
|
40
|
+
# cvxpy-base rather than cvxpy, which has the same import name.
|
|
41
|
+
#
|
|
42
|
+
# sbtabpy is floored at 1.1.1: 1.1.0 was the first release with no required
|
|
43
|
+
# dependencies (earlier ones declared pandas, pyarrow, python-libsbml and
|
|
44
|
+
# openpyxl, none of which its SBtab parser ever imports), and 1.1.1 added the
|
|
45
|
+
# table API the core reads models and writes reports with (`to_dict_rows`,
|
|
46
|
+
# `column_names`, `get_attribute(..., default=)`, `SBtabTable.from_rows`).
|
|
47
|
+
# Install plain `sbtabpy`, never an extra such as `sbtabpy[pandas]`.
|
|
48
|
+
dependencies = [
|
|
49
|
+
"numpy>=1.26",
|
|
50
|
+
"scipy>=1.13",
|
|
51
|
+
"cvxpy>=1.6",
|
|
52
|
+
"sbtabpy>=1.1.1",
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
[dependency-groups]
|
|
56
|
+
test = [
|
|
57
|
+
"pytest",
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
[project.urls]
|
|
61
|
+
repository = "https://gitlab.com/equilibrator/equilibrator-pathway-core/"
|
|
62
|
+
|
|
63
|
+
[build-system]
|
|
64
|
+
requires = ["setuptools>=77", "wheel"]
|
|
65
|
+
build-backend = "setuptools.build_meta"
|
|
66
|
+
|
|
67
|
+
[tool.setuptools.packages.find]
|
|
68
|
+
where = ["src"]
|
|
69
|
+
|
|
70
|
+
[tool.ruff]
|
|
71
|
+
target-version = "py311"
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Max-min driving force and enzyme cost minimization, without a compound database.
|
|
2
|
+
|
|
3
|
+
This is the database-free lower layer of equilibrator-pathway. It analyses
|
|
4
|
+
*fully specified* pathway models -- SBtab files that already carry the
|
|
5
|
+
stoichiometry, bounds, fluxes and standard Gibbs energies -- and never predicts
|
|
6
|
+
or looks anything up. It is shared by equilibrator-pathway (which adds identifier
|
|
7
|
+
resolution, dG'0 prediction and a pint-based API on top) and by the in-browser
|
|
8
|
+
pathway client.
|
|
9
|
+
|
|
10
|
+
Everything here works on plain floats in the canonical units documented in
|
|
11
|
+
:mod:`equilibrator_pathway_core.constants`.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from .constants import (
|
|
17
|
+
DEFAULT_RT,
|
|
18
|
+
DEFAULT_T,
|
|
19
|
+
PHYSIOLOGICAL_CONCENTRATION,
|
|
20
|
+
STANDARD_CONCENTRATION,
|
|
21
|
+
R,
|
|
22
|
+
)
|
|
23
|
+
from .ecm import ECF_DEFAULTS, EnzymeCostFunction
|
|
24
|
+
from .errors import ModelError, SolverFailure
|
|
25
|
+
from .mdf import MdfResult, mdf_analysis
|
|
26
|
+
|
|
27
|
+
__version__ = "0.1.0"
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"DEFAULT_RT",
|
|
31
|
+
"DEFAULT_T",
|
|
32
|
+
"ECF_DEFAULTS",
|
|
33
|
+
"PHYSIOLOGICAL_CONCENTRATION",
|
|
34
|
+
"STANDARD_CONCENTRATION",
|
|
35
|
+
"EnzymeCostFunction",
|
|
36
|
+
"MdfResult",
|
|
37
|
+
"ModelError",
|
|
38
|
+
"R",
|
|
39
|
+
"SolverFailure",
|
|
40
|
+
"mdf_analysis",
|
|
41
|
+
]
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
"""The editable Configuration table: its schema, and applying edits to it.
|
|
2
|
+
|
|
3
|
+
The web form that lets the user tweak a model's `Configuration` table is built
|
|
4
|
+
from the schema here rather than from a hand-written list in the JavaScript, so
|
|
5
|
+
that the offered choices cannot drift away from what the optimization actually
|
|
6
|
+
accepts (`ECF_DEFAULTS` and the branches of `EnzymeCostFunction`).
|
|
7
|
+
|
|
8
|
+
Three kinds of options:
|
|
9
|
+
|
|
10
|
+
* `select` -- a fixed set of choices (`algorithm`, `denominator`, ...)
|
|
11
|
+
* `number` -- a numeric parameter (`stdev_factor`, `ln_conc_confidence`, ...)
|
|
12
|
+
* `display` -- shown but not editable: the aqueous conditions, because the ΔG'°
|
|
13
|
+
values stored in the model were already transformed for exactly those
|
|
14
|
+
conditions. Changing the pH here would not re-transform them -- that needs
|
|
15
|
+
Component Contribution, i.e. the compound database this app deliberately does
|
|
16
|
+
without.
|
|
17
|
+
|
|
18
|
+
Moved here from the browser client (``pathway_client/configuration.py``), step 4
|
|
19
|
+
of CORE_EXTRACTION_PLAN.txt: the schema is a capability the library lacked, and
|
|
20
|
+
keeping it next to the code that consumes the options is what stops the two
|
|
21
|
+
drifting apart. The notes are user-facing text, written for the browser's form.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from sbtab.SBtab import SBtabDocument, SBtabTable
|
|
25
|
+
|
|
26
|
+
from .errors import ConfigurationError
|
|
27
|
+
from .model import read_sbtab
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"DISPLAY",
|
|
31
|
+
"NUMBER",
|
|
32
|
+
"OPTION_SCHEMA",
|
|
33
|
+
"SELECT",
|
|
34
|
+
"ConfigurationError",
|
|
35
|
+
"apply_overrides",
|
|
36
|
+
"describe_configuration",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
SELECT = "select"
|
|
40
|
+
NUMBER = "number"
|
|
41
|
+
DISPLAY = "display"
|
|
42
|
+
|
|
43
|
+
# Applies to: which algorithm an option has an effect on ("MDF", "ECM" or
|
|
44
|
+
# "both"), so the form can say so.
|
|
45
|
+
OPTION_SCHEMA = {
|
|
46
|
+
"algorithm": {
|
|
47
|
+
"kind": SELECT,
|
|
48
|
+
"choices": ["MDF", "ECM"],
|
|
49
|
+
"label": "Algorithm",
|
|
50
|
+
"applies_to": "both",
|
|
51
|
+
"note": "Which optimization to run. An ECM model also needs a "
|
|
52
|
+
"'Parameter' table with kcat, KM and molecular masses.",
|
|
53
|
+
},
|
|
54
|
+
"version": {
|
|
55
|
+
"kind": SELECT,
|
|
56
|
+
"choices": ["1", "2", "3", "4"],
|
|
57
|
+
"label": "Enzyme cost function level",
|
|
58
|
+
"applies_to": "ECM",
|
|
59
|
+
"note": "1 = capacity only, 2 = + thermodynamics, 3 = + saturation, "
|
|
60
|
+
"4 = + allosteric regulation.",
|
|
61
|
+
},
|
|
62
|
+
"kcat_source": {
|
|
63
|
+
"kind": SELECT,
|
|
64
|
+
"choices": ["gmean", "fwd"],
|
|
65
|
+
"label": "kcat source",
|
|
66
|
+
"applies_to": "ECM",
|
|
67
|
+
"note": "'gmean' distributes the geometric-mean kcat over both "
|
|
68
|
+
"directions using the Haldane relationship; 'fwd' takes kcat in the "
|
|
69
|
+
"direction of the flux as given.",
|
|
70
|
+
},
|
|
71
|
+
"denominator": {
|
|
72
|
+
"kind": SELECT,
|
|
73
|
+
"choices": ["S", "1S", "SP", "1SP", "CM"],
|
|
74
|
+
"label": "Rate law denominator",
|
|
75
|
+
"applies_to": "ECM",
|
|
76
|
+
"note": "The saturation term of the rate law; 'CM' is the common "
|
|
77
|
+
"modular rate law.",
|
|
78
|
+
},
|
|
79
|
+
"regularization": {
|
|
80
|
+
"kind": SELECT,
|
|
81
|
+
"choices": ["volume"],
|
|
82
|
+
"label": "Regularization",
|
|
83
|
+
"applies_to": "ECM",
|
|
84
|
+
"note": "Only 'volume' is implemented: support for the other options "
|
|
85
|
+
"was dropped when equilibrator-pathway moved to CVXPY.",
|
|
86
|
+
},
|
|
87
|
+
"objective": {
|
|
88
|
+
"kind": SELECT,
|
|
89
|
+
"choices": ["enzyme", "enzyme + metabolite"],
|
|
90
|
+
"label": "Objective",
|
|
91
|
+
"applies_to": "ECM",
|
|
92
|
+
"note": "Minimize the enzyme weight alone, or the enzyme plus "
|
|
93
|
+
"metabolite weight.",
|
|
94
|
+
},
|
|
95
|
+
"ln_conc_confidence": {
|
|
96
|
+
"kind": NUMBER,
|
|
97
|
+
"label": "Log-concentration confidence",
|
|
98
|
+
"applies_to": "both",
|
|
99
|
+
"minimum": 0.0,
|
|
100
|
+
"maximum": 0.999999,
|
|
101
|
+
"note": "The confidence level of the concentration bounds (default "
|
|
102
|
+
"0.95). Lowering it narrows the allowed concentration range.",
|
|
103
|
+
},
|
|
104
|
+
"dg_confidence": {
|
|
105
|
+
"kind": NUMBER,
|
|
106
|
+
"label": "ΔG'° confidence",
|
|
107
|
+
"applies_to": "MDF",
|
|
108
|
+
"minimum": 0.0,
|
|
109
|
+
"maximum": 0.999999,
|
|
110
|
+
"note": "Has no effect on an uploaded model: it widens the ΔG'° "
|
|
111
|
+
"bounds using the uncertainty covariance, which a model SBtab does "
|
|
112
|
+
"not store (dg_sigma is None).",
|
|
113
|
+
},
|
|
114
|
+
"stdev_factor": {
|
|
115
|
+
"kind": NUMBER,
|
|
116
|
+
"label": "Standard deviation factor",
|
|
117
|
+
"applies_to": "both",
|
|
118
|
+
"note": "Currently unused by the optimization -- in "
|
|
119
|
+
"equilibrator-pathway it is still a TODO in ecm_model.py, so changing "
|
|
120
|
+
"it will not change the results.",
|
|
121
|
+
},
|
|
122
|
+
"solver": {
|
|
123
|
+
"kind": DISPLAY,
|
|
124
|
+
"label": "Solver",
|
|
125
|
+
"applies_to": "both",
|
|
126
|
+
"note": "CLARABEL is the only solver available in the browser (it is "
|
|
127
|
+
"also eQuilibrator's default).",
|
|
128
|
+
},
|
|
129
|
+
"p_h": {
|
|
130
|
+
"kind": DISPLAY,
|
|
131
|
+
"label": "pH",
|
|
132
|
+
"applies_to": "both",
|
|
133
|
+
"note": "The ΔG'° values in this model were transformed for these "
|
|
134
|
+
"conditions; re-transforming them needs the compound database.",
|
|
135
|
+
},
|
|
136
|
+
"p_mg": {"kind": DISPLAY, "label": "pMg", "applies_to": "both"},
|
|
137
|
+
"ionic_strength": {
|
|
138
|
+
"kind": DISPLAY,
|
|
139
|
+
"label": "Ionic strength",
|
|
140
|
+
"applies_to": "both",
|
|
141
|
+
},
|
|
142
|
+
"temperature": {"kind": DISPLAY, "label": "Temperature", "applies_to": "both"},
|
|
143
|
+
"e_potential": {
|
|
144
|
+
"kind": DISPLAY,
|
|
145
|
+
"label": "Electrostatic potential",
|
|
146
|
+
"applies_to": "both",
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _configuration_table(document: SBtabDocument) -> SBtabTable:
|
|
152
|
+
table = document.get_sbtab_by_id("Configuration")
|
|
153
|
+
if table is None:
|
|
154
|
+
raise ConfigurationError("Missing 'Configuration' table")
|
|
155
|
+
if "Option" not in table.column_names or "Value" not in table.column_names:
|
|
156
|
+
raise ConfigurationError(
|
|
157
|
+
"The Configuration table must have an Option and a Value column"
|
|
158
|
+
)
|
|
159
|
+
return table
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def describe_configuration(text: str, filename: str = "pathway.tsv") -> dict:
|
|
163
|
+
"""Describe the Configuration table of a model, for the edit form.
|
|
164
|
+
|
|
165
|
+
Returns the options in the order they appear in the file, each with its
|
|
166
|
+
current value and how it may be edited. Options that are not in the schema
|
|
167
|
+
are reported as `display`, so an unknown option is shown but never silently
|
|
168
|
+
changed.
|
|
169
|
+
"""
|
|
170
|
+
document = read_sbtab(text, filename)
|
|
171
|
+
table = _configuration_table(document)
|
|
172
|
+
|
|
173
|
+
options = []
|
|
174
|
+
for row in table.to_dict_rows():
|
|
175
|
+
name = row.get("Option", "").strip()
|
|
176
|
+
if not name:
|
|
177
|
+
continue
|
|
178
|
+
value = row.get("Value", "").strip()
|
|
179
|
+
schema = OPTION_SCHEMA.get(name, {})
|
|
180
|
+
option = {
|
|
181
|
+
"name": name,
|
|
182
|
+
"value": value,
|
|
183
|
+
"kind": schema.get("kind", DISPLAY),
|
|
184
|
+
"label": schema.get("label", name),
|
|
185
|
+
"applies_to": schema.get("applies_to", "both"),
|
|
186
|
+
"note": schema.get("note"),
|
|
187
|
+
"comment": (row.get("Comment") or "").strip(),
|
|
188
|
+
}
|
|
189
|
+
if option["kind"] == SELECT:
|
|
190
|
+
choices = list(schema["choices"])
|
|
191
|
+
# keep whatever the file says as a choice, even when it is not one
|
|
192
|
+
# we know about, so that opening the form cannot alter the model
|
|
193
|
+
if not any(choice.lower() == value.lower() for choice in choices):
|
|
194
|
+
if value:
|
|
195
|
+
choices.append(value)
|
|
196
|
+
else:
|
|
197
|
+
# normalize to the schema's spelling of the current value
|
|
198
|
+
option["value"] = next(
|
|
199
|
+
choice for choice in choices if choice.lower() == value.lower()
|
|
200
|
+
)
|
|
201
|
+
option["choices"] = choices
|
|
202
|
+
elif option["kind"] == NUMBER:
|
|
203
|
+
for key in ("minimum", "maximum"):
|
|
204
|
+
if key in schema:
|
|
205
|
+
option[key] = schema[key]
|
|
206
|
+
options.append(option)
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
"filename": filename,
|
|
210
|
+
"options": options,
|
|
211
|
+
"algorithm": next(
|
|
212
|
+
(
|
|
213
|
+
option["value"].upper()
|
|
214
|
+
for option in options
|
|
215
|
+
if option["name"] == "algorithm"
|
|
216
|
+
),
|
|
217
|
+
None,
|
|
218
|
+
),
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def apply_overrides(
|
|
223
|
+
document: SBtabDocument, overrides: dict[str, str] | None
|
|
224
|
+
) -> list[str]:
|
|
225
|
+
"""Apply edited Configuration values to a document, in place.
|
|
226
|
+
|
|
227
|
+
Returns a list of human-readable descriptions of what changed, for the
|
|
228
|
+
result page. Raises `ConfigurationError` if an override names an option
|
|
229
|
+
that is absent or not editable, or gives a value the code would not accept.
|
|
230
|
+
"""
|
|
231
|
+
if not overrides:
|
|
232
|
+
return []
|
|
233
|
+
|
|
234
|
+
table = _configuration_table(document)
|
|
235
|
+
# the rows are copies, so edits go back through `change_value`, by position
|
|
236
|
+
# (1-based): the Option column need not come first, which rules out
|
|
237
|
+
# `change_value_by_name`
|
|
238
|
+
value_column = table.column_names.index("Value") + 1
|
|
239
|
+
rows_by_name = {}
|
|
240
|
+
for position, row in enumerate(table.to_dict_rows(), start=1):
|
|
241
|
+
name = row.get("Option", "").strip()
|
|
242
|
+
if name:
|
|
243
|
+
rows_by_name.setdefault(name, (position, row))
|
|
244
|
+
|
|
245
|
+
changes = []
|
|
246
|
+
for name, new_value in overrides.items():
|
|
247
|
+
if name not in rows_by_name:
|
|
248
|
+
raise ConfigurationError(
|
|
249
|
+
f"The Configuration table of this model has no option '{name}'"
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
position, row = rows_by_name[name]
|
|
253
|
+
schema = OPTION_SCHEMA.get(name, {})
|
|
254
|
+
kind = schema.get("kind", DISPLAY)
|
|
255
|
+
new_value = str(new_value).strip()
|
|
256
|
+
|
|
257
|
+
if kind == DISPLAY:
|
|
258
|
+
raise ConfigurationError(f"The option '{name}' cannot be edited")
|
|
259
|
+
if kind == SELECT:
|
|
260
|
+
choices = schema["choices"]
|
|
261
|
+
match = next(
|
|
262
|
+
(choice for choice in choices if choice.lower() == new_value.lower()),
|
|
263
|
+
None,
|
|
264
|
+
)
|
|
265
|
+
if match is None:
|
|
266
|
+
raise ConfigurationError(
|
|
267
|
+
f"'{new_value}' is not a valid value for '{name}' "
|
|
268
|
+
f"(choose one of: {', '.join(choices)})"
|
|
269
|
+
)
|
|
270
|
+
new_value = match
|
|
271
|
+
elif kind == NUMBER:
|
|
272
|
+
try:
|
|
273
|
+
number = float(new_value)
|
|
274
|
+
except ValueError:
|
|
275
|
+
raise ConfigurationError(
|
|
276
|
+
f"The option '{name}' must be a number, not '{new_value}'"
|
|
277
|
+
)
|
|
278
|
+
minimum, maximum = schema.get("minimum"), schema.get("maximum")
|
|
279
|
+
if minimum is not None and number < minimum:
|
|
280
|
+
raise ConfigurationError(
|
|
281
|
+
f"The option '{name}' must be at least {minimum}"
|
|
282
|
+
)
|
|
283
|
+
if maximum is not None and number > maximum:
|
|
284
|
+
raise ConfigurationError(
|
|
285
|
+
f"The option '{name}' must be at most {maximum}"
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
old_value = row.get("Value", "").strip()
|
|
289
|
+
if old_value != new_value:
|
|
290
|
+
table.change_value(position, value_column, new_value)
|
|
291
|
+
changes.append(f"{name}: '{old_value}' → '{new_value}'")
|
|
292
|
+
|
|
293
|
+
return changes
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Physical constants, as plain floats in the core's canonical units.
|
|
2
|
+
|
|
3
|
+
The core does all its arithmetic on unitless numbers in one fixed set of units:
|
|
4
|
+
|
|
5
|
+
concentration M
|
|
6
|
+
energy kJ/mol
|
|
7
|
+
temperature K
|
|
8
|
+
rate constant 1/s
|
|
9
|
+
molecular mass Da
|
|
10
|
+
|
|
11
|
+
Units are attached, if at all, at the boundary: equilibrator-pathway wraps these
|
|
12
|
+
in pint quantities for its public API, and the browser client parses unit
|
|
13
|
+
strings straight into these units. Nothing in here may import pint.
|
|
14
|
+
|
|
15
|
+
These are *defined* here rather than imported from
|
|
16
|
+
``equilibrator_cache.thermodynamic_constants``. Importing that module executes
|
|
17
|
+
``equilibrator_cache/__init__.py``, which pulls in sqlalchemy, pandas, pooch and
|
|
18
|
+
rapidfuzz -- the dependency chain this package exists to avoid. The values are
|
|
19
|
+
held equal to equilibrator-cache's by ``tests/test_constants.py``.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"DEFAULT_RT",
|
|
26
|
+
"DEFAULT_T",
|
|
27
|
+
"PHYSIOLOGICAL_CONCENTRATION",
|
|
28
|
+
"STANDARD_CONCENTRATION",
|
|
29
|
+
"R",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
#: Gas constant, kJ/(mol*K).
|
|
33
|
+
#:
|
|
34
|
+
#: Note: 8.31e-3, *not* CODATA's 8.314462618e-3. This is the value
|
|
35
|
+
#: equilibrator-cache has always used, and every published eQuilibrator number
|
|
36
|
+
#: -- and every golden result this package is tested against -- was computed
|
|
37
|
+
#: with it. "Correcting" it would move every driving force by ~0.05%, which the
|
|
38
|
+
#: golden tests would (rightly) report as a regression. Change it, if ever, in
|
|
39
|
+
#: equilibrator-cache first and here second.
|
|
40
|
+
R: float = 8.31e-3
|
|
41
|
+
|
|
42
|
+
#: Default temperature, K.
|
|
43
|
+
DEFAULT_T: float = 298.15
|
|
44
|
+
|
|
45
|
+
#: R * DEFAULT_T, kJ/mol.
|
|
46
|
+
DEFAULT_RT: float = R * DEFAULT_T
|
|
47
|
+
|
|
48
|
+
#: Standard-state concentration for a solute, M.
|
|
49
|
+
STANDARD_CONCENTRATION: float = 1.0
|
|
50
|
+
|
|
51
|
+
#: Reference concentration for "physiological" (1 mM) Gibbs energies, M.
|
|
52
|
+
PHYSIOLOGICAL_CONCENTRATION: float = 1.0e-3
|