From 675adb2adb9faab25efdbf367e89396a08573c35 Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 13 Jul 2021 15:42:42 +1200 Subject: [PATCH 1/3] Migrate tests of parser.jl --- src/Utilities/parser.jl | 193 ++++++++++------- test/Utilities/parser.jl | 451 ++++++++++++++++++++------------------- 2 files changed, 346 insertions(+), 298 deletions(-) diff --git a/src/Utilities/parser.jl b/src/Utilities/parser.jl index f01acda460..2093e67e86 100644 --- a/src/Utilities/parser.jl +++ b/src/Utilities/parser.jl @@ -1,117 +1,101 @@ using Base.Meta: isexpr -# A parser for a simple human-readable of an MOI model. -# This should be thought of as a compact way to write small models -# for tests, and not an exchange format. -# -# variables: x, y, z -# minobjective: 2x + 3y -# con1: x + y <= 1 -# con2: [x,y] in Set -# x >= 0.0 -# -# special labels: variables, minobjective, maxobjective -# everything else denotes a constraint with a name -# -# "x - y" does NOT currently parse, needs to be written as "x + -1.0*y" -# "x^2" does NOT currently parse, needs to be written as "x*x" - -struct ParsedScalarAffineTerm +struct _ParsedScalarAffineTerm coefficient::Float64 variable::Symbol end -struct ParsedScalarAffineFunction - terms::Vector{ParsedScalarAffineTerm} +struct _ParsedScalarAffineFunction + terms::Vector{_ParsedScalarAffineTerm} constant::Float64 end -struct ParsedVectorAffineTerm +struct _ParsedVectorAffineTerm output_index::Int64 - scalar_term::ParsedScalarAffineTerm + scalar_term::_ParsedScalarAffineTerm end -struct ParsedVectorAffineFunction - terms::Vector{ParsedVectorAffineTerm} +struct _ParsedVectorAffineFunction + terms::Vector{_ParsedVectorAffineTerm} constant::Vector{Float64} end -struct ParsedScalarQuadraticTerm +struct _ParsedScalarQuadraticTerm coefficient::Float64 variable_1::Symbol variable_2::Symbol end -struct ParsedScalarQuadraticFunction - affine_terms::Vector{ParsedScalarAffineTerm} - quadratic_terms::Vector{ParsedScalarQuadraticTerm} +struct _ParsedScalarQuadraticFunction + affine_terms::Vector{_ParsedScalarAffineTerm} + quadratic_terms::Vector{_ParsedScalarQuadraticTerm} constant::Float64 end -struct ParsedVectorQuadraticTerm +struct _ParsedVectorQuadraticTerm output_index::Int64 - scalar_term::ParsedScalarQuadraticTerm + scalar_term::_ParsedScalarQuadraticTerm end -struct ParsedVectorQuadraticFunction - affine_terms::Vector{ParsedVectorAffineTerm} - quadratic_terms::Vector{ParsedVectorQuadraticTerm} +struct _ParsedVectorQuadraticFunction + affine_terms::Vector{_ParsedVectorAffineTerm} + quadratic_terms::Vector{_ParsedVectorQuadraticTerm} constant::Vector{Float64} end -struct ParsedSingleVariable +struct _ParsedSingleVariable variable::Symbol end -struct ParsedVectorOfVariables +struct _ParsedVectorOfVariables variables::Vector{Symbol} end # Not written with any considerations for performance -function parsefunction(ex) +function _parse_function(ex) if isa(ex, Symbol) - return ParsedSingleVariable(ex) + return _ParsedSingleVariable(ex) elseif isexpr(ex, :vect) if all(s -> isa(s, Symbol), ex.args) - return ParsedVectorOfVariables(copy(ex.args)) + return _ParsedVectorOfVariables(copy(ex.args)) else - singlefunctions = parsefunction.(ex.args) - affine_terms = ParsedVectorAffineTerm[] - quadratic_terms = ParsedVectorQuadraticTerm[] + singlefunctions = _parse_function.(ex.args) + affine_terms = _ParsedVectorAffineTerm[] + quadratic_terms = _ParsedVectorQuadraticTerm[] constant = Float64[] for (outindex, f) in enumerate(singlefunctions) - if isa(f, ParsedSingleVariable) + if isa(f, _ParsedSingleVariable) push!( affine_terms, - ParsedVectorAffineTerm( + _ParsedVectorAffineTerm( outindex, - ParsedScalarAffineTerm(1.0, f.variable), + _ParsedScalarAffineTerm(1.0, f.variable), ), ) push!(constant, 0.0) - elseif isa(f, ParsedScalarAffineFunction) + elseif isa(f, _ParsedScalarAffineFunction) append!( affine_terms, - ParsedVectorAffineTerm.(outindex, f.terms), + _ParsedVectorAffineTerm.(outindex, f.terms), ) push!(constant, f.constant) else - @assert isa(f, ParsedScalarQuadraticFunction) + @assert isa(f, _ParsedScalarQuadraticFunction) append!( affine_terms, - ParsedVectorAffineTerm.(outindex, f.affine_terms), + _ParsedVectorAffineTerm.(outindex, f.affine_terms), ) append!( quadratic_terms, - ParsedVectorQuadraticTerm.(outindex, f.quadratic_terms), + _ParsedVectorQuadraticTerm.(outindex, f.quadratic_terms), ) push!(constant, f.constant) end end if length(quadratic_terms) == 0 - return ParsedVectorAffineFunction(affine_terms, constant) + return _ParsedVectorAffineFunction(affine_terms, constant) else - return ParsedVectorQuadraticFunction( + return _ParsedVectorQuadraticFunction( affine_terms, quadratic_terms, constant, @@ -132,8 +116,8 @@ function parsefunction(ex) if ex.args[1] != :+ error("Expected `+`, got `$(ex.args[1])`.") end - affine_terms = ParsedScalarAffineTerm[] - quadratic_terms = ParsedScalarQuadraticTerm[] + affine_terms = _ParsedScalarAffineTerm[] + quadratic_terms = _ParsedScalarQuadraticTerm[] constant = 0.0 for subex in ex.args[2:end] if isexpr(subex, :call) && subex.args[1] == :* @@ -143,7 +127,7 @@ function parsefunction(ex) @assert isa(subex.args[3], Symbol) push!( affine_terms, - ParsedScalarAffineTerm(subex.args[2], subex.args[3]), + _ParsedScalarAffineTerm(subex.args[2], subex.args[3]), ) else # constant * variable * variable for quadratic @@ -158,7 +142,7 @@ function parsefunction(ex) end push!( quadratic_terms, - ParsedScalarQuadraticTerm( + _ParsedScalarQuadraticTerm( coefficient, subex.args[3], subex.args[4], @@ -166,16 +150,16 @@ function parsefunction(ex) ) end elseif isa(subex, Symbol) - push!(affine_terms, ParsedScalarAffineTerm(1.0, subex)) + push!(affine_terms, _ParsedScalarAffineTerm(1.0, subex)) else @assert isa(subex, Number) constant += subex end end if length(quadratic_terms) == 0 - return ParsedScalarAffineFunction(affine_terms, constant) + return _ParsedScalarAffineFunction(affine_terms, constant) else - return ParsedScalarQuadraticFunction( + return _ParsedScalarQuadraticFunction( affine_terms, quadratic_terms, constant, @@ -185,12 +169,12 @@ function parsefunction(ex) end # see tests for examples -function separatelabel(ex) +function _separate_label(ex) if isexpr(ex, :call) && ex.args[1] == :(:) # A line like `variables: x`. return ex.args[2], ex.args[3] elseif isexpr(ex, :tuple) - # A line like `variables: x, y`. Parsed as `((variables:x), y)` + # A line like `variables: x, y`. _Parsed as `((variables:x), y)` ex = copy(ex) @assert isexpr(ex.args[1], :call) && ex.args[1].args[1] == :(:) label = ex.args[1].args[2] @@ -212,7 +196,7 @@ function separatelabel(ex) end end -function parsedtoMOI(model, s::Symbol) +function _parsed_to_moi(model, s::Symbol) index = MOI.get(model, MOI.VariableIndex, String(s)) if index === nothing error("Invalid variable name $s.") @@ -220,25 +204,26 @@ function parsedtoMOI(model, s::Symbol) return index end -# Used for Vector{Symbol}, Vector{ParsedScalarAffineTerm}, Vector{ParsedVectorAffineTerm}, -# Vector{ParsedScalarQuadraticTerm} and Vector{ParsedVectorQuadraticTerm} -parsedtoMOI(model, s::Vector) = parsedtoMOI.(model, s) +# Used for Vector{Symbol}, Vector{_ParsedScalarAffineTerm}, +# Vector{_ParsedVectorAffineTerm}, Vector{_ParsedScalarQuadraticTerm} and +# Vector{_ParsedVectorQuadraticTerm}. +_parsed_to_moi(model, s::Vector) = _parsed_to_moi.(model, s) -parsedtoMOI(model, s::Union{Float64,Int64}) = s +_parsed_to_moi(model, s::Union{Float64,Int64}) = s for typename in [ - :ParsedScalarAffineTerm, - :ParsedScalarAffineFunction, - :ParsedVectorAffineTerm, - :ParsedVectorAffineFunction, - :ParsedScalarQuadraticTerm, - :ParsedScalarQuadraticFunction, - :ParsedVectorQuadraticTerm, - :ParsedVectorQuadraticFunction, - :ParsedSingleVariable, - :ParsedVectorOfVariables, + :_ParsedScalarAffineTerm, + :_ParsedScalarAffineFunction, + :_ParsedVectorAffineTerm, + :_ParsedVectorAffineFunction, + :_ParsedScalarQuadraticTerm, + :_ParsedScalarQuadraticFunction, + :_ParsedVectorQuadraticTerm, + :_ParsedVectorQuadraticFunction, + :_ParsedSingleVariable, + :_ParsedVectorOfVariables, ] - moiname = Meta.parse(replace(string(typename), "Parsed" => "MOI.")) + moiname = Meta.parse(replace(string(typename), "_Parsed" => "MOI.")) fields = fieldnames(eval(typename)) constructor = Expr( :call, @@ -246,20 +231,62 @@ for typename in [ [ Expr( :call, - :parsedtoMOI, + :_parsed_to_moi, :model, Expr(:., :f, Base.Meta.quot(field)), ) for field in fields ]..., ) - @eval parsedtoMOI(model, f::$typename) = $constructor + @eval _parsed_to_moi(model, f::$typename) = $constructor end -function loadfromstring!(model, s) - parsedlines = filter(ex -> ex != nothing, Meta.parse.(split(s, "\n"))) +# Ideally, this should be load_from_string +""" + loadfromstring!(model, s) + +A utility function to aid writing tests. + +## WARNING + +This function is not intended for widespread use! It is mainly used as a tool to +simplify writing tests in MathOptInterface. Do not use it as an exchange format +for storing or transmitting problem instances. Use the FileFormats submodule +instead. + +## Example +``` +MOI.Utilities.loadfromstring!( + model, + \"\"\" + variables: x, y, z + minobjective: 2x + 3y + con1: x + y <= 1 + con2: [x, y] in MOI.Nonnegatives(2) + x >= 0.0 + \"\"\" +) +``` + +## Notes + +Special labels are: + - variables + - minobjective + - maxobjectives +Everything else denotes a constraint with a name. + +Do not name `SingleVariable` constraints. + +## Exceptions + + * `x - y` does NOT currently parse. Instead, write `x + -1.0 * y`. + * `x^2` does NOT currently parse. Instead, write `x * x`. +""" +function loadfromstring!(model, s) + parsedlines = filter(ex -> ex !== nothing, Meta.parse.(split(s, "\n"))) for line in parsedlines - label, ex = separatelabel(line) + label, ex = _separate_label(line) if label == :variables if isexpr(ex, :tuple) for v in ex.args @@ -272,17 +299,17 @@ function loadfromstring!(model, s) MOI.set(model, MOI.VariableName(), vindex, String(ex)) end elseif label == :maxobjective - f = parsedtoMOI(model, parsefunction(ex)) + f = _parsed_to_moi(model, _parse_function(ex)) MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f) elseif label == :minobjective - f = parsedtoMOI(model, parsefunction(ex)) + f = _parsed_to_moi(model, _parse_function(ex)) MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f) else # constraint @assert isexpr(ex, :call) - f = parsedtoMOI(model, parsefunction(ex.args[2])) + f = _parsed_to_moi(model, _parse_function(ex.args[2])) if ex.args[1] == :in # Could be safer here set = Core.eval(MOI, ex.args[3]) diff --git a/test/Utilities/parser.jl b/test/Utilities/parser.jl index 1972569ae9..9fb73642e4 100644 --- a/test/Utilities/parser.jl +++ b/test/Utilities/parser.jl @@ -1,264 +1,285 @@ -using Test +module TestParser + using MathOptInterface +using Test + const MOI = MathOptInterface const MOIU = MOI.Utilities -function structeq(a::T, b::T) where {T} +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 _struct_isequal(a::T, b::T) where {T} return all(f -> getfield(a, f) == getfield(b, f), fieldnames(T)) end -@testset "parsefunction" begin - @test structeq(MOIU.parsefunction(:x), MOIU.ParsedSingleVariable(:x)) - @test structeq( - MOIU.parsefunction(:([x, y, z])), - MOIU.ParsedVectorOfVariables([:x, :y, :z]), +function test__parse_function() + @test _struct_isequal(MOIU._parse_function(:x), MOIU._ParsedSingleVariable(:x)) + @test _struct_isequal( + MOIU._parse_function(:([x, y, z])), + MOIU._ParsedVectorOfVariables([:x, :y, :z]), ) - @test structeq( - MOIU.parsefunction(:(x + y + 2.0)), - MOIU.ParsedScalarAffineFunction( - MOIU.ParsedScalarAffineTerm.([1.0, 1.0], [:x, :y]), + @test _struct_isequal( + MOIU._parse_function(:(x + y + 2.0)), + MOIU._ParsedScalarAffineFunction( + MOIU._ParsedScalarAffineTerm.([1.0, 1.0], [:x, :y]), 2.0, ), ) - @test structeq( - MOIU.parsefunction(:(x + -3y + 2.0)), - MOIU.ParsedScalarAffineFunction( - MOIU.ParsedScalarAffineTerm.([1.0, -3.0], [:x, :y]), + @test _struct_isequal( + MOIU._parse_function(:(x + -3y + 2.0)), + MOIU._ParsedScalarAffineFunction( + MOIU._ParsedScalarAffineTerm.([1.0, -3.0], [:x, :y]), 2.0, ), ) - @test structeq( - MOIU.parsefunction(:(2 * x * y + y + 1.0)), - MOIU.ParsedScalarQuadraticFunction( - MOIU.ParsedScalarAffineTerm.([1.0], [:y]), - MOIU.ParsedScalarQuadraticTerm.([2.0], [:x], [:y]), + @test _struct_isequal( + MOIU._parse_function(:(2 * x * y + y + 1.0)), + MOIU._ParsedScalarQuadraticFunction( + MOIU._ParsedScalarAffineTerm.([1.0], [:y]), + MOIU._ParsedScalarQuadraticTerm.([2.0], [:x], [:y]), 1.0, ), ) err = ErrorException("Expected `+`, got `-`.") - @test_throws err MOIU.parsefunction(:(x - y)) + @test_throws err MOIU._parse_function(:(x - y)) - @test structeq( - MOIU.parsefunction(:([x, 2x + y + 5.0])), - MOIU.ParsedVectorAffineFunction( - MOIU.ParsedVectorAffineTerm.( + @test _struct_isequal( + MOIU._parse_function(:([x, 2x + y + 5.0])), + MOIU._ParsedVectorAffineFunction( + MOIU._ParsedVectorAffineTerm.( [1, 2, 2], - MOIU.ParsedScalarAffineTerm.([1.0, 2.0, 1.0], [:x, :x, :y]), + MOIU._ParsedScalarAffineTerm.([1.0, 2.0, 1.0], [:x, :x, :y]), ), [0.0, 5.0], ), ) - @test structeq( - MOIU.parsefunction(:([x, 2x + y + 5.0, 1 * x * x])), - MOIU.ParsedVectorQuadraticFunction( - MOIU.ParsedVectorAffineTerm.( + @test _struct_isequal( + MOIU._parse_function(:([x, 2x + y + 5.0, 1 * x * x])), + MOIU._ParsedVectorQuadraticFunction( + MOIU._ParsedVectorAffineTerm.( [1, 2, 2], - MOIU.ParsedScalarAffineTerm.([1.0, 2.0, 1.0], [:x, :x, :y]), + MOIU._ParsedScalarAffineTerm.([1.0, 2.0, 1.0], [:x, :x, :y]), ), - MOIU.ParsedVectorQuadraticTerm.( + MOIU._ParsedVectorQuadraticTerm.( [3], - MOIU.ParsedScalarQuadraticTerm.([2.0], [:x], [:x]), + MOIU._ParsedScalarQuadraticTerm.([2.0], [:x], [:x]), ), [0.0, 5.0, 0.0], ), ) + return end -@testset "separatelabel" begin - @test MOIU.separatelabel(:(variables:x)) == (:variables, :x) - @test MOIU.separatelabel(:(variables:x, y)) == (:variables, :((x, y))) - @test MOIU.separatelabel(:(minobjective:x+y)) == (:minobjective, :(x + y)) - @test MOIU.separatelabel(:(con1:2x <= 1)) == (:con1, :(2x <= 1)) - @test MOIU.separatelabel(:(con1:[x, y] in S)) == (:con1, :([x, y] in S)) +function test__separate_label() + @test MOIU._separate_label(:(variables:x)) == (:variables, :x) + @test MOIU._separate_label(:(variables:x, y)) == (:variables, :((x, y))) + @test MOIU._separate_label(:(minobjective:x+y)) == (:minobjective, :(x + y)) + @test MOIU._separate_label(:(con1:2x <= 1)) == (:con1, :(2x <= 1)) + @test MOIU._separate_label(:(con1:[x, y] in S)) == (:con1, :([x, y] in S)) + return end -@testset "loadfromstring" begin - @testset "one variable" begin - s = """ - variables: x - x >= 1.0 - """ - model = MOIU.Model{Float64}() - x = MOI.add_variable(model) - MOI.set(model, MOI.VariableName(), x, "x") - bound = MOI.add_constraint( - model, - MOI.SingleVariable(x), - MOI.GreaterThan(1.0), - ) +function test_one_variable() + s = """ + variables: x + x >= 1.0 + """ + model = MOIU.Model{Float64}() + x = MOI.add_variable(model) + MOI.set(model, MOI.VariableName(), x, "x") + bound = + MOI.add_constraint(model, MOI.SingleVariable(x), MOI.GreaterThan(1.0)) - model2 = MOIU.Model{Float64}() - MOIU.loadfromstring!(model2, s) - MOIU.test_models_equal( - model, - model2, - ["x"], - String[], - [("x", MOI.GreaterThan{Float64}(1.0))], - ) - end + model2 = MOIU.Model{Float64}() + MOIU.loadfromstring!(model2, s) + MOIU.test_models_equal( + model, + model2, + ["x"], + String[], + [("x", MOI.GreaterThan{Float64}(1.0))], + ) + return +end - @testset "linear constraints" begin - s = """ - variables: x, y - linear1: x + y >= 1.0 - linear2: x + y <= 1.0 - linear3: x + y == 1.0 - """ - model = MOIU.Model{Float64}() - x = MOI.add_variable(model) - y = MOI.add_variable(model) - MOI.set(model, MOI.VariableName(), x, "x") - MOI.set(model, MOI.VariableName(), y, "y") - linear1 = MOI.add_constraint( - model, - MOI.ScalarAffineFunction( - MOI.ScalarAffineTerm.([1.0, 1.0], [x, y]), - 0.0, - ), - MOI.GreaterThan(1.0), - ) - linear2 = MOI.add_constraint( - model, - MOI.ScalarAffineFunction( - MOI.ScalarAffineTerm.([1.0, 1.0], [x, y]), - 0.0, - ), - MOI.LessThan(1.0), - ) - linear3 = MOI.add_constraint( - model, - MOI.ScalarAffineFunction( - MOI.ScalarAffineTerm.([1.0, 1.0], [x, y]), - 0.0, - ), - MOI.EqualTo(1.0), - ) - MOI.set(model, MOI.ConstraintName(), linear1, "linear1") - MOI.set(model, MOI.ConstraintName(), linear2, "linear2") - MOI.set(model, MOI.ConstraintName(), linear3, "linear3") +function test_linear_constraints() + s = """ + variables: x, y + linear1: x + y >= 1.0 + linear2: x + y <= 1.0 + linear3: x + y == 1.0 + """ + model = MOIU.Model{Float64}() + x = MOI.add_variable(model) + y = MOI.add_variable(model) + MOI.set(model, MOI.VariableName(), x, "x") + MOI.set(model, MOI.VariableName(), y, "y") + linear1 = MOI.add_constraint( + model, + MOI.ScalarAffineFunction( + MOI.ScalarAffineTerm.([1.0, 1.0], [x, y]), + 0.0, + ), + MOI.GreaterThan(1.0), + ) + linear2 = MOI.add_constraint( + model, + MOI.ScalarAffineFunction( + MOI.ScalarAffineTerm.([1.0, 1.0], [x, y]), + 0.0, + ), + MOI.LessThan(1.0), + ) + linear3 = MOI.add_constraint( + model, + MOI.ScalarAffineFunction( + MOI.ScalarAffineTerm.([1.0, 1.0], [x, y]), + 0.0, + ), + MOI.EqualTo(1.0), + ) + MOI.set(model, MOI.ConstraintName(), linear1, "linear1") + MOI.set(model, MOI.ConstraintName(), linear2, "linear2") + MOI.set(model, MOI.ConstraintName(), linear3, "linear3") - model2 = MOIU.Model{Float64}() - MOIU.loadfromstring!(model2, s) - MOIU.test_models_equal( - model, - model2, - ["x", "y"], - ["linear1", "linear2", "linear3"], - ) - end + model2 = MOIU.Model{Float64}() + MOIU.loadfromstring!(model2, s) + MOIU.test_models_equal( + model, + model2, + ["x", "y"], + ["linear1", "linear2", "linear3"], + ) + return +end - @testset "minimization: linear objective" begin - s = """ - variables: x, y - minobjective: x + -2y + 1.0 - """ - model = MOIU.Model{Float64}() - x = MOI.add_variable(model) - y = MOI.add_variable(model) - MOI.set(model, MOI.VariableName(), x, "x") - MOI.set(model, MOI.VariableName(), y, "y") - MOI.set( - model, - MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), - MOI.ScalarAffineFunction( - MOI.ScalarAffineTerm.([1.0, -2.0], [x, y]), - 1.0, - ), - ) - MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) +function test_minimization_linear_objective() + s = """ + variables: x, y + minobjective: x + -2y + 1.0 + """ + model = MOIU.Model{Float64}() + x = MOI.add_variable(model) + y = MOI.add_variable(model) + MOI.set(model, MOI.VariableName(), x, "x") + MOI.set(model, MOI.VariableName(), y, "y") + MOI.set( + model, + MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), + MOI.ScalarAffineFunction( + MOI.ScalarAffineTerm.([1.0, -2.0], [x, y]), + 1.0, + ), + ) + MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) - model2 = MOIU.Model{Float64}() - MOIU.loadfromstring!(model2, s) - MOIU.test_models_equal(model, model2, ["x", "y"], String[]) - end + model2 = MOIU.Model{Float64}() + MOIU.loadfromstring!(model2, s) + MOIU.test_models_equal(model, model2, ["x", "y"], String[]) + return +end - @testset "maximization: linear objective" begin - s = """ - variables: x, y - maxobjective: x + -2y + 1.0 - """ - model = MOIU.Model{Float64}() - x = MOI.add_variable(model) - y = MOI.add_variable(model) - MOI.set(model, MOI.VariableName(), x, "x") - MOI.set(model, MOI.VariableName(), y, "y") - MOI.set( - model, - MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), - MOI.ScalarAffineFunction( - MOI.ScalarAffineTerm.([1.0, -2.0], [x, y]), - 1.0, - ), - ) - MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) +function test_maximization_linear_objective() + s = """ + variables: x, y + maxobjective: x + -2y + 1.0 + """ + model = MOIU.Model{Float64}() + x = MOI.add_variable(model) + y = MOI.add_variable(model) + MOI.set(model, MOI.VariableName(), x, "x") + MOI.set(model, MOI.VariableName(), y, "y") + MOI.set( + model, + MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), + MOI.ScalarAffineFunction( + MOI.ScalarAffineTerm.([1.0, -2.0], [x, y]), + 1.0, + ), + ) + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) - model2 = MOIU.Model{Float64}() - MOIU.loadfromstring!(model2, s) - MOIU.test_models_equal(model, model2, ["x", "y"], String[]) - end + model2 = MOIU.Model{Float64}() + MOIU.loadfromstring!(model2, s) + MOIU.test_models_equal(model, model2, ["x", "y"], String[]) + return +end - @testset "SOC constraints" begin - s = """ - variables: x, y, z - varsoc: [x,y,z] in SecondOrderCone(3) - affsoc: [2x,y+1,-1*z] in SecondOrderCone(3) - affsoc2: [1.0,2.0,3.0] in SecondOrderCone(3) - """ - model = MOIU.Model{Float64}() - x = MOI.add_variable(model) - y = MOI.add_variable(model) - z = MOI.add_variable(model) - MOI.set(model, MOI.VariableName(), x, "x") - MOI.set(model, MOI.VariableName(), y, "y") - MOI.set(model, MOI.VariableName(), z, "z") - varsoc = MOI.add_constraint( - model, - MOI.VectorOfVariables([x, y, z]), - MOI.SecondOrderCone(3), - ) - affsoc = MOI.add_constraint( - model, - MOI.VectorAffineFunction( - MOI.VectorAffineTerm.( - [1, 2, 3], - MOI.ScalarAffineTerm.([2.0, 1.0, -1.0], [x, y, z]), - ), - [0.0, 1.0, 0.0], - ), - MOI.SecondOrderCone(3), - ) - affsoc2 = MOI.add_constraint( - model, - MOI.VectorAffineFunction( - MOI.VectorAffineTerm{Float64}[], - [1.0, 2.0, 3.0], +function test_SecondOrderCone_constraints() + s = """ + variables: x, y, z + varsoc: [x,y,z] in SecondOrderCone(3) + affsoc: [2x,y+1,-1*z] in SecondOrderCone(3) + affsoc2: [1.0,2.0,3.0] in SecondOrderCone(3) + """ + model = MOIU.Model{Float64}() + x = MOI.add_variable(model) + y = MOI.add_variable(model) + z = MOI.add_variable(model) + MOI.set(model, MOI.VariableName(), x, "x") + MOI.set(model, MOI.VariableName(), y, "y") + MOI.set(model, MOI.VariableName(), z, "z") + varsoc = MOI.add_constraint( + model, + MOI.VectorOfVariables([x, y, z]), + MOI.SecondOrderCone(3), + ) + affsoc = MOI.add_constraint( + model, + MOI.VectorAffineFunction( + MOI.VectorAffineTerm.( + [1, 2, 3], + MOI.ScalarAffineTerm.([2.0, 1.0, -1.0], [x, y, z]), ), - MOI.SecondOrderCone(3), - ) - MOI.set(model, MOI.ConstraintName(), varsoc, "varsoc") - MOI.set(model, MOI.ConstraintName(), affsoc, "affsoc") - MOI.set(model, MOI.ConstraintName(), affsoc2, "affsoc2") + [0.0, 1.0, 0.0], + ), + MOI.SecondOrderCone(3), + ) + affsoc2 = MOI.add_constraint( + model, + MOI.VectorAffineFunction( + MOI.VectorAffineTerm{Float64}[], + [1.0, 2.0, 3.0], + ), + MOI.SecondOrderCone(3), + ) + MOI.set(model, MOI.ConstraintName(), varsoc, "varsoc") + MOI.set(model, MOI.ConstraintName(), affsoc, "affsoc") + MOI.set(model, MOI.ConstraintName(), affsoc2, "affsoc2") - model2 = MOIU.Model{Float64}() - MOIU.loadfromstring!(model2, s) - MOIU.test_models_equal( - model, - model2, - ["x", "y", "z"], - ["varsoc", "affsoc", "affsoc2"], - ) - end + model2 = MOIU.Model{Float64}() + MOIU.loadfromstring!(model2, s) + MOIU.test_models_equal( + model, + model2, + ["x", "y", "z"], + ["varsoc", "affsoc", "affsoc2"], + ) + return +end - @testset "Invalid variable name" begin - s = """ - variables: x - y >= 1.0 - """ - model = MOIU.Model{Float64}() - err = ErrorException("Invalid variable name y.") - @test_throws err MOIU.loadfromstring!(model, s) - end +function test_Invalid_variable_name() + s = """ + variables: x + y >= 1.0 + """ + model = MOIU.Model{Float64}() + err = ErrorException("Invalid variable name y.") + @test_throws err MOIU.loadfromstring!(model, s) + return end + +end # module + +TestParser.runtests() From ab85b6d02f3c4eecbfb428c9641e47b76397c5aa Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 13 Jul 2021 16:05:18 +1200 Subject: [PATCH 2/3] Fix formatting --- test/Utilities/parser.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/Utilities/parser.jl b/test/Utilities/parser.jl index 9fb73642e4..872a3b625a 100644 --- a/test/Utilities/parser.jl +++ b/test/Utilities/parser.jl @@ -22,7 +22,10 @@ function _struct_isequal(a::T, b::T) where {T} end function test__parse_function() - @test _struct_isequal(MOIU._parse_function(:x), MOIU._ParsedSingleVariable(:x)) + @test _struct_isequal( + MOIU._parse_function(:x), + MOIU._ParsedSingleVariable(:x), + ) @test _struct_isequal( MOIU._parse_function(:([x, y, z])), MOIU._ParsedVectorOfVariables([:x, :y, :z]), From ae1ba5ce669ba388b29865e5da0a9c296249c89d Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Wed, 14 Jul 2021 10:04:00 +1200 Subject: [PATCH 3/3] Update parser.jl --- src/Utilities/parser.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Utilities/parser.jl b/src/Utilities/parser.jl index 2093e67e86..11b417533c 100644 --- a/src/Utilities/parser.jl +++ b/src/Utilities/parser.jl @@ -87,7 +87,10 @@ function _parse_function(ex) ) append!( quadratic_terms, - _ParsedVectorQuadraticTerm.(outindex, f.quadratic_terms), + _ParsedVectorQuadraticTerm.( + outindex, + f.quadratic_terms, + ), ) push!(constant, f.constant) end