I would expect z2 to be [[1],[1],[1]] because the PWL output steps up from 0 to 1 between 0 and +1e-5 (input). Here are some things to get closer to the solution:
- Try the
APOPT solver with m.options.SOLVER=1. It is an active set solver instead of the default IPOPT solver that uses a barrier method. The active set solver tends to return more precise solutions, especially where a barrier term is involved in defining the solution.
- Use an integer variable for
z2 (with the APOPT solver). This also will help you find an integer solution, if that is a constraint.
- Use a tighter solution tolerance such as
m.options.RTOL=1e-9. The default tolerance is 1e-6. You may also need to adjust m.options.OTOL if you need a more precise objective function.
I switched the equation to model.Equation(z[0] == -1e-5) to show a solution of [[0],[1],[1]]
from gekko import GEKKO
model = GEKKO(remote=False)
z = model.Array(model.Var,(3))
model.Equation(z[0] == -1e-5)
model.Equation(z[1] == 4)
model.Equation(z[2] == 5.99999)
z2 = model.Array(model.Var,(3)) #,integer=True)
for i in range(3):
d = 1e-5
x_data = [-1e5, 0, 0+d, 6]
y_data = [0, 0, 1, 1]
model.pwl(z[i], z2[i], x_data, y_data)
#model.options.RTOL = 1e-9
model.options.SOLVER = 1
model.solve()
print(z)
print(z2)
Edit
As you discovered, the PWL function uses slack variables. Including another objective can interfere with the correct PWL solution. The way to handle that is to may the relative objective of model.Obj(model.sum(z2)) have a smaller gradient than the gradient of the PWL function. This is an example where 0.1*sum(z2) is minimized and 0.01*sum(z) is maximized.
from gekko import GEKKO
model = GEKKO(remote=False)
z = model.Array(model.Var,(3),value=1)
z2 = model.Array(model.Var,(3)) #,integer=True)
for i in range(3):
d = 1e-5
x_data = [-1e5, 0, 0+d, 6]
y_data = [0, 0, 1, 1]
model.pwl(z[i], z2[i], x_data, y_data)
# first most important objective is PWL (gradient = 1)
# second most important objective is to minimize z2
model.Minimize(0.1*model.sum(z2))
# third most important objective is to maximize z
model.Maximize(0.01*model.sum(z))
model.options.SOLVER = 1
model.solve()
print(z)
print(z2)
This correctly gives the solution:
[[0.0] [0.0] [0.0]]
[[0.0] [0.0] [0.0]]
Alternatively, you can maximize 0.1*sum(z2) and minimize 0.01*sum(z) with:
from gekko import GEKKO
model = GEKKO(remote=False)
z = model.Array(model.Var,(3),value=1)
z2 = model.Array(model.Var,(3)) #,integer=True)
for i in range(3):
d = 1e-5
x_data = [-1e5, 0, 0+d, 6]
y_data = [0, 0, 1, 1]
model.pwl(z[i], z2[i], x_data, y_data)
# first most important objective is PWL (gradient = 1)
# second most important objective is to minimize z2
model.Maximize(0.1*model.sum(z2))
# third most important objective is to maximize z
model.Minimize(0.01*model.sum(z))
model.options.SOLVER = 1
model.solve()
print(z)
print(z2)
to give:
[[1e-05] [1e-05] [1e-05]]
[[1.0] [1.0] [1.0]]
I appreciate the warning message suggestion. However, it is generally too hard to find errors in models when the syntax is correct but there is a configuration that would lead to an undesirable solution. Hopefully your excellent question will help others with the PWL function.