Skip to content

Commit c8a84b0

Browse files
committed
add additional tests
1 parent a94f8f1 commit c8a84b0

2 files changed

Lines changed: 122 additions & 34 deletions

File tree

src/nonlinear.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ function _relax_complementarity_upper_bound!(
204204
)
205205
lb1, ub1 = MOIU.get_bounds(model, Float64, x1)
206206
if isinf(ub1)
207-
MOI.add_constraint(model, x1, MOI.GreaterThan(0.0))
207+
MOI.add_constraint(model, x1, MOI.LessThan(0.0))
208208
else
209209
@assert ub1 == 0.0 # ensure we follow MOI's convention
210210
# TODO: what should we do if lb1 is finite?
@@ -379,7 +379,7 @@ function _relax_complementarity_upper_bound!(
379379
)
380380
lb1, ub1 = MOIU.get_bounds(model, Float64, x1)
381381
if isinf(ub1)
382-
MOI.add_constraint(model, x1, MOI.GreaterThan(0.0))
382+
MOI.add_constraint(model, x1, MOI.LessThan(0.0))
383383
else
384384
@assert ub1 == 0.0 # ensure we follow MOI's convention
385385
# TODO: what should we do if lb1 is finite?

test/runtests.jl

Lines changed: 120 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,66 @@ function fletcher_leyffer_ex1_nonlinear_model()
2121
return model
2222
end
2323

24-
function nonlinear_test_model()
24+
function nonlinear_test_reformulated_model()
2525
model = Model()
2626
@variable(model, x >= 0.0)
2727
@variable(model, y >= 0.0)
28+
@variable(model, slack >= 0)
2829
@objective(model, Min, x^2 + y^2 - 4*x*y)
2930
# Build complementarity constraints with nonlinear expression
30-
@constraint(model, [sin(x), y] MOI.Complements(2))
31+
@constraint(model, sin(x) == slack)
32+
@constraint(model, slack * y <= 0.0)
3133
return model
3234
end
3335

34-
function nonlinear_test_reformulated_model()
36+
function simple_ncp()
37+
model = Model()
38+
@variable(model, y <= 0)
39+
@constraint(model, y + 1 y)
40+
return model, [y], [-1.0]
41+
end
42+
43+
# Solve min_x x subject to 0 <= x <= 1
44+
function simple_lp_1()
45+
model = Model()
46+
@variable(model, 0 <= x <= 1)
47+
@variable(model, μ)
48+
@constraint(model, 1 - μ == 0.0)
49+
@constraint(model, μ x)
50+
return model, [x, μ], [0.0, 1.0]
51+
end
52+
53+
# Solve min_x -x subject to 0 <= x <= 1
54+
function simple_lp_2()
55+
model = Model()
56+
@variable(model, 0 <= x <= 1)
57+
@variable(model, μ)
58+
@constraint(model, -1 - μ == 0.0)
59+
@constraint(model, μ x)
60+
return model, [x, μ], [1.0, -1.0]
61+
end
62+
63+
# Solve min -x2 subject to (x1, x2) >= 0; x1 + x2 = 1
64+
function simple_lp_3()
65+
model = Model()
66+
@variable(model, 0.0 <= x[1:2])
67+
@variable(model, 0.0 <= z[1:2])
68+
@variable(model, y)
69+
@constraint(model, -z[1] + y == 0.0)
70+
@constraint(model, -1.0 - z[2] + y == 0.0)
71+
@constraint(model, x[1] + x[2] == 1.0)
72+
@constraint(model, z[1] x[1])
73+
@constraint(model, z[2] x[2])
74+
return model, [x; z; y], [0.0, 1.0, 1.0, 0.0, 1.0]
75+
end
76+
77+
function nonlinear_test_model()
3578
model = Model()
3679
@variable(model, x >= 0.0)
3780
@variable(model, y >= 0.0)
38-
@variable(model, slack >= 0)
3981
@objective(model, Min, x^2 + y^2 - 4*x*y)
4082
# Build complementarity constraints with nonlinear expression
41-
@constraint(model, sin(x) == slack)
42-
@constraint(model, slack * y <= 0.0)
83+
@constraint(model, [sin(x), y] MOI.Complements(2))
4384
return model
4485
end
4586

@@ -100,8 +141,17 @@ function test_nonlinear_mispecified_1()
100141
return model
101142
end
102143

103-
# RHS is unbounded
144+
# LHS has a non-trivial upper-bound
104145
function test_nonlinear_mispecified_2()
146+
model = Model()
147+
@variable(model, x1 <= 1.0)
148+
@variable(model, y1 <= 0.0)
149+
@constraint(model, [x1, y1] MOI.Complements(2))
150+
return model
151+
end
152+
153+
# RHS is unbounded
154+
function test_nonlinear_mispecified_3()
105155
model = Model()
106156
@variable(model, x1)
107157
@variable(model, y1)
@@ -147,23 +197,12 @@ end
147197
@test_throws Exception MOI.Utilities.attach_optimizer(model)
148198
end
149199

150-
@testset "Nonlinear reformulation $(relax)" for relax in [
200+
@testset "Nonlinear reformulation with $(relax)" for relax in [
151201
ComplementOpt.ScholtesRelaxation(0.0),
152202
ComplementOpt.FischerBurmeisterRelaxation(1e-8),
153203
ComplementOpt.LiuFukushimaRelaxation(1e-8),
154204
ComplementOpt.KanzowSchwarzRelaxation(1e-8),
155205
]
156-
model = test_nonlinear_reformulation()
157-
set_optimizer(model, () -> ComplementOpt.Optimizer(Ipopt.Optimizer()))
158-
MOI.set(model, ComplementOpt.RelaxationMethod(), relax)
159-
MOI.Utilities.attach_optimizer(model)
160-
161-
for test_func in (test_nonlinear_mispecified_1, test_vertical_mispecified_2)
162-
model = test_func()
163-
set_optimizer(model, () -> ComplementOpt.Optimizer(Ipopt.Optimizer()))
164-
MOI.set(model, ComplementOpt.RelaxationMethod(), relax)
165-
@test_throws Exception MOI.Utilities.attach_optimizer(model)
166-
end
167206
end
168207

169208
instances = filter(names(Instances; all = true)) do name
@@ -180,22 +219,71 @@ end
180219
test_nonlinear_expr()
181220
end
182221

183-
@testset "Relaxation method $(relax)" for relax in [
222+
@testset "Relaxation method: $(relax)" for relax in [
184223
ComplementOpt.ScholtesRelaxation(0.0),
185224
ComplementOpt.FischerBurmeisterRelaxation(1e-8),
186225
ComplementOpt.LiuFukushimaRelaxation(1e-8),
187226
ComplementOpt.KanzowSchwarzRelaxation(1e-8),
188227
]
189-
model = Instances.fletcher_leyffer_ex1_model()
190-
191-
JuMP.set_optimizer(model, () -> ComplementOpt.Optimizer(Ipopt.Optimizer()))
192-
MOI.set(model, ComplementOpt.RelaxationMethod(), relax)
193-
# Need to set the bound relaxation explicitly to 0 for LiuFukushimaRelaxation
194-
JuMP.set_optimizer_attribute(model, "bound_relax_factor", 0.0)
195-
JuMP.set_silent(model)
196-
JuMP.optimize!(model)
197-
198-
@test JuMP.is_solved_and_feasible(model)
199-
@test JuMP.objective_value(model) 0.5 atol=1e-7
200-
@test JuMP.value.(model[:z]) [0.5, 0.5] atol=1e-7
228+
@testset "Test reformulation" begin
229+
model = test_nonlinear_reformulation()
230+
set_optimizer(model, () -> ComplementOpt.Optimizer(Ipopt.Optimizer()))
231+
MOI.set(model, ComplementOpt.RelaxationMethod(), relax)
232+
MOI.Utilities.attach_optimizer(model)
233+
234+
for test_func in (
235+
test_nonlinear_mispecified_1,
236+
test_nonlinear_mispecified_2,
237+
test_nonlinear_mispecified_3,
238+
)
239+
model = test_func()
240+
set_optimizer(model, () -> ComplementOpt.Optimizer(Ipopt.Optimizer()))
241+
MOI.set(model, ComplementOpt.RelaxationMethod(), relax)
242+
@test_throws Exception MOI.Utilities.attach_optimizer(model)
243+
end
244+
end
245+
246+
@testset "Solve Fletcher-Leyffer Ex1 problem" begin
247+
model = Instances.fletcher_leyffer_ex1_model()
248+
JuMP.set_optimizer(model, () -> ComplementOpt.Optimizer(Ipopt.Optimizer()))
249+
MOI.set(model, ComplementOpt.RelaxationMethod(), relax)
250+
JuMP.set_optimizer_attribute(model, "bound_relax_factor", 0.0)
251+
JuMP.set_silent(model)
252+
JuMP.optimize!(model)
253+
254+
@test JuMP.is_solved_and_feasible(model)
255+
@test JuMP.objective_value(model) 0.5 atol=1e-7
256+
@test JuMP.value.(model[:z]) [0.5, 0.5] atol=1e-7
257+
end
258+
259+
@testset "Solve NCP problem $(func)" for func in [simple_ncp, simple_lp_3]
260+
model, vars, sol = func()
261+
JuMP.set_optimizer(model, () -> ComplementOpt.Optimizer(Ipopt.Optimizer()))
262+
MOI.set(model, ComplementOpt.RelaxationMethod(), relax)
263+
JuMP.set_optimizer_attribute(model, "bound_relax_factor", 0.0)
264+
JuMP.set_silent(model)
265+
JuMP.optimize!(model)
266+
267+
@test JuMP.is_solved_and_feasible(model)
268+
@test JuMP.value.(vars) sol atol=1e-7
269+
end
270+
end
271+
272+
# N.B.: at the moment, mixed-complementarity problems are supported only
273+
# with ScholtesRelaxation and with FischerBurmeisterRelaxation
274+
@testset "Mixed-complementarity problem with $(relax)" for relax in [
275+
ComplementOpt.ScholtesRelaxation(0.0),
276+
ComplementOpt.FischerBurmeisterRelaxation(1e-8),
277+
]
278+
@testset "Solve NCP problem $(func)" for func in [simple_lp_1, simple_lp_2]
279+
model, vars, sol = func()
280+
JuMP.set_optimizer(model, () -> ComplementOpt.Optimizer(Ipopt.Optimizer()))
281+
MOI.set(model, ComplementOpt.RelaxationMethod(), relax)
282+
JuMP.set_optimizer_attribute(model, "bound_relax_factor", 0.0)
283+
JuMP.set_silent(model)
284+
JuMP.optimize!(model)
285+
286+
@test JuMP.is_solved_and_feasible(model)
287+
@test JuMP.value.(vars) sol atol=1e-7
288+
end
201289
end

0 commit comments

Comments
 (0)