@@ -21,25 +21,66 @@ function fletcher_leyffer_ex1_nonlinear_model()
2121 return model
2222end
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
3234end
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
4485end
4586
@@ -100,8 +141,17 @@ function test_nonlinear_mispecified_1()
100141 return model
101142end
102143
103- # RHS is unbounded
144+ # LHS has a non-trivial upper-bound
104145function 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)
148198end
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
167206end
168207
169208instances = filter (names (Instances; all = true )) do name
@@ -180,22 +219,71 @@ end
180219 test_nonlinear_expr ()
181220end
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
201289end
0 commit comments