Skip to content

Commit c424201

Browse files
authored
Add comprehensive tests for Farkas certificates (#1190)
1 parent 594eb36 commit c424201

File tree

2 files changed

+326
-0
lines changed

2 files changed

+326
-0
lines changed

src/Test/UnitTests/solve.jl

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,212 @@ function solve_result_index(model::MOI.ModelLike, config::TestConfig)
173173
end
174174
unittests["solve_result_index"] = solve_result_index
175175

176+
function solve_farkas_equalto_upper(model::MOI.ModelLike, config::TestConfig)
177+
MOI.empty!(model)
178+
x = MOI.add_variables(model, 2)
179+
clb = MOI.add_constraint.(
180+
model, MOI.SingleVariable.(x), MOI.GreaterThan(0.0)
181+
)
182+
c = MOI.add_constraint(
183+
model,
184+
MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([2.0, 1.0], x), 0.0),
185+
MOI.EqualTo(-1.0),
186+
)
187+
if config.solve && config.infeas_certificates
188+
MOI.optimize!(model)
189+
@test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE
190+
@test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE
191+
clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb)
192+
c_dual = MOI.get(model, MOI.ConstraintDual(), c)
193+
@test clb_dual[1] > config.atol
194+
@test clb_dual[2] > config.atol
195+
@test c_dual[1] < -config.atol
196+
@test clb_dual [2, 1] .* -c_dual atol=config.atol rtol=config.rtol
197+
end
198+
end
199+
unittests["solve_farkas_equalto_upper"] = solve_farkas_equalto_upper
200+
201+
function solve_farkas_equalto_lower(model::MOI.ModelLike, config::TestConfig)
202+
MOI.empty!(model)
203+
x = MOI.add_variables(model, 2)
204+
clb = MOI.add_constraint.(
205+
model, MOI.SingleVariable.(x), MOI.GreaterThan(0.0)
206+
)
207+
c = MOI.add_constraint(
208+
model,
209+
MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([-2.0, -1.0], x), 0.0),
210+
MOI.EqualTo(1.0),
211+
)
212+
if config.solve && config.infeas_certificates
213+
MOI.optimize!(model)
214+
@test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE
215+
@test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE
216+
clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb)
217+
c_dual = MOI.get(model, MOI.ConstraintDual(), c)
218+
@test clb_dual[1] > config.atol
219+
@test clb_dual[2] > config.atol
220+
@test c_dual[1] > config.atol
221+
@test clb_dual [2, 1] .* c_dual atol=config.atol rtol=config.rtol
222+
end
223+
end
224+
unittests["solve_farkas_equalto_lower"] = solve_farkas_equalto_lower
225+
226+
function solve_farkas_lessthan(model::MOI.ModelLike, config::TestConfig)
227+
MOI.empty!(model)
228+
x = MOI.add_variables(model, 2)
229+
clb = MOI.add_constraint.(
230+
model, MOI.SingleVariable.(x), MOI.GreaterThan(0.0)
231+
)
232+
c = MOI.add_constraint(
233+
model,
234+
MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([2.0, 1.0], x), 0.0),
235+
MOI.LessThan(-1.0),
236+
)
237+
if config.solve && config.infeas_certificates
238+
MOI.optimize!(model)
239+
@test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE
240+
@test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE
241+
clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb)
242+
c_dual = MOI.get(model, MOI.ConstraintDual(), c)
243+
@test clb_dual[1] > config.atol
244+
@test clb_dual[2] > config.atol
245+
@test c_dual[1] < -config.atol
246+
@test clb_dual [2, 1] .* -c_dual atol=config.atol rtol=config.rtol
247+
end
248+
end
249+
unittests["solve_farkas_lessthan"] = solve_farkas_lessthan
250+
251+
function solve_farkas_greaterthan(model::MOI.ModelLike, config::TestConfig)
252+
MOI.empty!(model)
253+
x = MOI.add_variables(model, 2)
254+
clb = MOI.add_constraint.(
255+
model, MOI.SingleVariable.(x), MOI.GreaterThan(0.0)
256+
)
257+
c = MOI.add_constraint(
258+
model,
259+
MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([-2.0, -1.0], x), 0.0),
260+
MOI.GreaterThan(1.0),
261+
)
262+
if config.solve && config.infeas_certificates
263+
MOI.optimize!(model)
264+
@test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE
265+
@test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE
266+
clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb)
267+
c_dual = MOI.get(model, MOI.ConstraintDual(), c)
268+
@test clb_dual[1] > config.atol
269+
@test clb_dual[2] > config.atol
270+
@test c_dual[1] > config.atol
271+
@test clb_dual [2, 1] .* c_dual atol=config.atol rtol=config.rtol
272+
end
273+
end
274+
unittests["solve_farkas_greaterthan"] = solve_farkas_greaterthan
275+
276+
function solve_farkas_interval_upper(model::MOI.ModelLike, config::TestConfig)
277+
MOI.empty!(model)
278+
x = MOI.add_variables(model, 2)
279+
clb = MOI.add_constraint.(
280+
model, MOI.SingleVariable.(x), MOI.GreaterThan(0.0)
281+
)
282+
c = MOI.add_constraint(
283+
model,
284+
MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([2.0, 1.0], x), 0.0),
285+
MOI.Interval(-2.0, -1.0),
286+
)
287+
if config.solve && config.infeas_certificates
288+
MOI.optimize!(model)
289+
@test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE
290+
@test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE
291+
clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb)
292+
c_dual = MOI.get(model, MOI.ConstraintDual(), c)
293+
@test clb_dual[1] > config.atol
294+
@test clb_dual[2] > config.atol
295+
@test c_dual[1] < -config.atol
296+
@test clb_dual [2, 1] .* -c_dual atol=config.atol rtol=config.rtol
297+
end
298+
end
299+
unittests["solve_farkas_interval_upper"] = solve_farkas_interval_upper
300+
301+
function solve_farkas_interval_lower(model::MOI.ModelLike, config::TestConfig)
302+
MOI.empty!(model)
303+
x = MOI.add_variables(model, 2)
304+
clb = MOI.add_constraint.(
305+
model, MOI.SingleVariable.(x), MOI.GreaterThan(0.0)
306+
)
307+
c = MOI.add_constraint(
308+
model,
309+
MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([-2.0, -1.0], x), 0.0),
310+
MOI.Interval(1.0, 2.0),
311+
)
312+
if config.solve && config.infeas_certificates
313+
MOI.optimize!(model)
314+
@test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE
315+
@test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE
316+
clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb)
317+
c_dual = MOI.get(model, MOI.ConstraintDual(), c)
318+
@test clb_dual[1] > config.atol
319+
@test clb_dual[2] > config.atol
320+
@test c_dual[1] > config.atol
321+
@test clb_dual [2, 1] .* c_dual atol=config.atol rtol=config.rtol
322+
end
323+
end
324+
unittests["solve_farkas_interval_lower"] = solve_farkas_interval_lower
325+
326+
function solve_farkas_variable_lessthan(model::MOI.ModelLike, config::TestConfig)
327+
MOI.empty!(model)
328+
x = MOI.add_variables(model, 2)
329+
clb = MOI.add_constraint.(
330+
model, MOI.SingleVariable.(x), MOI.LessThan(0.0)
331+
)
332+
c = MOI.add_constraint(
333+
model,
334+
MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([2.0, 1.0], x), 0.0),
335+
MOI.GreaterThan(1.0),
336+
)
337+
if config.solve && config.infeas_certificates
338+
MOI.optimize!(model)
339+
@test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE
340+
@test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE
341+
clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb)
342+
c_dual = MOI.get(model, MOI.ConstraintDual(), c)
343+
@test clb_dual[1] < -config.atol
344+
@test clb_dual[2] < -config.atol
345+
@test c_dual[1] > config.atol
346+
@test clb_dual [2, 1] .* -c_dual atol=config.atol rtol=config.rtol
347+
end
348+
end
349+
unittests["solve_farkas_variable_lessthan"] = solve_farkas_variable_lessthan
350+
351+
function solve_farkas_variable_lessthan_max(model::MOI.ModelLike, config::TestConfig)
352+
MOI.empty!(model)
353+
x = MOI.add_variables(model, 2)
354+
clb = MOI.add_constraint.(
355+
model, MOI.SingleVariable.(x), MOI.LessThan(0.0)
356+
)
357+
c = MOI.add_constraint(
358+
model,
359+
MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([2.0, 1.0], x), 0.0),
360+
MOI.GreaterThan(1.0),
361+
)
362+
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
363+
MOI.set(
364+
model,
365+
MOI.ObjectiveFunction{MOI.SingleVariable}(),
366+
MOI.SingleVariable(x[1]),
367+
)
368+
if config.solve && config.infeas_certificates
369+
MOI.optimize!(model)
370+
@test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE
371+
@test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE
372+
clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb)
373+
c_dual = MOI.get(model, MOI.ConstraintDual(), c)
374+
@test clb_dual[1] < -config.atol
375+
@test clb_dual[2] < -config.atol
376+
@test c_dual[1] > config.atol
377+
@test clb_dual [2, 1] .* -c_dual atol=config.atol rtol=config.rtol
378+
end
379+
end
380+
unittests["solve_farkas_variable_lessthan_max"] = solve_farkas_variable_lessthan_max
381+
176382
function solve_twice(model::MOI.ModelLike, config::TestConfig)
177383
MOI.empty!(model)
178384
x = MOI.add_variable(model)

