Skip to content

Commit 5b50dfb

Browse files
authored
Improve test coverage of the MOI wrapper (#335)
1 parent 888a3a0 commit 5b50dfb

File tree

6 files changed

+152
-30
lines changed

6 files changed

+152
-30
lines changed

src/MOI_wrapper.jl

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -484,17 +484,6 @@ function MOI.get(
484484
return attributes
485485
end
486486

487-
# MOI.ListOfOptimizerAttributesSet
488-
489-
function MOI.get(::Optimizer, ::MOI.ListOfOptimizerAttributesSet)
490-
attributes = MOI.ListOfOptimizerAttributesSet[]
491-
timelim = MOI.get(o, MOI.TimeLimitSec())
492-
if timelim !== nothing
493-
push!(attributes, MOI.TimeLimitSec())
494-
end
495-
return attributes
496-
end
497-
498487
include(joinpath("MOI_wrapper", "variable.jl"))
499488
include(joinpath("MOI_wrapper", "constraints.jl"))
500489
include(joinpath("MOI_wrapper", "linear_constraints.jl"))

src/MOI_wrapper/HeuristicCallback.jl

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,9 @@ end
1515
mutable struct HeuristicCb <: Heuristic
1616
scipd::SCIPData
1717
heurcallback::Function
18-
end
1918

20-
# If no cut callback is given, the cut callback does nothing.
21-
HeuristicCb(scipd::SCIPData) = HeuristicCb(scipd, cb_data -> nothing)
19+
HeuristicCb(scipd::SCIPData, cb=cb_data -> nothing) = new(scipd, cb)
20+
end
2221

2322
"""
2423
Used for an argument to the heuristic callback, which in turn uses that argument to

src/MOI_wrapper/UserCutCallback.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,11 @@ end
2929
mutable struct CutCbSeparator <: AbstractSeparator
3030
scipd::SCIPData
3131
cutcallback::Function
32+
33+
CutCbSeparator(scipd::SCIPData, cb=cb_data -> nothing) = new(scipd, cb)
3234
end
3335

3436
# If no cut callback is given, the cut callback does nothing.
35-
CutCbSeparator(scipd::SCIPData) = CutCbSeparator(scipd, cb_data -> nothing)
3637

3738
"""
3839
Used for an argument to the cut callback, which in turn uses that argument to

src/MOI_wrapper/quadratic_constraints.jl

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,6 @@ function MOI.add_constraint(
5050
return ci
5151
end
5252

53-
function MOI.set(
54-
o::SCIP.Optimizer,
55-
::MOI.ConstraintSet,
56-
ci::MOI.ConstraintIndex{MOI.ScalarQuadraticFunction{Float64},S},
57-
set::S,
58-
) where {S<:BOUNDS}
59-
allow_modification(o)
60-
lhs, rhs = bounds(o, set)
61-
@SCIP_CALL SCIPchgLhsQuadratic(o, cons(o, ci), lhs)
62-
@SCIP_CALL SCIPchgRhsQuadratic(o, cons(o, ci), rhs)
63-
return nothing
64-
end
65-
6653
function MOI.get(
6754
o::Optimizer,
6855
::MOI.ConstraintFunction,

src/MOI_wrapper/variable.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,10 +199,10 @@ function MOI.add_constraint(o::Optimizer, vi::MOI.VariableIndex, ::MOI.ZeroOne)
199199
allow_modification(o)
200200
v = var(o, vi)
201201
p_infeas = Ref{SCIP_Bool}()
202-
@SCIP_CALL SCIPchgVarType(o, v, SCIP_VARTYPE_BINARY, p_infeas)
203202
lb, ub = SCIPvarGetLbOriginal(v), SCIPvarGetUbOriginal(v)
204203
# Store old bounds for later recovery.
205204
o.binbounds[vi] = MOI.Interval(lb, ub)
205+
@SCIP_CALL SCIPchgVarType(o, v, SCIP_VARTYPE_BINARY, p_infeas)
206206
@SCIP_CALL SCIPchgVarLb(o, v, max(lb, 0.0))
207207
@SCIP_CALL SCIPchgVarUb(o, v, min(ub, 1.0))
208208
ci = MOI.ConstraintIndex{MOI.VariableIndex,MOI.ZeroOne}(vi.value)
@@ -370,7 +370,7 @@ function MOI.delete(
370370
if type == _kSCIP_LESS_AND_GREATER_THAN && S <: MOI.LessThan
371371
o.bound_types[vi] = _kSCIP_GREATER_THAN
372372
elseif type == _kSCIP_LESS_AND_GREATER_THAN && S <: MOI.GreaterThan
373-
o.bound_types[vi] = _kSCIP_LESS_AND_GREATER_THAN
373+
o.bound_types[vi] = _kSCIP_LESS_THAN
374374
else
375375
delete!(o.bound_types, vi)
376376
end

test/MOI_tests.jl

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,7 @@ function _test_presolving(presolving)
730730
),
731731
)
732732
MOI.set(optimizer, MOI.ObjectiveSense(), MOI.MAX_SENSE)
733+
@test MOI.supports(optimizer, SCIP.Presolving())
733734
@test MOI.get(optimizer, SCIP.Presolving()) == true
734735
MOI.set(optimizer, SCIP.Presolving(), presolving)
735736
@test MOI.get(optimizer, SCIP.Presolving()) == presolving
@@ -865,9 +866,11 @@ function test_heuristic_callback()
865866
values[findmax(x_frac)[2]] = 1.0
866867
values[1] = 1.0
867868
values[2] = 1.0
869+
@test MOI.supports(o, MOI.HeuristicSolution(callback_data))
868870
MOI.submit(o, MOI.HeuristicSolution(callback_data), x, values)
869871
global ncalls[] += 1
870872
end
873+
@test MOI.supports(o, MOI.HeuristicCallback())
871874
MOI.set(o, MOI.HeuristicCallback(), heuristic_callback)
872875
MOI.optimize!(o)
873876
@test ncalls[] > 0
@@ -1195,6 +1198,7 @@ function test_obtaining_the_LP_solution()
11951198
)
11961199
calls += 1
11971200
end
1201+
@test MOI.supports(optimizer, MOI.UserCutCallback())
11981202
MOI.set(optimizer, MOI.UserCutCallback(), cutcallback)
11991203
# solve the problem
12001204
MOI.optimize!(optimizer)
@@ -1237,6 +1241,7 @@ function test_cutting_one_optimal_solution()
12371241
MOI.set(optimizer, MOI.ObjectiveSense(), MOI.MAX_SENSE)
12381242
calls = 0
12391243
function cutcallback(cb_data)
1244+
@test MOI.supports(optimizer, MOI.UserCut{SCIP.CutCbData}(cb_data))
12401245
MOI.submit(
12411246
optimizer,
12421247
MOI.UserCut{SCIP.CutCbData}(cb_data),
@@ -1500,6 +1505,147 @@ function test_AddSingleCut_too_strong_cut()
15001505
return
15011506
end
15021507

1508+
function test_ScalarFunctionConstantNotZero_quadratic()
1509+
model = SCIP.Optimizer()
1510+
x = MOI.add_variable(model)
1511+
f = 1.0 * x * x + 2.0
1512+
@test_throws(
1513+
MOI.ScalarFunctionConstantNotZero,
1514+
MOI.add_constraint(model, f, MOI.LessThan(3.0)),
1515+
)
1516+
return
1517+
end
1518+
1519+
function test_ListOfSupportedNonlinearOperators()
1520+
model = SCIP.Optimizer()
1521+
op = MOI.get(model, MOI.ListOfSupportedNonlinearOperators())
1522+
@test op isa Vector{Symbol}
1523+
@test length(op) == 11
1524+
return
1525+
end
1526+
1527+
function test_nonlinear_epigraph_functions()
1528+
op(f, args...) = MOI.ScalarNonlinearFunction(f, Any[args...])
1529+
default_set = MOI.Interval(1.0, 2.0)
1530+
for (fn, set, t_value) in [
1531+
(x -> op(:exp, 1.0 * x + 2.0), default_set, exp(3)),
1532+
(x -> op(:exp, 1.0 * x * x + 2.0), default_set, exp(3)),
1533+
(x -> op(:exp, 1.0 * x * x + 2.0 * x + 3.0), default_set, exp(6)),
1534+
# :/
1535+
(x -> op(:/, 2, x), MOI.Interval(1.0, 2.0), 1.0),
1536+
(x -> op(:/, 2, x), MOI.Interval(1.0, 1.5), 4 / 3),
1537+
# :abs
1538+
(x -> op(:abs, x), MOI.Interval(1.0, 2.0), 1.0),
1539+
(x -> op(:abs, x), MOI.Interval(-2.0, 2.0), 0.0),
1540+
(x -> op(:abs, x), MOI.Interval(-2.0, -1.3), 1.3),
1541+
# :exp
1542+
(x -> op(:exp, x), MOI.Interval(-2.0, -1.3), exp(-2)),
1543+
(x -> op(:exp, x), MOI.Interval(2.0, 3.0), exp(2)),
1544+
# :log
1545+
(x -> op(:-, op(:log, x)), MOI.Interval(2.0, 3.0), -log(3)),
1546+
# :sin
1547+
(x -> op(:sin, x), MOI.Interval(pi, 2 * pi), -1.0),
1548+
(x -> op(:sin, x), MOI.Interval(0.0, pi), 0.0),
1549+
# :cos
1550+
(x -> op(:cos, x), MOI.Interval(pi, 2 * pi), -1.0),
1551+
(x -> op(:cos, x), MOI.Interval(0.0, pi), -1.0),
1552+
(x -> op(:cos, x), MOI.Interval(-pi / 2, pi / 2), 0.0),
1553+
# :sqrt
1554+
(x -> op(:-, op(:sqrt, x)), MOI.Interval(2.0, 3.0), -sqrt(3)),
1555+
]
1556+
model = SCIP.Optimizer()
1557+
MOI.set(model, MOI.Silent(), true)
1558+
x, _ = MOI.add_constrained_variable(model, set)
1559+
t = MOI.add_variable(model)
1560+
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
1561+
F = MOI.ScalarAffineFunction{Float64}
1562+
MOI.set(model, MOI.ObjectiveFunction{F}(), 1.0 * t)
1563+
MOI.add_constraint(model, op(:-, t, fn(x)), MOI.GreaterThan(0.0))
1564+
MOI.optimize!(model)
1565+
@test (MOI.get(model, MOI.VariablePrimal(), t), t_value; atol=1e-4)
1566+
end
1567+
return
1568+
end
1569+
1570+
function test_unsupported_nonlinear_operator()
1571+
model = SCIP.Optimizer()
1572+
x = MOI.add_variable(model)
1573+
f = MOI.ScalarNonlinearFunction(:foo, Any[x])
1574+
@test_throws(
1575+
MOI.UnsupportedNonlinearOperator(:foo),
1576+
MOI.add_constraint(model, f, MOI.GreaterThan(0.0)),
1577+
)
1578+
f = MOI.ScalarNonlinearFunction(:^, Any[x, x])
1579+
@test_throws(
1580+
MOI.UnsupportedNonlinearOperator(:^),
1581+
MOI.add_constraint(model, f, MOI.GreaterThan(0.0)),
1582+
)
1583+
return
1584+
end
1585+
1586+
function test_delete_variable_with_bounds()
1587+
for sets in (
1588+
(MOI.LessThan(1.0),),
1589+
(MOI.GreaterThan(1.0),),
1590+
(MOI.EqualTo(1.0),),
1591+
(MOI.Interval(1.0, 2.0),),
1592+
(MOI.GreaterThan(1.0), MOI.LessThan(2.0)),
1593+
)
1594+
model = SCIP.Optimizer()
1595+
x = MOI.add_variable(model)
1596+
for set in sets
1597+
MOI.add_constraint(model, x, set)
1598+
end
1599+
ret = MOI.get(model, MOI.ListOfConstraintTypesPresent())
1600+
@test length(ret) == length(sets)
1601+
for set in sets
1602+
(MOI.VariableIndex, typeof(set)) in ret
1603+
end
1604+
MOI.delete(model, x)
1605+
@test isempty(MOI.get(model, MOI.ListOfConstraintTypesPresent()))
1606+
end
1607+
return
1608+
end
1609+
1610+
function test_BoundAlreadySet()
1611+
model = SCIP.Optimizer()
1612+
x = MOI.add_variable(model)
1613+
MOI.add_constraint(model, x, MOI.GreaterThan(1.0))
1614+
MOI.add_constraint(model, x, MOI.LessThan(2.0))
1615+
@test_throws(
1616+
MOI.LowerBoundAlreadySet,
1617+
MOI.add_constraint(model, x, MOI.GreaterThan(1.0)),
1618+
)
1619+
@test_throws(
1620+
MOI.UpperBoundAlreadySet,
1621+
MOI.add_constraint(model, x, MOI.LessThan(1.0)),
1622+
)
1623+
return
1624+
end
1625+
1626+
function test_delete_bounds()
1627+
model = SCIP.Optimizer()
1628+
x = MOI.add_variable(model)
1629+
c_l = MOI.add_constraint(model, x, MOI.GreaterThan(1.0))
1630+
c_u = MOI.add_constraint(model, x, MOI.LessThan(2.0))
1631+
MOI.delete(model, c_u)
1632+
MOI.add_constraint(model, x, MOI.LessThan(3.0))
1633+
@test MOI.get(model, MOI.ConstraintSet(), c_u) == MOI.LessThan(3.0)
1634+
MOI.delete(model, c_l)
1635+
MOI.add_constraint(model, x, MOI.GreaterThan(-1.0))
1636+
@test MOI.get(model, MOI.ConstraintSet(), c_l) == MOI.GreaterThan(-1.0)
1637+
return
1638+
end
1639+
1640+
function test_get_binary_with_bounds()
1641+
model = SCIP.Optimizer()
1642+
x = MOI.add_variable(model)
1643+
c = MOI.add_constraint(model, x, MOI.Interval(0.5, 1.1))
1644+
MOI.add_constraint(model, x, MOI.ZeroOne())
1645+
@test MOI.get(model, MOI.ConstraintSet(), c) == MOI.Interval(0.5, 1.1)
1646+
return
1647+
end
1648+
15031649
end # module TestMOIWrapper
15041650

15051651
TestMOIWrapper.runtests()

0 commit comments

Comments
 (0)