Skill Tracer
Skills › Scientific Agent Skills › cobrapy › api_quick_reference.md

api_quick_reference.md

310 lines · skills/cobrapy/references/api_quick_reference.md at 5f2bc03

Open on GitHub
1# COBRApy API reference
2
3Checked against cobra **0.32.1** installed signatures/source and current official
4docs. Examples with `model` assume a fresh `load_model("textbook")`; placeholders
5such as `filename` are illustrative, not standalone runnable commands.
6

COBRApy API reference

Checked against cobra 0.32.1 installed signatures/source and current official docs. Examples with model assume a fresh load_model("textbook"); placeholders such as filename are illustrative, not standalone runnable commands.

Raw lines 1 to 6
7## Model I/O and repository contracts
8
9```python
10from cobra.io import (
11 load_model, read_sbml_model, write_sbml_model,
12 load_json_model, save_json_model, load_yaml_model, save_yaml_model,
13 load_matlab_model, save_matlab_model,
16model = load_model("textbook") # Local, model.id == "e_coli_core"
17# Also local: load_model("iJO1366"), load_model("salmonella")
18# Remote illustrative calls; adapters may fail at redirects (see below):
19# model = load_model("e_coli_core")
20# model = load_model("iML1515")
22# Illustrative file I/O: use your actual paths.
23# model = read_sbml_model("model.xml.gz")
24# write_sbml_model(model, "model.xml")
25# save_json_model(model, "model.json", pretty=True)
26# model = load_json_model("model.json")
27# save_yaml_model(model, "model.yml")
28# model = load_yaml_model("model.yml")
29# save_matlab_model(model, "model.mat", varname="model")
30# model = load_matlab_model("model.mat", variable_name="model")
31```
33MATLAB I/O needs SciPy (`cobra[array]`). Keep the default SBML `f_replace` mapping
34unless intentionally preserving SBML-encoded IDs; `f_replace={}` disables prefix
35and character conversion. Verify identifiers after round-trip before selecting an
36objective. Formats may not retain arbitrary custom solver constraints; store those
37separately and reconstruct them on reload.
39`load_model(model_id, repositories=..., cache=True)` searches bundled data, BiGG,
40then BioModels. In released 0.32.1 the `cache` argument is unused despite its
41docstring: save downloaded files explicitly. A non-404 HTTP failure aborts lookup;
42it does not necessarily fall through to the next provider. Repository-specific
43IDs are not interchangeable. The BioModels adapter only supports a directly listed
44XML file; OMEX-only records require an explicitly chosen SBML extraction workflow.
46Verified unauthenticated provider routes (2026-09-30):
48| Provider | Request and response | Current adapter caveat |
49| --- | --- | --- |
50| BiGG | `GET https://bigg.ucsd.edu/static/models/{id}.xml.gz` returns gzip-compressed SBML; iML1515 parsed as 2,712 reactions | 0.32.1 uses HTTP and does not follow the HTTP-to-HTTPS redirect |
51| BioModels files | `GET https://www.biomodels.org/model/files/{id}` with `Accept: application/json`; `main` is a list of file records with `name` and `fileSize` | 0.32.1 uses the bare hostname and does not follow its listing redirect |
52| BioModels download | `GET https://www.biomodels.org/model/download/{id}?filename={name}` returns the selected file; follow redirects | Select an actual XML name from the listing, not an invented name or arbitrary first file |
54These are single-model/file operations with no pagination or credentials. The
55`MODEL1510010000` listing and selected XML download were executed and parsed. That BioModels file lacked an optimization objective and many explicit flux bounds,
56so its successful parse is not a validated FBA model. SDK
57remote failures were observed separately; they do not mean the services retired.
59A bounded BiGG fallback, executed on iML1515:
61```python
62from pathlib import Path
63from urllib.request import urlopen
64from cobra.io import read_sbml_model
66path = Path("iML1515.xml.gz") # Use the task's output directory.
67with urlopen("https://bigg.ucsd.edu/static/models/iML1515.xml.gz", timeout=30) as response:
68 data = response.read(16 * 1024 * 1024 + 1)
69if len(data) > 16 * 1024 * 1024:
70 raise ValueError("Model exceeds this example's 16 MiB download limit")
71path.write_bytes(data)
72model = read_sbml_model(path)
73assert len(model.reactions) == 2712
74```
76For BioModels, fetch the JSON listing, choose `main` entries whose `name` ends in
77`.xml`, URL-encode that filename using `urllib.parse.urlencode`, and use the download
78route in the table with the same finite timeout/size check. Save the original file,
79retrieval date and checksum. Do not assume every SBML record is a constraint-based
80metabolic model with the required objective and flux bounds.