test/Test/unit.jl

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ end
4949
"solve_single_variable_dual_min",
5050
"solve_single_variable_dual_max",
5151
"solve_result_index",
52+
"solve_farkas_equalto_lower",
53+
"solve_farkas_equalto_upper",
54+
"solve_farkas_lessthan",
55+
"solve_farkas_greaterthan",
56+
"solve_farkas_interval_lower",
57+
"solve_farkas_interval_upper",
58+
"solve_farkas_variable_lessthan",
59+
"solve_farkas_variable_lessthan_max",
5260
"solve_twice",
5361
])
5462
MOI.empty!(model)
@@ -394,6 +402,118 @@ end
394402
MOIT.solve_result_index(mock, config)
395403
end
396404

405+
@testset "solve_farkas_equalto_upper" begin
406+
MOIU.set_mock_optimize!(mock,
407+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
408+
mock,
409+
MOI.INFEASIBLE,
410+
(MOI.NO_SOLUTION, [NaN, NaN]),
411+
MOI.INFEASIBILITY_CERTIFICATE,
412+
(MOI.SingleVariable, MOI.GreaterThan{Float64}) => [2.0, 1.0],
413+
(MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => [-1.0],
414+
)
415+
)
416+
MOIT.solve_farkas_equalto_upper(mock, config)
417+
end
418+
419+
@testset "solve_farkas_equalto_lower" begin
420+
MOIU.set_mock_optimize!(mock,
421+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
422+
mock,
423+
MOI.INFEASIBLE,
424+
(MOI.NO_SOLUTION, [NaN, NaN]),
425+
MOI.INFEASIBILITY_CERTIFICATE,
426+
(MOI.SingleVariable, MOI.GreaterThan{Float64}) => [2.0, 1.0],
427+
(MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => [1.0],
428+
)
429+
)
430+
MOIT.solve_farkas_equalto_lower(mock, config)
431+
end
432+
433+
@testset "solve_farkas_lessthan" begin
434+
MOIU.set_mock_optimize!(mock,
435+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
436+
mock,
437+
MOI.INFEASIBLE,
438+
(MOI.NO_SOLUTION, [NaN, NaN]),
439+
MOI.INFEASIBILITY_CERTIFICATE,
440+
(MOI.SingleVariable, MOI.GreaterThan{Float64}) => [2.0, 1.0],
441+
(MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1.0],
442+
)
443+
)
444+
MOIT.solve_farkas_lessthan(mock, config)
445+
end
446+
447+
@testset "solve_farkas_greaterthan" begin
448+
MOIU.set_mock_optimize!(mock,
449+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
450+
mock,
451+
MOI.INFEASIBLE,
452+
(MOI.NO_SOLUTION, [NaN, NaN]),
453+
MOI.INFEASIBILITY_CERTIFICATE,
454+
(MOI.SingleVariable, MOI.GreaterThan{Float64}) => [2.0, 1.0],
455+
(MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [1.0],
456+
)
457+
)
458+
MOIT.solve_farkas_greaterthan(mock, config)
459+
end
460+
461+
@testset "solve_farkas_interval_upper" begin
462+
MOIU.set_mock_optimize!(mock,
463+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
464+
mock,
465+
MOI.INFEASIBLE,
466+
(MOI.NO_SOLUTION, [NaN, NaN]),
467+
MOI.INFEASIBILITY_CERTIFICATE,
468+
(MOI.SingleVariable, MOI.GreaterThan{Float64}) => [2.0, 1.0],
469+
(MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [-1.0],
470+
)
471+
)
472+
MOIT.solve_farkas_interval_upper(mock, config)
473+
end
474+
475+
@testset "solve_farkas_interval_lower" begin
476+
MOIU.set_mock_optimize!(mock,
477+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
478+
mock,
479+
MOI.INFEASIBLE,
480+
(MOI.NO_SOLUTION, [NaN, NaN]),
481+
MOI.INFEASIBILITY_CERTIFICATE,
482+
(MOI.SingleVariable, MOI.GreaterThan{Float64}) => [2.0, 1.0],
483+
(MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [1.0],
484+
)
485+
)
486+
MOIT.solve_farkas_interval_lower(mock, config)
487+
end
488+
489+
@testset "solve_farkas_variable_lessthan" begin
490+
MOIU.set_mock_optimize!(mock,
491+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
492+
mock,
493+
MOI.INFEASIBLE,
494+
(MOI.NO_SOLUTION, [NaN, NaN]),
495+
MOI.INFEASIBILITY_CERTIFICATE,
496+
(MOI.SingleVariable, MOI.LessThan{Float64}) => [-2.0, -1.0],
497+
(MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [1.0],
498+
)
499+
)
500+
MOIT.solve_farkas_variable_lessthan(mock, config)
501+
end
502+
503+
@testset "solve_farkas_variable_lessthan_max" begin
504+
MOIU.set_mock_optimize!(mock,
505+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
506+
mock,
507+
MOI.INFEASIBLE,
508+
(MOI.NO_SOLUTION, [NaN, NaN]),
509+
MOI.INFEASIBILITY_CERTIFICATE,
510+
(MOI.SingleVariable, MOI.LessThan{Float64}) => [-2.0, -1.0],
511+
(MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [1.0],
512+
)
513+
)
514+
MOIT.solve_farkas_variable_lessthan_max(mock, config)
515+
end
516+
397517
@testset "solve_twice" begin
398518
MOIU.set_mock_optimize!(mock,
399519
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(

0 commit comments

Comments
 (0)