According to my experience in global optimization, I only encounter these situations so far.
# ✅ The foremost indispensable querying functions are `JuMP.value` and `JuMP.objective_bound`
# Theoretically, `JuMP.objective_value` is redundant, with the presence of `JuMP.value`
function JuMP_objective_bound_works_properly(model)::Bool
# ✅ I think JuMP.jl should have a function `JuMP.has_objective_bound()` in JuMP.jl
# But as I didn't find it, I suppose that we have this function instead
# this function returns true::Bool if `JuMP.objective_bound(model)` works properly after `JuMP.optimize!(model)`
# which partly means that @assert JuMP.objective_bound(model) <= JuMP.objective_value(model) in a Min-program
# where LHS is a valid dual bound, while RHS is a valid primal bound
end
JuMP.optimize!(model)
# After optimize!,
# we employ 8 functions who return Bools to help us judge
# 1. if it is normal (true::Bool), we want it to be silent, then we just fetch things we need and proceed
# 2. if it is abnormal (false::Bool), we want to see an immediate red julia ERROR with useful info (better with the model's name, since we tend to build ≥2 models), then we go back to modify the corresponding model properly until it becomes normal
# In practice, we must only want the 1. (normal) to happen, such that we can finish the whole algorithm
# Mnemonic:
# `p`: primal
# `d`: dual
# `s`: simple (do not set TIME_LIMIT)
# `t`: have set a TIME_LIMIT
# `l`: local solvers
# `g`: global solvers
if no_dual # if I don't need dual solutions, i.e., `JuMP.dual` related things
if no_time_limit # if I haven't set TIME_LIMIT
if local_solver # I only want a primal-side feasible solution, e.g. I used Ipopt
psl(model)
else # I want these 2 functions work properly: `JuMP.value` and `JuMP.objective_bound`, e.g. I used Gurobi
psg(model)
end
else # e.g. if this is a practical large problem such that I had set a TIME_LIMIT
if local_solver
ptl(model)
else
ptg(model)
end
end
else # if I need the `JuMP.dual` related things (e.g. do sensitivity analysis)
if no_time_limit
if local_solver
dsl(model)
else
dsg(model)
end
else
if local_solver
dtl(model)
else
dtg(model)
end
end
end
function psl(model) # ✅
JuMP.has_values(model) || return false
JuMP.termination_status(model) == JuMP.LOCALLY_SOLVED || return false
return true
end
function dsl(model)
JuMP.has_duals(model) || return false
return psl(model)
end
function psg(model) # ✅
JuMP_objective_bound_works_properly(model) || return false
JuMP.has_values(model) || return false
JuMP.termination_status(model) == JuMP.OPTIMAL || return false
return true
end
function dsg(model)
JuMP.has_duals(model) || return false
return psg(model)
end
function ptl(model) # ✅
JuMP.termination_status(model) in [JuMP.LOCALLY_SOLVED, JuMP.TIME_LIMIT] || return false
JuMP.has_values(model) || return false
return true
end
function dtl(model)
JuMP.has_duals(model) || return false
return ptl(model)
end
function ptg(model) # ✅
JuMP.termination_status(model) in [JuMP.OPTIMAL, JuMP.TIME_LIMIT] || return false
JuMP_objective_bound_works_properly(model) || return false
JuMP.has_values(model) || return false
return true
end
function dtg(model)
JuMP.has_duals(model) || return false
return ptg(model)
end