Model I/O and repository contracts

from cobra.io import (
    load_model, read_sbml_model, write_sbml_model,
    load_json_model, save_json_model, load_yaml_model, save_yaml_model,
    load_matlab_model, save_matlab_model,
)

model = load_model("textbook")  # Local, model.id == "e_coli_core"
# Also local: load_model("iJO1366"), load_model("salmonella")
# Remote illustrative calls; adapters may fail at redirects (see below):
# model = load_model("e_coli_core")
# model = load_model("iML1515")

# Illustrative file I/O: use your actual paths.
# model = read_sbml_model("model.xml.gz")
# write_sbml_model(model, "model.xml")
# save_json_model(model, "model.json", pretty=True)
# model = load_json_model("model.json")
# save_yaml_model(model, "model.yml")
# model = load_yaml_model("model.yml")
# save_matlab_model(model, "model.mat", varname="model")
# model = load_matlab_model("model.mat", variable_name="model")

MATLAB I/O needs SciPy (cobra[array]). Keep the default SBML f_replace mapping unless intentionally preserving SBML-encoded IDs; f_replace={} disables prefix and character conversion. Verify identifiers after round-trip before selecting an objective. Formats may not retain arbitrary custom solver constraints; store those separately and reconstruct them on reload.

load_model(model_id, repositories=..., cache=True) searches bundled data, BiGG, then BioModels. In released 0.32.1 the cache argument is unused despite its docstring: save downloaded files explicitly. A non-404 HTTP failure aborts lookup; it does not necessarily fall through to the next provider. Repository-specific IDs are not interchangeable. The BioModels adapter only supports a directly listed XML file; OMEX-only records require an explicitly chosen SBML extraction workflow.

Verified unauthenticated provider routes (2026-09-30):

ProviderRequest and responseCurrent adapter caveat
BiGGGET https://bigg.ucsd.edu/static/models/{id}.xml.gz returns gzip-compressed SBML; iML1515 parsed as 2,712 reactions0.32.1 uses HTTP and does not follow the HTTP-to-HTTPS redirect
BioModels filesGET https://www.biomodels.org/model/files/{id} with Accept: application/json; main is a list of file records with name and fileSize0.32.1 uses the bare hostname and does not follow its listing redirect
BioModels downloadGET https://www.biomodels.org/model/download/{id}?filename={name} returns the selected file; follow redirectsSelect an actual XML name from the listing, not an invented name or arbitrary first file

These are single-model/file operations with no pagination or credentials. The MODEL1510010000 listing and selected XML download were executed and parsed. That BioModels file lacked an optimization objective and many explicit flux bounds, so its successful parse is not a validated FBA model. SDK remote failures were observed separately; they do not mean the services retired.

A bounded BiGG fallback, executed on iML1515:

from pathlib import Path
from urllib.request import urlopen
from cobra.io import read_sbml_model

path = Path("iML1515.xml.gz")  # Use the task's output directory.
with urlopen("https://bigg.ucsd.edu/static/models/iML1515.xml.gz", timeout=30) as response:
    data = response.read(16 * 1024 * 1024 + 1)
if len(data) > 16 * 1024 * 1024:
    raise ValueError("Model exceeds this example's 16 MiB download limit")
