I added is_solved_and_feasible
for a few reasons:
- Many questions on this forum were because people did not check the termination status before querying results
- Checking the termination status is a bit subtle, because users need to understand
OPTIMAL
and LOCALLY_SOLVED
, and also the primal (and dual) statuses.
- For the basic use case, it saves quite a few lines of code, and it makes the code written easier to understand.
I don’t think we will add this macro. The first reason is that it hides too much from the user while saving too few lines of code. Your line:
@optimise_assert_optimal(model_123) # be silent because optimality
is essentially just:
JuMP.optimize!(model_123)
if (status = JuMP.termination_status(model_123)) != JuMP.OPTIMAL
error("model_123: $status")
end
A second reason is that, while macros are cool and JuMP uses them a lot, they’re somewhat over-used and abused by Julia programmers. There is almost never a good reason to write a macro for user-code.
For example, at the cost of an extra argument, you could use a function:
function optimize!_and_assert_optimality(model, name)
JuMP.optimize!(model)
if (status = JuMP.termination_status(model)) != JuMP.OPTIMAL
error("$name: $status")
end
end
optimize!_and_assert_optimality(model_123, "model_123")
One place where the function approach would be better is:
models = Dict("A" => model_123, "B" => model456)
for (name, model) in models
@optimise_assert_optimal model
# or
optimize!_and_assert_optimality(model, name)
end
Your macro approach will just print ERROR: model: status
whereas the function will print ERROR: A: status
and ERROR: B: status
.
That being said, a great thing about JuMP is that because it is embedded in Julia you are more than welcome to write and use this macro yourself! We just won’t be adding it to JuMP.
On a more technical note, one reason that I strongly, strongly discourage you to write your own macro is that they can be tricky to get correct. Consider what happens if you use @optimise_assert_optimal
in a function:
julia> import JuMP
julia> import Gurobi
julia> function optimise(model) return (JuMP.optimize!(model); JuMP.termination_status(model)) end
optimise (generic function with 1 method)
julia> macro optimise_assert_optimal(model)
name = string(model)
return quote
status = optimise($model)
status == JuMP.OPTIMAL || error($name * ": " * string(status))
end
end
@optimise_assert_optimal (macro with 1 method)
julia> function foo()
model_123 = JuMP.Model(Gurobi.Optimizer)
@optimise_assert_optimal(model_123) # be silent because optimality
end
foo (generic function with 1 method)
julia> foo()
Set parameter LicenseID to value 890341
ERROR: UndefVarError: `model_123` not defined
Stacktrace:
[1] macro expansion
@ ./REPL[4]:4 [inlined]
[2] foo()
@ Main ./REPL[5]:3
[3] top-level scope
@ REPL[6]:1
to fix, you need to use esc
:
julia> import JuMP
julia> import Gurobi
julia> macro optimise_assert_optimal(model)
name_str = string(model, ": ")
code = quote
JuMP.optimize!($model)
if (status = JuMP.termination_status($model)) != JuMP.OPTIMAL
error($name_str * string(status))
end
end
return esc(code)
end
@optimise_assert_optimal (macro with 1 method)
julia> function foo()
model_123 = JuMP.Model(Gurobi.Optimizer)
@optimise_assert_optimal(model_123) # be silent because optimality
end
foo (generic function with 1 method)
julia> foo()
Set parameter LicenseID to value 890341
Gurobi Optimizer version 12.0.0 build v12.0.0rc1 (mac64[x86] - Darwin 24.1.0 24B83)
CPU model: Intel(R) Core(TM) i5-8259U CPU @ 2.30GHz
Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
Optimize a model with 0 rows, 0 columns and 0 nonzeros
Model fingerprint: 0xf9715da1
Coefficient statistics:
Matrix range [0e+00, 0e+00]
Objective range [0e+00, 0e+00]
Bounds range [0e+00, 0e+00]
RHS range [0e+00, 0e+00]
Presolve time: 0.00s
Presolve: All rows and columns removed
Iteration Objective Primal Inf. Dual Inf. Time
0 0.0000000e+00 0.000000e+00 0.000000e+00 0s
Solved in 0 iterations and 0.00 seconds (0.00 work units)
Optimal objective 0.000000000e+00
User-callback calls 23, time in user-callback 0.00 sec