Skip to content

API Reference

SBML

sbml

load_model(filename)

Source code in src/fluxbound/io/sbml.py
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
def load_model(filename: str) -> Model:

    sbml_model = load_sbml(filename)
    fbc_model = sbml_model.getPlugin("fbc")
    is_fbc = fbc_model is not None

    params = {p.getId(): p.getValue() for p in sbml_model.getListOfParameters()}

    model = Model(sbml_model.getId())
    load_compartments(sbml_model, model)
    load_metabolites(sbml_model, model, is_fbc)
    if is_fbc:
        load_genes(sbml_model, model)
    load_reactions(sbml_model, model, is_fbc, params)
    if is_fbc:
        load_objective(sbml_model, model)
    apply_heuristics(model)

    return model

save_model(model, filename)

Source code in src/fluxbound/io/sbml.py
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
def save_model(model: Model, filename: str) -> None:

    document = sb.SBMLDocument()
    sbml_model = document.createModel(model.id)
    document.enablePackage(sb.FbcExtension.getXmlnsL3V1V2(), "fbc", True)
    fbc_model = sbml_model.getPlugin("fbc")
    fbc_model.setStrict(True)
    document.setPackageRequired("fbc", False)

    save_compartments(model, sbml_model)
    save_metabolites(model, sbml_model)
    save_genes(model, sbml_model)
    save_reactions(model, sbml_model)
    save_objective(model, sbml_model)
    save_fluxbounds(model, sbml_model)
    save_metadata(model, sbml_model)
    writer = sb.SBMLWriter()
    writer.writeSBML(document, filename)

Simulation

FBA(model, objective=None, minimize=False, parsimonious=False, obj_frac=1.0, constraints=None, solver=None, get_values=True, shadow_prices=False)

Run a Flux Balance Analysis (FBA) simulation:

Parameters:

Name Type Description Default
model

a constraint-based model

required
objective str | dict | None

objective coefficients or single reaction to optimize

None
minimize bool

whether to minimize the objective function

False
parsimonious bool

run parsimonious FBA (pFBA)

False
obj_frac float

minimum fraction of FBA objective (for pFBA)

1.0
constraints dict | None

environmental or additional constraints

None
solver Solver | None

re-use a solver pre-initialized with the model (for speed)

None
get_values bool | list

set to false if you only care about the objective value (for speed)

True
shadow_prices bool

calculate shadow prices

False

Returns:

Type Description
Solution

Solution

Source code in src/fluxbound/simulation/fba.py
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
def FBA(
    model,
    objective: str | dict | None = None,
    minimize: bool = False,
    parsimonious: bool = False,
    obj_frac: float = 1.0,
    constraints: dict | None = None,
    solver: Solver | None = None,
    get_values: bool | list = True,
    shadow_prices: bool = False,
) -> Solution:
    """Run a Flux Balance Analysis (FBA) simulation:

    Arguments:
        model: a constraint-based model
        objective: objective coefficients or single reaction to optimize
        minimize: whether to minimize the objective function
        parsimonious: run parsimonious FBA (pFBA)
        obj_frac: minimum fraction of FBA objective (for pFBA)
        constraints: environmental or additional constraints
        solver: re-use a solver pre-initialized with the model (for speed)
        get_values: set to false if you only care about the objective value (for speed)
        shadow_prices: calculate shadow prices

    Returns:
        Solution
    """

    if not objective:
        objective = model.objective

    if not solver:
        solver = solver_instance(model)

    if not parsimonious:
        solution = solver.solve(
            objective,
            minimize=minimize,
            constraints=constraints,
            get_values=get_values,
            shadow_prices=shadow_prices,
        )
        return solution
    else:
        pre_solution = solver.solve(
            objective,
            minimize=minimize,
            constraints=constraints,
            get_values=False,
        )

        if pre_solution.status != Status.OPTIMAL:
            warn("Failed to find an optimal solution for initial problem.")
            return pre_solution

        if isinstance(objective, str):
            objective = {objective: 1}

        solver.add_constraint("obj", objective, ">", obj_frac * pre_solution.fobj)  # pyright: ignore

        if solver.reuse_for is None:
            solver.reuse_for = "pFBA"
            for r_id, rxn in model.reactions.items():
                if rxn.lb < 0:
                    solver.add_variable(f"{r_id}_abs", 0, inf)
            solver.update()
            for r_id, rxn in model.reactions.items():
                if rxn.lb < 0:
                    solver.add_constraint(
                        f"c_{r_id}_lower", {r_id: -1, f"{r_id}_abs": 1}, ">", 0
                    )
                    solver.add_constraint(
                        f"c_{r_id}_upper", {r_id: 1, f"{r_id}_abs": 1}, ">", 0
                    )
            solver.update()
        elif solver.reuse_for != "pFBA":
            raise RuntimeError(
                "Attempting to reuse a solver for pFBA previously used for: "
                + solver.reuse_for
            )

        objective = dict()
        for r_id, rxn in model.reactions.items():
            if rxn.lb < 0:
                objective[f"{r_id}_abs"] = 1
            else:
                objective[r_id] = 1

        solution = solver.solve(
            objective,
            minimize=True,
            constraints=constraints,
            get_values=list(model.reactions),
            shadow_prices=shadow_prices,
        )
        solver.remove_constraint("obj")

        return solution