path.write_bytes(data)
model = read_sbml_model(path)
assert len(model.reactions) == 2712

For BioModels, fetch the JSON listing, choose main entries whose name ends in .xml, URL-encode that filename using urllib.parse.urlencode, and use the download route in the table with the same finite timeout/size check. Save the original file, retrieval date and checksum. Do not assume every SBML record is a constraint-based metabolic model with the required objective and flux bounds.

Raw lines 7 to 81
82## Core objects and editing
84```python
85from cobra import Model, Reaction, Metabolite, Gene
86from cobra.io import load_model
88model = load_model("textbook")
89reaction = model.reactions.get_by_id("PFK")
90metabolite = model.metabolites.get_by_id("atp_c")
91print(reaction.bounds, reaction.gene_reaction_rule, metabolite.formula)
92print(model.exchanges, model.demands, model.sinks, model.boundary)
94# DictList.query uses a regular expression, case-sensitive by default.
95import re
96matches = model.reactions.query(re.compile("atp", re.IGNORECASE))
97glycolytic = model.reactions.query(lambda r: r.subsystem == "Glycolysis")
98assert "PFK" in model.reactions
100# Temporary changes are restored when the model context exits.
101with model:
102 reaction.bounds = (0, 5)
103 reaction.gene_reaction_rule = "gene1 and (gene2 or gene3)"
104 model.genes.get_by_id("gene1").knock_out()
105 solution = model.optimize()
106```
108Use `add_reactions([reaction])`, not removed `add_reaction`. Set GPRs on reactions
109so associated genes and reverse links are updated; do not mutate `model.genes`
110directly to remove genes. For permanent deletion use
111`cobra.manipulation.remove_genes(model, ["gene_id"], remove_reactions=True)` and
112inspect affected GPRs/reactions. `remove_reactions([...])` accepts IDs or objects;
113`remove_metabolites([...], destructive=False)` removes them from stoichiometries
114and needs biological review.
116`model.copy()` creates an independent model. To form a subset, copy each reaction:
117`new_model.add_reactions([r.copy() for r in chosen_reactions])`; adding the same
118Reaction objects to another model risks moving their model/solver associations.
119A reaction is reversible when **both** `lower_bound < 0` and `upper_bound > 0`.

Core objects and editing

from cobra import Model, Reaction, Metabolite, Gene
from cobra.io import load_model

model = load_model("textbook")
reaction = model.reactions.get_by_id("PFK")
metabolite = model.metabolites.get_by_id("atp_c")
print(reaction.bounds, reaction.gene_reaction_rule, metabolite.formula)
print(model.exchanges, model.demands, model.sinks, model.boundary)

# DictList.query uses a regular expression, case-sensitive by default.
import re
matches = model.reactions.query(re.compile("atp", re.IGNORECASE))
glycolytic = model.reactions.query(lambda r: r.subsystem == "Glycolysis")
assert "PFK" in model.reactions

# Temporary changes are restored when the model context exits.
with model:
    reaction.bounds = (0, 5)
    reaction.gene_reaction_rule = "gene1 and (gene2 or gene3)"
    model.genes.get_by_id("gene1").knock_out()
    solution = model.optimize()

Use add_reactions([reaction]), not removed add_reaction. Set GPRs on reactions so associated genes and reverse links are updated; do not mutate model.genes directly to remove genes. For permanent deletion use cobra.manipulation.remove_genes(model, ["gene_id"], remove_reactions=True) and inspect affected GPRs/reactions. remove_reactions([...]) accepts IDs or objects; remove_metabolites([...], destructive=False) removes them from stoichiometries and needs biological review.

model.copy() creates an independent model. To form a subset, copy each reaction: new_model.add_reactions([r.copy() for r in chosen_reactions]); adding the same Reaction objects to another model risks moving their model/solver associations. A reaction is reversible when both lower_bound < 0 and upper_bound > 0.

