Code owners
Assign users and groups as approvers for specific file changes. Learn more.
CoreModel.jl 18.60 KiB
"""
add_reactions(
m::CoreModel,
s::V1,
b::V2,
c::AbstractFloat,
xl::AbstractFloat,
xu::AbstractFloat;
check_consistency = false,
) where {V1<:VecType,V2<:VecType}
Add reaction(s) to a `CoreModel` model `m`.
"""
function add_reactions(
m::CoreModel,
s::V1,
b::V2,
c::AbstractFloat,
xl::AbstractFloat,
xu::AbstractFloat;
check_consistency = false,
) where {V1<:VecType,V2<:VecType}
return add_reactions(
m,
sparse(reshape(s, (length(s), 1))),
sparse(b),
sparse([c]),
sparse([xl]),
sparse([xu]),
check_consistency = check_consistency,
)
end
"""
add_reactions(
m::CoreModel,
s::V1,
b::V2,
c::AbstractFloat,
xl::AbstractFloat,
xu::AbstractFloat,
rxn::String,
mets::K;
check_consistency = false,
) where {V1<:VecType,V2<:VecType,K<:StringVecType}
"""
function add_reactions(
m::CoreModel,
s::V1,
b::V2,
c::AbstractFloat,
xl::AbstractFloat,
xu::AbstractFloat,
rxn::String,
mets::K;
check_consistency = false,
) where {V1<:VecType,V2<:VecType,K<:StringVecType}
return add_reactions(
m,
sparse(reshape(s, (length(s), 1))),
sparse(b),
sparse([c]),
sparse([xl]),
sparse([xu]),
[rxn],
mets,
check_consistency = check_consistency,
)
end
"""
add_reactions(
m::CoreModel,
Sp::M,
b::V,
c::V,
xl::V,
xu::V;
check_consistency = false,
) where {M<:MatType,V<:VecType}
"""
function add_reactions(
m::CoreModel,
Sp::M,
b::V,
c::V,
xl::V,
xu::V;
check_consistency = false,
) where {M<:MatType,V<:VecType}
rxns = ["r$x" for x = length(m.rxns)+1:length(m.rxns)+length(xu)]
mets = ["m$x" for x = length(m.mets)+1:length(m.mets)+size(Sp)[1]]
return add_reactions(
m,
Sp,
b,
c,
xl,
xu,
rxns,
mets,
check_consistency = check_consistency,
)
end
"""
add_reactions(m1::CoreModel, m2::CoreModel; check_consistency = false)
Add all reactions from `m2` to `m1`.
"""
function add_reactions(m1::CoreModel, m2::CoreModel; check_consistency = false)
return add_reactions(
m1,
m2.S,
m2.b,
m2.c,
m2.xl,
m2.xu,
m2.rxns,
m2.mets,
check_consistency = check_consistency,
)
end
"""
add_reactions(
m::CoreModel,
Sp::M,
b::V,
c::V,
xl::V,
xu::V,
rxns::K,
mets::K;
check_consistency = false,
) where {M<:MatType,V<:VecType,K<:StringVecType}
"""
function add_reactions(
m::CoreModel,
Sp::M,
b::V,
c::V,
xl::V,
xu::V,
rxns::K,
mets::K;
check_consistency = false,
) where {M<:MatType,V<:VecType,K<:StringVecType}
Sp = sparse(Sp)
b = sparse(b)
c = sparse(c)
xl = sparse(xl)
xu = sparse(xu)
all([length(b), length(mets)] .== size(Sp, 1)) ||
throw(DimensionMismatch("inconsistent number of metabolites"))
all(length.([c, xl, xu, rxns]) .== size(Sp, 2)) ||
throw(DimensionMismatch("inconsistent number of reactions"))
new_reactions = findall(Bool[!(rxn in m.rxns) for rxn in rxns])
new_metabolites = findall(Bool[!(met in m.mets) for met in mets])
if check_consistency
(newReactions1, newMetabolites1) = verify_consistency(
m,
Sp,
b,
c,
xl,
xu,
rxns,
mets,
new_reactions,
new_metabolites,
)
end
new_mets = vcat(m.mets, mets[new_metabolites])
zero_block = spzeros(length(new_metabolites), n_reactions(m))
ext_s = vcat(sparse(m.S), zero_block)
mapping = [findfirst(isequal(met), new_mets) for met in mets]
(I, J, elements) = findnz(sparse(Sp[:, new_reactions]))
ext_sp = spzeros(length(new_mets), length(new_reactions))
for (k, i) in enumerate(I)
new_i = mapping[i]
ext_sp[new_i, J[k]] = elements[k]
end
new_s = hcat(ext_s, ext_sp)
newb = vcat(m.b, b[new_metabolites])
#new_c = hcat(m.C, spzeros(size(m.C, 1), length(new_reactions)))
newc = vcat(m.c, c[new_reactions])
newxl = vcat(m.xl, xl[new_reactions])
newxu = vcat(m.xu, xu[new_reactions])
new_rxns = vcat(m.rxns, rxns[new_reactions])
#new_lp = CoreModel(new_s, newb, new_c, m.cl, m.cu, newc, newxl, newxu, new_rxns, new_mets)
new_lp = CoreModel(new_s, newb, newc, newxl, newxu, new_rxns, new_mets)
if check_consistency
return (new_lp, new_reactions, new_metabolites)
else
return new_lp
end
end
"""
verify_consistency(
m::CoreModel,
Sp::M,
b::V,
c::V,
xl::V,
xu::V,
names::K,
mets::K,
new_reactions,
new_metabolites,
) where {M<:MatType,V<:VecType,K<:StringVecType}
Check the consistency of given reactions with existing reactions in `m`.
TODO: work in progress, doesn't return consistency status.
"""
function verify_consistency(
m::CoreModel,
Sp::M,
b::V,
c::V,
xl::V,
xu::V,
names::K,
mets::K,
new_reactions,
new_metabolites,
) where {M<:MatType,V<:VecType,K<:StringVecType}
if !isempty(new_reactions)
statuses = Vector{ReactionStatus}(undef, length(names))
for (i, name) in enumerate(names)
rxn_index = findfirst(isequal(name), m.rxns)
reaction = Sp[:, i]
stoich_index = findfirst(Bool[reaction == m.S[:, j] for j = 1:size(m.S, 2)])
if isnothing(rxn_index) & isnothing(stoich_index)
statuses[i] = ReactionStatus(false, 0, "new")
end
if !isnothing(rxn_index) & isnothing(stoich_index)
statuses[i] = ReactionStatus(true, 0, "same name")
end
if isnothing(rxn_index) & !isnothing(stoich_index)
statuses[i] = ReactionStatus(true, 0, "same stoichiometry")
end
if !isnothing(rxn_index) & !isnothing(stoich_index)
statuses[i] = ReactionStatus(true, 0, "same name, same stoichiometry")
end
end
end
return (new_reactions, new_metabolites)
end
"""
remove_metabolites(model::CoreModel, metabolites)
Removes a set of `metabolites` from the `model` of type `CoreModel` and returns
a new `CoreModel` without those metabolites. Here, `metabolites` can be either a
string, a vector of strings, an index or a vector of indices. Also removes any
reactions that have no associated metabolites after the metabolites have been
removed.
# Example
```
model = load_model(CoreModel, "e_coli_core.json")
m1 = remove_metabolites(model, ["glc__D_e", "for_c"])
m2 = remove_metabolites(model, "glc__D_e")
m3 = remove_metabolites(model, indexin(["glc__D_e", "for_c"], metabolites(model)))
m4 = remove_metabolites(model, first(indexin(["glc__D_e"], metabolites(model))))
```
"""
function remove_metabolites(model::CoreModel, mets)
mets_to_keep = filter(x -> x ∉ mets, 1:n_metabolites(model))
temp_S = model.S[mets_to_keep, :]
(I, rxns_to_keep, val) = findnz(temp_S)
sort!(rxns_to_keep)
unique!(rxns_to_keep)
new_S = model.S[mets_to_keep, rxns_to_keep]
new_b = model.b[mets_to_keep]
new_c = model.c[rxns_to_keep]
new_lbs = model.xl[rxns_to_keep]
new_ubs = model.xu[rxns_to_keep]
new_rxns = model.rxns[rxns_to_keep]
new_mets = model.mets[mets_to_keep]
return CoreModel(new_S, new_b, new_c, new_lbs, new_ubs, new_rxns, new_mets)
end
function remove_metabolites(model::CoreModel, met::Integer)
return remove_metabolites(model, [met])
end
function remove_metabolites(model::CoreModel, met::String)
return remove_metabolites(model, [met])
end
function remove_metabolites(model::CoreModel, mets::Vector{String})
met_indices = filter(!isnothing, indexin(mets, metabolites(model)))
if isempty(met_indices)
return model
else
return remove_metabolites(model, met_indices)
end
end
"""
Removes a set of reactions from a CoreModel.
Also removes the metabolites not involved in any reaction.
"""
function remove_reactions(m::CoreModel, rxns::Vector{Int})
rxns_to_keep = filter(e -> e ∉ rxns, 1:n_reactions(m))
temp_s = m.S[:, rxns_to_keep]
(mets_to_keep, J, val) = findnz(temp_s)
sort!(mets_to_keep)
unique!(mets_to_keep)
new_s = m.S[mets_to_keep, rxns_to_keep]
newb = m.b[mets_to_keep]
newc = m.c[rxns_to_keep]
newxl = m.xl[rxns_to_keep]
newxu = m.xu[rxns_to_keep]
new_rxns = m.rxns[rxns_to_keep]
new_mets = m.mets[mets_to_keep]
new_model = CoreModel(new_s, newb, newc, newxl, newxu, new_rxns, new_mets)
return new_model
end
"""
remove_reactions(m::CoreModel, rxn::Integer)
"""
function remove_reactions(m::CoreModel, rxn::Integer)
return remove_reactions(m, [rxn])
end
"""
remove_reactions(m::CoreModel, rxn::String)
"""
function remove_reactions(m::CoreModel, rxn::String)
return remove_reactions(m, [rxn])
end
"""
remove_reactions(m::CoreModel, rxns::Vector{String})
"""
function remove_reactions(m::CoreModel, rxns::Vector{String})
rxn_indices = [findfirst(isequal(name), m.rxns) for name in intersect(rxns, m.rxns)]
if isempty(rxn_indices)
return m
else
return remove_reactions(m, rxn_indices)
end
end
"""
change_bounds!(
model::CoreModel,
reaction_idxs::Vector{Int};
lower_bounds = fill(-_constants.default_reaction_bound, length(reaction_idxs)),
upper_bounds = fill(constants.default_reaction_bound, length(reaction_idxs)),
)
Change the bounds of all reactions with indices `reaction_idxs` in `model` in-place. Note
that if the bound argument is not supplied then a default (see
`_constants.default_reaction_bound`) is used. The same default bound argument is
used for each reaction bound if not supplied.
See also: [`change_bound`](@ref), [`change_bound!`](@ref), [`change_bounds`](@ref)
# Example
```
change_bounds!(model, [2, 3]; lower_bounds=[-10, -20], ub=[10, 22])
change_bounds!(model, [2, 3]; lower_bounds=[-10.2, -14.3]) # all upper_bounds are set to _constants.default_reaction_bound
change_bounds!(model, [2, 3]; upper_bounds=[10.2, 23])
```
"""
function change_bounds!(
model::CoreModel,
reaction_idxs::Vector{Int};
lower_bounds = fill(-_constants.default_reaction_bound, length(reaction_idxs)),
upper_bounds = fill(constants.default_reaction_bound, length(reaction_idxs)),
)
for (rxn_idx, lb, ub) in zip(reaction_idxs, lower_bounds, upper_bounds)
change_bound!(model, rxn_idx; lower_bound=lb, upper_bound=ub)
end
end
"""
change_bound!(
model::CoreModel,
reaction_idx::Int;
lower_bound = -_constants.default_reaction_bound,
upper_bound = _constants.default_reaction_bound,
)
Change the bound of a reaction with index `reaction_idx` in `model` in-place. Note
that if the bound argument is not supplied then a default (see
`_constants.default_reaction_bound`) is used. The same default bound argument is
used for each reaction bound if not supplied.
See also: [`change_bound`](@ref), [`change_bound!`](@ref), [`change_bounds`](@ref)
# Example
```
change_bound!(model, 2; lower_bounds=-10, ub=22)
change_bound!(model, 2; lower_bounds=-10.2) # all upper_bounds are set to _constants.default_reaction_bound
change_bound!(model, 2; upper_bounds=10.2)
```
"""
function change_bound!(
model::CoreModel,
rxn::Int;
lower_bound = -_constants.default_reaction_bound,
upper_bound = _constants.default_reaction_bound,
)
model.xl[rxn] = lower_bound
model.xu[rxn] = upper_bound
end
"""
change_bounds!(
model::CoreModel,
rxn_ids::Vector{String};
lower_bounds = fill(-_constants.default_reaction_bound, length(rxn_ids)),
upper_bounds = fill(constants.default_reaction_bound, length(rxn_ids)),
)
Change the bounds of all reactions with `rxn_ids` in `model` in-place. Note
that if the bound argument is not supplied then a default (see
`_constants.default_reaction_bound`) is used. The same default bound argument is
used for each reaction bound if not supplied.
See also: [`change_bound`](@ref), [`change_bound!`](@ref), [`change_bounds`](@ref)
# Example
```
change_bounds!(model, ["PFL", "FBA"]; lower_bounds=[-10, -20], ub=[10, 22])
change_bounds!(model, ["PFL", "FBA"]; lower_bounds=[-10.2, -14.3]) # all upper_bounds are set to _constants.default_reaction_bound
change_bounds!(model, ["PFL", "FBA"]; upper_bounds=[10.2, 23])
```
"""
function change_bounds!(
model::CoreModel,
rxn_ids::Vector{String};
lower_bounds = fill(-_constants.default_reaction_bound, length(rxn_ids)),
upper_bounds = fill(constants.default_reaction_bound, length(rxn_ids)),
)
change_bounds!(model, Int.(indexin(rxn_ids, reactions(model))); lower_bounds = lower_bounds, upper_bounds = upper_bounds)
end
"""
change_bound!(
model::CoreModel,
reaction_id::String;
lower_bound = -_constants.default_reaction_bound,
upper_bound = _constants.default_reaction_bound,
)
Change the bounds of a reaction with `reaction_id` in `model` in-place. Note
that if the bound argument is not supplied then a default (see
`_constants.default_reaction_bound`) is used.
See also: [`change_bound`](@ref), [`change_bounds!`](@ref), [`change_bounds!`](@ref)
# Example
```
change_bound!(model, "PFL"; lower_bound=-10, ub=10)
change_bound!(model, "PFL"; lower_bound=-10.2) # upper_bound is set to _constants.default_reaction_bound
change_bound!(model, "PFL"; upper_bound=10)
```
"""
function change_bound!(
model::CoreModel,
rxn_id::String;
lower_bound = -_constants.default_reaction_bound,
upper_bound = _constants.default_reaction_bound,
)
change_bound!(model, first(indexin([rxn_id], reactions(model))); lower_bound = lower_bound, upper_bound = upper_bound)
end
"""
change_bounds(
model::CoreModel,
rxns::Vector{Int};
lower_bounds = fill(-_constants.default_reaction_bound, length(rxns)),
upper_bounds = fill(constants.default_reaction_bound, length(rxns)),
)
Return a shallow copy of the `model` where the bounds of all reactions with
indices `rxns` in `model` have been changed. Note that if the bound
argument is not supplied then a default (see
`_constants.default_reaction_bound`) is used. The same default bound argument is
used for each reaction bound if not supplied.
See also: [`change_bound`](@ref), [`change_bound!`](@ref), [`change_bounds`](@ref)
# Example
```
new_model = change_bounds(model, [2, 3]; lower_bounds=[-10, -20], ub=[10, 22])
new_model = change_bounds(model, [2, 3]; lower_bounds=[-10.2, -14.3]) # all upper_bounds are set to _constants.default_reaction_bound
new_model = change_bounds(model, [2, 3]; upper_bounds=[10.2, 23])
```
"""
function change_bounds(
model::CoreModel,
rxns::Vector{Int};
lower_bounds = fill(-_constants.default_reaction_bound, length(rxns)),
upper_bounds = fill(constants.default_reaction_bound, length(rxns)),
)
m = copy(model)
m.xl = copy(model.xl)
m.xu = copy(model.xu)
m.xl[rxns] .= lower_bounds
m.xu[rxns] .= upper_bounds
return m
end
"""
change_bound(
model::CoreModel,
reaction_idx::Int;
lower_bound = -_constants.default_reaction_bound,
upper_bound = _constants.default_reaction_bound,
)
Return a shallow copy of the `model` where the bound of a reaction with index
`reaction_idx` in `model` has been changed. Note that if the bound argument is
not supplied then a default (see `_constants.default_reaction_bound`) is used.
The same default bound argument is used for each reaction bound if not supplied.
See also: [`change_bound`](@ref), [`change_bound!`](@ref), [`change_bounds`](@ref)
# Example
```
new_model = change_bound(model, 2; lower_bounds=-10, ub=22)
new_model = change_bound(model, 2; lower_bounds=-10.2) # all upper_bounds are set to _constants.default_reaction_bound
new_model = change_bound(model, 2; upper_bounds=10.2)
```
"""
function change_bound(
model::CoreModel,
rxn::Int;
lower_bound = -_constants.default_reaction_bound,
upper_bound = _constants.default_reaction_bound,
)
m = copy(model)
m.xl = copy(model.xl)
m.xu = copy(model.xu)
m.xl[rxn] = lower_bound
m.xu[rxn] = upper_bound
return m
end
"""
change_bounds(
model::CoreModel,
rxn_ids::Vector{String};
lower_bounds = fill(-_constants.default_reaction_bound, length(rxn_ids)),
upper_bounds = fill(constants.default_reaction_bound, length(rxn_ids)),
)
Change the bounds of all reactions with `reaction_ids` in `model` in-place. Note
that if the bound argument is not supplied then a default (see
`_constants.default_reaction_bound`) is used. The same default bound argument is
used for each reaction bound if not supplied.
See also: [`change_bound`](@ref), [`change_bound!`](@ref), [`change_bounds`](@ref)
# Example
```
new_model = change_bounds(model, ["PFL", "FBA"]; lower_bounds=[-10, -20], ub=[10, 22])
new_model = change_bounds(model, ["PFL", "FBA"]; lower_bounds=[-10.2, -14.3]) # all upper_bounds are set to _constants.default_reaction_bound
new_model = change_bounds(model, ["PFL", "FBA"]; upper_bounds=[10.2, 23])
```
"""
function change_bounds(
model::CoreModel,
rxn_ids::Vector{String};
lower_bounds = fill(-_constants.default_reaction_bound, length(rxn_ids)),
upper_bounds = fill(constants.default_reaction_bound, length(rxn_ids)),
)
change_bounds(model, Int.(indexin(rxn_ids, reactions(model))); lower_bounds = lower_bounds, upper_bounds = upper_bounds)
end
"""
change_bound(
model::CoreModel,
reaction_id::String;
lower_bound = -_constants.default_reaction_bound,
upper_bound = _constants.default_reaction_bound,
)
Change the bounds of a reaction with `reaction_id` in `model` in-place. Note
that if the bound argument is not supplied then a default (see
`_constants.default_reaction_bound`) is used.
See also: [`change_bound`](@ref), [`change_bounds!`](@ref), [`change_bounds!`](@ref)
# Example
```
new_model = change_bound!(model, "PFL"; lower_bound=-10, ub=10)
new_model = change_bound!(model, "PFL"; lower_bound=-10.2) # upper_bound is set to _constants.default_reaction_bound
new_model = change_bound!(model, "PFL"; upper_bound=10)
```
"""
function change_bound(
model::CoreModel,
rxn_id::String;
lower_bound = -_constants.default_reaction_bound,
upper_bound = _constants.default_reaction_bound,
)
change_bound(model, first(indexin([rxn_id], reactions(model))); lower_bound = lower_bound, upper_bound = upper_bound)
end