Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sudden outbreak of ConstraintTrees #796

Merged
merged 12 commits into from
Oct 24, 2023
22 changes: 4 additions & 18 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,25 @@ authors = ["The developers of COBREXA.jl"]
version = "2.0.0"

[deps]
AbstractFBCModels = "5a4f3dfa-1789-40f8-8221-69268c29937c"
ConstraintTrees = "5515826b-29c3-47a5-8849-8513ac836620"
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
DistributedData = "f6a0035f-c5ac-4ad0-b410-ad102ced35df"
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f"
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
MAT = "23992714-dd62-5051-b70f-ba57cb901cac"
MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
PikaParser = "3bbf5609-3e7b-44cd-8549-7c69f321e792"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
SBML = "e5567a89-2604-4b09-9718-f5f78e97c3bb"
Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"

[compat]
Clarabel = "0.3"
DistributedData = "0.1.4, 0.2"
ConstraintTrees = "0.2"
DistributedData = "0.2"
DocStringExtensions = "0.8, 0.9"
HDF5 = "0.16"
JSON = "0.21"
JuMP = "1"
MAT = "0.10"
MacroTools = "0.5.6"
OrderedCollections = "1.4"
PikaParser = "0.5"
SBML = "1.4.2"
StableRNGs = "1.0"
Tulip = "0.7.0, 0.8.0, 0.9.2"
julia = "1.5"

[extras]
Expand Down
70 changes: 8 additions & 62 deletions src/COBREXA.jl
Original file line number Diff line number Diff line change
@@ -1,69 +1,15 @@
"""
```
\\\\\\\\\\ // // | COBREXA.jl v$(COBREXA.version)
\\\\ \\\\// // |
\\\\ \\/ // | COnstraint-Based Reconstruction
\\\\ // | and EXascale Analysis in Julia
// \\\\ |
// /\\ \\\\ | See documentation and examples at:
// //\\\\ \\\\ | https://lcsb-biocore.github.io/COBREXA.jl
// // \\\\\\\\\\ |
```

To start up quickly, install your favorite optimizer, load a metabolic model in
a format such as SBML or JSON, and run a metabolic analysis such as the flux
balance analysis:
```
import Pkg; Pkg.add("GLPK")
using COBREXA, GLPK
model = load_model("e_coli_core.xml")
x = flux_balance_analysis_dict(model, GLPK.Optimizer)
flux_summary(x)
```

A complete overview of the functionality can be found in the documentation.
$(README)
"""
module COBREXA

import Pkg

# versioning tools
const _PKG_ROOT_DIR = normpath(joinpath(@__DIR__, ".."))
include_dependency(joinpath(_PKG_ROOT_DIR, "Project.toml"))

const version =
VersionNumber(Pkg.TOML.parsefile(joinpath(_PKG_ROOT_DIR, "Project.toml"))["version"])

# bootstrap the module machinery
include("moduletools.jl")
using .ModuleTools

# load various internal helpers first
@inc internal
@inc log

# start loading individual user-facing modules
@inc types
@inc wrappers
using DocStringExtensions

@inc io
@inc solver
@inc analysis
@inc reconstruction
@inc utils
import ConstraintTrees as C

module Everything
using ..ModuleTools
@reexport Accessors
@reexport Analysis
@reexport Analysis Modifications
@reexport IO
@reexport Reconstruction
@reexport Reconstruction Pipes
@reexport Solver
@reexport Types
@reexport Wrappers
@reexport Utils
end
include("types.jl")
include("solver.jl")
include("builders/core.jl") #TODO more stuff
include("utils/downloads.jl")

end # module
end # module COBREXA
77 changes: 0 additions & 77 deletions src/analysis.jl

This file was deleted.

72 changes: 72 additions & 0 deletions src/builders/core.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@

import AbstractFBCModels as F
import SparseArrays: sparse

"""
$(TYPEDSIGNATURES)

A constraint tree that models the content of the given instance of
`AbstractFBCModel`.
"""
metabolic_model(model::F.AbstractFBCModel) =
let
rxns =
Symbol.(F.reactions(model)), mets =
Symbol.(F.metabolites(model)), lbs, ubs =
F.bounds(model), stoi =
F.stoichiometry(model), bal =
F.balance(model), obj = F.objective(model)

:fluxes^C.variables(keys = rxns, bounds = zip(lbs, ubs)) *
:balance^C.ConstraintTree(
m => Constraint(value = Value(sparse(row)), bound = b) for
(m, row, b) in zip(mets, eachrow(stoi), bals)
) *
:objective^C.Constraint(C.Value(sparse(obj)))
end

"""
$(TYPEDSIGNATURES)

Shortcut for allocation non-negative ("unsigned") variables. The argument
`keys` is forwarded to `ConstraintTrees.variables` as `keys`.
"""
unsigned_variables(; keys) = C.variables(; keys, bounds = Ref((0.0, Inf)))

"""
$(TYPEDSIGNATURES)

A constraint tree that bound the values present in `signed` to be sums of pairs
of `positive` and `negative` contributions to the individual values.

Keys in the result are the same as the keys of `signed` constraints.

Typically, this can be used to create "unidirectional" fluxes
together with [`unsigned_variables`](@ref):
```
uvars = unsigned_variables(keys(myModel.fluxes))

myModel = myModel +
:fluxes_forward^uvars +
:fluxes_reverse^uvars

myModel *=
:direction_sums^sign_split_constraints(
positive = myModel.fluxes_forward,
negative = myModel.fluxes_reverse,
signed = myModel.fluxes,
)
```
#TODO this might go to the docs
"""
sign_split_constraints(;
positive::C.ConstraintTree,
negative::C.ConstraintTree,
signed::C.ConstraintTree,
) = C.ConstraintTree(
C.Constraint(
value = s + (haskey(negative, k) ? negative[k].value : zero(C.Value)) -
(haskey(positive, k) ? positive[k].value : zero(C.Value)),
bound = 0.0,
) for (k, s) in C.elems(signed)
)
14 changes: 14 additions & 0 deletions src/builders/enzymes.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

# the design space is:
# - total enzyme consumption y/n? (can be done very easily manually given the individual enzyme masses are present)
# - isozymes y/n (separates smoment from gecko)
# - mass groups y/n (this is basically summing up enzymes/isozymes by a label)
# - allow overproduction of enzymes (i.e., have extra variables for enzymes/isozymes to allow some slack)
# - anyone is probably able to do a partial sum over the above things themselves, we should make sure they squash well

#TODO: this does not need the variables allocated, it's just a fancy product
enzyme_mass_sum(; forward_fluxes, reverse_fluxes, enzymes, reaction_enzyme_association) =
missing

isozyme_mass_mapping(; forward_fluxes, reverse_fluxes, isozymes, ...) = missing
isozyme_mass_group_mapping(; forward_fluxes, reverse_fluxes, isozymes, ...) = missing
13 changes: 13 additions & 0 deletions src/builders/genes.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

#TODO maybe separate this into simple boolean eval function and actual builder
knockout_constraint(ko_genes; fluxes::SolutionTree, reaction_gene_association) =
C.ConstraintTree(
rxn => C.Constraint(C.value(fluxes[rxn]), 0.0) for rxn in keys(fluxes) if begin
gss = reaction_gene_association(rxn)
if isnothing(gss)
false
else
all(gs -> any(g -> g in ko_genes, gs), gss)
end
end
)
20 changes: 20 additions & 0 deletions src/builders/objectives.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

sum_objective(x) = C.Constraint(sum(c.value for c in x))
sum_objective(x::ConstraintTree) = sub_objective(values(x))

squared_error_objective(x) =
C.Constraint(sum(c.value * c.value for c in x, init in zero(C.Value)))
squared_error_objective(x::ConstraintTree) = squared_error_objective(values(x))

squared_error_objective(constraints::Vector, target::Vector) =
C.Constraint(sum(let tmp = (c.value - t)
tmp * tmp
end for (c, t) in zip(constraints, target)))

squared_error_objective(constraints::ConstraintTree, target) = C.Constraint(
sum(
let tmp = (c.value - target[k])
tmp * tmp
end for (k, c) in C.elems(constraints) if haskey(target, k)
),
)
18 changes: 0 additions & 18 deletions src/internal.jl

This file was deleted.

51 changes: 0 additions & 51 deletions src/io.jl

This file was deleted.

Loading