Skip to content

Commit

Permalink
Added Base.empty! method for JuMP.Model. (#2198)
Browse files Browse the repository at this point in the history
* Added Base.empty! method for JuMP.Model, as its documentation and tests. Documentation is generating and tests passing.

* As asked by @blegat,  is left alone. Updated comments to reflect this.
  • Loading branch information
henriquebecker91 authored Mar 13, 2020
1 parent 6f9192a commit 2532e46
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 11 deletions.
5 changes: 5 additions & 0 deletions docs/src/solvers.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ Model()
Model(::Any)
```

A JuMP model may be reused by emptying it first:
```@docs
Base.empty!(::Model)
```

```@meta
# TODO: how to control the caching optimizer states
```
Expand Down
26 changes: 26 additions & 0 deletions src/JuMP.jl
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,32 @@ function add_bridge(model::Model,
return
end

"""
empty!(model::Model) -> model
Empty the model, that is, remove all variables, constraints and model
attributes but not optimizer attributes. Always return the argument.
Note: removes extensions data.
"""
function Base.empty!(model::Model)::Model
# The method changes the Model object to, basically, the state it was when
# created (if the optimizer was already pre-configured). The exceptions
# are:
# * optimize_hook: it is basically an optimizer attribute and we promise
# to leave them alone (as do MOI.empty!).
# * bridge_types: for consistency with MOI.empty! for
# MOI.Bridges.LazyBridgeOptimizer.
# * operator_counter: it is just a counter for a single-time warning
# message (so keeping it helps to discover inneficiencies).
MOI.empty!(model.moi_backend)
empty!(model.shapes)
model.nlp_data = nothing
empty!(model.obj_dict)
empty!(model.ext)
return model
end

"""
num_variables(model::Model)::Int64
Expand Down
63 changes: 52 additions & 11 deletions test/model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,38 @@ end
# Custom set Nonnegative with bridge NonnegativeBridge
include("nonnegative_bridge.jl")

function test_result_attributes(; test_empty = false)
err = JuMP.OptimizeNotCalled()
model = Model(() -> MOIU.MockOptimizer(SimpleLPModel{Float64}()))
@variable(model, x)
c = @constraint(model, x 0)
@objective(model, Max, x)
if test_empty
optimize!(model)
empty!(model)
end
@test_throws err JuMP.objective_value(model)
@test_throws err JuMP.dual_objective_value(model)
@test_throws err JuMP.objective_bound(model)
@test_throws err JuMP.value(x)
@test_throws err JuMP.value(c)
@test_throws err JuMP.dual(c)
end

function fill_small_test_model!(model)
# The model does not need to make sense, just use many different features.
@variable(model, a[1:5] >= 0, Int)
@variable(model, b[6:10], Bin)
@variable(model, c[1:3] == 0)
@variable(model, 10 <= d[1:3] <= 20)
@constraint(model, con1, sum(a) + sum(b) <= 5)
@constraint(model, con2, sum(b) >= 3)
@constraint(model, con3, sum(d[1:2]) >= 5)
@constraint(model, con4, sum(d) <= (sum(c) + 10))
@objective(model, Max, sum(a) - sum(b) + sum(d))
return model
end

function test_model()
@testset "NoOptimizer" begin
err = NoOptimizer()
Expand All @@ -47,17 +79,26 @@ function test_model()
end

@testset "Result attributes" begin
err = JuMP.OptimizeNotCalled()
model = Model(() -> MOIU.MockOptimizer(SimpleLPModel{Float64}()))
@variable(model, x)
c = @constraint(model, x 0)
@objective(model, Max, x)
@test_throws err JuMP.objective_value(model)
@test_throws err JuMP.dual_objective_value(model)
@test_throws err JuMP.objective_bound(model)
@test_throws err JuMP.value(x)
@test_throws err JuMP.value(c)
@test_throws err JuMP.dual(c)
test_result_attributes()
end

@testset "Result attributes after empty!" begin
test_result_attributes(test_empty = true)
end

@testset "empty!(model)" begin
model = Model()
backend_type = typeof(backend(model))
model.optimize_hook === nothing
hook(m) = nothing
JuMP.set_optimize_hook(model, hook)
@test model.optimize_hook === hook
@test fill_small_test_model!(model) === model
@test_throws ErrorException fill_small_test_model!(model)
@test empty!(model) === model
@test model.optimize_hook === hook # empty! does not touch the hook
@test isa(backend(model), backend_type)
@test fill_small_test_model!(model) === model
end

@testset "Test variable/model 'hygiene'" begin
Expand Down

0 comments on commit 2532e46

Please sign in to comment.