Raw lines 82 to 120
121### Balanced toy network and gap filling
123A mass/charge-balanced isomerization illustrates building without creating an
124unphysical ATP source. Boundary reactions deliberately connect the model to its
125environment. The toy's demand objective is product export, not a growth rate.
127```python
128from cobra import Model, Reaction, Metabolite
129from cobra.flux_analysis import gapfill
131model = Model("toy")
132model.solver = "glpk"
133a_e = Metabolite("a_e", formula="C6H12O6", charge=0, compartment="e")
134a_c = Metabolite("a_c", formula="C6H12O6", charge=0, compartment="c")
135b_c = Metabolite("b_c", formula="C6H12O6", charge=0, compartment="c")
136transport = Reaction("T", lower_bound=0, upper_bound=1000)
137transport.add_metabolites({a_e: -1, a_c: 1})
138conversion = Reaction("A_TO_B", lower_bound=0, upper_bound=1000)
139conversion.add_metabolites({a_c: -1, b_c: 1})
140conversion.gene_reaction_rule = "(gene1 and gene2) or gene3"
141model.add_reactions([transport, conversion])
142model.add_boundary(a_e, type="exchange", lb=-10, ub=1000)
143model.add_boundary(b_c, type="demand")
144model.objective = "DM_b_c"
145assert conversion.check_mass_balance() == {}
146assert abs(model.slim_optimize(error_value=None) - 10) < 1e-6
148universal = Model("candidates")
149universal.add_reactions([conversion.copy()])
150model.remove_reactions(["A_TO_B"])
151assert abs(model.slim_optimize(error_value=None)) < 1e-6
152solutions = gapfill(
153 model, universal=universal, lower_bound=1.0,
154 demand_reactions=False, exchange_reactions=False, iterations=1,
156# Each entry is a LIST of Reaction objects; input model remains unchanged.
157assert [[r.id for r in candidate] for candidate in solutions] == [["A_TO_B"]]
158with model:
159 model.add_reactions([r.copy() for r in solutions[0]])
160 assert model.slim_optimize(error_value=None) >= 1 - 1e-6
161```
163`gapfill(..., penalties=None)` supports reaction-ID and category penalties
164(`universal`, `exchange`, `demand`). Defaults permit added demand reactions but not
165exchanges. `iterations=5` reuses one GapFiller and increases costs of used reactions;
166five fresh calls do not preserve that penalty history or guarantee unique answers.
167For real data, use a curated universal reaction model with matched metabolite IDs,
168compartments, chemistry and directionality. Do not treat a MILP-selected reaction
169as evidence that the organism encodes it.

Balanced toy network and gap filling

A mass/charge-balanced isomerization illustrates building without creating an unphysical ATP source. Boundary reactions deliberately connect the model to its environment. The toy's demand objective is product export, not a growth rate.

from cobra import Model, Reaction, Metabolite
from cobra.flux_analysis import gapfill

model = Model("toy")
model.solver = "glpk"
a_e = Metabolite("a_e", formula="C6H12O6", charge=0, compartment="e")
a_c = Metabolite("a_c", formula="C6H12O6", charge=0, compartment="c")
b_c = Metabolite("b_c", formula="C6H12O6", charge=0, compartment="c")
transport = Reaction("T", lower_bound=0, upper_bound=1000)
transport.add_metabolites({a_e: -1, a_c: 1})
conversion = Reaction("A_TO_B", lower_bound=0, upper_bound=1000)
conversion.add_metabolites({a_c: -1, b_c: 1})
conversion.gene_reaction_rule = "(gene1 and gene2) or gene3"
model.add_reactions([transport, conversion])
model.add_boundary(a_e, type="exchange", lb=-10, ub=1000)
model.add_boundary(b_c, type="demand")
model.objective = "DM_b_c"
assert conversion.check_mass_balance() == {}
assert abs(model.slim_optimize(error_value=None) - 10) < 1e-6

universal = Model("candidates")
universal.add_reactions([conversion.copy()])
model.remove_reactions(["A_TO_B"])
assert abs(model.slim_optimize(error_value=None)) < 1e-6
solutions = gapfill(
    model, universal=universal, lower_bound=1.0,
    demand_reactions=False, exchange_reactions=False, iterations=1,
)
# Each entry is a LIST of Reaction objects; input model remains unchanged.
assert [[r.id for r in candidate] for candidate in solutions] == [["A_TO_B"]]
with model:
    model.add_reactions([r.copy() for r in solutions[0]])
    assert model.slim_optimize(error_value=None) >= 1 - 1e-6

gapfill(..., penalties=None) supports reaction-ID and category penalties (universal, exchange, demand). Defaults permit added demand reactions but not exchanges. iterations=5 reuses one GapFiller and increases costs of used reactions; five fresh calls do not preserve that penalty history or guarantee unique answers. For real data, use a curated universal reaction model with matched metabolite IDs, compartments, chemistry and directionality. Do not treat a MILP-selected reaction as evidence that the organism encodes it.

Raw lines 121 to 170
171## Optimization and solution interpretation
173```python
174from cobra.io import load_model
175from cobra.flux_analysis import pfba, geometric_fba
177model = load_model("textbook")
178solution = model.optimize(raise_error=True)
179print(solution.status, solution.objective_value)
180print(solution.fluxes, solution.shadow_prices, solution.reduced_costs)
181value = model.slim_optimize(error_value=None)
182with model:
183 model.objective = "ATPM" # Also accepts Reaction or {Reaction: coefficient}.
184 model.objective_direction = "max"
185 atp_solution = model.optimize(raise_error=True)
186 print("ATPM flux:", atp_solution.objective_value) # Not biomass growth.
188parsimonious = pfba(model, fraction_of_optimum=1.0)
189centered = geometric_fba(model, epsilon=1e-6, max_tries=200, processes=1)
190```
192`pfba` first optimizes the specified objective, then minimizes total absolute flux.
193Its returned objective is that secondary sum. Read the biomass flux explicitly.
194Dual values are solver/problem dependent; integer problems need not provide them.
195A default `slim_optimize()` failure returns NaN; use `error_value=None` to raise.
196Do not label any feasible objective as growth unless it is the growth objective.
198`model.solver.configuration` exposes `timeout`, `verbosity` and supported tolerance
199settings. Inspect `cobra.util.solver.solvers` before selecting `glpk`, `hybrid`,
200`cplex`, or `gurobi`. Optional commercial solvers require installation/licensing;
201GLPK does not support quadratic objectives. Set and report realistic tolerances,
202not a silently unsupported solver-specific setting.

Optimization and solution interpretation

from cobra.io import load_model
from cobra.flux_analysis import pfba, geometric_fba

model = load_model("textbook")
solution = model.optimize(raise_error=True)
print(solution.status, solution.objective_value)
print(solution.fluxes, solution.shadow_prices, solution.reduced_costs)
value = model.slim_optimize(error_value=None)
with model:
    model.objective = "ATPM"  # Also accepts Reaction or {Reaction: coefficient}.
    model.objective_direction = "max"
    atp_solution = model.optimize(raise_error=True)
    print("ATPM flux:", atp_solution.objective_value)  # Not biomass growth.

parsimonious = pfba(model, fraction_of_optimum=1.0)
centered = geometric_fba(model, epsilon=1e-6, max_tries=200, processes=1)

pfba first optimizes the specified objective, then minimizes total absolute flux. Its returned objective is that secondary sum. Read the biomass flux explicitly. Dual values are solver/problem dependent; integer problems need not provide them. A default slim_optimize() failure returns NaN; use error_value=None to raise. Do not label any feasible objective as growth unless it is the growth objective.

model.solver.configuration exposes timeout, verbosity and supported tolerance settings. Inspect cobra.util.solver.solvers before selecting glpk, hybrid, cplex, or gurobi. Optional commercial solvers require installation/licensing; GLPK does not support quadratic objectives. Set and report realistic tolerances, not a silently unsupported solver-specific setting.

Raw lines 171 to 203
204## Analysis contracts
206| Operation | Relevant arguments | Return and interpretation |
207| --- | --- | --- |
208| `flux_variability_analysis` | `reaction_list=None`, `loopless=None`, `fraction_of_optimum=1.0`, `pfba_factor=None`, `processes=1` | DataFrame indexed by reaction ID, `minimum`/`maximum`; extrema optimized independently |
209| `single_gene_deletion` | `gene_list=None`, `method="fba"`, `solution=None`, `processes=1` | DataFrame columns `ids` (set), `growth`, `status` |
210| `single_reaction_deletion` | `reaction_list=None`, same method/solution/process arguments | Same schema; objective must be growth to call the result growth |
211| `double_gene_deletion` | `gene_list1`, `gene_list2`, method/solution/process arguments | Same schema; singleton sets are self-pairs, not double mutants |
212| `double_reaction_deletion` | `reaction_list1`, `reaction_list2`, method/solution/process arguments | Same schema; no MultiIndex assumption |
213| `find_blocked_reactions` | `reaction_list=None`, `zero_cutoff=None`, `open_exchanges=False`, `processes=1` | List of **ID strings** in released implementation; cutoff cannot be smaller than model tolerance |
214| `find_essential_genes` / `find_essential_reactions` | `threshold=None`, `processes=1` | Sets of Gene / Reaction objects; default cutoff is 1% of objective optimum; inspect failed solves separately |
215| `production_envelope` | `reactions`, `objective=None`, `carbon_sources=None`, `points=20`, `threshold=None` | Grid DataFrame with reaction coordinates, `flux_minimum`, `flux_maximum`, `carbon_yield_*`, `mass_yield_*`, `carbon_source` |
217FVA loopless choices are `"fastSNP"` (optimal loopless bounds) and
218`"cycleFreeFlux"` (loop removal without guaranteed optimal bounds); booleans are
219deprecated. A `pfba_factor` greater than one caps the total flux relative to pFBA.
220Loopless does not mean full thermodynamic consistency with measured energies.
222`production_envelope` accepts IDs or reaction objects for scanned reactions.
223The grid has `points ** len(reactions)` rows, so bound its dimension and resolution.
224`threshold` is a numerical zero cutoff, not a minimum objective requirement;
225default uses model tolerance. Set growth requirements explicitly. Yields need a
226single interpretable output objective and suitable carbon/formula annotations;
227missing or zero denominators can produce NaN. Two-axis plots should visualize the
228objective value as a surface/color, not only scatter the input coordinates.

Analysis contracts

OperationRelevant argumentsReturn and interpretation
flux_variability_analysisreaction_list=None, loopless=None, fraction_of_optimum=1.0, pfba_factor=None, processes=1DataFrame indexed by reaction ID, minimum/maximum; extrema optimized independently
single_gene_deletiongene_list=None, method="fba", solution=None, processes=1DataFrame columns ids (set), growth, status
single_reaction_deletionreaction_list=None, same method/solution/process argumentsSame schema; objective must be growth to call the result growth
double_gene_deletiongene_list1, gene_list2, method/solution/process argumentsSame schema; singleton sets are self-pairs, not double mutants
double_reaction_deletionreaction_list1, reaction_list2, method/solution/process argumentsSame schema; no MultiIndex assumption
find_blocked_reactionsreaction_list=None, zero_cutoff=None, open_exchanges=False, processes=1List of ID strings in released implementation; cutoff cannot be smaller than model tolerance
find_essential_genes / find_essential_reactionsthreshold=None, processes=1Sets of Gene / Reaction objects; default cutoff is 1% of objective optimum; inspect failed solves separately
production_envelopereactions, objective=None, carbon_sources=None, points=20, threshold=NoneGrid DataFrame with reaction coordinates, flux_minimum, flux_maximum, carbon_yield_*, mass_yield_*, carbon_source

