Skip to content

Commit

Permalink
Add docs and breaking tests
Browse files Browse the repository at this point in the history
  • Loading branch information
odow committed May 31, 2021
1 parent 952b7a4 commit f021eab
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 41 deletions.
5 changes: 5 additions & 0 deletions docs/src/manual/implementing.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,11 @@ For each attribute
* [`supports`](@ref) returns a `Bool` indicating whether the solver supports the
attribute.

!!! info
Use [`attribute_value_type`](@ref) to check the value expected by a given
attribute. You should make sure that your [`get`](@ref) function correctly
infers to this type (or a subtype of it).

Each column in the table indicates whether you need to implement the particular
method for each attribute.

Expand Down
1 change: 1 addition & 0 deletions docs/src/reference/models.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ get
get!
set
supports
attribute_value_type
```

## Model interface
Expand Down
85 changes: 63 additions & 22 deletions src/Test/UnitTests/attributes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -146,45 +146,61 @@ for attr in (
:Silent,
:SimplexIterations,
:SolverName,
:SolveTime,
:SolveTimeSec,
:TerminationStatus,
:TimeLimitSec,
)
f = Symbol("test_attribute_$(attr)")
@eval begin
function $(f)(model::MOI.ModelLike, config::TestConfig)
unittests["test_attribute_$(attr)"] = @eval begin
function $(f)(model::MOI.ModelLike, config::Config)
MOI.empty!(model)
MOI.optimize!(model)
attribute = MOI.$(attr)()
if MOI.is_set_by_optimize(attribute)
if !config.solve
return
end
MOI.optimize!(model)
end
T = MOI.attribute_value_type(attribute)
try
MOI.get(model, attribute)
catch err
if err isa ArgumentError
return # Solver does not support accessing the attribute.
end
end

@static if VERSION < v"1.5"
@test MOI.get(model, attribute) isa T
else
@test @inferred(T, MOI.get(model, attribute)) isa T
end
end
# unittests["test_attribute_$(attr)"]
end
end

# Variable attributes.
for attr in (:VariableName,)
f = Symbol("test_attribute_$(attr)")
@eval begin
function $(f)(model::MOI.ModelLike, config::TestConfig)
unittests["test_attribute_$(attr)"] = @eval begin
function $(f)(model::MOI.ModelLike, config::Config)
MOI.empty!(model)
x = MOI.add_variable(model)
MOI.set(model, MOI.VariableName(), x, "x")
MOI.optimize!(model)
attribute = MOI.$(attr)()
if MOI.is_set_by_optimize(attribute)
if !config.solve
return
end
MOI.optimize!(model)
end
T = MOI.attribute_value_type(attribute)
@static if VERSION < v"1.5"
@test MOI.get(model, attribute, x) isa T
else
@test @inferred(T, MOI.get(model, attribute, x)) isa T
end
end
# unittests["test_attribute_$(attr)"]
end
end

Expand All @@ -196,26 +212,51 @@ for attr in (
:ConstraintSet,
)
f = Symbol("test_attribute_$(attr)")
@eval begin
function $(f)(model::MOI.ModelLike, config::TestConfig)
unittests["test_attribute_$(attr)"] = @eval begin
function $(f)(model::MOI.ModelLike, config::Config{T}) where {T}
MOI.empty!(model)
x = MOI.add_variable(model)
ci = MOI.add_constraint(
model,
MOI.SingleVariable(x),
MOI.GreaterThan(0.0),
)
MOI.optimize!(model)
MOI.set(model, MOI.ConstraintName(), ci, "ci")
ci =
if MOI.supports_constraint(
model,
MOI.ScalarAffineFunction{T},
MOI.GreaterThan{T},
)
MOI.add_constraint(
model,
MOI.ScalarAffineFunction(
[MOI.ScalarAffineTerm(one(T), x)],
zero(T),
),
MOI.GreaterThan(zero(T)),
)
elseif MOI.supports_constraint(
model,
MOI.VectorOfVariables,
MOI.Nonnegatives,
)
MOI.add_constraint(
model,
MOI.VectorOfVariables([x]),
MOI.Nonnegatives(1),
)
else
return
end
attribute = MOI.$(attr)()
T = MOI.attribute_value_type(attribute)
if MOI.is_set_by_optimize(attribute)
if !config.solve
return
end
MOI.optimize!(model)
end
VT = MOI.attribute_value_type(attribute)
@static if VERSION < v"1.5"
@test MOI.get(model, attribute, ci) isa T
@test MOI.get(model, attribute, ci) isa VT
else
@test @inferred(T, MOI.get(model, attribute, ci)) isa T
@test @inferred(T, MOI.get(model, attribute, ci)) isa VT
end
end
# unittests["test_attribute_$(attr)"]
end
end

Expand Down
22 changes: 22 additions & 0 deletions test/Test/unit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,28 @@ end
"solve_farkas_variable_lessthan",
"solve_farkas_variable_lessthan_max",
"solve_twice",
"test_attribute_BarrierIterations",
"test_attribute_ConflictStatus",
"test_attribute_DualStatus",
"test_attribute_Name",
"test_attribute_NodeCount",
"test_attribute_NumberOfThreads",
"test_attribute_NumberOfVariables",
"test_attribute_ObjectiveFunctionType",
"test_attribute_ObjectiveSense",
"test_attribute_PrimalStatus",
"test_attribute_RelativeGap",
"test_attribute_ResultCount",
"test_attribute_Silent",
"test_attribute_SimplexIterations",
"test_attribute_SolverName",
"test_attribute_SolveTimeSec",
"test_attribute_TerminationStatus",
"test_attribute_TimeLimitSec",
"test_attribute_VariableName",
"test_attribute_ConstraintFunction",
"test_attribute_ConstraintName",
"test_attribute_ConstraintSet",
],
)
MOI.empty!(model)
Expand Down
12 changes: 11 additions & 1 deletion test/Utilities/cachingoptimizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,17 @@ for state in (MOIU.NO_OPTIMIZER, MOIU.EMPTY_OPTIMIZER, MOIU.ATTACHED_OPTIMIZER)

config = MOIT.Config(solve = false)
@testset "Unit" begin
MOIT.unittest(m, config)
MOIT.unittest(
m,
config,
# Exclude these tests because no optimizer is attached.
[
"test_attribute_Silent",
"test_attribute_SolverName",
"test_attribute_NumberOfThreads",
"test_attribute_TimeLimitSec",
],
)
end
@testset "Continuous Linear" begin
exclude = ["partial_start"] # VariablePrimalStart not supported.
Expand Down
28 changes: 11 additions & 17 deletions test/Utilities/mockoptimizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -241,29 +241,23 @@ end
model = MOI.Utilities.MockOptimizer(
MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
)
config = MOI.Test.TestConfig()
config = MOI.Test.Config()
MOI.set(model, MOI.NumberOfThreads(), 4)
MOI.set(model, MOI.Silent(), true)
MOI.set(model, MOI.Name(), "Mock")
MOI.set(model, MOI.TimeLimitSec(), 1.0)
MOI.Utilities.set_mock_optimize!(
model,
mock -> MOI.set(mock, MOI.BarrierIterations(), 1),
mock -> nothing, # ConflictStatus
mock -> nothing, # DualStatus
mock -> MOI.set(mock, MOI.Name(), "Mock"),
mock -> nothing,
mock -> nothing,
mock -> MOI.set(mock, MOI.NodeCount(), 1),
mock -> nothing, # NumberOfThreads
mock -> nothing, # NumberOfVariables
mock -> nothing, # ObjectiveFunctionType
mock -> nothing, # ObjectiveSense
mock -> nothing, # PrimalStatus
mock -> nothing,
mock -> MOI.set(mock, MOI.RelativeGap(), 0.0),
mock -> nothing, # ResultCount
mock -> nothing, # Silent
mock -> MOI.set(mock, MOI.SimplexIterations(), 1),
mock -> nothing,
mock -> MOI.set(mock, MOI.SolveTime(), 1.0),
mock -> MOI.set(mock, MOI.SimplexIterations(), 1),
mock -> MOI.set(mock, MOI.SolveTimeSec(), 1.0),
mock -> MOI.set(mock, MOI.TerminationStatus(), MOI.OPTIMAL),
mock -> MOI.set(mock, MOI.TimeLimitSec(), 1.0),
)
MOI.Test.test_attribute_BarrierIterations(model, config)
MOI.Test.test_attribute_ConflictStatus(model, config)
Expand All @@ -280,7 +274,7 @@ end
MOI.Test.test_attribute_Silent(model, config)
MOI.Test.test_attribute_SimplexIterations(model, config)
MOI.Test.test_attribute_SolverName(model, config)
MOI.Test.test_attribute_SolveTime(model, config)
MOI.Test.test_attribute_SolveTimeSec(model, config)
MOI.Test.test_attribute_TerminationStatus(model, config)
MOI.Test.test_attribute_TimeLimitSec(model, config)
end
Expand All @@ -289,15 +283,15 @@ end
model = MOI.Utilities.MockOptimizer(
MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
)
config = MOI.Test.TestConfig()
config = MOI.Test.Config()
MOI.Test.test_attribute_VariableName(model, config)
end

@testset "test_constraint_attributes" begin
model = MOI.Utilities.MockOptimizer(
MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
)
config = MOI.Test.TestConfig()
config = MOI.Test.Config()
MOI.Test.test_attribute_ConstraintFunction(model, config)
MOI.Test.test_attribute_ConstraintName(model, config)
MOI.Test.test_attribute_ConstraintSet(model, config)
Expand Down
12 changes: 11 additions & 1 deletion test/Utilities/universalfallback.jl
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,17 @@ config = MOIT.Config(solve = false)
@test MOI.is_empty(uf)
end
@testset "Unit" begin
MOIT.unittest(uf, config)
MOIT.unittest(
uf,
config,
# Exclude these tests because no optimizer is attached.
[
"test_attribute_Silent",
"test_attribute_SolverName",
"test_attribute_NumberOfThreads",
"test_attribute_TimeLimitSec",
],
)
end
@testset "Modification" begin
MOIT.modificationtest(uf, config)
Expand Down

0 comments on commit f021eab

Please sign in to comment.