diff --git a/test/JuMPExtension.jl b/test/JuMPExtension.jl index c44a21b043d..b1b7421b1d3 100644 --- a/test/JuMPExtension.jl +++ b/test/JuMPExtension.jl @@ -8,19 +8,26 @@ using MathOptInterface const MOI = MathOptInterface using JuMP +struct ConstraintIndex + value::Int # Index in `model.constraints` +end + mutable struct MyModel <: JuMP.AbstractModel nextvaridx::Int # Next variable index is nextvaridx+1 variables::Dict{Int, JuMP.ScalarVariable} # Map varidx -> variable varnames::Dict{Int, String} # Map varidx -> name nextconidx::Int # Next constraint index is nextconidx+1 - constraints::Dict{Int, JuMP.AbstractConstraint} # Map conidx -> variable - connames::Dict{Int, String} # Map varidx -> name + constraints::Dict{ConstraintIndex, + JuMP.AbstractConstraint} # Map conidx -> variable + connames::Dict{ConstraintIndex, String} # Map conidx -> name objectivesense::MOI.OptimizationSense objective_function::JuMP.AbstractJuMPScalar obj_dict::Dict{Symbol, Any} # Same that JuMP.Model's field `obj_dict` function MyModel() - new(0, Dict{Int, JuMP.AbstractVariable}(), Dict{Int, String}(), # Variables - 0, Dict{Int, JuMP.AbstractConstraint}(), Dict{Int, String}(), # Constraints + new(0, Dict{Int, JuMP.AbstractVariable}(), + Dict{Int, String}(), # Variables + 0, Dict{ConstraintIndex, JuMP.AbstractConstraint}(), + Dict{ConstraintIndex, String}(), # Constraints MOI.FeasibilitySense, zero(JuMP.GenericAffExpr{Float64, MyVariableRef}), Dict{Symbol, Any}()) end @@ -185,32 +192,28 @@ function JuMP.unset_integer(vref::MyVariableRef) end # Constraints -struct MyConstraintRef - model::MyModel # `model` owning the constraint - idx::Int # Index in `model.constraints` -end +const MyConstraintRef = JuMP.ConstraintRef{MyModel, ConstraintIndex} JuMP.constraint_type(::MyModel) = MyConstraintRef -if VERSION >= v"0.7-" - Base.broadcastable(cref::MyConstraintRef) = Ref(cref) -end -function JuMP.add_constraint(m::MyModel, c::JuMP.AbstractConstraint, name::String="") - m.nextconidx += 1 - cref = MyConstraintRef(m, m.nextconidx) - m.constraints[cref.idx] = c +function JuMP.add_constraint(model::MyModel, c::JuMP.AbstractConstraint, + name::String="") + model.nextconidx += 1 + index = ConstraintIndex(model.nextconidx) + cref = JuMP.ConstraintRef(model, index, JuMP.shape(c)) + model.constraints[index] = c JuMP.set_name(cref, name) - cref + return cref end function JuMP.delete(model::MyModel, constraint_ref::MyConstraintRef) @assert JuMP.is_valid(model, constraint_ref) - delete!(model.constraints, constraint_ref.idx) - delete!(model.connames, constraint_ref.idx) + delete!(model.constraints, constraint_ref.index) + delete!(model.connames, constraint_ref.index) end function JuMP.is_valid(model::MyModel, constraint_ref::MyConstraintRef) return (model === constraint_ref.model && - constraint_ref.idx in keys(model.constraints)) + constraint_ref.index in keys(model.constraints)) end function JuMP.constraint_object(cref::MyConstraintRef) - return cref.model.constraints[cref.idx] + return cref.model.constraints[cref.index] end # Objective @@ -240,9 +243,9 @@ JuMP.name(vref::MyVariableRef) = vref.model.varnames[vref.idx] function JuMP.set_name(vref::MyVariableRef, name::String) vref.model.varnames[vref.idx] = name end -JuMP.name(cref::MyConstraintRef) = cref.model.connames[cref.idx] +JuMP.name(cref::MyConstraintRef) = cref.model.connames[cref.index] function JuMP.set_name(cref::MyConstraintRef, name::String) - cref.model.connames[cref.idx] = name + cref.model.connames[cref.index] = name end end diff --git a/test/print.jl b/test/print.jl index 1ac0777d251..557ba242ad9 100644 --- a/test/print.jl +++ b/test/print.jl @@ -81,6 +81,79 @@ Base.isless(u::UnitNumber, v::UnitNumber) = isless(u.α, v.α) io_test(IJuliaMode, ex, "-2 x_{1}\\times z + x_{1}\\times x_{2}") end + # See https://github.com/JuliaOpt/JuMP.jl/pull/1352 + @testset "Expression of coefficient type with unit" begin + m = Model() + @variable m x + @variable m y + u = UnitNumber(2.0) + aff = JuMP.GenericAffExpr(zero(u), x => u, y => zero(u)) + io_test(REPLMode, aff, "UnitNumber(2.0) x") + io_test(IJuliaMode, aff, "UnitNumber(2.0) x") + quad = aff * x + io_test(REPLMode, quad, "UnitNumber(2.0) x² + UnitNumber(0.0)") + io_test(IJuliaMode, quad, "UnitNumber(2.0) x^2 + UnitNumber(0.0)") + end + + @testset "Nonlinear expressions" begin + model = Model() + @variable(model, x) + expr = @NLexpression(model, x + 1) + io_test(REPLMode, expr, "\"Reference to nonlinear expression #1\"") + end + + @testset "Nonlinear parameters" begin + model = Model() + @NLparameter(model, param == 1.0) + io_test(REPLMode, param, "\"Reference to nonlinear parameter #1\"") + end + + @testset "NLPEvaluator" begin + model = Model() + evaluator = JuMP.NLPEvaluator(model) + io_test(REPLMode, evaluator, "\"A JuMP.NLPEvaluator\"") + end + + @testset "Nonlinear constraints" begin + le = JuMP.math_symbol(REPLMode, :leq) + ge = JuMP.math_symbol(REPLMode, :geq) + eq = JuMP.math_symbol(REPLMode, :eq) + + model = Model() + @variable(model, x) + constr_le = @NLconstraint(model, sin(x) <= 1) + constr_ge = @NLconstraint(model, sin(x) >= 1) + constr_eq = @NLconstraint(model, sin(x) == 1) + constr_range = @NLconstraint(model, 0 <= sin(x) <= 1) + + io_test(REPLMode, constr_le, "sin(x) - 1.0 $le 0") + io_test(REPLMode, constr_ge, "sin(x) - 1.0 $ge 0") + io_test(REPLMode, constr_eq, "sin(x) - 1.0 $eq 0") + # Note: This is inconsistent with the "x in [-1, 1]" printing for + # regular constraints. + io_test(REPLMode, constr_range, "0 $le sin(x) $le 1") + + io_test(IJuliaMode, constr_le, "sin(x) - 1.0 \\leq 0") + io_test(IJuliaMode, constr_ge, "sin(x) - 1.0 \\geq 0") + io_test(IJuliaMode, constr_eq, "sin(x) - 1.0 = 0") + io_test(IJuliaMode, constr_range, "0 \\leq sin(x) \\leq 1") + end + + @testset "Nonlinear constraints with embedded parameters/expressions" begin + le = JuMP.math_symbol(REPLMode, :leq) + + model = Model() + @variable(model, x) + expr = @NLexpression(model, x + 1) + @NLparameter(model, param == 1.0) + + constr = @NLconstraint(model, expr - param <= 0) + io_test(REPLMode, constr, "(subexpression[1] - parameter[1]) - 0.0 $le 0") + io_test(IJuliaMode, constr, "(subexpression_{1} - parameter_{1}) - 0.0 \\leq 0") + end +end + +function printing_test(ModelType::Type{<:JuMP.AbstractModel}) @testset "VariableRef" begin m = Model() @variable(m, 0 <= x <= 2) @@ -144,20 +217,6 @@ Base.isless(u::UnitNumber, v::UnitNumber) = isless(u.α, v.α) io_test(IJuliaMode, w[1,3], "symm_{1,3}") end - # See https://github.com/JuliaOpt/JuMP.jl/pull/1352 - @testset "Expression of coefficient type with unit" begin - m = Model() - @variable m x - @variable m y - u = UnitNumber(2.0) - aff = JuMP.GenericAffExpr(zero(u), x => u, y => zero(u)) - io_test(REPLMode, aff, "UnitNumber(2.0) x") - io_test(IJuliaMode, aff, "UnitNumber(2.0) x") - quad = aff * x - io_test(REPLMode, quad, "UnitNumber(2.0) x² + UnitNumber(0.0)") - io_test(IJuliaMode, quad, "UnitNumber(2.0) x^2 + UnitNumber(0.0)") - end - @testset "SingleVariable constraints" begin ge = JuMP.math_symbol(REPLMode, :geq) in_sym = JuMP.math_symbol(REPLMode, :in) @@ -240,61 +299,12 @@ Base.isless(u::UnitNumber, v::UnitNumber) = isless(u.α, v.α) io_test(REPLMode, quad_constr, "2 x$sq $le 1.0") # TODO: Test in IJulia mode. end +end - @testset "Nonlinear expressions" begin - model = Model() - @variable(model, x) - expr = @NLexpression(model, x + 1) - io_test(REPLMode, expr, "\"Reference to nonlinear expression #1\"") - end - - @testset "Nonlinear parameters" begin - model = Model() - @NLparameter(model, param == 1.0) - io_test(REPLMode, param, "\"Reference to nonlinear parameter #1\"") - end - - @testset "NLPEvaluator" begin - model = Model() - evaluator = JuMP.NLPEvaluator(model) - io_test(REPLMode, evaluator, "\"A JuMP.NLPEvaluator\"") - end - - @testset "Nonlinear constraints" begin - le = JuMP.math_symbol(REPLMode, :leq) - ge = JuMP.math_symbol(REPLMode, :geq) - eq = JuMP.math_symbol(REPLMode, :eq) - - model = Model() - @variable(model, x) - constr_le = @NLconstraint(model, sin(x) <= 1) - constr_ge = @NLconstraint(model, sin(x) >= 1) - constr_eq = @NLconstraint(model, sin(x) == 1) - constr_range = @NLconstraint(model, 0 <= sin(x) <= 1) - - io_test(REPLMode, constr_le, "sin(x) - 1.0 $le 0") - io_test(REPLMode, constr_ge, "sin(x) - 1.0 $ge 0") - io_test(REPLMode, constr_eq, "sin(x) - 1.0 $eq 0") - # Note: This is inconsistent with the "x in [-1, 1]" printing for - # regular constraints. - io_test(REPLMode, constr_range, "0 $le sin(x) $le 1") - - io_test(IJuliaMode, constr_le, "sin(x) - 1.0 \\leq 0") - io_test(IJuliaMode, constr_ge, "sin(x) - 1.0 \\geq 0") - io_test(IJuliaMode, constr_eq, "sin(x) - 1.0 = 0") - io_test(IJuliaMode, constr_range, "0 \\leq sin(x) \\leq 1") - end - - @testset "Nonlinear constraints with embedded parameters/expressions" begin - le = JuMP.math_symbol(REPLMode, :leq) - - model = Model() - @variable(model, x) - expr = @NLexpression(model, x + 1) - @NLparameter(model, param == 1.0) +@testset "Printing for JuMP.Model" begin + printing_test(Model) +end - constr = @NLconstraint(model, expr - param <= 0) - io_test(REPLMode, constr, "(subexpression[1] - parameter[1]) - 0.0 $le 0") - io_test(IJuliaMode, constr, "(subexpression_{1} - parameter_{1}) - 0.0 \\leq 0") - end +@testset "Printing for JuMPExtension.MyModel" begin + printing_test(JuMPExtension.MyModel) end