SMomentModel.jl 5.46 KB
Newer Older
1
2
3
4
5

"""
    struct _smoment_column

A helper type that describes the contents of [`SMomentModel`](@ref)s.
St. Elmo's avatar
St. Elmo committed
6
"""
7
8
9
10
11
12
13
14
15
16
17
struct _smoment_column
    reaction_id::Int # number of the corresponding reaction in the inner model
    direction::Int # 0 if "as is" and unique, -1 if reverse-only part, 1 if forward-only part
    coupling_row::Int # number of row in the coupling (0 if direction==0)
    lb::Float64 # must be 0 if the reaction is unidirectional (if direction!=0)
    ub::Float64
    capacity_required::Float64 # must be 0 for bidirectional reactions (if direction==0)
end

# TODO fix the docstring
"""
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
    struct SMomentModel <: ModelWrapper

Construct an enzyme-capacity constrained model using sMOMENT algorithm, as
described by *Bekiaris, Pavlos Stephanos, and Steffen Klamt, "Automatic
construction of metabolic models with enzyme constraints" BMC bioinformatics,
2020*.

Use [`make_smoment_model`](@ref) or [`with_smoment`](@ref) to construct the
models.

The model is constructed as follows:
- stoichiometry of the original model is retained as much as possible, but
  enzymatic reations are split into forward and reverse parts (marked by a
  suffix like `...#forward` and `...#reverse`),
- sums of forward and reverse reaction pair fluxes are constrained accordingly
  to the original model,
- stoichiometry is expanded by a virtual metabolite "enzyme capacity" which is
  consumed by all enzymatic reactions at a rate given by enzyme mass divided by
  the corresponding kcat,
- the total consumption of the enzyme capacity is constrained by a fixed
  maximum.

The `SMomentModel` structure contains a worked-out representation of the
optimization problem atop a wrapped [`MetabolicModel`](@ref), in particular the
separation of certain reactions into unidirectional forward and reverse parts,
the grouping of these reactions together into virtual "arm" reactions constrained
by bounds from the inner model, an "enzyme capacity" required for each
reaction, and the value of the maximum capacity constraint.

In the structure, field `columns` describes the correspondence of stoichiometry
columns to the stoichiometry and data of the internal wrapped model; field
`coupling_row_reaction` maps the generated coupling constraints to reaction
indexes in the wrapped model, and `total_enzyme_capacity` is the total bound on
the enzyme capacity consumption as specified in sMOMENT algorithm.

This implementation allows easy access to fluxes from the split reactions
(available in `reactions(model)`), while the original "simple" reactions from
the wrapped model are retained as [`fluxes`](@ref). All additional constraints
are implemented using [`coupling`](@ref) and [`coupling_bounds`](@ref).
Original coupling is retained.
"""
struct SMomentModel <: ModelWrapper
60
61
62
    columns::Vector{_smoment_column}
    coupling_row_reaction::Vector{Int}
    total_enzyme_capacity::Float64
St. Elmo's avatar
St. Elmo committed
63

64
    inner::MetabolicModel
St. Elmo's avatar
St. Elmo committed
65
66
end

67
unwrap_model(model::SMomentModel) = model.inner
St. Elmo's avatar
St. Elmo committed
68
69

"""
70
    stoichiometry(model::SMomentModel)
St. Elmo's avatar
St. Elmo committed
71

72
73
Return a stoichiometry of the [`SMomentModel`](@ref). The enzymatic reactions
are split into unidirectional forward and reverse ones.
St. Elmo's avatar
St. Elmo committed
74
"""
75
76
stoichiometry(model::SMomentModel) =
    stoichiometry(model.inner) * _smoment_column_reactions(model)
St. Elmo's avatar
St. Elmo committed
77
78
79
80

"""
    objective(model::SMomentModel)

81
Reconstruct an objective of the [`SMomentModel`](@ref).
St. Elmo's avatar
St. Elmo committed
82
"""
83
objective(model::SMomentModel) = _smoment_column_reactions(model)' * objective(model.inner)
St. Elmo's avatar
St. Elmo committed
84
85

