diff --git a/src/functions.jl b/src/functions.jl index 4cf7348002..bc658737c4 100644 --- a/src/functions.jl +++ b/src/functions.jl @@ -914,6 +914,11 @@ function Base.isapprox( ) end +# This method is used by CBF in testing. +function Base.isapprox(f::VectorOfVariables, g::VectorAffineFunction; kwargs...) + return isapprox(convert(typeof(g), f), g; kwargs...) +end + _is_approx(x, y; kwargs...) = isapprox(x, y; kwargs...) function _is_approx(x::AbstractArray, y::AbstractArray; kwargs...) diff --git a/test/FileFormats/CBF/CBF.jl b/test/FileFormats/CBF/CBF.jl index 31861884c0..bf085f200c 100644 --- a/test/FileFormats/CBF/CBF.jl +++ b/test/FileFormats/CBF/CBF.jl @@ -9,95 +9,90 @@ module TestCBF using Test import MathOptInterface as MOI -import MathOptInterface.Utilities as MOIU -const CBF = MOI.FileFormats.CBF -const CBF_TEST_FILE = "test.cbf" +import MathOptInterface.FileFormats: CBF + const MODELS_DIR = joinpath(@__DIR__, "models") +function runtests() + for name in names(@__MODULE__, all = true) + if startswith("$(name)", "test_") + @testset "$name" begin + getfield(@__MODULE__, name)() + end + end + end + return +end + function _set_var_and_con_names(model::MOI.ModelLike) variable_names = String[] - for j in MOI.get(model, MOI.ListOfVariableIndices()) - var_name_j = "v" * string(j.value) - push!(variable_names, var_name_j) - MOI.set(model, MOI.VariableName(), j, var_name_j) + for xi in MOI.get(model, MOI.ListOfVariableIndices()) + push!(variable_names, "v$(xi.value)") + MOI.set(model, MOI.VariableName(), xi, "v$(xi.value)") end - idx = 0 - constraint_names = String[] - single_variable_constraints = Tuple[] - for i in MOI.get( - model, - MOI.ListOfConstraintIndices{MOI.VariableIndex,MOI.Integer}(), - ) + constraint_names, single_variable_constraints = String[], Tuple[] + attr = MOI.ListOfConstraintIndices{MOI.VariableIndex,MOI.Integer}() + for ci in MOI.get(model, attr) idx += 1 - x = MOI.get(model, MOI.VariableName(), MOI.VariableIndex(i.value)) + x = MOI.get(model, MOI.VariableName(), MOI.VariableIndex(ci.value)) push!(single_variable_constraints, (x, MOI.Integer())) end - for S in [ - MOI.Reals, - MOI.Zeros, - MOI.Nonnegatives, - MOI.Nonpositives, - MOI.SecondOrderCone, - MOI.RotatedSecondOrderCone, - MOI.PositiveSemidefiniteConeTriangle, - MOI.ExponentialCone, - MOI.DualExponentialCone, - MOI.PowerCone{Float64}, - MOI.DualPowerCone{Float64}, - ], - F in [MOI.VectorOfVariables, MOI.VectorAffineFunction{Float64}] - - for i in MOI.get(model, MOI.ListOfConstraintIndices{F,S}()) - idx += 1 - con_name_i = "c" * string(idx) - push!(constraint_names, con_name_i) - MOI.set(model, MOI.ConstraintName(), i, con_name_i) + for S in ( + MOI.Reals, + MOI.Zeros, + MOI.Nonnegatives, + MOI.Nonpositives, + MOI.SecondOrderCone, + MOI.RotatedSecondOrderCone, + MOI.PositiveSemidefiniteConeTriangle, + MOI.ExponentialCone, + MOI.DualExponentialCone, + MOI.PowerCone{Float64}, + MOI.DualPowerCone{Float64}, + ) + for F in (MOI.VectorOfVariables, MOI.VectorAffineFunction{Float64}) + for ci in MOI.get(model, MOI.ListOfConstraintIndices{F,S}()) + idx += 1 + push!(constraint_names, "c$idx") + MOI.set(model, MOI.ConstraintName(), ci, "c$idx") + end end end - return variable_names, constraint_names, single_variable_constraints end -function Base.isapprox( - f::MOI.VectorOfVariables, - g::MOI.VectorAffineFunction{Float64}; - kwargs..., -) - return isapprox(MOI.VectorAffineFunction{Float64}(f), g) -end - function _test_write_then_read(model_string::String) model1 = CBF.Model() - MOIU.loadfromstring!(model1, model_string) + MOI.Utilities.loadfromstring!(model1, model_string) args = _set_var_and_con_names(model1) - - MOI.write_to_file(model1, CBF_TEST_FILE) + io = IOBuffer() + write(io, model1) model2 = CBF.Model() - MOI.read_from_file(model2, CBF_TEST_FILE) + seekstart(io) + read!(io, model2) _set_var_and_con_names(model2) - return MOI.Test.util_test_models_equal(model1, model2, args...) end function _test_read(filename::String, model_string::String) model1 = CBF.Model() - MOIU.loadfromstring!(model1, model_string) + MOI.Utilities.loadfromstring!(model1, model_string) args = _set_var_and_con_names(model1) - model2 = CBF.Model() MOI.read_from_file(model2, filename) _set_var_and_con_names(model2) - - return MOI.Test.util_test_models_equal(model1, model2, args...) + MOI.Test.util_test_models_equal(model1, model2, args...) + return end function test_show() @test sprint(show, CBF.Model()) == "A Conic Benchmark Format (CBF) model" + return end function test_support_errors() - for set in [ + for set in ( MOI.EqualTo(1.0), MOI.LessThan(1.0), MOI.GreaterThan(1.0), @@ -105,7 +100,7 @@ function test_support_errors() MOI.Semiinteger(1.0, 2.0), MOI.Semicontinuous(1.0, 2.0), MOI.ZeroOne(), - ] + ) model_string = """ variables: x minobjective: x @@ -113,8 +108,9 @@ function test_support_errors() """ model = CBF.Model() err = MOI.UnsupportedConstraint{MOI.VariableIndex,typeof(set)} - @test_throws err MOIU.loadfromstring!(model, model_string) + @test_throws err MOI.Utilities.loadfromstring!(model, model_string) end + return end function test_read_nonempty() @@ -124,6 +120,7 @@ function test_read_nonempty() ErrorException("Cannot read in file because model is not empty."), MOI.read_from_file(model, joinpath(MODELS_DIR, "example_A.cbf")), ) + return end function test_read_incompatible() @@ -132,6 +129,7 @@ function test_read_incompatible() model, joinpath(MODELS_DIR, "incompatible_version.cbf"), ) + return end function test_read_badcones() @@ -147,6 +145,7 @@ function test_read_badcones() joinpath(MODELS_DIR, filename), ) end + return end function test_read_badpowerdim() @@ -157,6 +156,7 @@ function test_read_badpowerdim() joinpath(MODELS_DIR, filename), ) end + return end function test_read_corrupt() @@ -173,212 +173,213 @@ function test_read_corrupt() joinpath(MODELS_DIR, filename), ) end + return end const _WRITE_READ_MODELS = [ ( "min VariableIndex", """ - variables: x - minobjective: x -""", + variables: x + minobjective: x + """, ), ( "min ScalarAffine", """ - variables: x, y - minobjective: 1.2x + -1y + 1 -""", + variables: x, y + minobjective: 1.2x + -1y + 1 + """, ), ( "max VariableIndex", """ - variables: x - maxobjective: x -""", + variables: x + maxobjective: x + """, ), ( "max ScalarAffine", """ - variables: x, y - maxobjective: 1.2x + -1y + 1 -""", + variables: x, y + maxobjective: 1.2x + -1y + 1 + """, ), ( "VariableIndex in Integer", """ - variables: x, y - minobjective: 1.2x - y in Integer() -""", + variables: x, y + minobjective: 1.2x + y in Integer() + """, ), ( "VectorOfVariables in Zeros", """ - variables: x, y - minobjective: x - c1: [x, y] in Zeros(2) -""", + variables: x, y + minobjective: x + c1: [x, y] in Zeros(2) + """, ), ( "VectorOfVariables in Nonnegatives", """ - variables: x, y - minobjective: x - c1: [x, y] in Nonnegatives(2) -""", + variables: x, y + minobjective: x + c1: [x, y] in Nonnegatives(2) + """, ), ( "VectorOfVariables in Nonpositives", """ - variables: x, y - minobjective: x - c1: [y, x] in Nonpositives(2) -""", + variables: x, y + minobjective: x + c1: [y, x] in Nonpositives(2) + """, ), ( "VectorAffineFunction in Zeros", """ - variables: x, y - minobjective: 1.2x - c1: [x + 2y + -1.1, 0] in Zeros(2) -""", + variables: x, y + minobjective: 1.2x + c1: [x + 2y + -1.1, 0] in Zeros(2) + """, ), ( "VectorAffineFunction in Reals", """ - variables: x, y - minobjective: 1.2x - c1: [1x, 2y] in Reals(2) -""", + variables: x, y + minobjective: 1.2x + c1: [1x, 2y] in Reals(2) + """, ), ( "VectorAffineFunction in Nonnegatives", """ - variables: x, y - minobjective: 1.2x - c1: [1.1 * x, y + 1] in Nonnegatives(2) -""", + variables: x, y + minobjective: 1.2x + c1: [1.1 * x, y + 1] in Nonnegatives(2) + """, ), ( "VectorAffineFunction in Nonpositives", """ - variables: x - minobjective: 1.2x - c1: [-1.1 * x + 1] in Nonpositives(1) -""", + variables: x + minobjective: 1.2x + c1: [-1.1 * x + 1] in Nonpositives(1) + """, ), ( "VectorOfVariables in SecondOrderCone", """ - variables: x, y, z - minobjective: x - c1: [x, y, z] in SecondOrderCone(3) -""", + variables: x, y, z + minobjective: x + c1: [x, y, z] in SecondOrderCone(3) + """, ), ( "VectorOfVariables in RotatedSecondOrderCone", """ - variables: x, y, z - minobjective: x - c1: [x, y, z] in RotatedSecondOrderCone(3) -""", + variables: x, y, z + minobjective: x + c1: [x, y, z] in RotatedSecondOrderCone(3) + """, ), ( "VectorOfVariables in ExponentialCone", """ - variables: x, y, z - minobjective: x - c1: [x, y, z] in ExponentialCone() -""", + variables: x, y, z + minobjective: x + c1: [x, y, z] in ExponentialCone() + """, ), ( "VectorOfVariables in DualExponentialCone", """ - variables: x, y, z - minobjective: x - c1: [x, y, z] in DualExponentialCone() -""", + variables: x, y, z + minobjective: x + c1: [x, y, z] in DualExponentialCone() + """, ), ( "VectorOfVariables in PowerCone", """ - variables: x, y, z - minobjective: x - c1: [x, y, z] in PowerCone(2.0) -""", + variables: x, y, z + minobjective: x + c1: [x, y, z] in PowerCone(2.0) + """, ), ( "VectorOfVariables in DualPowerCone", """ - variables: x, y, z - minobjective: x - c1: [x, y, z] in DualPowerCone(2.0) -""", + variables: x, y, z + minobjective: x + c1: [x, y, z] in DualPowerCone(2.0) + """, ), ( "VectorOfVariables in PositiveSemidefiniteConeTriangle", """ - variables: x, y, z - minobjective: x - c1: [x, y, z] in PositiveSemidefiniteConeTriangle(2) -""", + variables: x, y, z + minobjective: x + c1: [x, y, z] in PositiveSemidefiniteConeTriangle(2) + """, ), ( "VectorAffineFunction in SecondOrderCone", """ - variables: x, y, z - minobjective: 1.2x - c1: [1.1x, y + 1, 2x + z] in SecondOrderCone(3) -""", + variables: x, y, z + minobjective: 1.2x + c1: [1.1x, y + 1, 2x + z] in SecondOrderCone(3) + """, ), ( "VectorAffineFunction in RotatedSecondOrderCone", """ - variables: x, y, z - minobjective: 1.2x - c1: [1.1x, y + 1, 2x + z] in RotatedSecondOrderCone(3) -""", + variables: x, y, z + minobjective: 1.2x + c1: [1.1x, y + 1, 2x + z] in RotatedSecondOrderCone(3) + """, ), ( "VectorAffineFunction in ExponentialCone", """ - variables: x, y, z - minobjective: 1.2x - c1: [1.1x, y + 1, 2x + z] in ExponentialCone() -""", + variables: x, y, z + minobjective: 1.2x + c1: [1.1x, y + 1, 2x + z] in ExponentialCone() + """, ), ( "VectorAffineFunction in DualExponentialCone", """ - variables: x, y, z - minobjective: 1.2x - c1: [1.1x, y + 1, 2x + z] in DualExponentialCone() -""", + variables: x, y, z + minobjective: 1.2x + c1: [1.1x, y + 1, 2x + z] in DualExponentialCone() + """, ), ( "VectorAffineFunction in PowerCone", """ - variables: x, y, z - minobjective: 1.2x - c1: [1.1x, y + 1, 2x + z] in PowerCone(2.0) -""", + variables: x, y, z + minobjective: 1.2x + c1: [1.1x, y + 1, 2x + z] in PowerCone(2.0) + """, ), ( "VectorAffineFunction in DualPowerCone", """ - variables: x, y, z - minobjective: 1.2x - c1: [1.1x, y + 1, 2x + z] in DualPowerCone(2.0) -""", + variables: x, y, z + minobjective: 1.2x + c1: [1.1x, y + 1, 2x + z] in DualPowerCone(2.0) + """, ), ( "VectorAffineFunction in PositiveSemidefiniteConeTriangle", """ - variables: x, y, z - minobjective: 1.2x - c1: [1.1x, y + 1, 2x + z] in PositiveSemidefiniteConeTriangle(2) -""", + variables: x, y, z + minobjective: 1.2x + c1: [1.1x, y + 1, 2x + z] in PositiveSemidefiniteConeTriangle(2) + """, ), ] @@ -386,54 +387,55 @@ function test_write_read_models() for (model_name, model_string) in _WRITE_READ_MODELS _test_write_then_read(model_string) end + return end const _EXAMPLE_MODELS = [ ( "example_A.cbf", """ - variables: U, V, W, X, Y, Z, x, y, z - minobjective: y + 2U + 2V + 2W + 2Y + 2Z - c1: [U, V, W, X, Y, Z] in PositiveSemidefiniteConeTriangle(3) - c2: [y + U + W + Z + -1, x + z + U + 2V + W + 2X + 2Y + Z + -0.5] in Zeros(2) - c3: [y, x, z] in SecondOrderCone(3) -""", + variables: U, V, W, X, Y, Z, x, y, z + minobjective: y + 2U + 2V + 2W + 2Y + 2Z + c1: [U, V, W, X, Y, Z] in PositiveSemidefiniteConeTriangle(3) + c2: [y + U + W + Z + -1, x + z + U + 2V + W + 2X + 2Y + Z + -0.5] in Zeros(2) + c3: [y, x, z] in SecondOrderCone(3) + """, ), ( "example_B.cbf", """ - variables: X, Y, Z, x, y - minobjective: 1 + x + y + X + Z - c1: [X, Y, Z] in PositiveSemidefiniteConeTriangle(2) - c2: [2Y + -1x + -1y] in Nonnegatives(1) - c3: [3y + -1, x + y, 3x + -1] in PositiveSemidefiniteConeTriangle(2) -""", + variables: X, Y, Z, x, y + minobjective: 1 + x + y + X + Z + c1: [X, Y, Z] in PositiveSemidefiniteConeTriangle(2) + c2: [2Y + -1x + -1y] in Nonnegatives(1) + c3: [3y + -1, x + y, 3x + -1] in PositiveSemidefiniteConeTriangle(2) + """, ), ( "example_C.cbf", """ - variables: a, b, c, d, e, f, g, h, i, j - maxobjective: a + b + c + d + e + f + g + h + i + j + -1 - c1: [b] in Zeros(1) - c2: [c] in Nonnegatives(1) - c3: [d] in Nonpositives(1) - c4: [e, f, g] in SecondOrderCone(3) - c5: [h, i, j] in RotatedSecondOrderCone(3) -""", + variables: a, b, c, d, e, f, g, h, i, j + maxobjective: a + b + c + d + e + f + g + h + i + j + -1 + c1: [b] in Zeros(1) + c2: [c] in Nonnegatives(1) + c3: [d] in Nonpositives(1) + c4: [e, f, g] in SecondOrderCone(3) + c5: [h, i, j] in RotatedSecondOrderCone(3) + """, ), ( "example_D.cbf", """ - variables: u, v, w, x, y, z - maxobjective: w + z - c1: [u, v, w] in PowerCone(0.5) - c2: [x, y, z] in DualPowerCone(0.1) - c3: [1, u, u + v] in PowerCone(0.8) - c4: [1, y, x + y] in DualPowerCone(0.75) - u in Integer() - w in Integer() - y in Integer() -""", + variables: u, v, w, x, y, z + maxobjective: w + z + c1: [u, v, w] in PowerCone(0.5) + c2: [x, y, z] in DualPowerCone(0.1) + c3: [1, u, u + v] in PowerCone(0.8) + c4: [1, y, x + y] in DualPowerCone(0.75) + u in Integer() + w in Integer() + y in Integer() + """, ), ] @@ -442,6 +444,7 @@ function test_example_models() _test_read(joinpath(MODELS_DIR, model_name), model_string) _test_write_then_read(model_string) end + return end function test_write_variable_cones() @@ -624,19 +627,6 @@ function test_roundtrip_DualExponentialCone() return end -function runtests() - for name in names(@__MODULE__, all = true) - if startswith("$(name)", "test_") - @testset "$name" begin - getfield(@__MODULE__, name)() - end - end - end - sleep(1.0) # Allow time for unlink to happen. - rm(CBF_TEST_FILE, force = true) - return -end - end TestCBF.runtests()