Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Base.empty! method for JuMP.Model. #2198

Merged
merged 2 commits into from
Mar 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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