FVA(model, obj_frac=0, reactions=None, constraints=None)

Run Flux Variability Analysis (FVA).

Parameters:

Name Type Description Default
obj_frac float

minimum fraction of the default objective

0
reactions list | None

list of reactions to analyze (default: all)

None
constraints dict | None

additional constraints

None

Returns:

Type Description

flux variation

Source code in src/fluxbound/simulation/fva.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
def FVA(
    model: Model,
    obj_frac: float = 0,
    reactions: list | None = None,
    constraints: dict | None = None,
):
    """Run Flux Variability Analysis (FVA).

    Arguments:
        model a constraint-based model
        obj_frac: minimum fraction of the default objective
        reactions: list of reactions to analyze (default: all)
        constraints: additional constraints

    Returns:
        flux variation
    """

    solver = solver_instance(model)

    if obj_frac > 0:
        solution = FBA(model, constraints=constraints, solver=solver)
        if solution.status != Status.OPTIMAL:
            raise RuntimeError("Unable to calculate objective: " + str(solution.status))
        obj_min = obj_frac * solution.fobj  # pyright: ignore
        solver.add_constraint("obj_min", model.objective, ">", obj_min)

    if not reactions:
        reactions = list(model.reactions.keys())

    lower = {}
    for r_id in reactions:
        solution = FBA(
            model, r_id, True, constraints=constraints, solver=solver, get_values=False
        )

        if solution.status == Status.OPTIMAL:
            lower[r_id] = solution.fobj
        elif (
            solution.status == Status.UNBOUNDED or solution.status == Status.INF_OR_UNB
        ):
            lower[r_id] = -inf
        elif solution.status == Status.INFEASIBLE:
            lower[r_id] = None
            warn("Infeasible solution")
        else:
            lower[r_id] = None
            warn("Unknown solution status")

    upper = {}
    for r_id in reactions:
        solution = FBA(
            model, r_id, False, constraints=constraints, solver=solver, get_values=False
        )

        if solution.status == Status.OPTIMAL:
            upper[r_id] = solution.fobj
        elif (
            solution.status == Status.UNBOUNDED or solution.status == Status.INF_OR_UNB
        ):
            upper[r_id] = inf
        elif solution.status == Status.INFEASIBLE:
            upper[r_id] = None
            warn("Infeasible solution")
        else:
            upper[r_id] = None
            warn("Unknown solution status")

    return {(lower[r_id], upper[r_id]) for r_id in reactions}

Deletion

deletion

gene_deletion(model, genes, parsimonious=False, constraints=None, skip_silent=False, get_values=True, solver=None)

Source code in src/fluxbound/simulation/deletion.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def gene_deletion(
    model: Model,
    genes: str | list,
    parsimonious: bool = False,
    constraints: dict | None = None,
    skip_silent: bool = False,
    get_values: bool = True,
    solver: Solver | None = None,
) -> Solution | None:

    if isinstance(genes, str):
        genes = [genes]

    del_rxns = deleted_genes_to_reactions(model, genes)

    if len(del_rxns) == 0 and skip_silent:
        return

    return reaction_deletion(
        model, del_rxns, parsimonious, constraints, get_values, solver
    )

reaction_deletion(model, reactions, parsimonious=False, constraints=None, get_values=True, solver=None)

Source code in src/fluxbound/simulation/deletion.py
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
def reaction_deletion(
    model: Model,
    reactions: str | list,
    parsimonious: bool = False,
    constraints: dict | None = None,
    get_values: bool = True,
    solver: Solver | None = None,
) -> Solution:

    if isinstance(reactions, str):
        reactions = [reactions]

    del_and_env = {}

    if constraints is not None:
        del_and_env.update(constraints)

    for r_id in reactions:
        del_and_env[r_id] = 0

    return FBA(
        model,
        parsimonious=parsimonious,
        constraints=del_and_env,
        get_values=get_values,
        solver=solver,
    )

essential_genes(model, growth_frac=0.01, constraints=None)