FVA loopless choices are "fastSNP" (optimal loopless bounds) and "cycleFreeFlux" (loop removal without guaranteed optimal bounds); booleans are deprecated. A pfba_factor greater than one caps the total flux relative to pFBA. Loopless does not mean full thermodynamic consistency with measured energies.

production_envelope accepts IDs or reaction objects for scanned reactions. The grid has points ** len(reactions) rows, so bound its dimension and resolution. threshold is a numerical zero cutoff, not a minimum objective requirement; default uses model tolerance. Set growth requirements explicitly. Yields need a single interpretable output objective and suitable carbon/formula annotations; missing or zero denominators can produce NaN. Two-axis plots should visualize the objective value as a surface/color, not only scatter the input coordinates.

Raw lines 204 to 229
230### Sampling
232```python
233from cobra.io import load_model
234from cobra.sampling import sample, OptGPSampler, ACHRSampler
236model = load_model("textbook")
237samples = sample(model, n=20, method="achr", thinning=100, seed=7)
238sampler = OptGPSampler(model, processes=1, thinning=100, seed=7)
239samples = sampler.sample(20)
240validation = sampler.validate(samples)
241assert (validation == "v").all()
242for batch in sampler.batch(10, 2):
243 assert len(batch) == 10
244```
246`sample(..., method="auto")` now chooses CHRR if hopsy is installed, otherwise
247OptGP. Use `"optgp"`, `"achr"`, or optional `"chrr"` explicitly. ACHR uses one
248process; OptGP can return more than `n` samples when `n` is not divisible by
249`processes`. CHRR requires `cobra[chrr]` and incurs an initial rounding cost;
250that backend was not executed here. Validation codes `v`, `l`, `u`, `e` indicate
251valid, lower-bound, upper-bound and equality checks; codes may combine violations.
252Thinning and a passing feasibility check do not establish chain convergence.

Sampling

from cobra.io import load_model
from cobra.sampling import sample, OptGPSampler, ACHRSampler

model = load_model("textbook")
samples = sample(model, n=20, method="achr", thinning=100, seed=7)
sampler = OptGPSampler(model, processes=1, thinning=100, seed=7)
samples = sampler.sample(20)
validation = sampler.validate(samples)
assert (validation == "v").all()
for batch in sampler.batch(10, 2):
    assert len(batch) == 10

sample(..., method="auto") now chooses CHRR if hopsy is installed, otherwise OptGP. Use "optgp", "achr", or optional "chrr" explicitly. ACHR uses one process; OptGP can return more than n samples when n is not divisible by processes. CHRR requires cobra[chrr] and incurs an initial rounding cost; that backend was not executed here. Validation codes v, l, u, e indicate valid, lower-bound, upper-bound and equality checks; codes may combine violations. Thinning and a passing feasibility check do not establish chain convergence.

Raw lines 230 to 253
254### Media
256```python
257from cobra.medium import minimal_medium
259medium = model.medium
260medium["EX_glc__D_e"] = 10.0
261medium["EX_o2_e"] = 20.0
262model.medium = medium
263min_medium = minimal_medium(
264 model, min_objective_value=0.1, exports=False,
265 minimize_components=False, open_exchanges=False,
267```
269There is **no `penalties` argument** to `minimal_medium` in 0.32.1. Its default
270minimizes total import flux; `minimize_components=True` uses MILP to minimize
271component count. With `open_exchanges=False`, the allowed nutrient universe stays
272fixed but the optimizer selects the subset and import bounds within it. An integer
273greater than one asks for alternative media. The
274return is a Series, a DataFrame of alternatives, or None on failure. `exports`
275controls whether exporting fluxes appear in the returned medium; it does not
276control whether secretion is allowed in the model. The medium mapping lists
277positive import bounds; assigning it closes omitted import directions while
278retaining export bounds.

Media

from cobra.medium import minimal_medium

medium = model.medium
medium["EX_glc__D_e"] = 10.0
medium["EX_o2_e"] = 20.0
model.medium = medium
min_medium = minimal_medium(
    model, min_objective_value=0.1, exports=False,
    minimize_components=False, open_exchanges=False,
)