"""
Miroslav Kratochvil's avatar
Miroslav Kratochvil committed
86
    reactions(model::SMomentModel)
St. Elmo's avatar
St. Elmo committed
87

Miroslav Kratochvil's avatar
Miroslav Kratochvil committed
88
89
90
Returns the internal reactions in a [`SMomentModel`](@ref) (these may be split
to forward- and reverse-only parts; reactions IDs mangled accordingly with
suffixes).
St. Elmo's avatar
St. Elmo committed
91
"""
92
93
94
95
96
97
98
reactions(model::SMomentModel) =
    let inner_reactions = reactions(model.inner)
        [
            _smoment_reaction_name(inner_reactions[col.reaction_id], col.direction) for
            col in model.columns
        ]
    end
St. Elmo's avatar
St. Elmo committed
99
100

"""
101
    n_reactions(model::SMomentModel)
St. Elmo's avatar
St. Elmo committed
102

103
The number of reactions (including split ones) in [`SMomentModel`](@ref).
St. Elmo's avatar
St. Elmo committed
104
"""
105
n_reactions(model::SMomentModel) = length(model.columns)
St. Elmo's avatar
St. Elmo committed
106
107

"""
108
    bounds(model::SMomentModel)
St. Elmo's avatar
St. Elmo committed
109

110
Return the variable bounds for [`SMomentModel`](@ref).
St. Elmo's avatar
St. Elmo committed
111
"""
112
113
bounds(model::SMomentModel) =
    ([col.lb for col in model.columns], [col.ub for col in model.columns])
St. Elmo's avatar
St. Elmo committed
114
115

"""
116
    reaction_flux(model::SMomentModel)
St. Elmo's avatar
St. Elmo committed
117

118
119
Get the mapping of the reaction rates in [`SMomentModel`](@ref) to the original
fluxes in the wrapped model.
St. Elmo's avatar
St. Elmo committed
120
"""
121
reaction_flux(model::SMomentModel) =
122
    _smoment_column_reactions(model)' * reaction_flux(model.inner)
St. Elmo's avatar
St. Elmo committed
123
"""
124
    coupling(model::SMomentModel)
St. Elmo's avatar
St. Elmo committed
125

126
127
128
Return the coupling of [`SMomentModel`](@ref). That combines the coupling of
the wrapped model, coupling for split reactions, and the coupling for the total
enzyme capacity.
St. Elmo's avatar
St. Elmo committed
129
"""
130
coupling(model::SMomentModel) = vcat(
131
    coupling(model.inner) * _smoment_column_reactions(model),
132
133
134
    _smoment_reaction_coupling(model),
    [col.capacity_required for col in model.columns]',
)
St. Elmo's avatar
St. Elmo committed
135
136

"""
137
    n_coupling_constraints(model::SMomentModel)
St. Elmo's avatar
St. Elmo committed
138

139
140
Count the coupling constraints in [`SMomentModel`](@ref) (refer to
[`coupling`](@ref) for details).
St. Elmo's avatar
St. Elmo committed
141
"""
142
143
n_coupling_constraints(model::SMomentModel) =
    n_coupling_constraints(model.inner) + _smoment_n_reaction_couplings(model) + 1
St. Elmo's avatar
St. Elmo committed
144
145

"""
146
    coupling_bounds(model::SMomentModel)
St. Elmo's avatar
St. Elmo committed
147

148
149
The coupling bounds for [`SMomentModel`](@ref) (refer to [`coupling`](@ref) for
details).
St. Elmo's avatar
St. Elmo committed
150
"""
Miroslav Kratochvil's avatar
Miroslav Kratochvil committed
151
function coupling_bounds(model::SMomentModel)
152
    (ilb, iub) = coupling_bounds(model.inner)
Miroslav Kratochvil's avatar
Miroslav Kratochvil committed
153
    (rlb, rub) = _smoment_reaction_coupling_bounds(model)
154
    return (vcat(ilb, rlb, [0.0]), vcat(iub, rub, [model.total_enzyme_capacity]))
Miroslav Kratochvil's avatar
Miroslav Kratochvil committed
155
end