Search code examples
gekko

How to change a binary variable's value based on decision variable in GEKKO?


I ran the following code in Excel and received I_g=[0,0,0], I_d=[14.8, 14.3, 10.3] and Objective=383.546, but I am having a problem to get a same results via GEKKO in Python. I used the four last constraints to enforce either I_g or I_d become 1 when their corresponding S_g or S_d is greater than zero, but both I_d and I_g are zero again. Maybe, my problem is the binary variables' definition in Gekko, because I expect the last four constraints check the following conditions: If S_g>0 then I_g=1 else I_g=0, If S_d>0 then I_d=1 else I_d=0. While S_d is greater than zero, but I_d is again 0. I appreciate your help.

from gekko import GEKKO
import pandas as pd
import sys

Inp_Data=pd.DataFrame([[10.8,10.6,15.0,0.2,0.0],[10.2,10.3,15.0,0.7,0.0],[10.1,10.4,15.0,4.9,0.0]])

col_name=list(Inp_Data)
row=len(Inp_Data)
P_D=Inp_Data.loc[:,col_name[0]].tolist()
P_I=Inp_Data.loc[:,col_name[1]].tolist()

Demand=Inp_Data.loc[:,col_name[2]].tolist()

Cap=300
FCV=[0.246,0.08145]

V_V=0.001

G_w=Inp_Data.loc[:,col_name[3]]
G_p=Inp_Data.loc[:,col_name[4]]


Big_M=100000000
epsilon=sys.float_info.epsilon

m = GEKKO(remote=False)
m.options.SOLVER=1
m.solver_options = ['minlp_gap_tol 1.0e-2',\
                    'minlp_maximum_iterations 10000',\
                    'minlp_max_iter_with_int_sol 500',\
                    'minlp_branch_method 1',\
                    'minlp_integer_leaves 2']

#Definition of the MIP variables
I_g=[m.Var(integer=True,lb=0,ub=1) for i in range(row)]
S_g=[m.Var(lb=0) for i in range(row)]

I_v=[m.Var(integer=True,lb=0,ub=1) for i in range(row)]
S_v=[m.Var(lb=0) for i in range(row)]

I_d=[m.Var(integer=True,lb=0,ub=1) for i in range(row)]
S_d=[m.Var(lb=0) for i in range(row)]
G_d=[m.Var(lb=0) for i in range(row)]

I_w=[m.Var(integer=True,lb=0,ub=1) for i in range(row)]
I_p=[m.Var(integer=True,lb=0,ub=1) for i in range(row)]

I_DA=[m.Var(integer=True,lb=0,ub=1)]

c = [None]*row
for t in range(row):
    c[t] = (I_DA[0]*Demand[t]*P_D[t]+(1-I_DA[0])*S_v[t]*P_I[t])-(I_g[t]*P_I[t]*S_g[t]+I_d[t]*(FCV[0]*S_d[t]+FCV[1]*Cap)+V_V*S_v[t])

m.Equations(S_v[t]+S_g[t]+S_d[t]-(I_DA[0]*Demand[t]+(1-I_DA[0])*S_v[t])==0 for t in range(row))

m.Equations(S_v[t]==(G_w[t]+G_p[t]) for t in range(row))

m.Equations(I_d[t]+I_g[t]-I_DA[0]<=0 for t in range(row))


m.Equations(I_g[t]<(1+S_g[t]-epsilon) for t in range(row))
m.Equations(S_g[t]<(Big_M*I_g[t]) for t in range(row))
#
m.Equations(I_d[t]<(1+S_d[t]-epsilon) for t in range(row))
m.Equations(S_d[t]<(Big_M*I_d[t]) for t in range(row))


m.Maximize(m.sum(c))

m.solve(disp=True)

print('Results')
for t in range(row):
    print('I_g{}={}, I_d{}={}'.format(t,str(I_g[t].value),t,str(I_d[t].value)))
print('Objective: ' + str(m.options.objfcnval))

