Indicator constraints Not working

I’m encountering a problem with an optimization model in Julia where I need to enforce a specific condition using indicator variables. The goal is to ensure that for each time period ( t ) (from 2 to T), either ( y_{i,t} ) is positive and ( z_{i,t} ) is zero, or the other way around. Despite several attempts, I’m not achieving the expected behavior.

My initial approach was to use the constraint @constraint(model,[i=1:n,t=2:T],z[i,t]*y[i,t] == 0) to prevent both ( y ) and ( z ) from being positive at the same time ( t ). However, this did not work as planned, as I still observe cases where both ( y ) and ( z ) are positive simultaneously.

To address this, I introduced binary variables ( b_t[t] ) and added constraints like @constraint(model, [i=1:n, t=2:T], y[i,t] <= 1 - b_t[t]) and @constraint(model, [i=1:n, t=2:T], z[i,t] <= b_t[t]). Unfortunately, this approach leads to the model running indefinitely without finding a solution.

Here’s the relevant portion of my Julia code:

model = Model()
@variables(model, begin
    0 <= x[i=1:n,t=2:T] <= 1
    0 <= y[i=1:n, t=2:T] <= 1
    0 <= z[i=1:n,t=2:T] <= 1
    w[t=2:T], Bin
end)

# Active when w[t] == 1
@constraint(model, [t=2:T], w[t] => {sum(CF[i,t] * x[i,t] for i in 1:n) - L[t] == -sum(PBid[i,t] * y[i,t] for i in 1:n)})

# Active when w[t] == 0
@constraint(model, [t=2:T], !w[t] => {sum(CF[i,t] * x[i,t] for i in 1:n) - L[t] == -sum(PAsk[i,t] * z[i,t] for i in 1:n)})

I’m seeking advice on what might be causing these issues or how to implement these constraints correctly in Julia. Any insights or suggestions would be greatly appreciated, as I’m currently unable to progress with this model.

You don’t need indicator constraints. Do instead:

model = Model()
@variables(model, begin
    0 <= x[1:n, 2:T] <= 1
    0 <= y[1:n, 2:T] <= 1
    0 <= z[1:n, 2T] <= 1
    w[1:n, 2:T], Bin
end)
@constraints(model, begin
    [i in 1:n, t in 2:T], y[i, t] <= w[i, t]
    [i in 1:n, t in 2:T], z[i, t] <= 1 - w[i, t]
    [t in 2:T], sum(CF[i,t] * x[i,t] - PBid[i,t] * y[i,t] - PAsk[i,t] * z[i,t] for i in 1:n) == L[t]
end)

(Your previous attempts were not working because the indicator constraint enforced the equality constraint, not whether y or z were zero.)

1 Like

Thank you for the helpful insights provided earlier. I’ve followed the logic suggested and made some modifications to my Julia model. However, I have a few additional clarifications and would like to confirm the correctness of these modifications:

  1. Adjusting Upper Bound of Variable y:
    My model currently sets the upper bound for variable y to 1. If I need to change this upper bound to a different value, say a given number 1500000, should the constraint be modified to [i in 1:n, t in 2:T], y[i, t] <= 1500000 * w[i, t] to reflect this change?

  2. Prioritizing Sales Represented by y Over x:
    I’ve introduced variables z_y and z_x to represent sales from y and x respectively. The aim is to prioritize sales from y before utilizing x, especially to satisfy the last constraint of the model. Here’s the modified section of the model for this logic:

    @constraints(model, begin
        ...
        [i in 1:n, t in 2:T], z_y[i, t] <= y[i, t]
        [i in 1:n, t in 2:T], z_x[i, t] <= x[i, t]
        [i in 1:n, t in 2:T], z_y[i, t] >= z_x[i, t]
        ...
       # Updated last constraint
        [t in 2:T], sum(CF[i,t] * x[i,t] - PBid[i,t] * y[i,t] - PAsk[i,t] * (z_y[i, t] + z_x[i, t]) for i in 1:n) == L[t]
    
    end)
    

    Is this the correct approach to ensure the sales priority of y over x?

  3. Ensuring Either y or x is Sold, But Not Both:
    To add another layer of constraint where either y or x is sold, but not both, with a priority for selling y, I’ve introduced binary variables use_y[i, t] and use_x[i, t]. The constraint I’ve added is as follows:

    @constraints(model, begin
        ...
        [i in 1:n, t in 2:T], use_y[i, t] + use_x[i, t] <= 1
        [i in 1:n, t in 2:T], z_y[i, t] <= use_y[i, t]
        [i in 1:n, t in 2:T], z_x[i, t] <= use_x[i, t]
        ...
        
    end)
    

    Does this effectively enforce the rule of selling either y or x, but not both?

Any insights or suggestions to validate these modifications or propose better approaches would be greatly appreciated. Thank you!

You seem to have a good understanding of the problem and the reformulations, and you seem to be on the right track :smile:

  1. Yes
  2. I’m not really sure what you mean. The constraint looks like it requires more sales of z_y than z_x, but it doesn’t enforce that you can only sell x if you have maxed out the sales of y. (That might be the solution though, you should check the result to see if it works.)
  3. Yes
1 Like

Thank you again for the previous advice. On (2), I want to enforce that you can only sell x if I have maxed out the sales of `y. My earlier method was not correct. How can I enforce this condition?

For (2), something like this:

model = Model()
@variable(model, 0 <= x <= u)
@variable(model, 0 <= y <= u)
# If x > 0 then y == u
@variable(model, z, Bin)
# z == 0 --> 0 <= x <= 0
# z == 1 --> 0 <= x <= u
@constraint(model, x <= u * z)
# z == 0 --> 0 <= y <= u
# z == 1 --> u <= y <= u
@constraint(model, u - y <= u * (1 - z))

I find it useful to write out what you want as a logical proposition, like # If x > 0 then y == u. Then introduce a binary to model that proposition, like z. And then enforce the then part dependent on the binary.

1 Like

Thank you for your guidance. To avoid confusion with the variables in the original question, I replaced z with δ and set distinct upper bounds for x and y (u_x and u_y). Here’s my adjusted code:

model = Model()
@variable(model, 0 <= x <= u_x)
@variable(model, 0 <= y <= u_y)
@variable(model, δ, Bin)  # Using delta as the binary variable

# δ == 0 --> 0 <= x <= 0
# δ == 1 --> 0 <= x <= u_x
@constraint(model, x <= u_x * δ)

# δ == 0 --> 0 <= y <= u_y
# δ == 1 --> u_y <= y <= u_y
@constraint(model, u_y - y <= u_y * (1 - δ))

If there’s anything inconsistent with your approach, please let me know.

1 Like