4

I'm using gekko for INLP, i.e. integer non linear programming. I have an array of integer variables; however, my condition is that no value must appear more than once, i.e.

enter image description here

Code:

from gekko import GEKKO

model = GEKKO()

model.options.SOLVER = 1

x = model.Array(model.Var, 2, lb=0, ub=10, integer=True)

for m in range(11):
    model.Equation(x.count(m) <= 1)

model.Obj(sum(x))

model.solve()

Gives error: TypeError: object of type 'int' has no len()

Why can't I do it that way?

Kenji
  • 61
  • 6

1 Answers1

1

The Numpy count function does not provide gradients for the solvers and is therefore not supported in gekko equations. An alternative way to solve the problem is to use a binary variable b to determine which 2 numbers are selected between 0 and 10.

from gekko import GEKKO
model = GEKKO()
model.options.SOLVER = 1

n = 10
b = model.Array(model.Var, 11, lb=0, ub=1, integer=True)
y = [model.Intermediate(b[i]*i) for i in range(11)]
model.Equation(model.sum(b)==2)
model.Minimize(model.sum(y))

model.solve()

print('b: ' + str(b))
print('y: ' + str(y))

The solution is:

b: [[1.0] [1.0] [0.0] [0.0] [0.0] [0.0] [0.0] [0.0] [0.0] [0.0] [0.0]]
y: [[0.0], [1.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0]]

It is correct because 0 and 1 are selected to minimize the summation. If you switch to m.Maximize(model.sum(y)) then the solver selects 9 and 10.

b: [[0.0] [0.0] [0.0] [0.0] [0.0] [0.0] [0.0] [0.0] [0.0] [1.0] [1.0]]
y: [[0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [9.0], [10.0]]

This doesn't give the solution for x as an array of only 2 numbers. It is also possible that you could use y in your application.

Here is a way to condense y down to x:

from gekko import GEKKO
model = GEKKO()
model.options.SOLVER = 1

b = model.Array(model.Var, (11,2), lb=0, ub=1, integer=True)
x = [None]*2
for j in range(2):
    x[j] = model.sum([model.Intermediate(b[i,j]*i) for i in range(11)])

model.Equations([model.sum(b[:,j])==1 for j in range(2)])
model.Equations([model.sum(b[i,:])<=1 for i in range(11)])

model.Minimize(model.sum(x))

model.solve()

print('b: ' + str(b))
print('x: ' + str(x))

This gives the solution:

b: [[[0.0] [1.0]]
 [[1.0] [0.0]]
 [[0.0] [0.0]]
 [[0.0] [0.0]]
 [[0.0] [0.0]]
 [[0.0] [0.0]]
 [[0.0] [0.0]]
 [[0.0] [0.0]]
 [[0.0] [0.0]]
 [[0.0] [0.0]]
 [[0.0] [0.0]]]
x: [[1.0], [0.0]]

This method requires more binary variables but the solution is still fast.

John Hedengren
  • 12,068
  • 1
  • 21
  • 25
  • Thanks for that suggestion. I like that you answer gekko questions personally. However, your solution changes room size from k^n to 2^k. In my case, k is about 2000 (maybe minus some heuristics) and n somewhere between 5 and 20. So this seems to be a rather expensive deal. – Kenji Jun 21 '20 at 17:00
  • You could try a gradient-free method to see if the scale-up would be better. The branch-and-bound method does not try all of the solutions so it may still be fast to obtain a solution with larger-scale problems. – John Hedengren Jun 21 '20 at 18:50