Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic tests to catch edge cases #379

Merged
merged 4 commits into from
Jun 6, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions src/Test/UnitTests/constraints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,119 @@ function solve_affine_interval(model::MOI.ModelLike, config::TestConfig)
end
end
unittests["solve_affine_interval"] = solve_affine_interval

"""
solve_qcp_edge_cases(model::MOI.ModelLike, config::TestConfig)

Test various edge cases relating to quadratically constrainted programs (i.e.,
with a ScalarQuadraticFunction-in-Set constraint.

If `config.solve=true` confirm that it solves correctly.
"""
function solve_qcp_edge_cases(model::MOI.ModelLike, config::TestConfig)
@testset "Duplicate on-diagonal" begin
# max x + 2y | y + x^2 + x^2 <= 1, x >= 0.5, y >= 0.5
MOI.empty!(model)
x = MOI.addvariables!(model, 2)
MOI.set!(model, MOI.ObjectiveSense(), MOI.MaxSense)
MOI.set!(model,
MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(),
MOI.ScalarAffineFunction{Float64}(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't have to explicitely specify the type {Float64}. At least it is inconsistent with other tests

MOI.ScalarAffineTerm{Float64}.([1.0, 2.0], x),
0.0
)
)
MOI.addconstraint!(model, MOI.SingleVariable(x[1]), MOI.GreaterThan{Float64}(0.5))
MOI.addconstraint!(model, MOI.SingleVariable(x[2]), MOI.GreaterThan{Float64}(0.5))
MOI.addconstraint!(model,
MOI.ScalarQuadraticFunction{Float64}(
MOI.ScalarAffineTerm{Float64}.([1.0], [x[2]]), # affine terms
MOI.ScalarQuadraticTerm{Float64}.([2.0, 2.0], [x[1], x[1]], [x[1], x[1]]), # quad
0.0 # constant
),
MOI.LessThan{Float64}(1.0)
)
test_model_solution(model, config;
objective_value = 1.5,
variable_primal = [(x[1], 0.5), (x[2], 0.5)]
)
end
@testset "Duplicate off-diagonal" begin
# max x + 2y | x^2 + 0.25y*x + 0.25x*y + 0.5x*y + y^2 <= 1, x >= 0.5, y >= 0.5
MOI.empty!(model)
x = MOI.addvariables!(model, 2)
MOI.set!(model, MOI.ObjectiveSense(), MOI.MaxSense)
MOI.set!(model,
MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(),
MOI.ScalarAffineFunction{Float64}(
MOI.ScalarAffineTerm{Float64}.([1.0, 2.0], x),
0.0
)
)
MOI.addconstraint!(model, MOI.SingleVariable(x[1]), MOI.GreaterThan{Float64}(0.5))
MOI.addconstraint!(model, MOI.SingleVariable(x[2]), MOI.GreaterThan{Float64}(0.5))
MOI.addconstraint!(model,
MOI.ScalarQuadraticFunction{Float64}(
MOI.ScalarAffineTerm{Float64}[], # affine terms
MOI.ScalarQuadraticTerm{Float64}.(
[ 2.0, 0.25, 0.25, 0.5, 2.0],
[x[1], x[1], x[2], x[1], x[2]],
[x[1], x[2], x[1], x[2], x[2]]), # quad
0.0 # constant
),
MOI.LessThan{Float64}(1.0)
)
test_model_solution(model, config;
objective_value = 0.5 + (√13-1)/2,
variable_primal = [(x[1], 0.5), (x[2], (√13-1)/4)]
)
end
end
unittests["solve_qcp_edge_cases"] = solve_qcp_edge_cases

"""
solve_affine_deletion_edge_cases(model::MOI.ModelLike, config::TestConfig)

Test various edge cases relating to deleting affine constraints.

If `config.solve=true` confirm that it solves correctly.
"""
function solve_affine_deletion_edge_cases(model::MOI.ModelLike, config::TestConfig)
MOI.empty!(model)
x = MOI.addvariable!(model)
# helpers. The function 1.0x + 0.0
saf = MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0], x), 0.0)
vaf = MOI.VectorAffineFunction([MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(1.0, x))], [0.0])
vaf2 = MOI.VectorAffineFunction([MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(1.0, x))], [-2.0])
# max x
MOI.set!(model, MOI.ObjectiveSense(), MOI.MaxSense)
MOI.set!(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), saf)
# test adding a VectorAffineFunction -in- LessThan
c1 = MOI.addconstraint!(model, vaf, MOI.Nonpositives(1))
test_model_solution(model, config; objective_value = 0.0,
constraint_primal = [(c1, [0.0])]
)
# test adding a ScalarAffineFunction -in- LessThan
c2 = MOI.addconstraint!(model, saf, MOI.LessThan(1.0))
test_model_solution(model, config; objective_value = 0.0,
constraint_primal = [(c1, [0.0]), (c2, 0.0)]
)
# now delete the VectorAffineFunction
@test MOI.candelete(model, c1)
MOI.delete!(model, c1)
test_model_solution(model, config; objective_value = 1.0,
constraint_primal = [(c2, 1.0)]
)
# add a different VectorAffineFunction constraint
c3 = MOI.addconstraint!(model, vaf2, MOI.Nonpositives(1))
test_model_solution(model, config; objective_value = 1.0,
constraint_primal = [(c2, 1.0), (c3, [-1.0])]
)
# delete the ScalarAffineFunction
@test MOI.candelete(model, c2)
MOI.delete!(model, c2)
test_model_solution(model, config; objective_value = 2.0,
constraint_primal = [(c3, [0.0])]
)
end
unittests["solve_affine_deletion_edge_cases"] = solve_affine_deletion_edge_cases
64 changes: 64 additions & 0 deletions src/Test/UnitTests/objectives.jl
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,67 @@ function solve_singlevariable_obj(model::MOI.ModelLike, config::TestConfig)
end
end
unittests["solve_singlevariable_obj"] = solve_singlevariable_obj

