Skip to content

Commit

Permalink
Merge pull request #1 from COBREXA/mk-tests-and-docs
Browse files Browse the repository at this point in the history
tests and docs
  • Loading branch information
exaexa authored Sep 13, 2023
2 parents 6d489b2 + d33fcd0 commit ada1315
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 12 deletions.
7 changes: 6 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
[compat]
DataStructures = "0.18"
DocStringExtensions = "0.8, 0.9"
SBML = "1"
JuMP = "1"
julia = "1.6"

[extras]
Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
SBML = "e5567a89-2604-4b09-9718-f5f78e97c3bb"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test"]
test = ["Downloads", "JuMP", "SBML", "Test"]
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ The development was supported by European Union's Horizon 2020 Programme under
PerMedCoE project ([permedcoe.eu](https://www.permedcoe.eu/)),
agreement no. 951773.

<img src="docs/src/assets/unilu.svg" alt="Uni.lu logo" height="64px">   <img src="docs/src/assets/lcsb.svg" alt="LCSB logo" height="64px">   <img src="https://lcsb-biocore.github.io/COBREXA.jl/stable/assets/hhu.svg" alt="HHU logo" height="64px" style="height:64px; width:auto">   <img src="https://lcsb-biocore.github.io/COBREXA.jl/stable/assets/qtb.svg" alt="QTB logo" height="64px" style="height:64px; width:auto">   <img src="docs/src/assets/permedcoe.svg" alt="PerMedCoE logo" height="64px">
<img src="docs/src/assets/unilu.svg" alt="Uni.lu logo" height="64px">   <img src="docs/src/assets/lcsb.svg" alt="LCSB logo" height="64px">   <img src="docs/src/assets/hhu.svg" alt="HHU logo" height="64px" style="height:64px; width:auto">   <img src="docs/src/assets/qtb.svg" alt="QTB logo" height="64px" style="height:64px; width:auto">   <img src="docs/src/assets/permedcoe.svg" alt="PerMedCoE logo" height="64px">
3 changes: 3 additions & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
[deps]
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306"
SBML = "e5567a89-2604-4b09-9718-f5f78e97c3bb"
51 changes: 51 additions & 0 deletions docs/src/metabolic-modeling.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@

# # Example: Metabolic modeling
#
# In this example we demonstrate the use of `ConstraintTree` structure for
# solving the metabolic modeling tasks. At the same time, we show how to export
# the structure to JuMP, and use `SolutionTree` to find useful information
# about the result.
#
# First, let's import some packages:

import ConstraintTrees as C
import JuMP, SBML

# We will need a constraint-based metabolic model; for this test we will use
# the usual "E. Coli core metabolism" model as available from BiGG:

import Downloads: download

download("http://bigg.ucsd.edu/static/models/e_coli_core.xml", "e_coli_core.xml")

ecoli = SBML.readSBML("e_coli_core.xml")

# Let's first build the constrained representation of the problem. First, we
# will need a variable for each of the reactions in the model:

c = C.allocate_variables(keys = Symbol.(keys(ecoli.reactions)));

@test length(C.elems(c)) == length(ecoli.reactions) #src

# The above operation returns a `ConstraintTree`. You can browse these as a
# dictionary:
C.elems(c)

# ...or much more conveniently using the record dot syntax as properties:
c.R_PFK

# Operator `^` is used to name individual constraints and directories in the
# hierarchy. Let us name our constraints as "fluxes" (which is a common name in
# metabolic modeling) and explore the result:

c = :fluxes^c;

# We can see that there is now only a single "top-level directory" in the
# constraint system:
collect(keys(C.elems(c)))

@test collect(keys(c)) == [:fluxes] #src
@test issetequal(collect(keys(c.fluxes)), sort(Symbol.(collect(keys(ecoli.reactions))))) #src

# ...which can be explored with the dot access again:
c.fluxes.R_PFK
38 changes: 29 additions & 9 deletions src/constraint_tree.jl
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,15 @@ function Base.getproperty(x::ConstraintTree, sym::Symbol)
elems(x)[sym]
end

Base.propertynames(x::ConstraintTree) = keys(elems(x))
Base.keys(x::ConstraintTree) = keys(elems(x))

Base.values(x::ConstraintTree) = values(elems(x))

Base.iterate(x::ConstraintTree, st) = iterate(elems(x), st)

Base.eltype(x::ConstraintTree) = eltype(elems(x))

Base.propertynames(x::ConstraintTree) = keys(x)

Base.getindex(x::ConstraintTree, sym::Symbol) = getindex(elems(x), sym)

Expand Down Expand Up @@ -152,15 +160,27 @@ end
"""
$(TYPEDSIGNATURES)
Make a trivial constraint system that constraints variables in range
`1:length(keys)` named as given by `keys` to the given interval.
Make a trivial constraint system that creates variables with indexes in
range `1:length(keys)` named in order as given by `keys`.
Parameter `bounds` is either `nothing` for creating unconstrained variables, a
single bound (of precise length 1) for creating all variables of the same
constraint, or an iterable object of same length as `keys` with individual
bounds for each variable in the same order as `keys`.
The individual bounds should be of type [`Bound`](@ref). To pass a single
interval bound for all variables, it is impossible to use a tuple (since its
length is 2); in such case use `bound = Ref((minimum, maximum))`, which has the
correct length.
"""
function allocate_variables(; keys::Vector{Symbol}, lower_bounds = -Inf, upper_bounds = Inf)
# TODO: generalize to all constraint kinds as given by `Bound`
lbs = length(lower_bounds) == 1 ? Base.Iterators.cycle(lower_bounds) : lower_bounds
ubs = length(upper_bounds) == 1 ? Base.Iterators.cycle(upper_bounds) : upper_bounds
function allocate_variables(; keys::Vector{Symbol}, bounds = nothing)
bs =
isnothing(bounds) ? Base.Iterators.cycle(tuple(nothing)) :
length(bounds) == 1 ? Base.Iterators.cycle(bounds) :
length(bounds) == length(keys) ? bounds :
error("lengths of bounds and keys differ for allocated variables")
make_constraint_tree(
k => Constraint(value = Value(Int[i], Float64[1.0]), bound = (lb, ub)) for
((i, k), lb, ub) in zip(enumerate(keys), lbs, ubs)
k => Constraint(value = Value(Int[i], Float64[1.0]), bound = b) for
((i, k), b) in zip(enumerate(keys), bs)
)
end
8 changes: 8 additions & 0 deletions src/solution.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ function Base.getproperty(x::SolutionTree, sym::Symbol)
elems(x)[sym]
end

Base.keys(x::SolutionTree) = keys(elems(x))

Base.values(x::SolutionTree) = values(elems(x))

Base.iterate(x::SolutionTree, st) = iterate(elems(x), st)

Base.eltype(x::SolutionTree) = eltype(elems(x))

Base.propertynames(x::SolutionTree) = keys(elems(x))

Base.getindex(x::SolutionTree, sym::Symbol) = getindex(elems(x), sym)
Expand Down
2 changes: 1 addition & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ import ConstraintTrees
using Test

@testset "ConstraintTrees tests" begin

include("../docs/src/metabolic-modeling.jl")
end

0 comments on commit ada1315

Please sign in to comment.