Changing optimizer parameters

Many optimizers require fine-tuning to produce best results. We can pass in additional optimizer settings via the settings parameter of flux_balance_analysis. These include e.g.

  • set_optimizer_attribute, allowing us to tune e.g. iteration limits, tolerances, or floating-point precision (see JuMP documentation for more solver-specific settings)
  • set_objective_sense, allowing the user to change and reverse the optimization direction, if required
  • silence for disabling the debug output of the optimizers
  • set_optimizer for replacing the optimizer implementation used (this is not quite useful in this case, but becomes beneficial with more complex, multi-stage optimization problems)
  • set_time_limit for putting a time limit on the solver computation (this is quite useful for MILP solvers)

To demonstrate this, let's use the usual toy model:

using COBREXA
import JSONFBCModels, Tulip

download_model(
    "http://bigg.ucsd.edu/static/models/e_coli_core.json",
    "e_coli_core.json",
    "7bedec10576cfe935b19218dc881f3fb14f890a1871448fc19a9b4ee15b448d8",
)

model = load_model("e_coli_core.json")
JSONFBCModels.JSONFBCModel(#= 95 reactions, 72 metabolites =#)

Running a FBA with a silent optimizer that has slightly increased iteration limit for IPM algorithm may now look as follows:

solution = flux_balance_analysis(
    model,
    optimizer = Tulip.Optimizer,
    settings = [set_optimizer_attribute("IPM_IterationsLimit", 1000)],
)
ConstraintTrees.Tree{Float64} with 4 elements:
  :coupling           => ConstraintTrees.Tree{Float64}(#= 0 elements =#)
  :flux_stoichiometry => ConstraintTrees.Tree{Float64}(#= 72 elements =#)
  :fluxes             => ConstraintTrees.Tree{Float64}(#= 95 elements =#)
  :objective          => 0.873922

To see some of the effects of the configuration changes, we may e.g. deliberately cripple the optimizer's possibilities to a few iterations and only a little time, which will cause it to fail and return no solution:

solution = flux_balance_analysis(
    model,
    optimizer = Tulip.Optimizer,
    settings = [set_optimizer_attribute("IPM_IterationsLimit", 2), set_time_limit(0.1)],
)

println(solution)
nothing

To see what failed, users may examine the solver output. Because all solver output is silenced by default for efficiency reasons, we need to explicitly pass in the unsilence setting:

solution = flux_balance_analysis(
    model,
    optimizer = Tulip.Optimizer,
    settings = [
        set_optimizer_attribute("IPM_IterationsLimit", 2),
        set_time_limit(0.1),
        unsilence,
    ],
)

Problem info
  Name        :
  Constraints : 262
  Variables   : 95
  Non-zeros   : 550

Reduced problem info
  Constraints : 62  (removed 200)
  Variables   : 81  (removed 14)
  Non-zeros   : 325  (removed 225)
Presolve time : 0.000s

Optimizer info (HSD)
Constraints  : 62
Variables    : 81
RHS          : [+0.00e+00, +0.00e+00]
Lower bounds : [-2.32e+03, +1.25e+01]
Upper bounds : [+0.00e+00, +1.16e+04]

Linear solver options
  Arithmetic   : Float64
  Backend      : CHOLMOD
  System       : Augmented system (K2)

 Itn            PObj            DObj     PFeas    DFeas    GFeas       Mu  Time
   0  -0.0000000e+00  +1.7686535e+05  1.16e+04 8.59e-02 1.77e+05  1.0e+00  0.00
   1  -5.3305641e-02  +1.7675067e+05  7.98e+03 5.26e-02 1.21e+05  6.9e-01  0.00
   2  -8.5532053e-02  +1.7615681e+05  2.06e+03 1.69e-02 3.13e+04  1.8e-01  0.00
Solver exited with status Trm_IterationLimit

Applicable optimizer attributes are documented in the documentations of the respective optimizers. To browse the possibilities, one might want to see the JuMP documentation page that summarizes the references to the available optimizers.

Default solver settings can be examined and changed via Configuration.


This page was generated using Literate.jl.