Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@

## Unreleased

* New: `level_complex_concs` and `level_complex_abundances` give every enzyme
complex one abundance, the median per copy over its measured subunits, so a
single badly quantified subunit no longer sets a complex's capacity.
`complex_families` groups isozyme variants into complexes and leaves shared
subunits and electron carriers as measured. See `docs/relaxation_methods.md`.
* New: `relax_enz_concs` relaxes enzyme caps (and, if need be, the protein
pool) by the least total fold increase that reaches the target growth, in one
LP, so the result does not depend on the order in which limiting enzymes are
found.
* `flexibilize_enz_concs` gained `per_complex=True`: complexes are ranked and
raised as one unit, keeping levelled subunits levelled. Off by default.

* Fix: `write_dlkcat_input` and the OpenKineticsPredictor input builder
raised `TypeError: cannot use 'list' as a set element` on any model that
had been saved and reloaded. cobra returns an annotation value as a list
Expand Down
69 changes: 68 additions & 1 deletion docs/relaxation_methods.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# `flexibilize_enz_concs` vs `relax_proteomics_greedy`
# Relaxing proteomics constraints: `flexibilize_enz_concs`, `relax_proteomics_greedy` and `relax_enz_concs`

Both functions answer the same question — *the measured proteomics constraints
make the ecModel grow too slowly (or not at all); which enzyme bounds do we
loosen, and by how much, to restore feasible growth?* — but they are **not**
duplicates. They use different selection signals, different relaxation step
sizes, and produce results of very different stringency. This note records the
differences and the recommendation (keep both; do not merge).
`relax_enz_concs`, a third method that solves the same question as one LP, and
the per-complex options for all of them are described at the end.

## Side by side

Expand Down Expand Up @@ -50,3 +52,68 @@ follow-ups would make them more interchangeable:
the over-relaxation point in the modeling-intent review, §3.3).
2. Align the failure convention — currently one returns a `converged=False`
result and the other raises; pick one so callers don't special-case each.

## `relax_enz_concs`: the least total relaxation, in one LP

Both methods above relax one enzyme at a time: rank, raise the top one,
re-solve, repeat. Which enzymes end up raised then depends on the ranking
signal and on the order in which enzymes become limiting. On a large model
with noisy proteomics, a small change to the ranking (for example, control
coefficient per mg against gain per fold) can change which enzymes are
raised and, downstream, predictions that depend on the remaining caps.

`relax_enz_concs` asks the question once. Every measured cap becomes
`usage <= conc * (1 + x)`, the protein pool may grow by a fold too, and one LP
minimises the sum of the `x` subject to `growth >= exp_growth`:

- `weight="relative"` (default) minimises summed fold increases, so an enzyme
measured at almost nothing is not relaxed freely because its absolute cost is
small; `weight="absolute"` minimises the mg added.
- The pool's fold costs `pool_weight` (default 1000) times an enzyme's, so the
pool grows only where raising enzymes cannot reach the target, the role of
the pool fallback in `flexibilize_enz_concs`.
- Folds count from the current caps, so a cap raised earlier is never lowered.
- If no relaxation reaches the target, it raises `ValueError`: growth is then
not limited by protein.

It returns a `FlexEnzResult` like `flexibilize_enz_concs` (`frequence` is 1 for
every entry). Use it when the outcome should not depend on the order of
relaxation, and to compare with the iterative methods: if they disagree on
which enzymes need raising, conclusions that hinge on those enzymes are
fragile.

## Enzyme complexes

Absolute proteomics recovers the subunits of one complex unevenly: subunits
present 1:1 can come out several-fold apart while their replicates agree within
a few percent. Capped one by one, a complex can then carry no more flux than
its least abundant measurement allows, and relaxation methods that raise one
subunit at a time leave its subunits at unrelated levels, or raise a different
subunit in each condition.

- `level_complex_concs` (after `fill_enz_concs`, before `constrain_enz_concs`)
gives every complex one abundance: the median per copy over its measured
subunits, scaled back to each subunit by its copies in `ec.rxn_enz_mat`;
unmeasured subunits are filled. `level_complex_abundances` does the same on
molar data before it is written into the model.
- `complex_families` defines the complexes. Two enzyme sets are one complex when
they share at least two subunits and half the larger set (isozyme variants,
the same complex on several reactions). A protein in three or more complexes,
in two with equal weight, or that also catalyses a reaction alone is *shared*
and keeps its own measurement: electron carriers (cytochrome c), thioredoxin,
or the E3 subunit of the 2-oxo-acid dehydrogenases, which would otherwise
chain unrelated complexes together. A subunit in two complexes belongs to the
one with more reactions (core subunits that also appear in an assembly step).
- `flexibilize_enz_concs(per_complex=True)` and `relax_enz_concs(per_complex=True)`
raise the measured subunits of a complex by one factor, so levelled
concentrations stay levelled. In `flexibilize_enz_concs` complexes are ranked
by the growth gained per fold of raising all their caps (the usage reactions'
reduced costs times the caps), which leaves out the protein pool's price and
does not depend on which of several simultaneously binding subunits the solver
puts the dual on.

Levelling relies on the stoichiometry in `ec.rxn_enz_mat`; where
`apply_complex_data` found no exact ComplexPortal match, every subunit counts
as one copy. Per-complex relaxation without levelling raises every subunit by
the fold its worst-measured subunit needs, which inflates the well-measured
ones; use the two together.
12 changes: 12 additions & 0 deletions src/geckopy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,16 +111,22 @@

# limit_proteins
from geckopy.limit_proteins import (
ComplexFamilies,
FlexEnzResult,
GreedyRelaxResult,
LevelComplexResult,
RelaxationStep,
apply_flux_data_constraints,
calculate_f_factor,
complex_families,
constrain_enz_concs,
constrain_flux_data, # deprecated; alias of apply_flux_data_constraints
fill_enz_concs,
flexibilize_enz_concs,
get_conc_control_coeffs,
level_complex_abundances,
level_complex_concs,
relax_enz_concs,
relax_proteomics_greedy,
)

Expand Down Expand Up @@ -206,15 +212,21 @@
"sensitivity_tuning",
"truncate_values",
# limit_proteins
"ComplexFamilies",
"FlexEnzResult",
"GreedyRelaxResult",
"LevelComplexResult",
"RelaxationStep",
"apply_flux_data_constraints",
"calculate_f_factor",
"complex_families",
"constrain_enz_concs",
"fill_enz_concs",
"flexibilize_enz_concs",
"get_conc_control_coeffs",
"level_complex_abundances",
"level_complex_concs",
"relax_enz_concs",
"relax_proteomics_greedy",
# utilities
"AddNewRxnsResult",
Expand Down
14 changes: 14 additions & 0 deletions src/geckopy/limit_proteins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,36 @@
from .fill_enz_concs import fill_enz_concs
from .flexibilize_enz_concs import FlexEnzResult, flexibilize_enz_concs
from .get_conc_control_coeffs import get_conc_control_coeffs
from .level_complex_concs import (
ComplexFamilies,
LevelComplexResult,
complex_families,
level_complex_abundances,
level_complex_concs,
)
from .relax_enz_concs import relax_enz_concs
from .relax_proteomics_greedy import (
GreedyRelaxResult,
RelaxationStep,
relax_proteomics_greedy,
)

__all__ = [
"ComplexFamilies",
"FlexEnzResult",
"LevelComplexResult",
"GreedyRelaxResult",
"RelaxationStep",
"apply_flux_data_constraints",
"calculate_f_factor",
"complex_families",
"constrain_enz_concs",
"constrain_flux_data", # deprecated; alias of apply_flux_data_constraints
"fill_enz_concs",
"flexibilize_enz_concs",
"get_conc_control_coeffs",
"level_complex_abundances",
"level_complex_concs",
"relax_enz_concs",
"relax_proteomics_greedy",
]
Loading
Loading