Decision Model
The following shows how to build and solve optimization problems in GeneDrive.jl
. Decision models allow us to determine the best (optimal) strategy for achieving a goal (objective), taking into account the limitations (constraints) of our system of interest.
To set up the decision model, it is necessary to define an additional component for our data model: our problem's operational limitations (constraints). Operational constraints enter the simulation as fields of the ReleaseStrategy
struct and are defined per node. View struct fields by running:
julia> ? ReleaseStrategy
Biological constraints are pre-defined within the decision model. For brevity, we will specify our example operational constraints on top of the node3
data model created previously.
# Define constraints using desired fields (re-use `release_genotype`)
node3_strategy = ReleaseStrategy(release_this_gene_index = release_genotype,
release_this_life_stage = Male, release_start_time = 7,
release_size_max_per_timestep = 1000)
# Assign constraints to dict of nodes
mystrategy = Dict(1 => node3_strategy)
# Build the optimization problem (re-use `node3`, `tspan`)
prob = create_decision_model(node3, tspan; node_strategy = mystrategy);
A JuMP Model
Feasibility problem with:
Variables: 16790
`JuMP.AffExpr`-in-`MathOptInterface.EqualTo{Float64}`: 5475 constraints
`JuMP.AffExpr`-in-`MathOptInterface.GreaterThan{Float64}`: 1095 constraints
`JuMP.AffExpr`-in-`MathOptInterface.LessThan{Float64}`: 2191 constraints
`JuMP.QuadExpr`-in-`MathOptInterface.EqualTo{Float64}`: 3285 constraints
`JuMP.VariableRef`-in-`MathOptInterface.EqualTo{Float64}`: 3291 constraints
`JuMP.VariableRef`-in-`MathOptInterface.GreaterThan{Float64}`: 13499 constraints
`JuMP.VariableRef`-in-`MathOptInterface.LessThan{Float64}`: 359 constraints
Nonlinear: 3285 constraints
Model mode: AUTOMATIC
CachingOptimizer state: EMPTY_OPTIMIZER
Solver name: Ipopt
Names registered in the model: E, E_con_A0, E_con_A1, E_con_B0, E_con_B1, F, F_con_0, F_con_1, L, L_con_A0, L_con_A1, L_con_B0, L_con_B1, M, M_con_0, M_con_1, P, P_con_A0, P_con_A1, P_con_B0, P_con_B1, Sets, control_F, control_M, control_equivalence, control_limit_schedule_lower_F, control_limit_schedule_lower_M, control_limit_schedule_upper_F, control_limit_schedule_upper_M, control_limit_total_F, control_limit_total_M, mate_bound, migration_E, migration_L, migration_M, migration_P, release_location
To solve the decision model as an optimization, a goal (objective) must be supplied. However, even in the absence of an objective function we can derive useful information: without an objective, the solution method auto-selected by GeneDrive.jl
acts as a nonlinear solver and allows us to compare the behavior of our dynamic and decision models.
# Solve
sol = solve_decision_model(prob);
# Format all results for analysis
results = format_decision_model_results(sol);
Dict{Any, Any} with 8 entries:
:node_1_scenario_1_organism_1_P => 365×3 DataFrame…
:node_1_scenario_1_organism_1_E => 365×3 DataFrame…
:node_1_scenario_1_organism_1_F => 365×3 DataFrame…
:node_1_scenario_1_organism_1_M => 365×3 DataFrame…
:node_1_scenario_1_organism_1_L => 365×3 DataFrame…
:node_1_organism_1_control_M => 365×3 DataFrame…
:Time => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10 … 356, 35…
:node_1_organism_1_control_F => 365×3 DataFrame…
To visualize a subset of the results, run plot_decision_ridl_females(sol)
. Because no objective function was specified, this output should qualitatively match those from the dynamic model when no intervention is conducted (i.e. when using the same data model, and no RIDL release object is passed to solve_dynamic_model
).