Search code examples
booleanjuliajulia-jump

Julia (JuMP): Indicator constraints with multiple conditional values (is a boolean expression possible?)


I want to implement a constraint depending on the change of values in my binary decision variable, x, over "time".

I am trying to implement a minimum operating time constraint for a unit commitment optimization problem for power systems. x is representing the unit activation where 0 and 1 show that a power unit, n, at a certain time, t, respectively is shut off or turned on.

For this, indicator constraints seem to be a promising solution and with the inspiration of a similar problem the implementation seemed quite straightforward.

So, since boolean operators are introduced (! and ¬), I prematurely wanted to express the change in a boolean way:

@constraint(m, xx1[n=1:N,t=2:T], (!x[n,t-1] && x[n,t]) => {next(t, 1) + next(t, 2) == 2})

Saying: if unit was deactivated before but now is on, then demand the unit to be active for the next 2 times.

Where next(t, i) = x[((t - 1 + i) % T) + 1].

I got the following error:

LoadError: MethodError: no method matching !(::VariableRef)
Closest candidates are:
  !(!Matched::Missing) at missing.jl:100
  !(!Matched::Bool) at bool.jl:33
  !(!Matched::Function) at operators.jl:896

I checked that the indicator constraint is working properly with a single term only.

Question: Is this possible or is there another obvious solution?

Troubleshooting and workarounds: I have tried the following (please correct me if my diagnosis is wrong):

  • Implement change as an expression: indicator constraints only work with binary integer variables.
  • Implement change as another variable relating to x. I have found a solution but it is quite sketchy, which is documented in a Julia discourse. The immediate problem, found from the solution, is that indicator constraints do not work as bi-implication but only one way, LHS->RHS. Please see the proper approach given by @Oscar Dowson.

You can get the working code from github.


Solution

  • The trick is to find constraint(s) that have an equivalent truth-table:

    # Like
    (!x[1] && x[2]) => {z == 1}
    
    # Is equivalent to:
    z >= -x[1] + x[2]
    
    # Proof
    -x[1] + x[2] = sum <= z
    --------------------------
     - 0  +  0   =  0  <= 0
     - 1  +  0   =  -1 <= 0 
     - 0  +  1   =  1  <= 1
     - 1  +  1   =  0  <= 0
    

    I was recommended MOSEK Modeling Cookbook to help working out the correct formulation of constraints.

    See eventually the thread here from where I got the answer for further details.