Skip to content
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

Feature Request: Reified constraint #2215

Closed
Wikunia opened this issue Mar 30, 2020 · 14 comments
Closed

Feature Request: Reified constraint #2215

Wikunia opened this issue Mar 30, 2020 · 14 comments

Comments

@Wikunia
Copy link
Contributor

Wikunia commented Mar 30, 2020

As indicator constraints of the form:
@constraint(m, x => {y == 1}) are allowed it would be valuable to have it in the opposite direction:
@constraint(m, {y == 1} => x) and @constraint(m, x <=> {y == 1}).

A solution can be similar to:
https://github.com/JuliaOpt/JuMP.jl/blob/1119b736b00f84ce210fee7ae9232378fb8d6753/src/indicator.jl

Maybe @dourouc05 has thought about something like this as I found:
https://github.com/dourouc05/ConstraintProgrammingExtensions.jl/blob/master/src/ConstraintProgrammingExtensions.jl#L275-L277

@dourouc05
Copy link
Contributor

dourouc05 commented Mar 30, 2020

The difference is that indicator constraints are natively supported by quite a few mathematical-optimisation solvers. This kind of things is more common in constraint-programming solvers (and yes, I'm thinking of including this kind of things, but it's not yet ready for prime-time :)!).

As I understand it, the goal of the base JuMP right now is more to provide access to existing solver features, not really to provide many things on top of that.

@Wikunia
Copy link
Contributor Author

Wikunia commented Mar 30, 2020

Oh found that you mentioned it in #2014
This would be interesting for https://github.com/Wikunia/ConstraintSolver.jl/
Otherwise I can extend JuMP inside my package in until it is supported by JuMP directly, correct?

@Wikunia
Copy link
Contributor Author

Wikunia commented Mar 30, 2020

As I understand it, the goal of the base JuMP right now is more to provide access to existing solver features, not really to provide many things on top of that.

Hope that JuMP is still the way to go for my ConstraintSolver 😄
There are quite a few other problems like expressing something like

a = [2,3,3,1,4]
@variable(m, 1 <= x <= 5, Int)
@variable(m, 1 <= y <= 5, Int)
@constraint(m, x == a[y])

which I'm not sure how to work around in JuMP atm.

@dourouc05
Copy link
Contributor

It's one of the constraints I'm working on, and indeed it's not that simple to implement…

@odow
Copy link
Member

odow commented Mar 30, 2020

New constraints like this are just a matter of defining a set with a formal spec. For example:

"""
    IndexedSet{T}

Defines the set `IndexedSet(data) = {(x, y) | x == data[y]}`.
"""
struct IndexedSet{T} <: MOI.AbstractVectorSet
    data::Vector{T}
end

a = [2,3,3,1,4]
@variable(m, 1 <= x <= 5, Int)
@variable(m, 1 <= y <= 5, Int)
@constraint(m, [x, y] in IndexedSet(a))

@Wikunia
Copy link
Contributor Author

Wikunia commented Mar 30, 2020

The question for this is more how to make a user friendly version of the underlying in Set variant.
Is this possible using parse_constraint or others? @odow
I think the sets are very powerful but often not the easiest to write/read.

@dourouc05
Copy link
Contributor

Depending on how exactly you want the constraint to parse, you may do like https://github.com/dourouc05/JuCP.jl/blob/master/src/sets.jl#L254 or need #2051.

@Wikunia
Copy link
Contributor Author

Wikunia commented Mar 30, 2020

I'm still not getting how parse_constraint is called.
If I have:

function JuMP.parse_constraint(_error::Function, sense::Symbol, F) 
    println(sense)
    println(F)
end

and:

@constraint(m, b := {x[1] <= 2})

and it prints:

b
{x[1] <= 2}

Where is the := 😃 ? Isn't this also where one could use sense_to_set?
Or does := is not a valid sense?
I failed with sense_to_set(::Function, ::Val{:(:=)}) = ReifiedSet(0.0) which doesn't seem to get called.

@dourouc05
Copy link
Contributor

sense_to_set is only used for "simple" constraints like lhs OP rhs, with OP being <=, >=, == in most cases; it only makes sense if you can compute lhs - rhs (which is definitely not your case). You have to provide a full implementation of function JuMP.parse_constraint(_error::Function, ::Val{:(:=)}, F...).

@Wikunia
Copy link
Contributor Author

Wikunia commented Mar 30, 2020

Ah okay thanks for the first part. The:

function JuMP.parse_constraint(_error::Function, ::Val{:(:=)}, F...)

doesn't get called for me I get a:

julia> @constraint(m, b := {y <= 2})
ERROR: LoadError: In `@constraint(m, $(Expr(:(:=), :b, :({y <= 2}))))`: Constraints must be in one of the following forms:
       expr1 <= expr2
       expr1 >= expr2
       expr1 == expr2
       lb <= expr <= ub

Edit: With JuMP v0.21.1. Do I need to change more to use your syntax?

@Wikunia
Copy link
Contributor Author

Wikunia commented Mar 31, 2020

I need the Val(x.head) in https://github.com/JuliaOpt/JuMP.jl/blob/36e14304bddaa8eb08cb79e9b833b5e447a9adf7/src/macros.jl#L389
as you @dourouc05 implemented it in #2051 for this.
Somehow the indicator constraint only seems to work and this doesn't because of:

julia> :(a => {y <= 2})
:(a => {y <= 2})

julia> :(a := {y <= 2})
:($(Expr(:(:=), :a, :({y <= 2}))))

Which might be the case because => is also used in dicts and others?

@odow
Copy link
Member

odow commented Feb 18, 2021

The difference is

julia> dump(:(a => {y <= 2}))
Expr
  head: Symbol call
  args: Array{Any}((3,))
    1: Symbol =>
    2: Symbol a
    3: Expr
      head: Symbol braces
      args: Array{Any}((1,))
        1: Expr
          head: Symbol call
          args: Array{Any}((3,))
            1: Symbol <=
            2: Symbol y
            3: Int64 2

julia> dump(:(a := {y <= 2}))
Expr
  head: Symbol :=
  args: Array{Any}((2,))
    1: Symbol a
    2: Expr
      head: Symbol braces
      args: Array{Any}((1,))
        1: Expr
          head: Symbol call
          args: Array{Any}((3,))
            1: Symbol <=
            2: Symbol y
            3: Int64 2

I'm slightly averse to adding support for constraints that are not part of MOI yet. Would there be any objection to closing this in favor of #2227 so we just have one "Support constraint programming" issue?

@dourouc05
Copy link
Contributor

I'm in favour of closing this for #2227.

@Wikunia
Copy link
Contributor Author

Wikunia commented Feb 18, 2021

I'm happy close this. I have it implemented in my solver now and extending it was relatively straightforward.

@Wikunia Wikunia closed this as completed Feb 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

3 participants