I'm a newbie to Pyomo and struggling with understanding the intuition behind Pyomo's syntax and how it builds its models. This may be why I cannot figure out how to define and solve a 'binary' problem where N variables must take only ±1 values using Pyomo and Couenne solver.
First I tried the Integers
domain with bounds=(-1, 1)
and tried to add a strict inequality:
import pyomo.environ as pyo
import numpy as np
N = 5
w = np.ones(range(N))
pyoModel = pyo.ConcreteModel('binary model')
pyoModel.o = pyo.Var(range(N), bounds=(-1, 1), within=pyo.Integers, initialize=1)
pyoModel.binaryConstraintP = pyo.Constraint(range(N), rule=strictlyPositive)
pyoModel.binaryConstraintN = pyo.Constraint(range(N), rule=strictlyNegative)
pyoModel.objective = pyo.Objective(expr=pyo.sum_product(pyoModel.o, w, index=range(N)), sense=pyo.maximize)
def strictlyPositive(model, i):
return model.o[i] > 0
def strictlyNegative(model, i):
return model.o[i] < 0
This ends up with:
ValueError: Constraint 'binaryConstraintP[0]' encountered a strict inequality expression ('>' or '<'). All constraints must be formulated using using '<=', '>=', or '=='.
Okay, no strict inequalities allowed (don't know why!), I tried to switch to the Binary
domain and do a workaround by manipulating the variable in the objective so it lies with in {-1, 1} - i.e., if o ∈ {0, 1} then 2 x o - 1 ∈ {-1, 1}:
import pyomo.environ as pyo
import numpy as np
N = 5
w = np.ones(range(N))
pyoModel = pyo.ConcreteModel('binary model')
pyoModel.o = pyo.Var(range(N), within=pyo.Binary, initialize=1)
pyoModel.objective = pyo.Objective(expr=pyo.sum_product(2 * pyoModel.o - 1, w, index=range(N)), sense=pyo.maximize)
Got:
TypeError: unsupported operand type(s) for *: 'int' and 'IndexedVar'
So I used an array of twos and ones instead of 2 and 1 but got another error about shape broadcasting. I'm sure I'm missing something here because it should be easy to construct a linear equation in the objective right?
I also tried to change the domain into a user-defined one:
...
pyoModel.domain = pyo.Set(initialize=[-1, 1])
...
pyoModel.o = pyo.Var(range(N), domain=pyoModel.domain, initialize=1)
...
with SolverFactory('couenne') as opt:
results = opt.solve(pyoModel, load_solutions=False)
...
and ended up with Couenne error:
TypeError: Invalid domain type for variable with name '%s'. Variable is not continuous, integer, or binary.
I also thought of using SOSs but it was even harder to understand how they work!
Again, I must be missing something in each approach. Any help would be appriciated.
Side note: I simplified the original code as possible to make it easier to read.