Source code in src/fluxbound/simulation/deletion.py
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
def essential_genes(
    model: Model, growth_frac: float = 0.01, constraints: dict | None = None
) -> list:

    solver = solver_instance(model)
    sol0 = FBA(model, constraints=constraints, solver=solver)

    if sol0.status != Status.OPTIMAL:
        raise RuntimeError("Reference condition is infeasible.")

    essential = []

    for gene in model.genes.keys():
        sol = gene_deletion(
            model,
            gene,
            constraints=constraints,
            solver=solver,
            skip_silent=True,
            get_values=False,
        )
        if sol is None:
            continue
        elif sol.status == Status.INFEASIBLE:
            essential.append(gene)
        elif sol.status == Status.OPTIMAL:
            if sol.fobj < growth_frac * sol0.fobj:  # pyright: ignore
                essential.append(gene)

    return essential

essential_reactions(model, growth_frac=0.01, constraints=None)

Source code in src/fluxbound/simulation/deletion.py
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
def essential_reactions(
    model: Model, growth_frac: float = 0.01, constraints: dict | None = None
) -> list:

    solver = solver_instance(model)
    sol0 = FBA(model, constraints=constraints, solver=solver)

    if sol0.status != Status.OPTIMAL:
        raise RuntimeError("Reference condition is infeasible.")

    if sol0.status == Status.OPTIMAL and abs(sol0.fobj) < 1e-6:  # pyright: ignore
        raise RuntimeError("No growth at reference condition.")

    essential = []

    for r_id in model.reactions.keys():
        sol = reaction_deletion(
            model, r_id, constraints=constraints, solver=solver, get_values=False
        )
        if sol.status == Status.INFEASIBLE:
            essential.append(r_id)
        elif sol.status == Status.OPTIMAL:
            if sol.fobj < growth_frac * sol0.fobj:  # pyright: ignore
                essential.append(r_id)

    return essential

Base classes

model

Gene(gene_id, name=None)

Source code in src/fluxbound/core/model.py
47
48
def __init__(self, gene_id: str, name: str | None = None) -> None:
    super().__init__(elem_id=gene_id, name=name)

Metabolite(met_id, compartment, name=None)

Source code in src/fluxbound/core/model.py
41
42
43
def __init__(self, met_id: str, compartment: str, name: str | None = None) -> None:
    super().__init__(elem_id=met_id, name=name)
    self.compartment: str = compartment

Compartment(comp_id, name=None, external=False, size=1.0)

Source code in src/fluxbound/core/model.py
27
28
29
30
31
32
33
34
35
36
37
def __init__(
    self,
    comp_id: str,
    name: str | None = None,
    external: bool = False,
    size: float = 1.0,
) -> None:

    super().__init__(elem_id=comp_id, name=name)
    self.size: float = size
    self.external = external

Reaction(rxn_id, name=None, stoichiometry=None, lb=-inf, ub=inf, gpr=None, rtype=ReactionType.OTHER)

Source code in src/fluxbound/core/model.py
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
def __init__(
    self,
    rxn_id: str,
    name: str | None = None,
    stoichiometry: AttrDict | None = None,
    lb: float = -inf,
    ub: float = inf,
    gpr: GPR | None = None,
    rtype: ReactionType = ReactionType.OTHER,
) -> None:

    super().__init__(rxn_id, name)
    self.stoichiometry: AttrDict = (
        stoichiometry if stoichiometry is not None else AttrDict()
    )

    if ub < lb:
        raise ValueError(
            f"Upper bound ({ub}) cannot be less than lower bound ({lb})"
        )

    self.lb: float = lb
    self.ub: float = ub
    self.gpr: GPR | None = gpr
    self.rtype: ReactionType = rtype

Model(model_id, name=None)

Source code in src/fluxbound/core/model.py
168
169
170
171
172
173
174
175
176
177
178
def __init__(self, model_id: str, name: str | None = None) -> None:

    super().__init__(elem_id=model_id, name=name)
    self.compartments: AttrDict = AttrDict()
    self.metabolites: AttrDict = AttrDict()
    self.genes: AttrDict = AttrDict()
    self.reactions: AttrDict = AttrDict()
    self.objective: AttrDict = AttrDict()
    self._updated: bool = True
    self._met_rxn_lookup: dict | None = None
    self._gene_rxn_lookup: dict | None = None

Environment()

Source code in src/fluxbound/core/environment.py
10
11
def __init__(self) -> None:
    AttrDict.__init__(self)

empty(model, inplace=False) staticmethod

Generate an empty growth medium for a given model

Parameters:

Name Type Description Default
inplace bool

apply to model or return environment object

