-
-
Notifications
You must be signed in to change notification settings - Fork 396
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
Add extension points for more generic @constraint
syntax
#2051
Conversation
f994a60
to
c3099f9
Compare
@blegat I did as you suggested (at least, I hope so…), which yielded a simpler implementation. Not all tests still pass, but quite a few of them do (issues with bridging). |
9e31bc9
to
9fc76c4
Compare
Most of the tests pass now, except one for the So, for now, this PR allows to write JuMP extensions to allow global constraints like |
@constraint
without relational operator
As I'll not be able to attend the developers call and currently can't check this as I'm on my phone: is it already possible to write a not equal constraint with this/JuMP? |
@dourouc05 In #2132, I was able to make |
@blegat thanks for your ping. However, this case is slightly different: for many constraints, you have notions similar to a left-hand and a right-hand sides.
Here, there is just one set of expressions, not two. An easy modification would be to force the user to just have function parse_constraint(_error::Function, sense::Symbol, F)
(sense, vectorized) = _check_vectorized(sense)
vectorized, parse_one_operator_constraint(_error, vectorized, Val(sense), F)...
end I made a new patch to still allow syntax like Here are some MOI sets that would be needed in complement to this PR: dourouc05/MathOptInterface.jl@df85074 |
054067d
to
29a47c2
Compare
Codecov Report
@@ Coverage Diff @@
## master #2051 +/- ##
==========================================
+ Coverage 91.16% 91.30% +0.13%
==========================================
Files 42 42
Lines 4165 4186 +21
==========================================
+ Hits 3797 3822 +25
+ Misses 368 364 -4
Continue to review full report at Codecov.
|
931340a
to
281cdf7
Compare
@blegat I made your solution work, it is in the current version of this PR. I'm not sure that With this, I have the majority of the extension points I need, with very little code in JuMP. It would be nice to allow a syntax like |
src/macros.jl
Outdated
end | ||
|
||
function parse_constraint(_error::Function, ::Val{:call}, args...) | ||
return parse_call_constraint(_error, Val(args[1]), args[2:end]...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would rather have this call parse_constraint(_error, args...)
to avoid breaking existing code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How can you then dispatch on the function being called (i.e. args[1]
)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can just keep the existing parse_constraint(_error::Function, sense::Symbol, lhs, rhs)
, the call will be dispatched to it, isn't it ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I skipped your comment… I don't see how Julia could dispatch on a symbol without Val. Or would you rather have this function called parse_constraint
? Wouldn't this be enough to preserve existing code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is dispatched in parse_one_operator_constraint
as it is currently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the new version what you meant?
(The tests don't pass locally with this commit, although all errors are related to MutableArithmetics.jl… I guess this is alright?)
e481b15
to
dc77f32
Compare
Actually, I'm starting to get lost in the implications of various parts of this PR… For now, I think that #2228 and #2229 would cover all this original PR was about. The next step would be to allow to rewrite more expressions, to allow things like |
9fd9877
to
597a02c
Compare
Add support for reification. Things moved to https://github.com/dourouc05/JuCP.jl Update macros.jl Pass the same type of argument to the different calls to parsefun. Restore two definitions of parse_constraint to avoid ambiguity. LoadError: LoadError: MethodError: parse_constraint(::JuMP.var"#_error#70"{Symbol}, ::Symbol, ::Symbol, ::Expr, ::Symbol, ::Symbol) is ambiguous. Candidates: parse_constraint(_error::Function, sense::Symbol, args...) in JuMP at C:\Users\Thibaut\.julia\dev\JuMP\src\macros.jl:180 parse_constraint(_error::Function, lb, lsign::Symbol, aff, rsign::Symbol, ub) in JuMP at C:\Users\Thibaut\.julia\dev\JuMP\src\macros.jl:205 Implement more closely @blegat's suggestion. Also adapt build_constraint. Also adapt indicator constraints. Also adapt SD constraint. For :call constraint, dispatch on the first argument. Clean up. Make tests pass. Useless extension point. Allow rewriting the rhs of a comparison constraint. Bug fixing (due to MA?). Don't break user code. If the constraint looks like `== f(x)`, whatever f or x, the previous code would always think the function f must be known by JuMP. Reduce diff size. @blegat's comments. Simplify PR wrt jump-dev#2228. Simplify PR wrt jump-dev#2228. Clean PR wrt jump-dev#2229.
597a02c
to
1e07538
Compare
I've just rewritten the PR to bring back the functionality of having constraints with just a function (i.e. point 2 for @blegat's message), now with a test case. |
All PRs for new syntax (this one and #2229) have been merged in: https://github.com/dourouc05/JuMP.jl/tree/constraints |
test/macros.jl
Outdated
@@ -176,6 +176,20 @@ function custom_expression_test(ModelType::Type{<:JuMP.AbstractModel}) | |||
end | |||
end | |||
|
|||
function JuMP.parse_constraint_call(_error::Function, ::Val{:f}, x) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not JuMP.parse_constraint_head(_error::Function, ::Val{:call}, ::Val{:f}, x)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I remove my changes in src/macros.jl
and adapt the test as you suggest, this is what I get:
ERROR: LoadError: LoadError: LoadError: In `@constraint(model, con_ref, f(x))`: Constraints must be in one of the following forms:
expr1 <= expr2
expr1 >= expr2
expr1 == expr2
lb <= expr <= ub
Stacktrace:
[1] error(::String, ::String) at .\error.jl:42
[2] _macro_error(::Symbol, ::Array{Any,1}, ::String) at C:\Users\Thibaut\.julia\dev\JuMP\src\macros.jl:891
[3] (::JuMP.var"#_error#68"{Symbol})(::String) at C:\Users\Thibaut\.julia\dev\JuMP\src\macros.jl:363
[4] _unknown_constraint_expr(::JuMP.var"#_error#68"{Symbol}) at C:\Users\Thibaut\.julia\dev\JuMP\src\macros.jl:240
[5] parse_constraint(::Function, ::Symbol, ::Symbol) at C:\Users\Thibaut\.julia\dev\JuMP\src\macros.jl:249
[6] parse_constraint_head(::Function, ::Val{:call}, ::Symbol, ::Symbol) at C:\Users\Thibaut\.julia\dev\JuMP\src\macros.jl:184
[7] parse_constraint_expr at C:\Users\Thibaut\.julia\dev\JuMP\src\macros.jl:181 [inlined]
[8] _constraint_macro(::Tuple{Symbol,Symbol,Expr}, ::Symbol, ::typeof(parse_constraint_expr)) at C:\Users\Thibaut\.julia\dev\JuMP\src\macros.jl:404
[9] @constraint(::LineNumberNode, ::Module, ::Vararg{Any,N} where N) at C:\Users\Thibaut\.julia\dev\JuMP\src\macros.jl:487
[10] include(::String) at .\client.jl:439
[11] top-level scope at C:\Users\Thibaut\.julia\dev\JuMP\test\runtests.jl:23
[12] include(::String) at .\client.jl:439
[13] top-level scope at none:6
in expression starting at C:\Users\Thibaut\.julia\dev\JuMP\test\macros.jl:186
in expression starting at C:\Users\Thibaut\.julia\dev\JuMP\test\macros.jl:182
in expression starting at C:\Users\Thibaut\.julia\dev\JuMP\test\runtests.jl:23
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think of the second suggestion in #2236 ?
With this you can add the test with the modification I suggested and we don't need to modify src/macros.jl
(BTW what you are suggesting is a breaking change).
I would suggest using parse_constraint_head
in JuCP, you will just have to rename it to parse_constraint
once we make the breaking change but that shouldn't break JuCP if you specifiy JuMP = "0.21"
in your Project.toml.
src/macros.jl
Outdated
return parse_constraint(_error, Val(args[1]), args[2:end]...) | ||
end | ||
|
||
function parse_constraint(_error::Function, sense::Val, F...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are these two methods needed ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, they are not needed, I've just removed them.
test/macros.jl
Outdated
@test jump_function(con) == x | ||
@test moi_set(con) isa CustomSet | ||
|
||
@test_macro_throws LoadError @constraint(model, g(x)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace LoadError
by ErrorException
. This is why travis is failing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't expect the macro to be so smart! Done in the next commit.
(By the way, why isn't it upstreamed? I guess many Julia projects could benefit from it!)
Prototype for #2014. Comments are welcome! This PR is mostly to enable more things from extensions.