StrainDesign
A COBRApy[1]-based package for computational design of metabolic networks
The comprehensive StrainDesign package for MILP-based strain design computation with the COBRApy toolbox supports MCS, MCS with nested optimization, OptKnock [2], RobustKnock [3] and OptCouple [4], GPR-rule integration, gene and reaction knockouts and additions as well as regulatory interventions. The automatic lossless network and GPR compression allows strain design computations from genome-scale metabolic networks. Supported solvers are GLPK (available from COBRApy), CPLEX, Gurobi and SCIP [5].
Download Jupyter notebook examples ...
The default compression uses a pure Python sparse RREF implementation. A legacy Java-based compression via EFMTool [6] is optionally available (see Legacy Methods). Note that the Java backend (via JPype) is known to conflict with CPLEX’s native library when both are loaded in the same Python session. If you use CPLEX, we recommend the default Python compression backend.
Installation:
The StrainDesign package is available on pip and Anaconda. To install the latest release, run:
pip install straindesign
or
conda install -c cnapy straindesign
Developer Installation:
Download the repository and run
pip install -e .
in the main folder. Through the installation with -e, updates from a ‘git pull’ are at once available in your Python environment without the need for a reinstallation.
Apple Silicon (ARM64) note:
On macOS with Apple Silicon (M1/M2/M3), the default GLPK solver (shipped with COBRApy) may produce inaccurate results for larger metabolic models due to numerical precision differences on the ARM64 architecture. If you encounter unexpected infeasibility or incorrect flux values, we recommend installing an alternative solver:
Gurobi (free academic license):
pip install gurobipySCIP (open source):
pip install pyscipopt
Then specify the solver explicitly when calling StrainDesign functions, e.g.
sd_setup = {..., 'solver': 'gurobi'} or sd_setup = {..., 'solver': 'scip'}.
For standalone analysis functions: fba(model, solver='gurobi') or fva(model, solver='scip').
Examples:
Computation examples are provided in the different chapters of this documentation. The original Jupyer notebook files are located in the StrainDesign package at docs/source/examples.
How to cite:
Schneider P., Bekiaris P. S., von Kamp A., Klamt S. - StrainDesign: a comprehensive Python package for computational design of metabolic networks. Bioinformatics, btac632 (2022)
Contents:
- Solvers
- Network Analysis
- Plotting the flux space
- Computational strain design: Growth-coupled production (GCP)
- Minimal Cut Sets (MCS)
- Prerequisites
- Example 1: Strain designs with a minimum product (1,4-butanediol) yield (SUCP strain design)
- Example 2: Enforce product (1,4-BDO) synthesis at all growth states (dGCP strain design)
- Example 3: Suppress flux states that are optimal with respect to a pre-defined objective function (wGCP strain design)
- Example 4: Protect flux states that are optimal with respect to a pre-defined objective function (pGCP strain design)
- Example 5: All single gene knockouts that prohibit growth (synthetic lethals).
- Example 6: Genome-scale strain designs with a minimum product (1,4-butanediol) yield (SUCP strain design)
- Example 7: Suppress flux states in a toy network
- Example 8: Suppress and protect flux states in a toy network
- Theoretical background
- Multi-level strain optimization approaches
- Standalone network compression
- CNApy interface
- StrainDesign API
- straindesign
- Submodules
- straindesign.compression
- straindesign.compute_strain_designs
- straindesign.cplex_interface
- straindesign.efmtool_cmp_interface
- straindesign.glpk_interface
- straindesign.gurobi_interface
- straindesign.indicatorConstraints
- straindesign.lptools
- straindesign.names
- straindesign.networktools
- straindesign.parse_constr
- straindesign.pool
- straindesign.scip_interface
- straindesign.solver_interface
- straindesign.speedy_fva
- straindesign.strainDesignMILP
- straindesign.strainDesignModule
- straindesign.strainDesignProblem
- straindesign.strainDesignSolutions
- Package Contents
- Submodules
- straindesign
- Legacy Methods
- StrainDesign Developer’s Guide
- How to read this guide
- Chapters at a glance
- Repository structure
- 1. Orientation & the strain-design problem
- 1.1 The metabolic model
- 1.2 Desired vs. undesired flux regions
- 1.3 SUPPRESS and PROTECT: precise semantics
- 1.4 The bilevel variants (conceptual only)
- 1.5 Interventions, costs, the binary vector
z, and “minimal” - 1.6 The end-to-end pipeline at a glance
- 1.7 How the package is invoked
- 1.8 Notation reference
- 2. The constraint-based foundation
- 3. Network compression
- 3.1 Why compress at all
- 3.2 The exact integer/rational nullspace engine
- 3.3 The compression working state and the single-kernel pass
- 3.4 Coupled / flux-coupled merge
- 3.5 Conservation-relation removal (ROW-rank reduction)
- 3.6 Blocked and zero-flux removal
- 3.7 The alternating fixpoint
- 3.8 Parallel merge
- 3.9 GPR propagation through compression
- 3.10 The compression map
cmp_mapReacand back-expansion - 3.11 The legacy efmtool (Java) backend
- 4. GPR integration
- 4.1 Why encode gene logic as flux structure at all
- 4.2
extend_model_gpr: turning a rule into pseudo-metabolites and pseudoreactions - 4.3 Why the original flux space is unchanged (the correctness invariant)
- 4.4 Reversible-reaction split and the
reac_mapremap - 4.5
reduce_gpr: pruning before extension - 4.6 The two-compression-pass boundary and why regulatory genes are exempt from pass #1
- 4.7 Name truncation (sha256), Gurobi/GLPK only
- 5. FVA in preprocessing
- 5.1 The essentiality test — geometry of
min(abs(range)) > 1e-10 and prod(sign(range)) > 0 - 5.2 FVA #1 — essential reactions in PROTECT/desired modules (pre-GPR)
- 5.3 FVA #2 —
bound_blocked_or_irrevers_fva: relaxing non-binding bounds - 5.4 FVA #3 — knockable-scoped essentials and size-1 MCS extraction
- 5.5 The
speedy_fvaacceleration engine - 5.6 Why FVA #2 is the ~117 s genome-scale bottleneck
- 5.1 The essentiality test — geometry of
- 6. Dualization (the mathematical core)
- 6.1 Why dualize at all
- 6.2 LP duality refresher, in the exact standard form the code uses
- 6.3 Farkas’ lemma and the SUPPRESS infeasibility certificate
- 6.4 Strong-duality encoding of bilevel problems
- 6.5 One dualization, reused across every module type
- 6.5.1 Inner-objective PROTECT / SUPPRESS (
strainDesignProblem.py) - 6.5.2 OptKnock — bilevel max-min (
strainDesignProblem.py) - 6.5.3 RobustKnock — three levels, two nested dualizations (
strainDesignProblem.py) - 6.5.4 OptCouple — growth-coupling distance (
strainDesignProblem.py) - 6.5.5 DoubleOpt — two parallel strong-duality links (
strainDesignProblem.py) - 6.5.6 The unifying picture
- 6.5.1 Inner-objective PROTECT / SUPPRESS (
- 6.6 Boundary with Chapter 7
- 7. MILP construction & the z-linking
- 7.1 Notation and the shape of the master problem
- 7.2
SDProblem.__init__— the seed rows,num_z, and the M switch - 7.3
addModule— block-diagonal assembly - 7.4
prevent_boundary_knockouts— why nonzero-sign bounds must be moved - 7.5
link_z— the heart of the chapter - 7.6 Indicator constraints (
indicatorConstraints.py) - 7.7 Why indicators give a tighter LP relaxation than big-M
- 7.8 The emergent SUPPRESS→indicator / PROTECT→big-M split
- 7.9 Final consolidation and the binary block
- 8. Solving & enumeration
- 8.1 The objective is both a vector and a constraint row
- 8.2
solveZ/solve: what one solver call returns - 8.3 The three approaches, their objective setups, and why
- 8.4 The iterative loop and integer cuts (the minimal-and-distinct guarantee)
- 8.5
verify_sd: re-checking validity in the true continuous subsystem - 8.6 Solver parameters and determinism
- 8.7 Verified performance: the phase timeline and CPLEX vs Gurobi
- 8.8 How SCIP and GLPK differ
- 9. Decompression & solution semantics
- 9.1 Why decompression is needed, and the shape of the compression map
- 9.2 The math of reverse expansion (
expand_sd) - 9.3 Size‑1 MCS re‑injection
- 9.4
filter_sd_maxcost: why a post‑expansion cost re‑check is mandatory - 9.5 KI/KO value encoding and the strip semantics
- 9.6 Gene‑level vs reaction‑level translation (
_translate_genes_to_reactions)
- 10. Known issues, gotchas & failure modes
- 10.1 Issue #43 (OPEN) — gene-level designs with no reaction-level effect
- 10.2 Issue #38 (OPEN) — superset/subset (non-minimal) solutions
- 10.3 Issue #44 (CLOSED 2026-06-23) — PROTECT violated under gene_kos, as a cautionary tale
- 10.4 Gotcha (a) —
compute_strain_designsmutates the caller’sreg_cost/module dicts in place - 10.5 Gotcha (b) — Gurobi/GLPK-only name truncation (sha256; CPLEX exempt)
- 10.6 Gotcha (c) — solver numeric-status robustness (Gurobi 12 NUMERIC; CPLEX 5/6 unscaled-infeasibilities)
- 10.7 Other footguns
- 11. Performance, benchmarking & roadmap
- 12. Model surgery & constraint parsing
- 12.1
remove_ext_mets— deleting the boundary layer before compression - 12.2
parse_constr.py— strings intoA·x {≤,=,≥} b - 12.3
extend_model_regulatory— a bound as an intervention - 12.4
gene_kos_to_constraints— a gene KO set as flux constraints - 12.5 Module & cost compression — keeping references consistent with a moving index
- 12.6 Ordering, and why it is load-bearing
- 12.1
- 13. The object model & result API
- 14. The solver-interface layer (
MILP_LP+ backends)- 14.1 Why an abstraction layer exists
- 14.2 The canonical problem and the
MILP_LPconstructor - 14.3 The three solve entry points and result normalization
- 14.4 Indicator constraints vs big-M, per backend
- 14.5 Status-code translation to the canonical vocabulary
- 14.6 Parameters: seed, threads, tolerances, time limit, working memory
- 14.7 The solution pool: native (CPLEX, Gurobi) vs emulated (SCIP, GLPK)
- 14.8 Numeric-status robustness
- 14.9 Where the CPLEX-vs-Gurobi performance story physically lives
- 15. Analysis & exploration API
- 15.1
fba— flux balance analysis and its parsimonious variants - 15.2
fva— flux variability analysis as a public tool - 15.3
yopt— yield optimization by linear-fractional programming - 15.4
plot_flux_space— production envelopes, yield space, and 3D projections - 15.5 Compressed-analysis tools (PR #56)
- 15.6 Where these fit the developer’s workflow
- 15.1
- 16. Testing & contributing
References:
[1] Ebrahim, A., Lerman, J.A., Palsson, B.O. et al. - COBRApy: COnstraints-Based Reconstruction and Analysis for Python. BMC Syst Biol 7, 74 (2013)
[2] Burgard, A. P., Pharkya, P., & Maranas, C. D. - Optknock: a bilevel programming framework for identifying gene knockout strategies for microbial strain optimization. Biotechnology and bioengineering, 84(6), 647–657 (2003)
[3] Tepper N., Shlomi T. - Predicting metabolic engineering knockout strategies for chemical production: accounting for competing pathways, Bioinformatics. Volume 26, Issue 4, Pages 536–543 (2010)
[4] Jensen K., Broeken V., Lærke Hansen A.S., et al. - OptCouple: Joint simulation of gene knockouts, insertions and medium modifications for prediction of growth-coupled strain designs. Metabolic Engineering Communications, Volume 8 (2019)
[5] Bestuzheva K., Besançon M., Chen W.K. et al. - The SCIP Optimization Suite 8.0. Available at Optimization Online and as ZIB-Report 21-41, (2021)
[6] Marco Terzer, Jörg Stelling, Large-scale computation of elementary flux modes with bit pattern trees, Bioinformatics, Volume 24, Issue 19, (2008), Pages 2229–2235,
