BYU-PRISM / GEKKO

GEKKO Python for Machine Learning and Dynamic Optimization
https://machinelearning.byu.edu
Other
573 stars 102 forks source link

Arithmetic zeroing everything out #94

Closed adavidzh closed 3 years ago

adavidzh commented 3 years ago

TL;DR - With python 3.8.5 on MacOS 10.15, it seems that any arithmetic operation with Const or Var yields zero. Same behaviour seen for 0.2.8, 0.2.7, 0.2.6, 0.2.5, and 0.2.0.

In more detail:

I was just getting started with GEKKO and trying to run the example associated with GEKKO().Array() (no direct link; search for z[j] = m.Intermediate). Here's a version with an couple extra printouts:

from gekko import GEKKO
m = GEKKO()
# variable array dimension
n = 3 # rows
p = 2 # columns
# create array
x = m.Array(m.Var,(n,p))
for i in range(n):
  for j in range(p):
            x[i,j].value = 2.0
            x[i,j].lower = -10.0
            x[i,j].upper = 10.0
# create parameter
y = m.Param(value = 1.0)
# sum columns
z = [None]*p
for j in range(p):
   z[j] = m.Intermediate(sum([x[i,j] for i in range(n)]))

print('x',x)
print('z',z) # <- This should not be zero!

# objective
m.Obj(sum([z[j]**2 + y for j in range(p)]))
# minimize objective
m.solve()
print('x',x)

that outputs

x [[2.0 2.0]
 [2.0 2.0]
 [2.0 2.0]]
z [0, 0]

The fact that z is all zeroes when the x are all twos looked very fishy so I tracked down what seems to be the issue and the following is a minimum working example to illustrate the problem:

from gekko import GEKKO
m = GEKKO()
n = 3

# OK
k = [ m.Const(value=1.95+0.10*random(), name=f'k_{i}') for i in range (n) ]
print('k',k, [v.value for v in k])

# OK
x = [ m.Var(value=1.95+0.10*random(), lb=0, ub=4, name=f'x_{i}') for i in range (n) ]
print('x',x)

# FAIL (all zeroes)
p1 = [ m.Intermediate(k[i] * x[i]) for i in range (n) ]
print('p1', p1)

# FAIL (all zeroes)
p2 = [ k[i] * x[i] for i in range (n) ]
print('p2',p2,[v.value for v in p2])

# FAIL (all zeroes)
q = [ k[i]*1.0 for i in range (n) ]
print('q',q,[v.value for v in q])

yields the following to my surprise:

k [k_0, k_1, k_2] [1.9763869261874616, 1.9585619463917063, 1.9558800388221458]
x [2.048237885356195, 1.985707629865146, 1.98243528547778]
p1 [0, 0, 0]
p2 [((k_0)*(x_0)), ((k_1)*(x_1)), ((k_2)*(x_2))] [0, 0, 0]
q [((k_0)*(1.0)), ((k_1)*(1.0)), ((k_2)*(1.0))] [0, 0, 0]

Am I just doing something blatantly wrong?

APMonitor commented 3 years ago

You can resolve this by moving the print statements after the m.solve() command.

from gekko import GEKKO
m = GEKKO()
# variable array dimension
n = 3 # rows
p = 2 # columns
# create array
x = m.Array(m.Var,(n,p))
for i in range(n):
  for j in range(p):
            x[i,j].value = 2.0
            x[i,j].lower = -10.0
            x[i,j].upper = 10.0
# create parameter
y = m.Param(value = 1.0)
# sum columns
z = [None]*p
for j in range(p):
   z[j] = m.Intermediate(sum([x[i,j] for i in range(n)]))

# objective
m.Obj(sum([z[j]**2 + y for j in range(p)]))
# minimize objective
m.solve()

print('x',x)
print('z',z)

Here is the solution:

x [[[-9.1115858651e-16] [-9.1115858651e-16]]
 [[-9.1115858527e-16] [-9.1115858527e-16]]
 [[5.4547859199e-15] [5.4547859199e-15]]]
z [[3.6324687481e-15], [3.6324687481e-15]]

Before the m.solve() command, the values are initialized to zero unless you specifically initialize a starting value to something else. There is no initialization for intermediate variables so they'll always be zero at the start, before the m.solve() command.

adavidzh commented 3 years ago

Ok, thanks for answering the noob question.

Two things:

  1. Is there a way to propagate the var values through to intermediates without solving the problem? Without it, it is hard to check a model's correctness before optimisation is run.
  2. 95 has a proposal for updating the example in order to make it clear that arithmetic in defining z is working properly. I.e., I would have had to dig deeper before being scared about all the zeroes, since in the new example, z converges to a non-zero value.