There is **no penalties argument** to minimal_medium in 0.32.1. Its default minimizes total import flux; minimize_components=True uses MILP to minimize component count. With open_exchanges=False, the allowed nutrient universe stays fixed but the optimizer selects the subset and import bounds within it. An integer greater than one asks for alternative media. The return is a Series, a DataFrame of alternatives, or None on failure. exports controls whether exporting fluxes appear in the returned medium; it does not control whether secretion is allowed in the model. The medium mapping lists positive import bounds; assigning it closes omitted import directions while retaining export bounds.

Raw lines 254 to 279
280## Validation and summaries
282`reaction.check_mass_balance()` returns elemental/charge imbalances or an empty
283dict, but missing formulas or charges can prevent a meaningful check. The
284`cobra.manipulation.check_mass_balance(model)` helper excludes annotated
285boundary/biomass/pseudo reactions. `check_metabolite_compartment_formula(model)`
286only checks whether nonempty formulas are alphanumeric: despite its name it does
287not comprehensively validate chemical formulas, compartment assignment, or charges.
288Use the explicit missing-data and exception reporting in [workflows](workflows.md).
290`model.summary()`, `model.metabolites.atp_c.summary()` and
291`model.reactions.PFK.summary()` return summary objects. `model.summary(fva=0.95)`
292adds FVA at that fraction. Summaries often compute pFBA internally; state the
293constraints and objective rather than treating them as measured fluxes.

Validation and summaries

reaction.check_mass_balance() returns elemental/charge imbalances or an empty dict, but missing formulas or charges can prevent a meaningful check. The cobra.manipulation.check_mass_balance(model) helper excludes annotated boundary/biomass/pseudo reactions. check_metabolite_compartment_formula(model) only checks whether nonempty formulas are alphanumeric: despite its name it does not comprehensively validate chemical formulas, compartment assignment, or charges. Use the explicit missing-data and exception reporting in workflows.

model.summary(), model.metabolites.atp_c.summary() and model.reactions.PFK.summary() return summary objects. model.summary(fva=0.95) adds FVA at that fraction. Summaries often compute pFBA internally; state the constraints and objective rather than treating them as measured fluxes.

Raw lines 280 to 294
295## Official sources and release/source distinctions
297- [0.32.1 release](https://github.com/opencobra/cobrapy/releases/tag/0.32.1)
298- [Model I/O](https://cobrapy.readthedocs.io/en/latest/io.html)
299- [Released model loader](https://github.com/opencobra/cobrapy/blob/0.32.1/src/cobra/io/web/load.py)
300- [Released BiGG adapter](https://github.com/opencobra/cobrapy/blob/0.32.1/src/cobra/io/web/bigg_models_repository.py)
301- [Released BioModels adapter](https://github.com/opencobra/cobrapy/blob/0.32.1/src/cobra/io/web/biomodels_repository.py)
302- [BiGG iML1515 record](https://bigg.ucsd.edu/models/iML1515)
303- [Model methods](https://cobrapy.readthedocs.io/en/latest/autoapi/cobra/core/model/index.html)
304- [Flux analysis](https://cobrapy.readthedocs.io/en/latest/autoapi/cobra/flux_analysis/index.html)
305- [Minimal medium signature](https://cobrapy.readthedocs.io/en/latest/autoapi/cobra/medium/minimal_medium/index.html)
306- [Released sampler selection](https://github.com/opencobra/cobrapy/blob/0.32.1/src/cobra/sampling/sampling.py)
308Some generated API prose still describes the old deletion index or blocked-reaction
309object type. The released implementation, tutorial tables and executed return
310values are the authority for the schemas recorded here.

Official sources and release/source distinctions

Some generated API prose still describes the old deletion index or blocked-reaction object type. The released implementation, tutorial tables and executed return values are the authority for the schemas recorded here.

Raw lines 295 to 310