From 010ec92856b8985e3acd61daa258615c3248a9ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 13 Feb 2019 16:26:00 +0100 Subject: [PATCH 1/7] Improve printing of SDP constraints --- docs/src/extensions.md | 3 +- src/constraints.jl | 6 ++- src/print.jl | 101 +++++++++++++++++++++++++++++------------ src/sd.jl | 11 ++++- src/shapes.jl | 40 ++++++++++++---- test/print.jl | 24 +++++++++- 6 files changed, 140 insertions(+), 45 deletions(-) diff --git a/docs/src/extensions.md b/docs/src/extensions.md index a966cad183a..d4b92934926 100644 --- a/docs/src/extensions.md +++ b/docs/src/extensions.md @@ -92,7 +92,8 @@ used to reshape the result computed in [`value`](@ref) and [`dual`](@ref). ```@docs AbstractShape shape -reshape_result +reshape_vector +reshape_set dual_shape ScalarShape VectorShape diff --git a/src/constraints.jl b/src/constraints.jl index 3fba3df0b32..8c5415b325b 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -305,6 +305,7 @@ end jump_function(constraint::ScalarConstraint) = constraint.func moi_set(constraint::ScalarConstraint) = constraint.set +reshape_set(set::MOI.AbstractScalarSet, ::ScalarShape) = set shape(::ScalarConstraint) = ScalarShape() function constraint_object(ref::ConstraintRef{Model, _MOICON{FuncType, SetType}}) where @@ -342,6 +343,7 @@ end jump_function(constraint::VectorConstraint) = constraint.func moi_set(constraint::VectorConstraint) = constraint.set +reshape_set(set::MOI.AbstractVectorSet, ::VectorShape) = set shape(c::VectorConstraint) = c.shape function constraint_object(ref::ConstraintRef{Model, _MOICON{FuncType, SetType}}) where {FuncType <: MOI.AbstractVectorFunction, SetType <: MOI.AbstractVectorSet} @@ -442,7 +444,7 @@ evaluation of `2x + 3y`. ``` """ function value(cref::ConstraintRef{Model, <:_MOICON}) - return reshape_result(_constraint_primal(cref), cref.shape) + return reshape_vector(_constraint_primal(cref), cref.shape) end # Returns the value of MOI.ConstraintPrimal in a type-stable way @@ -475,7 +477,7 @@ Use `has_dual` to check if a result exists before asking for values. See also [`shadow_price`](@ref). """ function dual(cref::ConstraintRef{Model, <:_MOICON}) - return reshape_result(_constraint_dual(cref), dual_shape(cref.shape)) + return reshape_vector(_constraint_dual(cref), dual_shape(cref.shape)) end # Returns the value of MOI.ConstraintPrimal in a type-stable way diff --git a/src/print.jl b/src/print.jl index ac99dd44183..57875436ef2 100644 --- a/src/print.jl +++ b/src/print.jl @@ -220,7 +220,17 @@ function model_string(print_mode, model::AbstractModel) end str *= eol str *= ijl ? "\\text{Subject to} \\quad" : "Subject to" * eol - str *= constraints_string(print_mode, model, sep, eol) + constraints = constraints_string(print_mode, model) + if print_mode == REPLMode + constraints = map(str -> replace(str, '\n' => eol * sep), constraints) + end + if !isempty(constraints) + str *= sep + end + str *= join(constraints, eol * sep) + if !isempty(constraints) + str *= eol + end if ijl str = "\\begin{alignat*}{1}" * str * "\\end{alignat*}\n" end @@ -259,12 +269,7 @@ end #------------------------------------------------------------------------ ## VariableRef #------------------------------------------------------------------------ -function Base.show(io::IO, v::AbstractVariableRef) - print(io, function_string(REPLMode, v)) -end -function Base.show(io::IO, ::MIME"text/latex", v::AbstractVariableRef) - print(io, _wrap_in_math_mode(function_string(IJuliaMode, v))) -end + function function_string(::Type{REPLMode}, v::AbstractVariableRef) var_name = name(v) if !isempty(var_name) @@ -283,10 +288,9 @@ function function_string(::Type{IJuliaMode}, v::AbstractVariableRef) end end -Base.show(io::IO, a::GenericAffExpr) = print(io, function_string(REPLMode, a)) -function Base.show(io::IO, ::MIME"text/latex", a::GenericAffExpr) - print(io, _wrap_in_math_mode(function_string(IJuliaMode, a))) -end +#------------------------------------------------------------------------ +## GenericAffExpr +#------------------------------------------------------------------------ function function_string(mode, a::GenericAffExpr, show_constant=true) # If the expression is empty, return the constant (or 0) @@ -326,10 +330,6 @@ end #------------------------------------------------------------------------ ## GenericQuadExpr #------------------------------------------------------------------------ -Base.show(io::IO, q::GenericQuadExpr) = print(io, function_string(REPLMode, q)) -function Base.show(io::IO, ::MIME"text/latex", q::GenericQuadExpr) - print(io, _wrap_in_math_mode(function_string(IJuliaMode, q))) -end function function_string(mode, q::GenericQuadExpr) length(quad_terms(q)) == 0 && return function_string(mode, q.aff) @@ -398,26 +398,25 @@ function show_constraints_summary(io::IO, model::Model) end """ - constraints_string(print_mode, model::AbstractModel, sep, eol)::String + constraints_string(print_mode, model::AbstractModel)::Vector{String} -Return a `String` describing the constraints of the model, each on a line -starting with `sep` and ending with `eol` (which already contains `\n`). +Return a list of `String`s describing each constraints of the model. """ -function constraints_string(print_mode, model::Model, sep, eol) - str = "" +function constraints_string(print_mode, model::Model) + strings = String[] for (F, S) in list_of_constraint_types(model) for cref in all_constraints(model, F, S) con = constraint_object(cref) - str *= sep * constraint_string(print_mode, con) * eol + push!(strings, constraint_string(print_mode, con)) end end if model.nlp_data !== nothing for nl_constraint in model.nlp_data.nlconstr - str *= sep * nl_constraint_string(model, print_mode, nl_constraint) - str *= eol + push!(strings, + nl_constraint_string(model, print_mode, nl_constraint)) end end - return str + return strings end ## Notes for extensions @@ -445,10 +444,45 @@ Return a `String` representing the function `func` using print mode """ function function_string end +function Base.show(io::IO, f::AbstractJuMPScalar) + print(io, function_string(REPLMode, f)) +end +function Base.show(io::IO, ::MIME"text/latex", f::AbstractJuMPScalar) + print(io, _wrap_in_math_mode(function_string(IJuliaMode, f))) +end + function function_string(print_mode, vector::Vector{<:AbstractJuMPScalar}) return "[" * join(function_string.(print_mode, vector), ", ") * "]" end +function function_string(::Type{REPLMode}, + A::AbstractMatrix{<:AbstractJuMPScalar}) + str = sprint(show, MIME"text/plain"(), A) + # We drop the first line with the signature "m×n Array{...}:" + return str[(findfirst(isequal('\n'), str) + 1):end] +end + +function function_string(print_mode::Type{IJuliaMode}, + A::AbstractMatrix{<:AbstractJuMPScalar}) + str = sprint(show, MIME"text/plain"(), A) + str = "\\begin{bmatrix}\n" + for i in 1:size(A, 1) + line = "" + for j in 1:size(A, 2) + if j != 1 + line *= " & " + end + if A isa Symmetric && i > j + line *= "\\cdot" + else + line *= function_string(print_mode, A[i, j]) + end + end + str *= line * "\\\\\n" + end + return str * "\\end{bmatrix}" +end + """ function_string(print_mode::{<:JuMP.PrintMode}, constraint::JuMP.AbstractConstraint) @@ -457,7 +491,8 @@ Return a `String` representing the function of the constraint `constraint` using print mode `print_mode`. """ function function_string(print_mode, constraint::AbstractConstraint) - return function_string(print_mode, jump_function(constraint)) + f = reshape_vector(jump_function(constraint), shape(constraint)) + return function_string(print_mode, f) end function in_set_string(print_mode, set::MOI.LessThan) @@ -486,13 +521,12 @@ in_set_string(print_mode, ::MOI.Integer) = "integer" # regular text in math mode which looks a bit awkward. """ in_set_string(print_mode::Type{<:JuMP.PrintMode}, - set::Union{JuMP.AbstractJuMPScalar, - Vector{<:JuMP.AbstractJuMPScalar}}) + set::Union{PSDCone, MOI.AbstractSet}) Return a `String` representing the membership to the set `set` using print mode `print_mode`. """ -function in_set_string(print_mode, set::MOI.AbstractSet) +function in_set_string(print_mode, set::Union{PSDCone, MOI.AbstractSet}) return string(_math_symbol(print_mode, :in), " ", set) end @@ -504,13 +538,20 @@ Return a `String` representing the membership to the set of the constraint `constraint` using print mode `print_mode`. """ function in_set_string(print_mode, constraint::AbstractConstraint) - return in_set_string(print_mode, moi_set(constraint)) + set = reshape_set(moi_set(constraint), shape(constraint)) + return in_set_string(print_mode, set) end function constraint_string(print_mode, constraint_object::AbstractConstraint) func_str = function_string(print_mode, constraint_object) in_set_str = in_set_string(print_mode, constraint_object) - return func_str * " " * in_set_str + if print_mode == REPLMode + lines = split(func_str, '\n') + lines[1 + div(length(lines), 2)] *= " " * in_set_str + return join(lines, '\n') + else + return func_str * " " * in_set_str + end end function constraint_string(print_mode, constraint_name, constraint_object::AbstractConstraint) diff --git a/src/sd.jl b/src/sd.jl index 333344209e7..c5ba827241c 100644 --- a/src/sd.jl +++ b/src/sd.jl @@ -53,7 +53,7 @@ lower-left triangular part given row by row). struct SymmetricMatrixShape <: AbstractShape side_dimension::Int end -function reshape_result(vectorized_form::Vector{T}, shape::SymmetricMatrixShape) where T +function reshape_vector(vectorized_form::Vector{T}, shape::SymmetricMatrixShape) where T matrix = Matrix{T}(undef, shape.side_dimension, shape.side_dimension) k = 0 for j in 1:shape.side_dimension @@ -64,6 +64,10 @@ function reshape_result(vectorized_form::Vector{T}, shape::SymmetricMatrixShape) end return Symmetric(matrix) end +function reshape_set(::MOI.PositiveSemidefiniteConeTriangle, + ::SymmetricMatrixShape) + return PSDCone() +end """ SquareMatrixShape @@ -76,9 +80,12 @@ row). struct SquareMatrixShape <: AbstractShape side_dimension::Int end -function reshape_result(vectorized_form::Vector{T}, shape::SquareMatrixShape) where T +function reshape_vector(vectorized_form::Vector{T}, shape::SquareMatrixShape) where T return reshape(vectorized_form, shape.side_dimension, shape.side_dimension) end +function reshape_set(::MOI.PositiveSemidefiniteConeSquare, ::SquareMatrixShape) + return PSDCone() +end """ function build_constraint(_error::Function, Q::Symmetric{V, M}, diff --git a/src/shapes.jl b/src/shapes.jl index 22e65def936..a5a756d26ea 100644 --- a/src/shapes.jl +++ b/src/shapes.jl @@ -12,7 +12,7 @@ AbstractShape Abstract vectorizable shape. Given a flat vector form of an object of shape -`shape`, the original object can be obtained by [`reshape_result`](@ref). +`shape`, the original object can be obtained by [`reshape_vector`](@ref). """ abstract type AbstractShape end @@ -36,7 +36,7 @@ end struct PolynomialShape <: AbstractShape monomials::Vector{Monomial} end -JuMP.reshape_result(x::Vector, shape::PolynomialShape) = Polynomial(x, shape.monomials) +JuMP.reshape_vector(x::Vector, shape::PolynomialShape) = Polynomial(x, shape.monomials) ``` and a shape for moments can be defined as follows: ```julia @@ -47,7 +47,7 @@ end struct MomentsShape <: AbstractShape monomials::Vector{Monomial} end -JuMP.reshape_result(x::Vector, shape::MomentsShape) = Moments(x, shape.monomials) +JuMP.reshape_vector(x::Vector, shape::MomentsShape) = Moments(x, shape.monomials) ``` The `dual_shape` allows to define the shape of the dual of polynomial and moment constraints: @@ -59,7 +59,26 @@ dual_shape(shape::MomentsShape) = PolynomialShape(shape.monomials) dual_shape(shape::AbstractShape) = shape """ - reshape_result(vectorized_form::Vector, shape::AbstractShape) + reshape_set(vectorized_set::MOI.AbstractSet, shape::AbstractShape) + +Return a set in its original shape `shape` given its vectorized form +`vectorized_form`. + +## Examples + +Given a [`SymmetricMatrixShape`](@ref) of vectorized form +`[1, 2, 3] in MOI.PositiveSemidefinieConeTriangle(2)`, the +following code returns the set of the original constraint +`Symmetric(Matrix[1 2; 2 3]) in PSDCone()`: +```jldoctest; setup = :(using JuMP) +julia> reshape_set(MOI.PositiveSemidefiniteConeTriangle(2), SymmetricMatrixShape(2)) +PSDCone() +``` +""" +function reshape_set end + +""" + reshape_vector(vectorized_form::Vector, shape::AbstractShape) Return an object in its original shape `shape` given its vectorized form `vectorized_form`. @@ -68,11 +87,14 @@ Return an object in its original shape `shape` given its vectorized form Given a [`SymmetricMatrixShape`](@ref) of vectorized form `[1, 2, 3]`, the following code returns the matrix `Symmetric(Matrix[1 2; 2 3])`: -```julia -reshape_result([1, 2, 3], SymmetricMatrixShape(2)) +```jldoctest; setup = :(using JuMP) +julia> reshape_vector([1, 2, 3], SymmetricMatrixShape(2)) +2×2 LinearAlgebra.Symmetric{Int64,Array{Int64,2}}: + 1 2 + 2 3 ``` """ -function reshape_result end +function reshape_vector end """ shape(c::AbstractConstraint)::AbstractShape @@ -87,7 +109,7 @@ function shape end Shape of scalar constraints. """ struct ScalarShape <: AbstractShape end -reshape_result(α, ::ScalarShape) = α +reshape_vector(α, ::ScalarShape) = α """ VectorShape @@ -95,4 +117,4 @@ reshape_result(α, ::ScalarShape) = α Vector for which the vectorized form corresponds exactly to the vector given. """ struct VectorShape <: AbstractShape end -reshape_result(vectorized_form, ::VectorShape) = vectorized_form +reshape_vector(vectorized_form, ::VectorShape) = vectorized_form diff --git a/test/print.jl b/test/print.jl index 48b63ed77a4..d0933284aad 100644 --- a/test/print.jl +++ b/test/print.jl @@ -12,7 +12,7 @@ ############################################################################# using MathOptInterface using JuMP -using Test +using LinearAlgebra, Test import JuMP.REPLMode, JuMP.IJuliaMode # Helper function to test IO methods work correctly @@ -367,6 +367,10 @@ function model_printing_test(ModelType::Type{<:JuMP.AbstractModel}) @constraint(model_1, a + b - 10c - 2x + c1 <= 1) @constraint(model_1, a*b <= 2) @constraint(model_1, [1 - a; u] in SecondOrderCone()) + @constraint(model_1, [a b; c x] in PSDCone()) + @constraint(model_1, Symmetric([a b; b x]) in PSDCone()) + @constraint(model_1, [a, b, c] in MOI.PositiveSemidefiniteConeTriangle(2)) + @constraint(model_1, [a, b, c, x] in MOI.PositiveSemidefiniteConeSquare(2)) VariableType = typeof(a) @@ -392,6 +396,12 @@ function model_printing_test(ModelType::Type{<:JuMP.AbstractModel}) c1 $le 1.0 a + b - 10 c - 2 x + c1 $le 1.0 a*b $le 2.0 + a b + b x ∈ PSDCone() + [a, b, c] ∈ MathOptInterface.PositiveSemidefiniteConeTriangle(2) + a b + c x ∈ PSDCone() + [a, b, c, x] ∈ MathOptInterface.PositiveSemidefiniteConeSquare(2) [-a + 1, u[1], u[2], u[3]] $inset MathOptInterface.SecondOrderCone(4) """, repl=:print) @@ -407,6 +417,8 @@ function model_printing_test(ModelType::Type{<:JuMP.AbstractModel}) `$VariableType`-in-`MathOptInterface.LessThan{Float64}`: 4 constraints `GenericAffExpr{Float64,$VariableType}`-in-`MathOptInterface.LessThan{Float64}`: 1 constraint `GenericQuadExpr{Float64,$VariableType}`-in-`MathOptInterface.LessThan{Float64}`: 1 constraint + `Array{$VariableType,1}`-in-`MathOptInterface.PositiveSemidefiniteConeTriangle`: 2 constraints + `Array{$VariableType,1}`-in-`MathOptInterface.PositiveSemidefiniteConeSquare`: 2 constraints `Array{GenericAffExpr{Float64,$VariableType},1}`-in-`MathOptInterface.SecondOrderCone`: 1 constraint Model mode: AUTOMATIC CachingOptimizer state: NO_OPTIMIZER @@ -434,6 +446,16 @@ function model_printing_test(ModelType::Type{<:JuMP.AbstractModel}) & c1 \\leq 1.0\\\\ & a + b - 10 c - 2 x + c1 \\leq 1.0\\\\ & a\\times b \\leq 2.0\\\\ + & \\begin{bmatrix} + a & b\\\\ + \\cdot & x\\\\ + \\end{bmatrix} \\in PSDCone()\\\\ + & [a, b, c] \\in MathOptInterface.PositiveSemidefiniteConeTriangle(2)\\\\ + & \\begin{bmatrix} + a & b\\\\ + c & x\\\\ + \\end{bmatrix} \\in PSDCone()\\\\ + & [a, b, c, x] \\in MathOptInterface.PositiveSemidefiniteConeSquare(2)\\\\ & [-a + 1, u_{1}, u_{2}, u_{3}] \\in MathOptInterface.SecondOrderCone(4)\\\\ \\end{alignat*} """) From c9f74b1ae3a82ff6afb5db1c0c080ecea7f48263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 13 Feb 2019 18:19:01 +0100 Subject: [PATCH 2/7] =?UTF-8?q?=E2=9C=85=20Fix=20printing=20in=20JuMPExten?= =?UTF-8?q?sion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/JuMPExtension.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/JuMPExtension.jl b/test/JuMPExtension.jl index 1471adf5db3..4aee406fa4a 100644 --- a/test/JuMPExtension.jl +++ b/test/JuMPExtension.jl @@ -319,14 +319,14 @@ function JuMP.show_constraints_summary(io::IO, model::MyModel) n = length(model.constraints) print(io, "Constraint", _plural(n), ": ", n) end -function JuMP.constraints_string(print_mode, model::MyModel, sep, eol) - str = "" +function JuMP.constraints_string(print_mode, model::MyModel) + strings = String[] # Sort by creation order, i.e. ConstraintIndex value constraints = sort(collect(model.constraints), by = c -> c.first.value) for (index, constraint) in constraints - str *= sep * JuMP.constraint_string(print_mode, constraint) * eol + push!(strings, JuMP.constraint_string(print_mode, constraint)) end - return str + return strings end end From 9765abfe5605c629260bbb51a5ecfc6ff98baed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 13 Feb 2019 20:15:51 +0100 Subject: [PATCH 3/7] Update docstrings --- docs/src/constraints.md | 20 ++++++++++++++++---- src/macros.jl | 15 +++++++++++++-- src/sd.jl | 29 +++++++++++++++++++++++++---- 3 files changed, 54 insertions(+), 10 deletions(-) diff --git a/docs/src/constraints.md b/docs/src/constraints.md index 3bef0975a5e..9174954d3f4 100644 --- a/docs/src/constraints.md +++ b/docs/src/constraints.md @@ -492,7 +492,8 @@ julia> @variable(model, x) x julia> @SDconstraint(model, [x 2x; 3x 4x] >= ones(2, 2)) -[x - 1, 3 x - 1, 2 x - 1, 4 x - 1] ∈ MathOptInterface.PositiveSemidefiniteConeSquare(2) + x - 1 2 x - 1 + 3 x - 1 4 x - 1 ∈ PSDCone() ``` Solvers supporting such constraints usually expect to be given a matrix that @@ -518,14 +519,25 @@ follows: julia> using LinearAlgebra julia> @constraint(model, Symmetric([x 2x; 2x 4x] - ones(2, 2)) in PSDCone()) -[x - 1, 2 x - 1, 4 x - 1] ∈ MathOptInterface.PositiveSemidefiniteConeTriangle(2) + x - 1 2 x - 1 + 2 x - 1 4 x - 1 ∈ PSDCone() ``` Note that the lower triangular entries are silently ignored even if they are different so use it with caution: ```jldoctest con_psd -julia> @constraint(model, Symmetric([x 2x; 3x 4x]) in PSDCone()) -[x, 2 x, 4 x] ∈ MathOptInterface.PositiveSemidefiniteConeTriangle(2) +julia> cref = @constraint(model, Symmetric([x 2x; 3x 4x]) in PSDCone()) + x 2 x + 2 x 4 x ∈ PSDCone() + +julia> jump_function(constraint_object(cref)) +3-element Array{GenericAffExpr{Float64,VariableRef},1}: + x + 2 x + 4 x + +julia> moi_set(constraint_object(cref)) +MathOptInterface.PositiveSemidefiniteConeTriangle(2) ``` ## Constraint modifications diff --git a/src/macros.jl b/src/macros.jl index 33b2db78d00..c6c8df9b358 100644 --- a/src/macros.jl +++ b/src/macros.jl @@ -759,8 +759,19 @@ julia> a = [x 2x julia> b = [1 2 3 4]; -julia> @SDconstraint(model, a ⪰ b) -[x - 1, -3, 2 x - 2, x - 4] ∈ MathOptInterface.PositiveSemidefiniteConeSquare(2) +julia> cref = @SDconstraint(model, a ⪰ b) + x - 1 2 x - 2 + -3 x - 4 ∈ PSDCone() + +julia> jump_function(constraint_object(cref)) +4-element Array{GenericAffExpr{Float64,VariableRef},1}: + x - 1 + -3 + 2 x - 2 + x - 4 + +julia> moi_set(constraint_object(cref)) +MathOptInterface.PositiveSemidefiniteConeSquare(2) ``` In the set `PositiveSemidefiniteConeSquare(2)` in the last output, `Square` means that the matrix is passed as a square matrix as the corresponding diff --git a/src/sd.jl b/src/sd.jl index c5ba827241c..e81d3618d11 100644 --- a/src/sd.jl +++ b/src/sd.jl @@ -24,8 +24,19 @@ julia> a = [ x 2x julia> b = [1 2 2 4]; -julia> @SDconstraint(model, a ⪰ b) -[x - 1, 2 x - 2, 2 x - 2, x - 4] ∈ MathOptInterface.PositiveSemidefiniteConeSquare(2) +julia> cref = @SDconstraint(model, a ⪰ b) + x - 1 2 x - 2 + 2 x - 2 x - 4 ∈ PSDCone() + +julia> jump_function(constraint_object(cref)) +4-element Array{GenericAffExpr{Float64,VariableRef},1}: + x - 1 + 2 x - 2 + 2 x - 2 + x - 4 + +julia> moi_set(constraint_object(cref)) +MathOptInterface.PositiveSemidefiniteConeSquare(2) ``` We see in the output of the last command that the matrix the vectorization of the matrix is constrained to belong to the `PositiveSemidefiniteConeSquare`. @@ -33,8 +44,18 @@ matrix is constrained to belong to the `PositiveSemidefiniteConeSquare`. ```jldoctest PSDCone julia> using LinearAlgebra # For Symmetric -julia> @constraint(model, Symmetric(a - b) in PSDCone()) -[x - 1, 2 x - 2, x - 4] ∈ MathOptInterface.PositiveSemidefiniteConeTriangle(2) +julia> cref = @constraint(model, Symmetric(a - b) in PSDCone()) + x - 1 2 x - 2 + 2 x - 2 x - 4 ∈ PSDCone() + +julia> jump_function(constraint_object(cref)) +3-element Array{GenericAffExpr{Float64,VariableRef},1}: + x - 1 + 2 x - 2 + x - 4 + +julia> moi_set(constraint_object(cref)) +MathOptInterface.PositiveSemidefiniteConeTriangle(2) ``` As we see in the output of the last command, the vectorization of only the upper triangular part of the matrix is constrained to belong to the From dd0a6262ca31729b8c92cc4cf0829fec071e711e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 13 Feb 2019 20:54:32 +0100 Subject: [PATCH 4/7] =?UTF-8?q?=E2=9C=85=20Fix=20tests=20on=20Windows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/print.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/print.jl b/test/print.jl index d0933284aad..313ecfef41b 100644 --- a/test/print.jl +++ b/test/print.jl @@ -397,11 +397,11 @@ function model_printing_test(ModelType::Type{<:JuMP.AbstractModel}) a + b - 10 c - 2 x + c1 $le 1.0 a*b $le 2.0 a b - b x ∈ PSDCone() - [a, b, c] ∈ MathOptInterface.PositiveSemidefiniteConeTriangle(2) + b x $inset PSDCone() + [a, b, c] $inset MathOptInterface.PositiveSemidefiniteConeTriangle(2) a b - c x ∈ PSDCone() - [a, b, c, x] ∈ MathOptInterface.PositiveSemidefiniteConeSquare(2) + c x $inset PSDCone() + [a, b, c, x] $inset MathOptInterface.PositiveSemidefiniteConeSquare(2) [-a + 1, u[1], u[2], u[3]] $inset MathOptInterface.SecondOrderCone(4) """, repl=:print) From aacc18240a84ed59515c7928f8d290c977852744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 14 Feb 2019 11:34:16 +0100 Subject: [PATCH 5/7] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix=20typo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/print.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/print.jl b/src/print.jl index 57875436ef2..2f067198ff1 100644 --- a/src/print.jl +++ b/src/print.jl @@ -400,7 +400,7 @@ end """ constraints_string(print_mode, model::AbstractModel)::Vector{String} -Return a list of `String`s describing each constraints of the model. +Return a list of `String`s describing each constraint of the model. """ function constraints_string(print_mode, model::Model) strings = String[] From 4832d2838a9eeeebaa2b0a31bcff198c80ab20be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 14 Feb 2019 12:15:50 +0100 Subject: [PATCH 6/7] Improve matrix printing --- src/print.jl | 9 ++++++++- test/print.jl | 8 ++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/print.jl b/src/print.jl index 2f067198ff1..d04236c32c2 100644 --- a/src/print.jl +++ b/src/print.jl @@ -458,8 +458,15 @@ end function function_string(::Type{REPLMode}, A::AbstractMatrix{<:AbstractJuMPScalar}) str = sprint(show, MIME"text/plain"(), A) + lines = split(str, '\n') # We drop the first line with the signature "m×n Array{...}:" - return str[(findfirst(isequal('\n'), str) + 1):end] + lines = lines[2:end] + # We replace the first space by an opening `[` + lines[1] = '[' * lines[1][2:end] + for i in 1:length(lines) + lines[i] = lines[i] * (i == length(lines) ? ']' : ';') + end + return join(lines, '\n') end function function_string(print_mode::Type{IJuliaMode}, diff --git a/test/print.jl b/test/print.jl index 313ecfef41b..dfb008e427a 100644 --- a/test/print.jl +++ b/test/print.jl @@ -396,11 +396,11 @@ function model_printing_test(ModelType::Type{<:JuMP.AbstractModel}) c1 $le 1.0 a + b - 10 c - 2 x + c1 $le 1.0 a*b $le 2.0 - a b - b x $inset PSDCone() + [a b; + b x] $inset PSDCone() [a, b, c] $inset MathOptInterface.PositiveSemidefiniteConeTriangle(2) - a b - c x $inset PSDCone() + [a b; + c x] $inset PSDCone() [a, b, c, x] $inset MathOptInterface.PositiveSemidefiniteConeSquare(2) [-a + 1, u[1], u[2], u[3]] $inset MathOptInterface.SecondOrderCone(4) """, repl=:print) From c9208dd2f6c384606fcd14c84a2127808606c652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 14 Feb 2019 14:38:03 +0100 Subject: [PATCH 7/7] Fix docstrings --- docs/src/constraints.md | 12 ++++++------ src/macros.jl | 4 ++-- src/sd.jl | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/src/constraints.md b/docs/src/constraints.md index 9174954d3f4..5880c849939 100644 --- a/docs/src/constraints.md +++ b/docs/src/constraints.md @@ -492,8 +492,8 @@ julia> @variable(model, x) x julia> @SDconstraint(model, [x 2x; 3x 4x] >= ones(2, 2)) - x - 1 2 x - 1 - 3 x - 1 4 x - 1 ∈ PSDCone() +[x - 1 2 x - 1; + 3 x - 1 4 x - 1] ∈ PSDCone() ``` Solvers supporting such constraints usually expect to be given a matrix that @@ -519,16 +519,16 @@ follows: julia> using LinearAlgebra julia> @constraint(model, Symmetric([x 2x; 2x 4x] - ones(2, 2)) in PSDCone()) - x - 1 2 x - 1 - 2 x - 1 4 x - 1 ∈ PSDCone() +[x - 1 2 x - 1; + 2 x - 1 4 x - 1] ∈ PSDCone() ``` Note that the lower triangular entries are silently ignored even if they are different so use it with caution: ```jldoctest con_psd julia> cref = @constraint(model, Symmetric([x 2x; 3x 4x]) in PSDCone()) - x 2 x - 2 x 4 x ∈ PSDCone() +[x 2 x; + 2 x 4 x] ∈ PSDCone() julia> jump_function(constraint_object(cref)) 3-element Array{GenericAffExpr{Float64,VariableRef},1}: diff --git a/src/macros.jl b/src/macros.jl index c6c8df9b358..11f778e5a69 100644 --- a/src/macros.jl +++ b/src/macros.jl @@ -760,8 +760,8 @@ julia> b = [1 2 3 4]; julia> cref = @SDconstraint(model, a ⪰ b) - x - 1 2 x - 2 - -3 x - 4 ∈ PSDCone() +[x - 1 2 x - 2; + -3 x - 4 ] ∈ PSDCone() julia> jump_function(constraint_object(cref)) 4-element Array{GenericAffExpr{Float64,VariableRef},1}: diff --git a/src/sd.jl b/src/sd.jl index e81d3618d11..b5854471d64 100644 --- a/src/sd.jl +++ b/src/sd.jl @@ -25,8 +25,8 @@ julia> b = [1 2 2 4]; julia> cref = @SDconstraint(model, a ⪰ b) - x - 1 2 x - 2 - 2 x - 2 x - 4 ∈ PSDCone() +[x - 1 2 x - 2; + 2 x - 2 x - 4 ] ∈ PSDCone() julia> jump_function(constraint_object(cref)) 4-element Array{GenericAffExpr{Float64,VariableRef},1}: @@ -45,8 +45,8 @@ matrix is constrained to belong to the `PositiveSemidefiniteConeSquare`. julia> using LinearAlgebra # For Symmetric julia> cref = @constraint(model, Symmetric(a - b) in PSDCone()) - x - 1 2 x - 2 - 2 x - 2 x - 4 ∈ PSDCone() +[x - 1 2 x - 2; + 2 x - 2 x - 4 ] ∈ PSDCone() julia> jump_function(constraint_object(cref)) 3-element Array{GenericAffExpr{Float64,VariableRef},1}: