diff --git a/src/FileFormats/LP/LP.jl b/src/FileFormats/LP/LP.jl index d6b447c78d..0e9794ea10 100644 --- a/src/FileFormats/LP/LP.jl +++ b/src/FileFormats/LP/LP.jl @@ -1001,7 +1001,7 @@ function _parse_section( for token in tokens[3:end] items = String.(split(token, ":")) if length(items) != 2 - error("Invalid sequence: $(token)") + error("Invalid token in SOS constraint: $(token)") end push!(variables, _get_variable_from_name(model, cache, items[1])) push!(weights, parse(Float64, items[2])) diff --git a/test/FileFormats/CBF/CBF.jl b/test/FileFormats/CBF/CBF.jl index 10520b27b7..ad40971891 100644 --- a/test/FileFormats/CBF/CBF.jl +++ b/test/FileFormats/CBF/CBF.jl @@ -293,6 +293,15 @@ const _WRITE_READ_MODELS = [ c1: [x, y, z] in ExponentialCone() """, ), + ( + "VectorOfVariables as function in ExponentialCone", + """ + variables: x, y, z + minobjective: x + c0: [x, y, z] in Nonnegatives(3) + c1: [x, y, z] in ExponentialCone() + """, + ), ( "VectorOfVariables in DualExponentialCone", """ @@ -301,6 +310,15 @@ const _WRITE_READ_MODELS = [ c1: [x, y, z] in DualExponentialCone() """, ), + ( + "VectorOfVariables as function in DualExponentialCone", + """ + variables: x, y, z + minobjective: x + c0: [x, y, z] in Nonnegatives(3) + c1: [x, y, z] in DualExponentialCone() + """, + ), ( "VectorOfVariables in PowerCone", """ @@ -674,6 +692,13 @@ function test_roundtrip_DualExponentialCone() return end +function test_supports_quadratic_objective() + model = CBF.Model() + F = MOI.ScalarQuadraticFunction{Float64} + @test !MOI.supports(model, MOI.ObjectiveFunction{F}()) + return end +end # module + TestCBF.runtests() diff --git a/test/FileFormats/FileFormats.jl b/test/FileFormats/FileFormats.jl index 7ac5a68b71..f1a5fe085a 100644 --- a/test/FileFormats/FileFormats.jl +++ b/test/FileFormats/FileFormats.jl @@ -117,9 +117,10 @@ function test_compression() filename = joinpath(@__DIR__, "free_integer.mps") MOI.write_to_file(model, filename * ".garbage") for ext in ["", ".bz2", ".gz"] - MOI.write_to_file(model, filename * ext) - model2 = MOI.FileFormats.Model(filename = filename * ext) - MOI.read_from_file(model2, filename) + filename_ext = filename * ext + MOI.write_to_file(model, filename_ext) + model2 = MOI.FileFormats.Model(filename = filename_ext) + MOI.read_from_file(model2, filename_ext) end sleep(1.0) # Allow time for unlink to happen. @@ -167,6 +168,27 @@ function test_Model() ) end +function test_generic_names() + # These methods were added to MOI, but then we changed the REW model to not + # need them. + model = MOI.Utilities.Model{Float64}() + x = MOI.add_variables(model, 2) + MOI.set.(model, MOI.VariableName(), x, "x") + c = MOI.add_constraint.(model, 1.0 .* x, MOI.EqualTo(1.0)) + MOI.set.(model, MOI.ConstraintName(), c, "c") + c2 = MOI.add_constraint(model, 1.0 * x[2], MOI.LessThan(1.0)) + MOI.set(model, MOI.ConstraintName(), c2, "R2") + MOI.FileFormats.create_generic_names(model) + @test MOI.get.(model, MOI.VariableName(), x) == ["C1", "C2"] + c_name = MOI.get.(model, MOI.ConstraintName(), c) + c2_name = MOI.get(model, MOI.ConstraintName(), c2) + names = vcat(c_name, c2_name) + # The names depend on the order that the constraints are parsed. Either is + # okay. + @test names == ["R1", "R2", "R3"] || names == ["R1", "R3", "R2"] + return +end + end TestFileFormats.runtests() diff --git a/test/FileFormats/LP/LP.jl b/test/FileFormats/LP/LP.jl index 37ad33c699..530c670a1c 100644 --- a/test/FileFormats/LP/LP.jl +++ b/test/FileFormats/LP/LP.jl @@ -13,6 +13,19 @@ import MathOptInterface.FileFormats: LP const LP_TEST_FILE = "test.lp" +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(LP_TEST_FILE, force = true) + return +end + function test_show() @test sprint(summary, LP.Model()) == "MOI.FileFormats.LP.Model" return @@ -1065,19 +1078,27 @@ function test_VectorAffineFunction_SOS() 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(LP_TEST_FILE, force = true) +function test_comprehensive_write() + model = LP.Model() + io = IOBuffer() + print( + io, + """ + minimize + obj: x + y + subject to + SOS + c11: S1:: x 1.0 y 2.0 + """, + ) + seekstart(io) + @test_throws( + ErrorException("Invalid token in SOS constraint: x"), + read!(io, model), + ) return end -end +end # module TestLP.runtests() diff --git a/test/FileFormats/MOF/MOF.jl b/test/FileFormats/MOF/MOF.jl index 2263f479e9..a9c7c0c3a2 100644 --- a/test/FileFormats/MOF/MOF.jl +++ b/test/FileFormats/MOF/MOF.jl @@ -1497,6 +1497,27 @@ function test_nonlinear_variable_real_nodes() return end +function test_nonlinear_variable_complex_nodes() + x = MOI.VariableIndex(1) + object = Dict{String,Any}( + "type" => "ScalarNonlinearFunction", + "root" => MOF.Dict{String,Any}( + "type" => "^", + "args" => Any[ + Dict{String,Any}("type" => "node", "index" => 1), + Dict{String,Any}("type" => "node", "index" => 2), + ], + ), + "node_list" => Any[ + Dict{String,Any}("type" => "variable", "name" => "x"), + Dict{String,Any}("type" => "complex", "real" => 2.0, "imag" => 3.0), + ], + ) + f = MOI.ScalarNonlinearFunction(:^, Any[x, 2.0+3im]) + @test MOF.function_to_moi(object, Dict("x" => x)) ≈ f + return +end + function test_mof_scalaraffinefunction() x = MOI.VariableIndex(1) f = 1.0 * x + 2.0 @@ -1535,6 +1556,40 @@ function test_mof_scalarquadraticfunction() return end +function test_nonlinear_expression_not_call() + model = MOF.Model() + x = MOI.add_variable(model) + expr = :($x[1]) + con = MOI.add_constraint(model, MOF.Nonlinear(expr), MOI.EqualTo(1.0)) + io = IOBuffer() + @test_throws( + ErrorException( + "Expected an expression that was a function. Got $expr.", + ), + write(io, model), + ) + return +end + +function test_write_NLPBlock_no_objective() + model = MOF.Model() + x = MOI.add_variables(model, 4) + for (index, variable) in enumerate(x) + MOI.set(model, MOI.VariableName(), variable, "var_$(index)") + end + MOI.add_constraints(model, x, Ref(MOI.Interval(1.0, 5.0))) + block = HS071(x) + new_block = + MOI.NLPBlockData(block.constraint_bounds, block.evaluator, false) + MOI.set(model, MOI.NLPBlock(), new_block) + io = IOBuffer() + write(io, model) + seekstart(io) + contents = read(io, String) + @test occursin(""""objective":{"sense":"feasibility"}""", contents) + return +end + end TestMOF.runtests() diff --git a/test/FileFormats/MPS/MPS.jl b/test/FileFormats/MPS/MPS.jl index 7917acc458..82ee41a56d 100644 --- a/test/FileFormats/MPS/MPS.jl +++ b/test/FileFormats/MPS/MPS.jl @@ -1451,6 +1451,39 @@ function test_qcmatrix_read_cplex() return end +function test_model_Name() + model = MPS.Model() + MOI.set(model, MOI.Name(), "TestFoo") + io = IOBuffer() + write(io, model) + seekstart(io) + contents = read(io, String) + @test occursin("NAME TestFoo", contents) + return +end + +function test_model_large_integer() + model = MPS.Model() + x = MOI.add_constrained_variable(model, MOI.GreaterThan(123.0)) + io = IOBuffer() + write(io, model) + seekstart(io) + contents = read(io, String) + @test occursin("LO bounds x1 123", contents) + return +end + +function test_model_not_empty() + model = MPS.Model() + x = MOI.add_variable(model) + io = IOBuffer() + @test_throws( + ErrorException("Cannot read in file because model is not empty."), + read!(io, model), + ) + return +end + end # TestMPS TestMPS.runtests() diff --git a/test/FileFormats/MPS/free_integer.mps b/test/FileFormats/MPS/free_integer.mps index 7c30609050..ee1ad43205 100644 --- a/test/FileFormats/MPS/free_integer.mps +++ b/test/FileFormats/MPS/free_integer.mps @@ -1,8 +1,12 @@ +* A model with blank lines and comments NAME + OBJSENSE MIN + ROWS N obj G con1 + COLUMNS MARKER 'MARKER' 'INTORG' x obj 1 con1 1 diff --git a/test/FileFormats/NL/sol.jl b/test/FileFormats/NL/sol.jl index 2b505d78fb..587929f613 100644 --- a/test/FileFormats/NL/sol.jl +++ b/test/FileFormats/NL/sol.jl @@ -358,6 +358,16 @@ function test_result_count() return end +function test_eol() + model = NL.Model() + io = IOBuffer() + @test_throws( + ErrorException("Reached end of sol file unexpectedly."), + NL.SolFileResults(io, model), + ) + return +end + end TestNonlinearSolFiles.runtests() diff --git a/test/FileFormats/SDPA/SDPA.jl b/test/FileFormats/SDPA/SDPA.jl index 98f25e26fd..02a769d6f5 100644 --- a/test/FileFormats/SDPA/SDPA.jl +++ b/test/FileFormats/SDPA/SDPA.jl @@ -10,10 +10,24 @@ using Test import MathOptInterface as MOI import MathOptInterface.Utilities as MOIU -const SDPA = MOI.FileFormats.SDPA +import MathOptInterface.FileFormats: SDPA + const SDPA_TEST_FILE = "test.sdpa" const SDPA_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 + sleep(1.0) # Allow time for unlink to happen. + rm(SDPA_TEST_FILE, force = true) + return +end + function _set_var_and_con_names(model::MOI.ModelLike) variable_names = String[] for j in MOI.get(model, MOI.ListOfVariableIndices()) @@ -330,19 +344,24 @@ function test_dim_reader() end 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(SDPA_TEST_FILE, force = true) +function test_integer_before_variables() + file = """ + *INTEGER + *1 + """ + io = IOBuffer() + print(io, file) + seekstart(io) + model = SDPA.Model() + @test_throws( + ErrorException( + "The number of variables should be given before *INTEGER section.", + ), + read!(io, model), + ) return end -end +end # module TestSDPA.runtests()