diff --git a/.gitignore b/.gitignore index ec0d7020029c6ff3dff5f48f26b5884c79fcf72a..ddae209f43c4546704866301774c0ed68c6a4f64 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ docs/build test/data/ecoli_core.xml /vscode -/.vscode \ No newline at end of file +/.vscode +*.jl.*.cov +.*.swp diff --git a/src/base/solver.jl b/src/base/solver.jl index 0e60d659e4cbdaeb9d0be8536a0d1d26101d90e6..fca335d40649659a24191fc72eb1eb22cb90aed1 100644 --- a/src/base/solver.jl +++ b/src/base/solver.jl @@ -30,7 +30,11 @@ end Use JuMP to optimize an instance of a [`MetabolicModel`](@ref). Returns a tuple that contains the new model and a vector of its variables. """ -function optimizeModel(model::LM, optimizer; sense = MOI.MIN_SENSE) where {LM<:MetabolicModel} +function optimizeModel( + model::LM, + optimizer; + sense = MOI.MIN_SENSE, +) where {LM<:MetabolicModel} optimization_model, x = makeOptimizationModel(model, optimizer; sense = sense) JuMP.optimize!(optimization_model) return (optimization_model, x) diff --git a/src/base/utilities.jl b/src/base/utilities.jl index b5b5487cd585cee78677504612ea62c92c1c46c6..59f202da7e1c259e25bac84fdb206e92d8e3b528 100644 --- a/src/base/utilities.jl +++ b/src/base/utilities.jl @@ -1,27 +1,20 @@ -function Base.isequal(model1::LinearModel, model2::LinearModel) - return model1.S == model2.S && - model1.b == model2.b && - model1.C == model2.C && - model1.cl == model2.cl && - model1.cu == model2.cu && - model1.c == model2.c && - model1.xl == model2.xl && - model1.xu == model2.xu && - model1.rxns == model2.rxns && - model1.mets == model2.mets -end +Base.isequal(model1::LinearModel, model2::LinearModel) = + isequal(model1.S, model2.S) && + isequal(model1.b, model2.b) && + isequal(model1.c, model2.c) && + isequal(model1.xl, model2.xl) && + isequal(model1.xu, model2.xu) && + isequal(model1.rxns, model2.rxns) && + isequal(model1.mets, model2.mets) -function Base.copy(model::LinearModel) - return LinearModel( - model.S, - model.b, - model.C, - model.cl, - model.cu, - model.c, - model.xl, - model.xu, - model.rxns, - model.mets, - ) -end +Base.copy(model::LinearModel) = + LinearModel(model.S, model.b, model.c, model.xl, model.xu, model.rxns, model.mets) + +Base.isequal(model1::CoupledLinearModel, model2::CoupledLinearModel) = + isequal(model1.lm, model2.lm) && + isequal(model1.C, model2.C) && + isequal(model1.cl, model2.cl) && + isequal(model1.cu, model2.cu) + +Base.copy(model::CoupledLinearModel) = + CoupledLinearModel(model.lm, model.C, model.cl, model.cu) diff --git a/src/io/sbml.jl b/src/io/sbml.jl index d150ed2eaadca1806ed340e9e64d60957129fa66..3031492d7888952abc7674dbc8a1a9076d64d1d0 100644 --- a/src/io/sbml.jl +++ b/src/io/sbml.jl @@ -2,35 +2,4 @@ """ Import a LinearModel from a SBML file """ -function loadSBMLModel(filename::String)::LinearModel - model = SBML.readSBML(filename) - - mets, rxns, S = SBML.getS(model) - b = zeros(length(mets)) - c = SBML.getOCs(model) - - lbu = SBML.getLBs(model) - ubu = SBML.getUBs(model) - - # SBML has units that we can't easily interpret yet, BUT the chances are - # that in a reasonable SBML file all units will be the same and we don't - # need to care about them at all. So we just check it here and throw an - # error if not. - - unit = lbu[1][2] - getvalue = (val, _)::Tuple -> val - getunit = (_, unit)::Tuple -> unit - - allunits = unique([getunit.(lbu) getunit.(ubu)]) - - if length(allunits) != 1 - throw( - DomainError( - allunits, - "The SBML file uses multiple units; loading needs conversion", - ), - ) - end - - return LinearModel(S, b, c, getvalue.(lbu), getvalue.(ubu), rxns, mets) -end +loadSBMLModel(filename::String)::SBMLModel = SBMLModel(SBML.readSBML(filename)) diff --git a/src/reconstruction/coupling.jl b/src/reconstruction/coupling.jl index ecce84d4b1b0a1f734fc6db480127a595f3cc483..9e8e0f61fc7d8a319d82d4031872ddf6b5510b60 100644 --- a/src/reconstruction/coupling.jl +++ b/src/reconstruction/coupling.jl @@ -1,31 +1,20 @@ """ -Verifies that vectors and matrices have the expected dimensions. -""" -function checkCouplingConstraintsInputDimensions( - m::LinearModel, - C::M, - cl::V, - cu::V, -) where {M<:MatType,V<:VecType} - - length(cu) == length(cl) || - throw(DimensionMismatch("`cl` and `cu` don't have the same size")) - size(C) == (length(cl), nReactions(m)) || throw( - DimensionMismatch("`C` size should be length(cl) x the number of reactions in m"), - ) -end - -""" -Add constraints of the following form to a LinearModel and return a modified one. +Add constraints of the following form to a CoupledLinearModel and return a modified one. The arguments are same as for in-place `addCouplingConstraints!`. """ -function addCouplingConstraints(m::LinearModel, args...) +function addCouplingConstraints(m::CoupledLinearModel, args...) newLp = deepcopy(m) addCouplingConstraints!(newLp, args...) return newLp end +""" +Add constraints to a plain `LinearModel` (converts it to the coupled type) +""" +addCouplingConstraints(m::LinearModel, args...) = + addCouplingConstraints(convert(CoupledLinearModel, m), args...) + """ In-place add coupling constraints in form ``` @@ -33,7 +22,7 @@ In-place add coupling constraints in form ``` """ function addCouplingConstraints!( - m::LinearModel, + m::CoupledLinearModel, c::V, cl::AbstractFloat, cu::AbstractFloat, @@ -48,21 +37,20 @@ end function addCouplingConstraints!( - m::LinearModel, + m::CoupledLinearModel, C::M, cl::V, cu::V, ) where {M<:MatType,V<:VecType} - C = sparse(C) - cl = sparse(cl) - cu = sparse(cu) - - checkCouplingConstraintsInputDimensions(m, C, cl, cu) + all([length(cu), length(cl)] .== size(C, 1)) || + throw(DimensionMismatch("mismatched numbers of constraints")) + size(C, 2) == nReactions(m) || + throw(DimensionMismatch("mismatched number of reactions")) - m.C = vcat(m.C, C) - m.cl = vcat(m.cl, cl) - m.cu = vcat(m.cu, cu) + m.C = vcat(m.C, sparse(C)) + m.cl = vcat(m.cl, sparse(cl)) + m.cu = vcat(m.cu, sparse(cu)) end @@ -71,7 +59,7 @@ Remove coupling constraints from the linear model and return the modified model. Arguments are the same as for in-place version `removeCouplingConstraints!`. """ -function removeCouplingConstraints(m::LinearModel, args...) +function removeCouplingConstraints(m::CoupledLinearModel, args...) newModel = deepcopy(m) removeCouplingConstraints!(newModel, args...) return newModel @@ -79,14 +67,14 @@ end """ -Removes a set of coupling constraints from a LinearModel in-place. +Removes a set of coupling constraints from a CoupledLinearModel in-place. """ -function removeCouplingConstraints!(m::LinearModel, constraint::Int) +function removeCouplingConstraints!(m::CoupledLinearModel, constraint::Int) removeCouplingConstraints!(m, [constraint]) end -function removeCouplingConstraints!(m::LinearModel, constraints::Vector{Int}) +function removeCouplingConstraints!(m::CoupledLinearModel, constraints::Vector{Int}) toBeKept = filter(e -> e ∉ constraints, 1:nCouplingConstraints(m)) m.C = m.C[toBeKept, :] m.cl = m.cl[toBeKept] @@ -98,7 +86,7 @@ end Change the lower and/or upper bounds ('cl' and 'cu') for given coupling constraints """ function changeCouplingBounds!( - model::LinearModel, + model::CoupledLinearModel, constraints::Vector{Int}; cl::V = Array{Float64}(undef, 0), cu::V = Array{Float64}(undef, 0), diff --git a/src/reconstruction/modeling.jl b/src/reconstruction/modeling.jl index d284eb4ac9f7effcf69d56e4ede847ac11efabaa..d19a17070aa2827eb2e5d26cf86a5b9d436f8ad5 100644 --- a/src/reconstruction/modeling.jl +++ b/src/reconstruction/modeling.jl @@ -104,12 +104,14 @@ function addReactions( xl = sparse(xl) xu = sparse(xu) - checkInputDimensions(Sp, b, c, xl, xu, rxns, mets) + 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")) newReactions = findall(Bool[!(rxn in m.rxns) for rxn in rxns]) newMetabolites = findall(Bool[!(met in m.mets) for met in mets]) - if checkConsistency (newReactions1, newMetabolites1) = verifyConsistency(m, Sp, b, c, xl, xu, rxns, mets, newReactions, newMetabolites) @@ -130,12 +132,13 @@ function addReactions( newS = hcat(extS, extSp) newb = vcat(m.b, b[newMetabolites]) - newC = hcat(m.C, spzeros(size(m.C, 1), length(newReactions))) + #newC = hcat(m.C, spzeros(size(m.C, 1), length(newReactions))) newc = vcat(m.c, c[newReactions]) newxl = vcat(m.xl, xl[newReactions]) newxu = vcat(m.xu, xu[newReactions]) newRxns = vcat(m.rxns, rxns[newReactions]) - newLp = LinearModel(newS, newb, newC, m.cl, m.cu, newc, newxl, newxu, newRxns, newMets) + #newLp = LinearModel(newS, newb, newC, m.cl, m.cu, newc, newxl, newxu, newRxns, newMets) + newLp = LinearModel(newS, newb, newc, newxl, newxu, newRxns, newMets) if checkConsistency return (newLp, newReactions, newMetabolites) @@ -144,56 +147,6 @@ function addReactions( end end -""" -Verifies that vectors and matrices have the expected dimensions. -""" -function checkInputDimensions( - S::M1, - b::V, - C::M2, - cl::V, - cu::V, - c::V, - xl::V, - xu::V, - rxns::K, - mets::K, -) where {M1<:MatType,M2<:MatType,V<:VecType,K<:StringVecType} - n_c = length(c) - - length(cu) == length(cl) || - throw(DimensionMismatch("`cl` and `cu` don't have the same size")) - size(C) == (length(cu), n_c) || - throw(DimensionMismatch("`C` shape doesn't match with `cu` and `c`")) - - checkInputDimensions(S, b, c, xl, xu, rxns, mets) - -end - -function checkInputDimensions( - S::M, - b::V, - c::V, - xl::V, - xu::V, - rxns::K, - mets::K, -) where {M<:MatType,V<:VecType,K<:StringVecType} - - n_c = length(c) - n_b = length(b) - - length(xu) == length(xl) || - throw(DimensionMismatch("`xl` and `xu` don't have the same size")) - n_c == length(xl) || throw(DimensionMismatch("`c` doesn't have the same size as `xl`")) - - size(S) == (n_b, n_c) || - throw(DimensionMismatch("`S` shape doesn't match with `c` and `mets`")) - - length(rxns) == n_c || throw(DimensionMismatch("`rxns` size doesn't match with `S`")) - length(mets) == n_b || throw(DimensionMismatch("`mets` size doesn't match with `S`")) -end - """ Verifies the consistency of a given model """ @@ -250,14 +203,15 @@ function removeReactions(m::LinearModel, rxns::Vector{Int}) unique!(metsToKeep) newS = m.S[metsToKeep, rxnsToKeep] newb = m.b[metsToKeep] - newC = m.C[:, rxnsToKeep] + #newC = m.C[:, rxnsToKeep] newc = m.c[rxnsToKeep] newxl = m.xl[rxnsToKeep] newxu = m.xu[rxnsToKeep] newRxns = m.rxns[rxnsToKeep] newMets = m.mets[metsToKeep] newModel = - LinearModel(newS, newb, newC, m.cl, m.cu, newc, newxl, newxu, newRxns, newMets) + #LinearModel(newS, newb, newC, m.cl, m.cu, newc, newxl, newxu, newRxns, newMets) + LinearModel(newS, newb, newc, newxl, newxu, newRxns, newMets) return newModel end @@ -271,7 +225,7 @@ function removeReactions(m::LinearModel, rxn::String) end -function removeReactions(m::LinearModel, rxns::Array{String,1}) +function removeReactions(m::LinearModel, rxns::Vector{String}) rxnIndices = [findfirst(isequal(name), m.rxns) for name in intersect(rxns, m.rxns)] if isempty(rxnIndices) return m diff --git a/src/types/linearModel.jl b/src/types/linearModel.jl index 5e83695b9667b9eb0a12f66180d37ca27bb389a3..74ecd6110002c0d67f40dc074a58c7a6a64949cc 100644 --- a/src/types/linearModel.jl +++ b/src/types/linearModel.jl @@ -6,16 +6,12 @@ A concrete linear optimization problem of the form: ``` min c^T x s.t. S x = b - cₗ ≤ C x ≤ cᵤ - xₗ ≤ x ≤ xᵤ + xₗ ≤ x ≤ xᵤ ``` """ mutable struct LinearModel <: MetabolicModel S::SparseMat b::SparseVec - C::SparseMat - cl::SparseVec - cu::SparseVec c::SparseVec xl::SparseVec xu::SparseVec @@ -32,43 +28,13 @@ mutable struct LinearModel <: MetabolicModel mets::K, ) where {V<:VecType,M<:MatType,K<:StringVecType} - sS = sparse(S) - sb = sparse(b) - sC = spzeros(0, length(c)) - scl = spzeros(0) - scu = spzeros(0) - sc = sparse(c) - sxl = sparse(xl) - sxu = sparse(xu) + all([length(b), length(mets)] .== size(S, 1)) || + throw(DimensionMismatch("inconsistent number of reactions")) - LinearModel(sS, sb, sC, scl, scu, sc, sxl, sxu, rxns, mets) - end - - function LinearModel( - S::M1, - b::V, - C::M2, - cl::V, - cu::V, - c::V, - xl::V, - xu::V, - rxns::K, - mets::K, - ) where {V<:VecType,M1<:MatType,M2<:MatType,K<:StringVecType} + all([length(c), length(xl), length(xu), length(rxns)] .== size(S, 2)) || + throw(DimensionMismatch("inconsistent number of reactions")) - checkInputDimensions(S, b, C, cl, cu, c, xl, xu, rxns, mets) - - sS = sparse(S) - sb = sparse(b) - sC = sparse(C) - scl = sparse(cl) - scu = sparse(cu) - sc = sparse(c) - sxl = sparse(xl) - sxu = sparse(xu) - - new(sS, sb, sC, scl, scu, sc, sxl, sxu, rxns, mets) + new(sparse(S), sparse(b), sparse(c), sparse(xl), sparse(xu), rxns, mets) end end @@ -129,17 +95,36 @@ end """ coupling(a::LinearModel)::SparseMat -Coupling constraint matrix for a `LinearModel`. +Coupling constraint matrix for a `LinearModel`, actually empty. """ function coupling(a::LinearModel)::SparseMat - a.C + spzeros(0, nReactions(a)) end """ couplingBounds(a::LinearModel)::Tuple{SparseVec,SparseVec} -Coupling bounds for a `LinearModel`. +Coupling bounds for a `LinearModel`, in fact always empty. """ function couplingBounds(a::LinearModel)::Tuple{SparseVec,SparseVec} - (a.cl, a.cu) + (spzeros(0), spzeros(0)) +end + + +""" + Base.convert(::Type{LinearModel}, m::M) where {M <: MetabolicModel} + +Make a `LinearModel` out of any compatible model type. +""" +function Base.convert(::Type{LinearModel}, m::M) where {M<:MetabolicModel} + (xl, xu) = bounds(m) + LinearModel( + stoichiometry(m), + balance(m), + objective(m), + xl, + xu, + reactions(m), + metabolites(m), + ) end diff --git a/src/types/linearModelCoupled.jl b/src/types/linearModelCoupled.jl new file mode 100644 index 0000000000000000000000000000000000000000..a7f3c2a7cb911a4abce830ee2da828e5bc35f125 --- /dev/null +++ b/src/types/linearModelCoupled.jl @@ -0,0 +1,74 @@ + +""" + struct CoupledLinearModel <: MetabolicModel + +The linear model with additional coupling constraints in the form +``` + cₗ ≤ C x ≤ cᵤ +``` +""" +mutable struct CoupledLinearModel <: MetabolicModel + lm::LinearModel + C::SparseMat + cl::SparseVec + cu::SparseVec + + function CoupledLinearModel( + lm::MetabolicModel, + C::M, + cl::V, + cu::V, + ) where {V<:VecType,M<:MatType} + + length(cu) == length(cl) || + throw(DimensionMismatch("`cl` and `cu` need to have the same size")) + size(C) == (length(cu), nReactions(lm)) || + throw(DimensionMismatch("wrong dimensions of `C`")) + + new(convert(LinearModel, lm), sparse(C), sparse(cl), sparse(cu)) + end +end + +reactions(a::CoupledLinearModel) = reactions(a.lm) +metabolites(a::CoupledLinearModel) = metabolites(a.lm) +stoichiometry(a::CoupledLinearModel) = stoichiometry(a.lm) +bounds(a::CoupledLinearModel) = bounds(a.lm) +balance(a::CoupledLinearModel) = balance(a.lm) +objective(a::CoupledLinearModel) = objective(a.lm) + +""" + coupling(a::CoupledLinearModel)::SparseMat + +Coupling constraint matrix for a `CoupledLinearModel`. +""" +function coupling(a::CoupledLinearModel)::SparseMat + a.C +end + +""" + nCouplingConstraints(a::CoupledLinearModel)::Int + +The number of coupling constraints in a `CoupledLinearModel`. +""" +function nCouplingConstraints(a::CoupledLinearModel)::Int + return size(a.C, 1) +end + +""" + couplingBounds(a::CoupledLinearModel)::Tuple{SparseVec,SparseVec} + +Coupling bounds for a `CoupledLinearModel`. +""" +function couplingBounds(a::CoupledLinearModel)::Tuple{SparseVec,SparseVec} + (a.cl, a.cu) +end + +""" + Base.convert(::Type{CoupledLinearModel}, m::M) where {M <: MetabolicModel} + +Make a `CoupledLinearModel` out of any compatible model type. +""" +function Base.convert(::Type{CoupledLinearModel}, m::M) where {M<:MetabolicModel} + (cl, cu) = couplingBounds(m) + CoupledLinearModel(convert(LinearModel, m), coupling(m), cl, cu) +end diff --git a/src/types/sbmlModel.jl b/src/types/sbmlModel.jl new file mode 100644 index 0000000000000000000000000000000000000000..79867000a320bdbaf292d2fd66f93331c978fef9 --- /dev/null +++ b/src/types/sbmlModel.jl @@ -0,0 +1,55 @@ +""" + struct SBMLModel + +Thin wrapper around the model from SBML.jl library. Allows easy conversion from +SBML to any other model format. +""" +struct SBMLModel <: MetabolicModel + m::SBML.Model +end + +reactions(a::SBMLModel)::Vector{String} = [k for k in keys(a.m.reactions)] +metabolites(a::SBMLModel)::Vector{String} = [k for k in keys(a.m.species)] +nReactions(a::SBMLModel)::Int = length(a.m.reactions) +nMetabolites(a::SBMLModel)::Int = length(a.m.species) + +""" + stoichiometry(a::SBMLModel)::SparseMat + +Recreate the stoichiometry matrix from the SBML model. +""" +function stoichiometry(a::SBMLModel)::SparseMat + _, _, S = SBML.getS(a.m) + return S +end + +""" + bounds(a::SBMLModel)::Tuple{SparseVec,SparseVec} + +Get the lower and upper flux bounds of a `SBMLModel`. Throws `DomainError` in +case if the SBML contains mismatching units. +""" +function bounds(a::SBMLModel)::Tuple{SparseVec,SparseVec} + lbu = SBML.getLBs(a.m) + ubu = SBML.getUBs(a.m) + + unit = lbu[1][2] + getvalue = (val, _)::Tuple -> val + getunit = (_, unit)::Tuple -> unit + + allunits = unique([getunit.(lbu) getunit.(ubu)]) + length(allunits) == 1 || throw( + DomainError( + allunits, + "The SBML file uses multiple units; loading needs conversion", + ), + ) + + return sparse.((getvalue.(lbu), getvalue.(ubu))) +end + +balance(a::SBMLModel)::SparseVec = spzeros(nMetabolites(a)) +objective(a::SBMLModel)::SparseVec = SBML.getOCs(a.m) +coupling(a::SBMLModel)::SparseMat = spzeros(0, nReactions(a)) +nCouplingConstraints(a::SBMLModel)::Int = 0 +couplingBounds(a::SBMLModel)::Tuple{SparseVec,SparseVec} = (spzeros(0), spzeros(0)) diff --git a/test/base/utilities.jl b/test/base/utilities.jl index d317dbba5d8aeb87f7a406b64a829f2b81f5b38b..2f735d5a2ab025a340eaddcf26c2e059aa416290 100644 --- a/test/base/utilities.jl +++ b/test/base/utilities.jl @@ -12,4 +12,5 @@ cp = test_coupledLP() @test nCouplingConstraints(cp) == 2000 + @test isequal(cp, copy(cp)) end diff --git a/test/data/testModels.jl b/test/data/testModels.jl index 6fc035805e7f8dd60155ffd0297661c2220f769e..de9809c08dafa64bbcf9de743df0799c1d486b62 100644 --- a/test/data/testModels.jl +++ b/test/data/testModels.jl @@ -41,15 +41,17 @@ test_sparseLP() = LinearModel( ["m$x" for x = 1:4000], ) -test_coupledLP() = LinearModel( - sprand(4000, 3000, 0.5), - sprand(4000, 0.5), +test_coupledLP() = CoupledLinearModel( + LinearModel( + sprand(4000, 3000, 0.5), + sprand(4000, 0.5), + sprand(3000, 0.5), + sprand(3000, 0.5), + sprand(3000, 0.5), + ["r$x" for x = 1:3000], + ["m$x" for x = 1:4000], + ), sprand(2000, 3000, 0.5), sprand(2000, 0.5), sprand(2000, 0.5), - sprand(3000, 0.5), - sprand(3000, 0.5), - sprand(3000, 0.5), - ["r$x" for x = 1:3000], - ["m$x" for x = 1:4000], ) diff --git a/test/io/sbml.jl b/test/io/sbml.jl index ed4f1d7e27fb91e0e1f77226daeb52016086fbb5..8800aa9617f7b017a3212275f5400e68c8914389 100644 --- a/test/io/sbml.jl +++ b/test/io/sbml.jl @@ -12,15 +12,20 @@ if cksum != "78692f8509fb36534f4f9b6ade23b23552044f3ecd8b48d84d484636922ae907" @warn "The downloaded E Coli core model seems to be different from the expected one. Tests may fail." cksum end -@testset "SBML import" begin - m = loadSBMLModel(sbmlfile) +@testset "SBML import and conversion" begin + sbmlm = loadSBMLModel(sbmlfile) + m = convert(LinearModel, sbmlm) - @test size(m.C) == (0, 95) - @test size(m.S) == (92, 95) + @test size(stoichiometry(sbmlm)) == (92, 95) + @test size(stoichiometry(m)) == (nMetabolites(sbmlm), nReactions(sbmlm)) @test length(m.S.nzval) == 380 - @test length(m.b) == 92 + @test length.(bounds(sbmlm)) == (95, 95) + @test length.(bounds(m)) == (95, 95) @test all([length(m.xl), length(m.xu), length(m.c)] .== 95) - @test m.mets[1:3] == ["M_succoa_c", "M_ac_c", "M_fru_b"] - @test m.rxns[1:3] == ["R_EX_fum_e", "R_ACONTb", "R_GLNS"] + @test metabolites(m)[1:3] == ["M_succoa_c", "M_ac_c", "M_fru_b"] + @test reactions(m)[1:3] == ["R_EX_fum_e", "R_ACONTb", "R_GLNS"] + + cm = convert(CoupledLinearModel, sbmlm) + @test nCouplingConstraints(cm) == 0 end diff --git a/test/reconstruction/coupling.jl b/test/reconstruction/coupling.jl index dc279da96e835e3948d864d34e39b463097fefdd..1f152abc49cbd35b642a50a7e9bb3f1154677853 100644 --- a/test/reconstruction/coupling.jl +++ b/test/reconstruction/coupling.jl @@ -1,16 +1,17 @@ @testset "Coupling constraints" begin - cp = test_LP() - @test size(cp.S) == (4, 3) - newCp = addCouplingConstraints(cp, cp.S[end, :], -1.0, 1.0) + cp = convert(CoupledLinearModel, test_LP()) + @test size(cp.lm.S) == (4, 3) + @test size(stoichiometry(convert(LinearModel, cp))) == (4, 3) + newCp = addCouplingConstraints(cp, stoichiometry(cp)[end, :], -1.0, 1.0) @test nCouplingConstraints(cp) + 1 == nCouplingConstraints(newCp) - newCp = addCouplingConstraints(cp, cp.S[1:2, :], [-1.0; -1.0], [1.0; 1.0]) + newCp = addCouplingConstraints(cp, stoichiometry(cp)[1:2, :], [-1.0; -1.0], [1.0; 1.0]) @test nCouplingConstraints(cp) + 2 == nCouplingConstraints(newCp) nC = nCouplingConstraints(cp) - addCouplingConstraints!(cp, cp.S[end, :], -1.0, 1.0) + addCouplingConstraints!(cp, stoichiometry(cp)[end, :], -1.0, 1.0) @test nC + 1 == nCouplingConstraints(cp) - addCouplingConstraints!(cp, cp.S[1:2, :], [-1.0; -1.0], [1.0; 1.0]) + addCouplingConstraints!(cp, stoichiometry(cp)[1:2, :], [-1.0; -1.0], [1.0; 1.0]) @test nC + 3 == nCouplingConstraints(cp) nC = nCouplingConstraints(cp) @@ -23,6 +24,7 @@ cp = test_coupledLP() nC = nCouplingConstraints(cp) newCp = removeCouplingConstraints(cp, 1) + @test size(coupling(cp)) == (nC, nReactions(cp)) @test nC - 1 == nCouplingConstraints(newCp) @test nCouplingConstraints(cp) == nC newCp = removeCouplingConstraints(cp, [1, 2]) @@ -33,8 +35,10 @@ cp = test_coupledLP() changeCouplingBounds!(cp, [3, 1], cl = [-10.0, -20], cu = [10.0, 20]) - @test cp.cl[[1, 3]] == [-20, -10] - @test cp.cu[[1, 3]] == [20, 10] + cl, cu = couplingBounds(cp) + @test cl[[1, 3]] == [-20, -10] + @test cu[[1, 3]] == [20, 10] changeCouplingBounds!(cp, [1000, 1001], cl = [-50.0, -60.0]) - @test cp.cl[[1000, 1001]] == [-50.0, -60.0] + cl, cu = couplingBounds(cp) + @test cl[[1000, 1001]] == [-50.0, -60.0] end