-
-
Notifications
You must be signed in to change notification settings - Fork 395
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Set inequality syntax suggestion #3770
Comments
@odow thank you for posting this for disscussion! My main concern is syntax divergence. With the current behavior, I agree that this is very much not a clear-cut choice, |
My answer to:
is "yes, because of the ambiguity that users will experience, particularly those coming from YALMIP, where There is no ambiguity for scalars and vectors. |
Ah, i did not know that. But then, is it not reasonable to expect them to read the documentation? |
I think |
Copying more of #3769 (comment) here, for clarity:
I.e. if you use >=/<=, and don't specify a set, or specify a non-conic set, you get vector behavior, So really, the only thing one needs to remember is to use |
Experience has told me: yes. Especially for novice users who are most at risk of making this mistake. (Not to pick on one example, but here is a recent one that I remember: https://discourse.julialang.org/t/lmi-optimization-problem/112218. They ended up trying I'm okay adding the |
What do you mean by |
This discussion is reminiscent of JuliaLang/julia#44358 where a breaking change in Base Julia was made to avoid element vs. vector division confusion. |
I specifically mean
I only mean
Right, though here the behavior would be consistent (and in fact, |
Thinking on this over the last few days, I don't even particularly want to add What we currently have is Pareto optimal on a set of tradeoffs. We could make it better for some people at the expense of others, and it isn't obvious that moving one particular way is preferable. |
Oh come on... "
Perhaps i'm missing some context that was communicated somewhere elsewhere, From where i'm sitting, i wouldn't expect those who have previously encountered
As someone who had to deal with users before, to me that tells that even with the current behavior |
The current design is pareto optimal with respect to the readability of models. If you see
Yes, or use JuMP again after some time so don't read the doc again, or know the distinction but made a mistake by using the wrong unicode character and don't notice it because you need to zoom in to see the difference. With the cone specified (including |
Okay, let's backtrack. My syntax complaint is: it seems like having to manually go between the Introduce a 'phony' JuMP set,
Hopefully surely that is a reasonable acceptable middle ground solution? :) |
First, note that if you want element wise inequality, you have always been able to write: @constraint(model, x .<= y) This is perhaps the simplest and most explicit way of writing it down. The only downside is that in the symmetric matrix case, it does not exploit symmetry. (But perhaps we could make it.)
@constraint(model, x <= y, Nonnegatives())
=>
@constraint(model, y - x in Nonnegatives()) Because of the slight mis-match, we currently error: We can't do this because it breaks existing syntax: @constraint(model, x <= y, PSDCone)
# error: use `>=` |
I'm very aware of that, yes.
Yes, that might be good. It seems not optimal to create duplicate constraints,
I'm getting increasingly more and more confused.
I'm afraid i don't quite follow. Firstly, it does not break existing syntax,
secondly, i'm specifically asking about It doesn't break syntax for Again, i don't actually care how exactly Perhaps i'm on the wrong end of the bell curve and missing a few decades of LP expirience, |
Let's make this discussion more concrete. What we currently supportHere is what we currently support. Vectorsjulia> model = Model();
julia> @variable(model, x[1:2])
2-element Vector{VariableRef}:
x[1]
x[2]
julia> y = [1, 2]
2-element Vector{Int64}:
1
2
julia> @constraint(model, x .>= y)
2-element Vector{ConstraintRef{Model, MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.GreaterThan{Float64}}, ScalarShape}}:
x[1] ≥ 1
x[2] ≥ 2
julia> @constraint(model, x .<= y)
2-element Vector{ConstraintRef{Model, MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.LessThan{Float64}}, ScalarShape}}:
x[1] ≤ 1
x[2] ≤ 2
julia> @constraint(model, x >= y)
[x[1] - 1, x[2] - 2] ∈ MathOptInterface.Nonnegatives(2)
julia> @constraint(model, x <= y)
[x[1] - 1, x[2] - 2] ∈ MathOptInterface.Nonpositives(2)
julia> @constraint(model, x >= y, SecondOrderCone())
[x[1] - 1, x[2] - 2] ∈ MathOptInterface.SecondOrderCone(2)
julia> @constraint(model, x <= y, SecondOrderCone())
[-x[1] + 1, -x[2] + 2] ∈ MathOptInterface.SecondOrderCone(2) We don't currently support julia> @constraint(model, x <= y, Nonnegatives())
ERROR: At REPL[21]:1: `@constraint(model, x <= y, Nonnegatives())`: Unrecognized constraint building format. Tried to invoke `build_constraint(error, AffExpr[x[1] - 1, x[2] - 2], MathOptInterface.LessThan{Bool}(false), Nonnegatives())`, but no such method exists. This is due to specifying an unrecognized function, constraint set, and/or extra positional/keyword arguments.
[...]
julia> @constraint(model, x >= y, Nonnegatives())
ERROR: At REPL[22]:1: `@constraint(model, x >= y, Nonnegatives())`: Unrecognized constraint building format. Tried to invoke `build_constraint(error, AffExpr[x[1] - 1, x[2] - 2], MathOptInterface.GreaterThan{Bool}(false), Nonnegatives())`, but no such method exists. This is due to specifying an unrecognized function, constraint set, and/or extra positional/keyword arguments.
[...] Matricesjulia> model = Model();
julia> @variable(model, x[1:2, 1:2])
2×2 Matrix{VariableRef}:
x[1,1] x[1,2]
x[2,1] x[2,2]
julia> y = [1 2; 2 3]
2×2 Matrix{Int64}:
1 2
2 3
julia> @constraint(model, x .>= y)
2×2 Matrix{ConstraintRef{Model, MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.GreaterThan{Float64}}, ScalarShape}}:
x[1,1] ≥ 1 x[1,2] ≥ 2
x[2,1] ≥ 2 x[2,2] ≥ 3
julia> @constraint(model, x .<= y)
2×2 Matrix{ConstraintRef{Model, MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.LessThan{Float64}}, ScalarShape}}:
x[1,1] ≤ 1 x[1,2] ≤ 2
x[2,1] ≤ 2 x[2,2] ≤ 3
julia> @constraint(model, x >= y, PSDCone())
[x[1,1] - 1 x[1,2] - 2;
x[2,1] - 2 x[2,2] - 3] ∈ PSDCone()
julia> @constraint(model, x <= y, PSDCone())
[-x[1,1] + 1 -x[1,2] + 2;
-x[2,1] + 2 -x[2,2] + 3] ∈ PSDCone() We don't support no cone or julia> @constraint(model, x >= y)
ERROR: At REPL[32]:1: `@constraint(model, x >= y)`: Unsupported matrix in vector-valued set. Did you mean to use the broadcasting syntax `.>=` instead? Alternatively, perhaps you are missing a set argument like `@constraint(model, X >= 0, PSDCone())` or `@constraint(model, X >= 0, HermmitianPSDCone())`.
Stacktrace:
[...]
julia> @constraint(model, x <= y)
ERROR: At REPL[33]:1: `@constraint(model, x <= y)`: Unsupported matrix in vector-valued set. Did you mean to use the broadcasting syntax `.<=` instead? Alternatively, perhaps you are missing a set argument like `@constraint(model, X <= 0, PSDCone())` or `@constraint(model, X <= 0, HermmitianPSDCone())`.
Stacktrace:
[...]
julia> @constraint(model, x >= y, Nonnegatives())
ERROR: At REPL[34]:1: `@constraint(model, x >= y, Nonnegatives())`: Unrecognized constraint building format. Tried to invoke `build_constraint(error, AffExpr[x[1,1] - 1 x[1,2] - 2; x[2,1] - 2 x[2,2] - 3], MathOptInterface.GreaterThan{Bool}(false), Nonnegatives())`, but no such method exists. This is due to specifying an unrecognized function, constraint set, and/or extra positional/keyword arguments.
[...] PR3766This PR will enable: julia> model = Model();
julia> @variable(model, x[1:2, 1:2])
2×2 Matrix{VariableRef}:
x[1,1] x[1,2]
x[2,1] x[2,2]
julia> y = [1 2; 2 3]
2×2 Matrix{Int64}:
1 2
2 3
julia> @constraint(model, x >= y, Nonnegatives())
[x[1,1] - 1 x[1,2] - 2
x[2,1] - 2 x[2,2] - 3] ∈ Nonnegatives()
julia> @constraint(model, x >= y, Nonpositives())
[x[1,1] - 1 x[1,2] - 2
x[2,1] - 2 x[2,2] - 3] ∈ Nonpositives() But it does not support: julia> @constraint(model, x <= y, Nonnegatives())
ERROR: At REPL[7]:1: `@constraint(model, x <= y, Nonnegatives())`: The syntax `x <= y, Nonnegatives()` not supported. Use `y >= x, Nonnegatives()` instead.
Stacktrace:
[...] This is because the user probably wants Alternatively, like the current Your askLet's leave aside the question over the symbols You're asking for julia> @constraint(model, x >= y, ElementwiseComparison()) to be the same as julia> @constraint(model, x <= y, ElementwiseComparison()) to mean Adding TradeoffsThe current design is bad for someone who wants to write: @constraint(model, x <= y) to mean Their current alternatives are: @constraint(model, x .<= y)
@constraint(model, y >= x, Nonnegatives()) We should probably also add: @constraint(model, x - y in Nonpositives()) # TBD Edit: oh, we can't add this because then the default AlternativesPerhaps this issue is really just asking for an efficient Then there is no confusion anywhere. If we do this, we should close #3766 without merging. I'll take a look at the details. Confusions
You suggested the example of erroring if
Please assume that everyone in the JuMP community engages with the best positive |
Okay. I remember why julia> using JuMP, LinearAlgebra
julia> model = Model();
julia> @variable(model, x[1:2, 1:2], Symmetric)
2×2 Symmetric{VariableRef, Matrix{VariableRef}}:
x[1,1] x[1,2]
x[1,2] x[2,2]
julia> x .- 1
2×2 Matrix{AffExpr}:
x[1,1] - 1 x[1,2] - 1
x[1,2] - 1 x[2,2] - 1
julia> y = LinearAlgebra.Symmetric([1 2; 2 3])
2×2 Symmetric{Int64, Matrix{Int64}}:
1 2
2 3
julia> y .- 1
2×2 Matrix{Int64}:
0 1
1 2 |
Thank you.
This does match my understanding of the preexisting situation.
Makes sense to me. It's an improvement on it's own already i think.
YES! Extra cognitive load + confusion + much larger room for error, compared to the the non-matrix cases.
No comment.
In other words,
Aha! But it is still a bit not clear to me what is the problem with that, I haven't tried, but i would expect that such a set would require a very few changes,
Yes.
It does not, not really. Though such an improvement could be interesting. There is an edge case though: what about
I was, in reality, thinking about (not necessarily symmetric) matrix support
Err, that's not exactly correct. I did suggest that, but iff
:) Right. I'm just saying that it was really NOT obvious to me if we were even understanding each other, |
How is the existing |
I suppose maybe it's not, other than stylistic preferences and differences in As i've said, it would be nice if matrix symmetry would be better respected in different constraint syntaxes, |
I have added this to the agenda of this week's monthly developer call. |
Yes, I agree making |
Developer call says:
Closing this issue as won't-fix. |
Thanks.
Does this take into the account all of the lengthy |
We already have element-wise comparison with the |
Is there an upstream issue tracking that bug? #3770 (comment) made it sound like it's improbable that will be fixed. |
JuliaLang/julia#38056 / JuliaLang/julia#19228 seem somewhat relevant,
|
Just for completeness, copying from jump-dev/MutableArithmetics.jl#295 (comment): |
Given
This is notably NOT what you probably expected to happen, which is for broadcast to ignore the singleton dimension and return a size |
See #3769 (comment)
Problem
Consider:
Does this mean:
or
Because of this ambiguity, we currently disallow the
x >= 0
syntax for matrix sets.Suggestion
@LebedevRI suggests that we introduce the
⪰
operator with the following behavior:Examples
Tradeoffs
The biggest pro is that it clarifies the distinction between
≥
and⪰
.This is also the biggest con: I worry that users will not make the distinction, and people will write
x >= 0
expecting that to be a PSD constraint when it is really a nonnegative constraint.Currenntly, they need to opt-in by passing a set to avoid the ambiguity.
The text was updated successfully, but these errors were encountered: