From ec9f4847625bf857dc339ea63185599b08d19551 Mon Sep 17 00:00:00 2001 From: Eric Hanson <5846501+ericphanson@users.noreply.github.com> Date: Tue, 14 May 2024 22:39:55 +0200 Subject: [PATCH 1/7] deprecate `+` on constraints --- .../examples/general_examples/basic_usage.jl | 5 ++- .../general_examples/chebyshev_center.jl | 12 ++++--- docs/src/examples/mixed_integer/n_queens.jl | 8 ++--- .../povm_simulation.jl | 26 +++++++++++---- src/deprecations.jl | 30 +++++++++++++++++ src/problem_depot/problems/affine.jl | 6 +--- src/problem_depot/problems/socp.jl | 3 +- src/problems.jl | 33 ++++++++----------- 8 files changed, 77 insertions(+), 46 deletions(-) diff --git a/docs/src/examples/general_examples/basic_usage.jl b/docs/src/examples/general_examples/basic_usage.jl index 503e3bd09..e47dd0630 100644 --- a/docs/src/examples/general_examples/basic_usage.jl +++ b/docs/src/examples/general_examples/basic_usage.jl @@ -22,9 +22,8 @@ x = Variable(4) c = [1; 2; 3; 4] A = I(4) b = [10; 10; 10; 10] -p = minimize(dot(c, x)) # or c' * x -p.constraints += A * x <= b -p.constraints += [x >= 1; x <= 10; x[2] <= 5; x[1] + x[4] - x[2] <= 10] +constraints = [A * x <= b, x >= 1, x <= 10, x[2] <= 5, x[1] + x[4] - x[2] <= 10] +p = minimize(dot(c, x), constraints) # or c' * x solve!(p, SCS.Optimizer; silent_solver = true) println(round(p.optval, digits = 2)) diff --git a/docs/src/examples/general_examples/chebyshev_center.jl b/docs/src/examples/general_examples/chebyshev_center.jl index b7897021e..6bdc328b3 100644 --- a/docs/src/examples/general_examples/chebyshev_center.jl +++ b/docs/src/examples/general_examples/chebyshev_center.jl @@ -22,11 +22,13 @@ b = ones(4, 1); # Create and solve the model r = Variable(1) x_c = Variable(2) -p = maximize(r) -p.constraints += a1' * x_c + r * norm(a1, 2) <= b[1]; -p.constraints += a2' * x_c + r * norm(a2, 2) <= b[2]; -p.constraints += a3' * x_c + r * norm(a3, 2) <= b[3]; -p.constraints += a4' * x_c + r * norm(a4, 2) <= b[4]; +constraints = [ + a1' * x_c + r * norm(a1, 2) <= b[1], + a2' * x_c + r * norm(a2, 2) <= b[2], + a3' * x_c + r * norm(a3, 2) <= b[3], + a4' * x_c + r * norm(a4, 2) <= b[4], +] +p = maximize(r, constraints) solve!(p, SCS.Optimizer; silent_solver = true) p.optval diff --git a/docs/src/examples/mixed_integer/n_queens.jl b/docs/src/examples/mixed_integer/n_queens.jl index 0b6e49662..58dd92f1e 100644 --- a/docs/src/examples/mixed_integer/n_queens.jl +++ b/docs/src/examples/mixed_integer/n_queens.jl @@ -10,12 +10,12 @@ x = Variable((n, n), BinVar) # Now we impose the constraints: at most one queen on any anti-diagonal, at most one queen on any diagonal, and we must have exactly one queen per row and per column. ## At most one queen on any anti-diagonal -constr = Constraint[sum(antidiag(x, k)) <= 1 for k in -n+2:n-2] +constraints = Constraint[sum(antidiag(x, k)) <= 1 for k in -n+2:n-2] ## At most one queen on any diagonal -constr += Constraint[sum(diag(x, k)) <= 1 for k in -n+2:n-2] +append!(constraints, [sum(diag(x, k)) <= 1 for k in -n+2:n-2]) ## Exactly one queen per row and one queen per column -constr += Constraint[sum(x, dims = 1)==1, sum(x, dims = 2)==1] -p = satisfy(constr) +append!(constraints, [sum(x, dims = 1) == 1, sum(x, dims = 2) == 1]) +p = satisfy(constraints) solve!(p, GLPK.Optimizer) # Let us test the results: diff --git a/docs/src/examples/optimization_with_complex_variables/povm_simulation.jl b/docs/src/examples/optimization_with_complex_variables/povm_simulation.jl index 5e3c3ac73..d8485bd51 100644 --- a/docs/src/examples/optimization_with_complex_variables/povm_simulation.jl +++ b/docs/src/examples/optimization_with_complex_variables/povm_simulation.jl @@ -43,13 +43,25 @@ function get_visibility(K) q = Variable(6, Positive()) t = Variable(1, Positive()) constraints = [isposdef(P[i][j]) for i in 1:6 for j in 1:2] - constraints += sum(q) == 1 - constraints += t <= 1 - constraints += [P[i][1] + P[i][2] == q[i] * I(2) for i in 1:6] - constraints += t * K[1] + (1 - t) * noise[1] == P[1][1] + P[2][1] + P[3][1] - constraints += t * K[2] + (1 - t) * noise[2] == P[1][2] + P[4][1] + P[5][1] - constraints += t * K[3] + (1 - t) * noise[3] == P[2][2] + P[4][2] + P[6][1] - constraints += t * K[4] + (1 - t) * noise[4] == P[3][2] + P[5][2] + P[6][2] + push!(constraints, sum(q) == 1) + push!(constraints, t <= 1) + push!(constraints, [P[i][1] + P[i][2] == q[i] * I(2) for i in 1:6]) + push!( + constraints, + t * K[1] + (1 - t) * noise[1] == P[1][1] + P[2][1] + P[3][1], + ) + push!( + constraints, + t * K[2] + (1 - t) * noise[2] == P[1][2] + P[4][1] + P[5][1], + ) + push!( + constraints, + t * K[3] + (1 - t) * noise[3] == P[2][2] + P[4][2] + P[6][1], + ) + push!( + constraints, + t * K[4] + (1 - t) * noise[4] == P[3][2] + P[5][2] + P[6][2], + ) p = maximize(t, constraints) solve!(p, SCS.Optimizer; silent_solver = true) return p.optval diff --git a/src/deprecations.jl b/src/deprecations.jl index 267a152b2..eb8022161 100644 --- a/src/deprecations.jl +++ b/src/deprecations.jl @@ -123,3 +123,33 @@ end function ComplexVariable(set::Symbol, sets::Symbol...) return ComplexVariable((1, 1), set, sets...) end + +# `+` on constraints +function warn_deprecated_constraint_concatenation() + @warn( + "Concatenating collections of constraints together with `+` or `+=` to produce a new list of constraints is deprecated. Instead, use `vcat` to concatenate collections of constraints.", + maxlog = 1 + ) +end +function Base.:+(x::Array{<:Constraint}, y::Array{<:Constraint}) + warn_deprecated_constraint_concatenation() + return vcat(x, y) +end + +function Base.:+(x::Constraint, y::Constraint) + @warn( + "Adding constraints together (with `+` or `+=`) to produce a list of constraints is deprecated. Instead, construct a list of constraints via `[constraint1, constraint2]`", + maxlog = 1 + ) + return [x, y] +end + +function Base.:+(x::Constraint, y::Array{<:Constraint}) + warn_deprecated_constraint_concatenation() + return vcat(x, y) +end + +function Base.:+(x::Array{<:Constraint}, y::Constraint) + warn_deprecated_constraint_concatenation() + return vcat(x, y) +end diff --git a/src/problem_depot/problems/affine.jl b/src/problem_depot/problems/affine.jl index bf47e391d..6ba920ca6 100644 --- a/src/problem_depot/problems/affine.jl +++ b/src/problem_depot/problems/affine.jl @@ -754,11 +754,7 @@ end @test p.status == MOI.OPTIMAL end - constr = x >= 0 - constr += x >= 1 - constr += x <= 10 - constr2 = x >= 0 - constr2 += [x >= 2, x <= 3] + constr + constr = [x >= 0, x >= 1, x <= 10] p = satisfy(constr; numeric_type = T) handle_problem!(p) diff --git a/src/problem_depot/problems/socp.jl b/src/problem_depot/problems/socp.jl index f1d05ef77..e7a13c253 100644 --- a/src/problem_depot/problems/socp.jl +++ b/src/problem_depot/problems/socp.jl @@ -476,9 +476,8 @@ end x = Variable(5) q = 1.379 # q norm constraint that generates many inequalities qs = q / (q - 1) # Conjugate to q - p = minimize(x' * v; numeric_type = T) + p = minimize(x' * v, norm(x, q) <= 1; numeric_type = T) - p.constraints += (norm(x, q) <= 1) if test @test problem_vexity(p) == ConvexVexity() end diff --git a/src/problems.jl b/src/problems.jl index fb6eb82b0..501d2cfec 100644 --- a/src/problems.jl +++ b/src/problems.jl @@ -197,7 +197,7 @@ function Problem{T}( constraint::Constraint, constraints::Constraint..., ) where {T<:Real} - return Problem{T}(head, objective, [constraint, constraints...]) + return Problem{T}(head, objective, Constraint[constraint, constraints...]) end # Allow users to simply type minimize @@ -206,12 +206,16 @@ function minimize( constraints::Constraint...; numeric_type = Float64, ) - return Problem{numeric_type}(:minimize, objective, collect(constraints)) + return Problem{numeric_type}( + :minimize, + objective, + collect(Constraint, constraints), + ) end function minimize( objective::AbstractExpr, - constraints::Array{<:Constraint} = Constraint[]; + constraints = Constraint[]; numeric_type = Float64, ) return Problem{numeric_type}(:minimize, objective, constraints) @@ -227,7 +231,7 @@ end function minimize( objective::Value, - constraints::Array{<:Constraint} = Constraint[]; + constraints = Constraint[]; numeric_type = Float64, ) return minimize(constant(objective), constraints; numeric_type) @@ -244,7 +248,7 @@ end function maximize( objective::AbstractExpr, - constraints::Array{<:Constraint} = Constraint[]; + constraints = Constraint[]; numeric_type = Float64, ) return Problem{numeric_type}(:maximize, objective, constraints) @@ -260,7 +264,7 @@ end function maximize( objective::Value, - constraints::Array{<:Constraint} = Constraint[]; + constraints = Constraint[]; numeric_type = Float64, ) return maximize(constant(objective), constraints; numeric_type) @@ -271,10 +275,7 @@ function satisfy(constraints::Constraint...; numeric_type = Float64) return Problem{numeric_type}(:satisfy, nothing, [constraints...]) end -function satisfy( - constraints::Array{<:Constraint} = Constraint[]; - numeric_type = Float64, -) +function satisfy(constraints = Constraint[]; numeric_type = Float64) return Problem{numeric_type}(:satisfy, nothing, constraints) end @@ -282,7 +283,7 @@ function satisfy(constraint::Constraint; numeric_type = Float64) return satisfy([constraint]; numeric_type = numeric_type) end -function add_constraints!(p::Problem, constraints::Array{<:Constraint}) +function add_constraints!(p::Problem, constraints) return append!(p.constraints, constraints) end @@ -290,7 +291,7 @@ function add_constraints!(p::Problem, constraint::Constraint) return add_constraints!(p, [constraint]) end -function add_constraint!(p::Problem, constraints::Array{<:Constraint}) +function add_constraint!(p::Problem, constraints) return add_constraints!(p, constraints) end @@ -298,14 +299,6 @@ function add_constraint!(p::Problem, constraint::Constraint) return add_constraints!(p, constraint) end -Base.:+(x::Array{<:Constraint}, y::Array{<:Constraint}) = vcat(x, y) - -Base.:+(x::Constraint, y::Constraint) = [x, y] - -Base.:+(x::Constraint, y::Array{<:Constraint}) = vcat(x, y) - -Base.:+(x::Array{<:Constraint}, y::Constraint) = vcat(x, y) - iscomplex(c::Constraint) = iscomplex(c.lhs) || iscomplex(c.rhs) function add_constraint!(context::Context, c::Constraint) From 2034ff9a6f2c2b580fb433f0178b8771a0a419f7 Mon Sep 17 00:00:00 2001 From: Eric Hanson <5846501+ericphanson@users.noreply.github.com> Date: Tue, 14 May 2024 22:51:05 +0200 Subject: [PATCH 2/7] add test, fix matrix case --- src/problems.jl | 8 ++++---- test/test_utilities.jl | 13 +++++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/problems.jl b/src/problems.jl index 501d2cfec..376f00934 100644 --- a/src/problems.jl +++ b/src/problems.jl @@ -6,13 +6,13 @@ mutable struct Problem{T<:Real} <: AbstractExpr head::Symbol objective::Union{AbstractExpr,Nothing} - constraints::Array{Constraint} + constraints::Vector{Constraint} status::MOI.TerminationStatusCode model::Union{MOI.ModelLike,Nothing} function Problem{T}( head::Symbol, objective::Union{AbstractExpr,Nothing}, - constraints::Array = Constraint[], + constraints = Constraint[], ) where {T<:Real} if objective !== nothing && sign(objective) == Convex.ComplexSign() error("Objective cannot be a complex expression") @@ -20,7 +20,7 @@ mutable struct Problem{T<:Real} <: AbstractExpr return new( head, objective, - constraints, + vec(constraints), MOI.OPTIMIZE_NOT_CALLED, nothing, ) @@ -272,7 +272,7 @@ end # Allow users to simply type satisfy (if there is no objective) function satisfy(constraints::Constraint...; numeric_type = Float64) - return Problem{numeric_type}(:satisfy, nothing, [constraints...]) + return Problem{numeric_type}(:satisfy, nothing, Constraint[constraints...]) end function satisfy(constraints = Constraint[]; numeric_type = Float64) diff --git a/test/test_utilities.jl b/test/test_utilities.jl index c096afec6..2239ef1c9 100644 --- a/test/test_utilities.jl +++ b/test/test_utilities.jl @@ -1118,6 +1118,19 @@ function test_dcp_rules() return end +function test_problem_untyped_constraints() + x = Variable(1, Positive()) + # Test untyped constraints + p = maximize(x, Any[x<=1]) + @test p isa Problem + p = maximize(x, Any[x <= 1;; x <= 1]) + @test p isa Problem + p = minimize(x, Any[x<=1]) + @test p isa Problem + p = minimize(x, Any[x <= 1;; x <= 1]) + @test p isa Problem +end + function test_problem_maximize() x = Variable(1, Positive()) p = maximize(exp(x), x <= 1) From 67e8e908f565b64718fe779fa8d422f4ba84fb30 Mon Sep 17 00:00:00 2001 From: Eric Hanson <5846501+ericphanson@users.noreply.github.com> Date: Tue, 14 May 2024 23:04:17 +0200 Subject: [PATCH 3/7] fix example --- .../optimization_with_complex_variables/povm_simulation.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/examples/optimization_with_complex_variables/povm_simulation.jl b/docs/src/examples/optimization_with_complex_variables/povm_simulation.jl index d8485bd51..80266cb98 100644 --- a/docs/src/examples/optimization_with_complex_variables/povm_simulation.jl +++ b/docs/src/examples/optimization_with_complex_variables/povm_simulation.jl @@ -42,7 +42,7 @@ function get_visibility(K) P = [[ComplexVariable(2, 2) for i in 1:2] for j in 1:6] q = Variable(6, Positive()) t = Variable(1, Positive()) - constraints = [isposdef(P[i][j]) for i in 1:6 for j in 1:2] + constraints = Constraint[isposdef(P[i][j]) for i in 1:6 for j in 1:2] push!(constraints, sum(q) == 1) push!(constraints, t <= 1) push!(constraints, [P[i][1] + P[i][2] == q[i] * I(2) for i in 1:6]) From 6512493df4e0b97e424d1082d81c9a72d34f9cdd Mon Sep 17 00:00:00 2001 From: Eric Hanson <5846501+ericphanson@users.noreply.github.com> Date: Tue, 14 May 2024 23:08:34 +0200 Subject: [PATCH 4/7] Update test/test_utilities.jl Co-authored-by: Oscar Dowson --- test/test_utilities.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_utilities.jl b/test/test_utilities.jl index 2239ef1c9..24e155bfc 100644 --- a/test/test_utilities.jl +++ b/test/test_utilities.jl @@ -1129,6 +1129,7 @@ function test_problem_untyped_constraints() @test p isa Problem p = minimize(x, Any[x <= 1;; x <= 1]) @test p isa Problem + return end function test_problem_maximize() From 2f1e9360e5eaff2a7372609f0109f74cc6d05dad Mon Sep 17 00:00:00 2001 From: Eric Hanson <5846501+ericphanson@users.noreply.github.com> Date: Tue, 14 May 2024 23:16:53 +0200 Subject: [PATCH 5/7] add test --- test/test_utilities.jl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/test_utilities.jl b/test/test_utilities.jl index 24e155bfc..99bade733 100644 --- a/test/test_utilities.jl +++ b/test/test_utilities.jl @@ -1008,6 +1008,16 @@ function test_deprecation_in_symbol() return end +function test_deprecation_adding_constraints() + x = Variable() + c = x == x + @test_logs (:warn, r"^Adding") c + c + @test_logs (:warn, r"^Concatenating") [c] + c + @test_logs (:warn, r"^Concatenating") c + [c] + @test_logs (:warn, r"^Concatenating") [c] + [c] + return +end + function test_dcp_rules() vexities = ( Convex.ConcaveVexity(), From 880b5983c62a11d61bebebb50331669691424850 Mon Sep 17 00:00:00 2001 From: Eric Hanson <5846501+ericphanson@users.noreply.github.com> Date: Tue, 14 May 2024 23:17:30 +0200 Subject: [PATCH 6/7] Apply suggestions from code review Co-authored-by: Oscar Dowson --- src/deprecations.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/deprecations.jl b/src/deprecations.jl index eb8022161..513cbe2bd 100644 --- a/src/deprecations.jl +++ b/src/deprecations.jl @@ -130,7 +130,9 @@ function warn_deprecated_constraint_concatenation() "Concatenating collections of constraints together with `+` or `+=` to produce a new list of constraints is deprecated. Instead, use `vcat` to concatenate collections of constraints.", maxlog = 1 ) + return end + function Base.:+(x::Array{<:Constraint}, y::Array{<:Constraint}) warn_deprecated_constraint_concatenation() return vcat(x, y) From 90016a6ebcf2b1e5abd3243806c70d9fa26cbb92 Mon Sep 17 00:00:00 2001 From: Eric Hanson <5846501+ericphanson@users.noreply.github.com> Date: Tue, 14 May 2024 23:32:04 +0200 Subject: [PATCH 7/7] Update docs/src/examples/optimization_with_complex_variables/povm_simulation.jl --- .../optimization_with_complex_variables/povm_simulation.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/examples/optimization_with_complex_variables/povm_simulation.jl b/docs/src/examples/optimization_with_complex_variables/povm_simulation.jl index 80266cb98..550c1a314 100644 --- a/docs/src/examples/optimization_with_complex_variables/povm_simulation.jl +++ b/docs/src/examples/optimization_with_complex_variables/povm_simulation.jl @@ -45,7 +45,7 @@ function get_visibility(K) constraints = Constraint[isposdef(P[i][j]) for i in 1:6 for j in 1:2] push!(constraints, sum(q) == 1) push!(constraints, t <= 1) - push!(constraints, [P[i][1] + P[i][2] == q[i] * I(2) for i in 1:6]) + append!(constraints, [P[i][1] + P[i][2] == q[i] * I(2) for i in 1:6]) push!( constraints, t * K[1] + (1 - t) * noise[1] == P[1][1] + P[2][1] + P[3][1],