Skip to content

Commit

Permalink
frontend for MOMA (involves quite some argument juggling)
Browse files Browse the repository at this point in the history
  • Loading branch information
exaexa committed Feb 2, 2024
1 parent c253ace commit 73f2852
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 86 deletions.
5 changes: 2 additions & 3 deletions docs/src/examples/03-parsimonious-flux-balance.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@

# # Parsimonious flux balance analysis

# We will use [`parsimonious_flux_balance_analysis`](@ref) and
# [`minimization_of_metabolic_adjustment`](@ref) to find the optimal flux
# distribution in the *E. coli* "core" model.
# We will use [`parsimonious_flux_balance_analysis`](@ref) to find the optimal
# flux distribution in the *E. coli* "core" model.
#
# TODO pFBA citation

Expand Down
6 changes: 4 additions & 2 deletions src/analysis/frontend.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@ front-end analysis function.
function frontend_optimized_values(
builder,
args...;
builder_kwargs = NamedTuple(),
objective,
output = identity,
sense = Maximal,
optimizer,
settings = [],
kwargs...,
)
constraints = builder(args...; kwargs...)
constraints = builder(args...; builder_kwargs..., kwargs...)

# arguments need to be kept in sync
optimized_values(
Expand All @@ -58,6 +59,7 @@ Like [`frontend_optimized_values`](@ref), but internally calls
function frontend_parsimonious_optimized_values(
builder,
args...;
builder_kwargs = NamedTuple(),
objective = identity,
output = identity,
sense = Maximal,
Expand All @@ -70,7 +72,7 @@ function frontend_parsimonious_optimized_values(
tolerances = [absolute_tolerance_bound(0)],
kwargs...,
)
constraints = builder(args...; kwargs...)
constraints = builder(args...; builder_kwargs..., kwargs...)

# arguments need to be kept in sync
parsimonious_optimized_values(
Expand Down
206 changes: 125 additions & 81 deletions src/frontend/moma.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
"""
$(TYPEDSIGNATURES)
Find a feasible solution of the "minimal metabolic adjustment analysis" (MOMA)
Find a solution of the "minimization of metabolic adjustment" (MOMA) analysis
for the `model`, which is the "closest" feasible solution to the given
`reference_fluxes`, in the sense of squared-sum error distance. The minimized
`reference_fluxes`, in the sense of squared-sum distance. The minimized
squared distance (the objective) is present in the result tree as
`minimal_adjustment_objective`.
Expand All @@ -33,83 +33,100 @@ objective is constructed via [`squared_sum_error_value`](@ref)).
Additional parameters are forwarded to [`optimized_values`](@ref).
"""
function minimization_of_metabolic_adjustment(
function metabolic_adjustment_minimization_constraints(
model::A.AbstractFBCModel,
reference_fluxes::Dict{Symbol,Float64},
optimizer;
kwargs...,
reference_fluxes::C.Tree,
)
constraints = flux_balance_constraints(model)
objective =
squared_sum_error_value(constraints.fluxes, x -> get(reference_fluxes, x, nothing))
optimized_values(
constraints * :minimal_adjustment_objective^C.Constraint(objective);
optimizer,
objective,
sense = Minimal,
kwargs...,
constraints *
:minimal_adjustment_objective^C.Constraint(
squared_sum_error_value(constraints.fluxes, reference_fluxes),
)
end

"""
$(TYPEDSIGNATURES)
A slightly easier-to-use version of
[`minimization_of_metabolic_adjustment`](@ref) that computes the reference flux
as the optimal solution of the `reference_model`. The reference flux is
calculated using `reference_optimizer` and `reference_modifications`, which
default to the `optimizer` and `settings`.
Leftover arguments are passed to the overload of
[`minimization_of_metabolic_adjustment`](@ref) that accepts the
reference flux dictionary.
[`metabolic_adjustment_minimization_constraints`](@ref) that computes the
reference flux as the parsimonious optimal solution of the `reference_model`.
The reference flux is calculated using `reference_optimizer` and
`reference_modifications`, which default to the `optimizer` and `settings`.
Other arguments are forwarded to the internal call of
[`parsimonious_optimized_values`](@ref).
Returns `nothing` if no feasible solution is found.
"""
function minimization_of_metabolic_adjustment(
function metabolic_adjustment_minimization_constraints(
model::A.AbstractFBCModel,
reference_model::A.AbstractFBCModel,
optimizer;
reference_optimizer = optimizer,
settings = [],
reference_settings = settings,
reference_model::A.AbstractFBCModel;
kwargs...,
)
reference_constraints = flux_balance_constraints(reference_model)
reference_fluxes = optimized_values(
reference_constraints = parsimonious_flux_balance_constraints(reference_model)
reference_fluxes = parsimonious_optimized_values(
reference_constraints;
optimizer = reference_optimizer,
settings = reference_settings,
objective = reference_constraints.objective.value,
parsimonious_objective = reference_constraints.parsimonious_objective.value,
output = reference_constraints.fluxes,
)
isnothing(reference_fluxes) && return nothing
minimization_of_metabolic_adjustment(
model,
reference_fluxes,
optimizer;
settings,
kwargs...,
)
isnothing(reference_fluxes) && return nothing
metabolic_adjustment_minimization_constraints(model, reference_fluxes)
end

export minimization_of_metabolic_adjustment
export metabolic_adjustment_minimization_constraints

"""
$(TYPEDSIGNATURES)
Like [`minimization_of_metabolic_adjustment`](@ref) but optimizes the L1 norm.
TODO
"""
metabolic_adjustment_minimization_analysis(
model::A.AbstractFBCModel,
args...;
optimizer,
settings,
reference_parsimonious_optimizer = optimizer,
reference_parsimonious_settings = settings,
reference_optimizer = optimizer,
reference_settings = settings,
kwargs...,
) = frontend_optimized_values(
metabolic_adjustment_minimization_constraints,
model,
args...;
builder_kwargs = (
optimizer = reference_optimizer,
settings = reference_settings,
parsimonious_optimizer = reference_parsimonious_optimizer,
parsimonious_settings = reference_parsimonious_settings,
),
objective = x -> x.minimal_adjustment_objective.value,
sense = Minimal,
optimizer,
settings,
kwargs...,
)

export metabolic_adjustment_minimization_analysis

"""
$(TYPEDSIGNATURES)
Like [`metabolic_adjustment_minimization_constraints`](@ref) but optimizes the L1 norm.
This typically produces a sufficiently good result with less resources,
depending on the situation. See documentation of
[`linear_parsimonious_flux_balance_analysis`](@ref) for some of the
considerations.
"""
function linear_minimization_of_metabolic_adjustment(
function linear_metabolic_adjustment_minimization_constraints(
model::A.AbstractFBCModel,
reference_fluxes::Dict{Symbol,Float64},
optimizer;
kwargs...,
reference_fluxes::C.Tree,
)
constraints = flux_balance_constraints(model)

difference = C.zip(ct.fluxes, C.Tree(reference_fluxes)) do orig, ref
difference = C.zip(ct.fluxes, reference_fluxes) do orig, ref
C.Constraint(orig.value - ref)
end

Expand All @@ -120,50 +137,77 @@ function linear_minimization_of_metabolic_adjustment(

# `difference` actually doesn't need to go to the CT, but we include it
# anyway to calm the curiosity of good neighbors.
constraints *= :reference_diff^difference
constraints *=
:reference_directional_diff_balance^sign_split_constraints(
positive = constraints.reference_positive_diff,
negative = constraints.reference_negative_diff,
signed = difference,
)

objective =
sum_value(constraints.reference_positive_diff, constraints.reference_negative_diff)

optimized_values(
constraints * :linear_minimal_adjustment_objective^C.Constraint(objective);
optimizer,
objective,
sense = Minimal,
kwargs...,
constraints *
:reference_diff^difference *
:reference_directional_diff_balance^sign_split_constraints(
positive = constraints.reference_positive_diff,
negative = constraints.reference_negative_diff,
signed = difference,
) *
:linear_minimal_adjustment_objective^C.Constraint(
sum_value(constraints.reference_positive_diff, constraints.reference_negative_diff),
)
end

function linear_minimization_of_metabolic_adjustment(
"""
$(TYPEDSIGNATURES)
Like [`metabolic_adjustment_minimization_constraints`](@ref) but optimizes the L1 norm.
This typically produces a sufficiently good result with less resources,
depending on the situation. See documentation of
[`linear_parsimonious_flux_balance_analysis`](@ref) for some of the
considerations.
"""
function linear_metabolic_adjustment_minimization_constraints(
model::A.AbstractFBCModel,
reference_model::A.AbstractFBCModel,
optimizer;
reference_optimizer = optimizer,
settings = [],
reference_settings = settings,
reference_model::A.AbstractFBCModel;
kwargs...,
)
reference_constraints = flux_balance_constraints(reference_model)
reference_fluxes = optimized_values(
reference_constraints = parsimonious_flux_balance_constraints(reference_model)
reference_fluxes = parsimonious_optimized_values(
reference_constraints;
optimizer = reference_optimizer,
settings = reference_settings,
objective = reference_constraints.objective.value,
parsimonious_objective = reference_constraints.parsimonious_objective.value,
output = reference_constraints.fluxes,
)
isnothing(reference_fluxes) && return nothing
linear_minimization_of_metabolic_adjustment(
model,
reference_fluxes,
optimizer;
settings,
kwargs...,
)
isnothing(reference_fluxes) && return nothing

linear_metabolic_adjustment_minimization_constraints(model, reference_fluxes)
end

export linear_minimization_of_metabolic_adjustment
export linear_metabolic_adjustment_minimization_constraints

"""
$(TYPEDSIGNATURES)
TODO
"""
linear_metabolic_adjustment_minimization_analysis(
model::A.AbstractFBCModel,
args...;
optimizer,
settings,
reference_parsimonious_optimizer = optimizer,
reference_parsimonious_settings = settings,
reference_optimizer = optimizer,
reference_settings = settings,
kwargs...,
) = frontend_optimized_values(
linear_metabolic_adjustment_minimization_constraints,
model,
args...;
builder_kwargs = (
optimizer = reference_optimizer,
settings = reference_settings,
parsimonious_optimizer = reference_parsimonious_optimizer,
parsimonious_settings = reference_parsimonious_settings,
),
objective = x -> x.linear_minimal_adjustment_objective.value,
sense = Minimal,
optimizer,
settings,
kwargs...,
)

export linear_metabolic_adjustment_minimization_analysis

0 comments on commit 73f2852

Please sign in to comment.