False

Returns:

Type Description
Environment | None

Environment (if not inplace)

Source code in src/fluxbound/core/environment.py
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
@staticmethod
def empty(model: Model, inplace: bool = False) -> "Environment | None":
    """
    Generate an empty growth medium for a given model

    Arguments:
        model (to extract a list of all exchange reactions)
        inplace: apply to model or return environment object

    Returns:
        Environment (if not inplace)

    """

    env = Environment()

    for r_id in model.get_exchange_reactions():
        env[r_id] = (0, inf)

    if inplace:
        env.apply(model, exclusive=False, inplace=True)
    else:
        return env

complete(model, max_uptake=10.0, inplace=False) staticmethod

Generate a complete growth medium for a given model

Parameters:

Name Type Description Default
max_uptake float

maximum uptake rate

10.0
inplace bool

apply to model or return environment object

False

Returns:

Type Description
Environment | None

Environment (if not inplace)

Source code in src/fluxbound/core/environment.py
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
@staticmethod
def complete(
    model: Model, max_uptake: float = 10.0, inplace: bool = False
) -> "Environment | None":
    """
    Generate a complete growth medium for a given model

    Arguments:
        model (to extract a list of all exchange reactions)
        max_uptake: maximum uptake rate
        inplace: apply to model or return environment object

    Returns:
        Environment (if not inplace)

    """

    env = Environment()

    for r_id in model.get_exchange_reactions():
        env[r_id] = (-max_uptake, inf)

    if inplace:
        env.apply(model, exclusive=False, inplace=True)
    else:
        return env

from_compounds(compounds, fmt_func, max_uptake=10.0) staticmethod

Initialize environment from list of medium compounds

Parameters:

Name Type Description Default
compounds list

list of compounds in the medium

required
fmt_func FunctionType

function to convert metabolite ids to exchange reaction ids

required
max_uptake float

maximum uptake rate for given compounds

10.0
Source code in src/fluxbound/core/environment.py
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
@staticmethod
def from_compounds(
    compounds: list, fmt_func: FunctionType, max_uptake: float = 10.0
) -> "Environment":
    """
    Initialize environment from list of medium compounds

    Arguments:
        compounds: list of compounds in the medium
        fmt_func: function to convert metabolite ids to exchange reaction ids
        max_uptake: maximum uptake rate for given compounds

    """

    env = Environment()

    for cpd in compounds:
        r_id = fmt_func(cpd)
        env[r_id] = (-max_uptake, inf)

    return env

from_model(model) staticmethod

Source code in src/fluxbound/core/environment.py
20
21
22
23
24
25
26
27
28
29
@staticmethod
def from_model(model: Model) -> "Environment":

    env = Environment()

    for r_id in model.get_exchange_reactions():
        rxn = model.reactions[r_id]
        env[r_id] = rxn.lb, rxn.ub

    return env

apply(model, exclusive=True, inplace=True, warning=True)

Apply environmental conditions to a given model

Parameters:

Name Type Description Default
exclusive bool

block uptake compounds not specified in this environment

True
warning bool

print warning for exchange reactions not found in the model

True
inplace bool

apply to model, otherwise return a constraints dict

True
Source code in src/fluxbound/core/environment.py
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def apply(
    self,
    model: Model,
    exclusive: bool = True,
    inplace: bool = True,
    warning: bool = True,
) -> dict | None:
    """
    Apply environmental conditions to a given model

    Args:
        model (to extract a list of all exchange reactions)
        exclusive: block uptake compounds not specified in this environment
        warning: print warning for exchange reactions not found in the model
        inplace: apply to model, otherwise return a constraints dict
    """

    if exclusive:
        env = Environment.empty(model, inplace=False)
        env.update(self)  # pyright: ignore
    else:
        env = self

    constraints = {}

    for r_id, (lb, ub) in env.items():  # pyright: ignore
        if r_id in model.reactions:
            if inplace:
                model.set_flux_bounds(r_id, lb, ub)
            else:
                constraints[r_id] = (lb, ub)
        elif warning:
            warn(f"Exchange reaction not in model: {r_id}")

    if not inplace:
        return constraints

simplify(inplace=False)

Remove reactions with blocked uptake.

Source code in src/fluxbound/core/environment.py
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
def simplify(self, inplace: bool = False) -> "Environment | None":
    """Remove reactions with blocked uptake."""

    if inplace:
        env = self
    else:
        env = self.copy()

    to_remove = []

    for r_id, (lb, _) in env.items():
        if lb >= 0:
            to_remove.append(r_id)

    for r_id in to_remove:
        del env[r_id]

    if not inplace:
        return env  # pyright: ignore