"""
solve_qp_edge_cases(model::MOI.ModelLike, config::TestConfig)

Test various edge cases relating to quadratic programs (i.e., with a quadratic
objective function).

If `config.solve=true` confirm that it solves correctly.
"""
function solve_qp_edge_cases(model::MOI.ModelLike, config::TestConfig)
MOI.empty!(model)
x = MOI.addvariables!(model, 2)
MOI.set!(model, MOI.ObjectiveSense(), MOI.MinSense)
MOI.addconstraint!(model, MOI.SingleVariable(x[1]), MOI.GreaterThan{Float64}(1.0))
MOI.addconstraint!(model, MOI.SingleVariable(x[2]), MOI.GreaterThan{Float64}(2.0))

# min x^2 + y^2 | x>=1, y>=2
MOI.set!(model,
MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(),
MOI.ScalarQuadraticFunction{Float64}(
MOI.ScalarAffineTerm{Float64}[], # affine terms
MOI.ScalarQuadraticTerm{Float64}.([2.0, 2.0], x, x), # quad
0.0 # constant
)
)
test_model_solution(model, config;
objective_value = 5.0,
variable_primal = [(x[1], 1.0), (x[2], 2.0)]
)

# duplicate terms on diagonal
# min x^2 + x^2 | x>=1, y>=2
MOI.set!(model,
MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(),
MOI.ScalarQuadraticFunction{Float64}(
MOI.ScalarAffineTerm{Float64}[], # affine terms
MOI.ScalarQuadraticTerm{Float64}.([2.0, 2.0], [x[1], x[1]], [x[1], x[1]]), # quad
0.0 # constant
)
)
test_model_solution(model, config;
objective_value = 2.0,
variable_primal = [(x[1], 1.0)]
)

# duplicate terms on off-diagonal
# min x^2 + 0.25x*y + 0.25y*x + 0.5x*y + y^2 | x>=1, y>=2
MOI.set!(model,
MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(),
MOI.ScalarQuadraticFunction{Float64}(
MOI.ScalarAffineTerm{Float64}[], # affine terms
MOI.ScalarQuadraticTerm{Float64}.(
[ 2.0, 0.25, 0.25, 0.5, 2.0],
[x[1], x[1], x[2], x[1], x[2]],
[x[1], x[2], x[1], x[2], x[2]]), # quad
0.0 # constant
)
)
test_model_solution(model, config;
objective_value = 7.0,
variable_primal = [(x[1], 1.0), (x[2], 2.0)]
)
end
unittests["solve_qp_edge_cases"] = solve_qp_edge_cases
58 changes: 57 additions & 1 deletion test/Test/unit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ end
"solve_affine_lessthan",
"solve_affine_greaterthan",
"solve_affine_equalto",
"solve_affine_interval"
"solve_affine_interval",
"solve_qp_edge_cases",
"solve_qcp_edge_cases",
"solve_affine_deletion_edge_cases"
])

@testset "solve_blank_obj" begin
Expand Down Expand Up @@ -120,4 +123,57 @@ end
)
MOIT.solve_affine_interval(mock, config)
end

@testset "solve_qcp_edge_cases" begin
MOIU.set_mock_optimize!(mock,
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock,
MOI.Success,
(MOI.FeasiblePoint, [0.5, 0.5])
),
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock,
MOI.Success,
(MOI.FeasiblePoint, [0.5, (√13 - 1)/4])
)
)
MOIT.solve_qcp_edge_cases(mock, config)
end

@testset "solve_qp_edge_cases" begin
MOIU.set_mock_optimize!(mock,
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock,
MOI.Success,
(MOI.FeasiblePoint, [1.0, 2.0])
),
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock,
MOI.Success,
(MOI.FeasiblePoint, [1.0, 2.0])
),
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock,
MOI.Success,
(MOI.FeasiblePoint, [1.0, 2.0])
)
)
MOIT.solve_qp_edge_cases(mock, config)
end

@testset "solve_affine_deletion_edge_cases" begin
MOIU.set_mock_optimize!(mock,
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock,
MOI.Success, (MOI.FeasiblePoint, [0.0])
),
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock,
MOI.Success, (MOI.FeasiblePoint, [0.0])
),
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock,
MOI.Success, (MOI.FeasiblePoint, [1.0])
),
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock,
MOI.Success, (MOI.FeasiblePoint, [1.0])
),
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock,
MOI.Success, (MOI.FeasiblePoint, [2.0])
)
)
MOIT.solve_affine_deletion_edge_cases(mock, config)
end
end