Search code examples
pythonoptimizationpyomo

How can I ensure that a variable in Pyomo either remains between boundaries or is equal to zero?


I want to solve an optimization problem using Pyomo and ensure that the decision variables are either between specified limits or equal to zero.

As a minimal example, I would like to show the following optimization problem:

Example Problem

Specifically, I want to keep the decision variable(s) x(t) always at either zero or between ten and 30.

I can already restrict x(t) to a range using the bounds alone when declaring the variables. However, this gives me a result where x(2) = 5 and is therefore less than ten.

import pyomo.environ as pyo


model = pyo.ConcreteModel()

model.time = pyo.Set(initialize=range(5))
model.price = pyo.Param(model.time, initialize=[40, 20, 10, 30, 50])
model.out = pyo.Param(model.time, initialize=[0, 0, 0, 40, 20])

initial_state = 50
min_state = 25

model.x = pyo.Var(model.time, bounds=[0, 30])


def expression_state(m, t):
    if t == m.time.first():
        return initial_state + model.x[t] - model.out[t]
    else:
        return m.state[m.time.prev(t)] + model.x[t] - model.out[t]


model.state = pyo.Expression(model.time, rule=expression_state)


def constraint_min_state(m, t):
    return m.state[t] >= min_state


model.state_constraint = pyo.Constraint(model.time, rule=constraint_min_state)


def objective(m):
    return sum(m.x[t] * m.price[t] for t in m.time)


model.objective = pyo.Objective(rule=objective, sense=pyo.minimize)


pyo.SolverFactory("glpk").solve(model)

model.x.display()

I have now tried various ways of inserting a constraint that gives me the desired behavior (either equal to zero or between ten and 30), but I usually always get the same error. Here is an example of the error mentioned:

def constraint_x(m, t):
    return (m.x[t] == 0) or (10, m.x[t], 30)


model.x_constraint = pyo.Constraint(model.time, rule=constraint_x)

Such a constraint (or similar ones) gives me the following error message:

pyomo.common.errors.PyomoException: Cannot convert non-constant Pyomo expression (x[0]  ==  0) to bool.
This error is usually caused by using a Var, unit, or mutable Param in a
Boolean context such as an "if" statement, or when checking container
membership or equality. For example,
    >>> m.x = Var()
    >>> if m.x >= 1:
    ...     pass
and
    >>> m.y = Var()
    >>> if m.y in [m.x, m.y]:
    ...     pass
would both cause this exception.

What options do I have to include such either/or constraints?


Solution

  • x[t] is zero or between L and U. This means that x[t] is a semi-continuous variable with bounds L and U. Some solvers and modeling tools have direct support for this.

    If not, you can simulate semi-continuous variables with an additional binary variable δ ∈ {0,1}:

      L⋅δ[t] ≤ x[t] ≤ U⋅δ[t]
    

    This is a linear constraint.