Solution

  • Add 'minlp_integer_tol 1e-8' to the solver options to get the correct solution. The default is 'minlp_integer_tol 1e-2' meaning that a binary value can be 0.01 or 0.99 and is still considered an integer solution. With the new tolerance, a value of 1e-8 or 0.99999999 is considered an integer value. Here is the new solution with the tolerance applied:

    I_g0=[0.0], I_d0=[1.0]
    I_g1=[0.0], I_d1=[1.0]
    I_g2=[0.0], I_d2=[1.0]
    Objective: -383.546
    

    Here is the complete program:

    from gekko import GEKKO
    import pandas as pd
    import sys
    
    Inp_Data=pd.DataFrame([[10.8,10.6,15.0,0.2,0.0],\
                           [10.2,10.3,15.0,0.7,0.0],\
                           [10.1,10.4,15.0,4.9,0.0]])
    
    col_name=list(Inp_Data)
    row=len(Inp_Data)
    P_D=Inp_Data.loc[:,col_name[0]].tolist()
    P_I=Inp_Data.loc[:,col_name[1]].tolist()
    
    Demand=Inp_Data.loc[:,col_name[2]].tolist()
    
    Cap=300
    FCV=[0.246,0.08145]
    
    V_V=0.001
    
    G_w=Inp_Data.loc[:,col_name[3]]
    G_p=Inp_Data.loc[:,col_name[4]]
    
    
    Big_M=100000000
    epsilon=sys.float_info.epsilon
    
    m = GEKKO(remote=False)
    m.options.SOLVER=1
    m.solver_options = ['minlp_gap_tol 1.0e-2',\
                        'minlp_maximum_iterations 10000',\
                        'minlp_max_iter_with_int_sol 500',\
                        'minlp_branch_method 1',\
                        'minlp_integer_tol 1e-8', \
                        'minlp_integer_leaves 2']
    
    #Definition of the MIP variables
    I_g=[m.Var(integer=True,lb=0,ub=1) for i in range(row)]
    S_g=[m.Var(lb=0) for i in range(row)]
    
    I_v=[m.Var(integer=True,lb=0,ub=1) for i in range(row)]
    S_v=[m.Var(lb=0) for i in range(row)]
    
    I_d=[m.Var(integer=True,lb=0,ub=1) for i in range(row)]
    S_d=[m.Var(lb=0) for i in range(row)]
    G_d=[m.Var(lb=0) for i in range(row)]
    
    I_w=[m.Var(integer=True,lb=0,ub=1) for i in range(row)]
    I_p=[m.Var(integer=True,lb=0,ub=1) for i in range(row)]
    
    I_DA=[m.Var(integer=True,lb=0,ub=1)]
    
    c = [None]*row
    for t in range(row):
        c[t] = (I_DA[0]*Demand[t]*P_D[t]+(1-I_DA[0])\
                *S_v[t]*P_I[t])-(I_g[t]*P_I[t]*S_g[t]\
                +I_d[t]*(FCV[0]*S_d[t]+FCV[1]*Cap)+V_V*S_v[t])
    
    m.Equations(S_v[t]+S_g[t]+S_d[t]-(I_DA[0]*Demand[t]\
                 +(1-I_DA[0])*S_v[t])==0 for t in range(row))
    
    m.Equations(S_v[t]==(G_w[t]+G_p[t]) for t in range(row))
    
    m.Equations(I_d[t]+I_g[t]-I_DA[0]<=0 for t in range(row))
    
    
    m.Equations(I_g[t]<(1+S_g[t]-epsilon) for t in range(row))
    m.Equations(S_g[t]<(Big_M*I_g[t]) for t in range(row))
    #
    m.Equations(I_d[t]<(1+S_d[t]-epsilon) for t in range(row))
    m.Equations(S_d[t]<(Big_M*I_d[t]) for t in range(row))
    
    m.Maximize(m.sum(c))
    
    m.solve(disp=True)
    
    print('Results')
    for t in range(row):
        print('I_g{}={}, I_d{}=\
         {}'.format(t,str(I_g[t].value),t,str(I_d[t].value)))
    print('Objective: ' + str(m.options.objfcnval))
    

    You may also be interested in the m.if3() function such as:

    m.Equations([I_g[t]==m.if3(S_g[t],0,1) for t in range(row)])
    m.Equations([I_d[t]==m.if3(S_d[t],0,1) for t in range(row)])