GEKKO 中的约束非线性优化,objective 函数与预期解不匹配
Constrained non-linear optimisation in GEKKO, objective function not matching expected solution
我有一个相当简单的约束非线性优化问题,最大化收入,给定一些支出限制(保持总支出不变,increase/decrease 每个渠道的支出最多增加 50%)和 objective 函数 revenue = spend * roi(其中 roi 是使用每个通道的对数对数模型系数计算的)。
首先,该解决方案与我认为的最佳解决方案不符。此外,当采用 GEKKO 建议的最佳支出值并将其放入 objective 函数时,这也不符合 GEKKO 给出的最佳 objective 函数值。
如果上面的问题没有很好地表达出来,下面的示例代码应该可以解决这个问题...
有人知道我错过了什么吗?
代码如下:
from gekko import GEKKO
import numpy as np
import pandas as pd
df1 = pd.DataFrame({'channel': ['fb', 'tv', 'ppc'],
'value': [5000, 5000, 5000],
'alpha': [1.00, 1.00, 1.00],
'beta': [0.03, 0.02, 0.01]
})
total_spend = df1['value'].sum()
m = GEKKO()
# Constraint parameters
spend_inc = 0.00
spend_dec = 0.00
channel_inc = 0.50
channel_dec = 0.50
channels = len(df1)
# Initialise decision variables
x = m.Array(m.Var, (channels), integer=True)
i = 0
for xi in x:
xi.value = df1['value'][i]
xi.lower = df1['value'][i] * (1 - channel_dec)
xi.upper = df1['value'][i] * (1 + channel_inc)
i += 1
# Initialise alpha
a = m.Array(m.Param, (channels))
i = 0
for ai in a:
ai.value = df1['alpha'][i]
i += 1
# Initialise beta
b = m.Array(m.Param, (channels))
i = 0
for bi in b:
bi.value = df1['beta'][i]
i += 1
# Initial global contraints
spend_min = total_spend * (1 - spend_dec)
spend_max = total_spend * (1 + spend_dec)
# Print out variabales
print(f'spend min: {spend_min}')
print(f'spend max: {spend_max}')
print('')
for i in range(0, channels):
print(f'x{i+1} value: {str(x[i].value)}')
print(f'x{i+1} lower: {str(x[i].lower)}')
print(f'x{i+1} upper: {str(x[i].upper)}')
print(f'x{i+1} alpha: {str(a[i].value)}')
print(f'x{i+1} beta: {str(b[i].value)}')
print('')
# Constraints
m.Equation(sum(x) >= spend_min)
m.Equation(sum(x) <= spend_max)
# Log-Log model
def roi(a, b, x):
roi = a + b * m.log(x[0])
roi = m.exp(roi[0])
return roi
# Objective function
m.Maximize(m.sum(x * roi(a, b, x)))
m.options.IMODE = 3
m.solve()
for i in range(0, channels):
print(f'x{i+1}: {str(x[i].value[0])}')
print('')
print(f'optimal solution: {str(m.options.objfcnval)}')
# THIS DOESN'T MATCH THE OBJECTIVE FUNCTION VALUE SUGGESTED BY GEKKO
opt = 0
for i in range(0, channels):
opt += x[i].value[0] * (np.exp((a[0].value[0] + b[i].value[0] * np.log(x[i].value[0]))))
print(f'optimal solution: {opt}')
# THIS IS THE EXPECETD SOLUTION, WHICH ALSO DOESN'T MATCH THE OBJECTIVE FUNCTION VALUE SUGGESTED BY GEKKO
7500 * np.exp(1.00 + 0.03 * np.log(7500)) + 5000 * np.exp(1.00 + 0.02 * np.log(5000)) + 2500 * np.exp(1.00 + 0.01 * np.log(2500))
objective 函数的定义方式存在问题。解决该问题的方法是在函数中一次使用一个 a[i]
、b[i]
、x[i]
。 Gekko 允许多个 objective 函数定义。最大化时,它转换为 min = -max
的最小化问题。因此,最大化的 objective 是 -m.options.OBJFCNVAL
.
# Log-Log model
def roi(ai, bi, xi):
return m.exp(ai + bi * m.log(xi))
# Objective function
for i,xi in enumerate(x):
m.Maximize(xi * roi(a[i], b[i], x[i]))
它还有助于定义一个新变量 sum_x
,上限 spend_max
和下限 spend_min
。
# Constraints
sum_x = m.Var(lb=spend_min,ub=spend_max)
m.Equation(sum_x==m.sum(x))
这是完整的脚本。
from gekko import GEKKO
import numpy as np
import pandas as pd
df1 = pd.DataFrame({'channel': ['fb', 'tv', 'ppc'],
'value': [5000, 5000, 5000],
'alpha': [1.00, 1.00, 1.00],
'beta': [0.03, 0.02, 0.01]
})
total_spend = df1['value'].sum()
m = GEKKO()
# Constraint parameters
spend_inc = 0.00
spend_dec = 0.00
channel_inc = 0.50
channel_dec = 0.50
channels = len(df1)
# Initialise decision variables
x = m.Array(m.Var, (channels), integer=True)
i = 0
for xi in x:
xi.value = df1['value'][i]
xi.lower = df1['value'][i] * (1 - channel_dec)
xi.upper = df1['value'][i] * (1 + channel_inc)
i += 1
# Initialise alpha
a = m.Array(m.Param, (channels))
i = 0
for ai in a:
ai.value = df1['alpha'][i]
i += 1
# Initialise beta
b = m.Array(m.Param, (channels))
i = 0
for bi in b:
bi.value = df1['beta'][i]
i += 1
# Initial global contraints
spend_min = total_spend * (1 - spend_dec)
spend_max = total_spend * (1 + spend_dec)
# Print out variabales
print(f'spend min: {spend_min}')
print(f'spend max: {spend_max}')
print('')
for i in range(0, channels):
print(f'x{i+1} value: {str(x[i].value)}')
print(f'x{i+1} lower: {str(x[i].lower)}')
print(f'x{i+1} upper: {str(x[i].upper)}')
print(f'x{i+1} alpha: {str(a[i].value)}')
print(f'x{i+1} beta: {str(b[i].value)}')
print('')
# Constraints
sum_x = m.Var(lb=spend_min,ub=spend_max)
m.Equation(sum_x==m.sum(x))
# Log-Log model
def roi(ai, bi, xi):
return m.exp(ai + bi * m.log(xi))
# Objective function
for i,xi in enumerate(x):
m.Maximize(xi * roi(a[i], b[i], x[i]))
m.options.IMODE = 3
m.solve()
for i in range(0, channels):
print(f'x{i+1}: {str(x[i].value[0])}')
print('')
print(f'optimal solution: {str(-m.options.objfcnval)}')
opt = 0
for i in range(0, channels):
opt += x[i].value[0] * (np.exp((a[i].value[0] \
+ b[i].value[0] \
* np.log(x[i].value[0]))))
print(f'optimal solution: {opt}')
# THIS IS THE EXPECTED SOLUTION
# IT NOW MATCHES THE OBJECTIVE FUNCTION VALUE SUGGESTED BY GEKKO
sol = 7500 * np.exp(1.00 + 0.03 * np.log(7500)) \
+ 5000 * np.exp(1.00 + 0.02 * np.log(5000)) \
+ 2500 * np.exp(1.00 + 0.01 * np.log(2500))
print(f'expected optimal solution: {sol}')
解决方法如下图
EXIT: Optimal Solution Found.
The solution was found.
The final value of the objective function is -50108.7613900549
---------------------------------------------------
Solver : IPOPT (v3.12)
Solution time : 9.499999985564500E-003 sec
Objective : -50108.7611889392
Successful solution
---------------------------------------------------
x1: 7500.0
x2: 4999.9999497
x3: 2500.0
optimal solution: 50108.761189
optimal solution: 50108.7611890521
expected optimal solution: 50108.76135441651
我有一个相当简单的约束非线性优化问题,最大化收入,给定一些支出限制(保持总支出不变,increase/decrease 每个渠道的支出最多增加 50%)和 objective 函数 revenue = spend * roi(其中 roi 是使用每个通道的对数对数模型系数计算的)。
首先,该解决方案与我认为的最佳解决方案不符。此外,当采用 GEKKO 建议的最佳支出值并将其放入 objective 函数时,这也不符合 GEKKO 给出的最佳 objective 函数值。
如果上面的问题没有很好地表达出来,下面的示例代码应该可以解决这个问题...
有人知道我错过了什么吗?
代码如下:
from gekko import GEKKO
import numpy as np
import pandas as pd
df1 = pd.DataFrame({'channel': ['fb', 'tv', 'ppc'],
'value': [5000, 5000, 5000],
'alpha': [1.00, 1.00, 1.00],
'beta': [0.03, 0.02, 0.01]
})
total_spend = df1['value'].sum()
m = GEKKO()
# Constraint parameters
spend_inc = 0.00
spend_dec = 0.00
channel_inc = 0.50
channel_dec = 0.50
channels = len(df1)
# Initialise decision variables
x = m.Array(m.Var, (channels), integer=True)
i = 0
for xi in x:
xi.value = df1['value'][i]
xi.lower = df1['value'][i] * (1 - channel_dec)
xi.upper = df1['value'][i] * (1 + channel_inc)
i += 1
# Initialise alpha
a = m.Array(m.Param, (channels))
i = 0
for ai in a:
ai.value = df1['alpha'][i]
i += 1
# Initialise beta
b = m.Array(m.Param, (channels))
i = 0
for bi in b:
bi.value = df1['beta'][i]
i += 1
# Initial global contraints
spend_min = total_spend * (1 - spend_dec)
spend_max = total_spend * (1 + spend_dec)
# Print out variabales
print(f'spend min: {spend_min}')
print(f'spend max: {spend_max}')
print('')
for i in range(0, channels):
print(f'x{i+1} value: {str(x[i].value)}')
print(f'x{i+1} lower: {str(x[i].lower)}')
print(f'x{i+1} upper: {str(x[i].upper)}')
print(f'x{i+1} alpha: {str(a[i].value)}')
print(f'x{i+1} beta: {str(b[i].value)}')
print('')
# Constraints
m.Equation(sum(x) >= spend_min)
m.Equation(sum(x) <= spend_max)
# Log-Log model
def roi(a, b, x):
roi = a + b * m.log(x[0])
roi = m.exp(roi[0])
return roi
# Objective function
m.Maximize(m.sum(x * roi(a, b, x)))
m.options.IMODE = 3
m.solve()
for i in range(0, channels):
print(f'x{i+1}: {str(x[i].value[0])}')
print('')
print(f'optimal solution: {str(m.options.objfcnval)}')
# THIS DOESN'T MATCH THE OBJECTIVE FUNCTION VALUE SUGGESTED BY GEKKO
opt = 0
for i in range(0, channels):
opt += x[i].value[0] * (np.exp((a[0].value[0] + b[i].value[0] * np.log(x[i].value[0]))))
print(f'optimal solution: {opt}')
# THIS IS THE EXPECETD SOLUTION, WHICH ALSO DOESN'T MATCH THE OBJECTIVE FUNCTION VALUE SUGGESTED BY GEKKO
7500 * np.exp(1.00 + 0.03 * np.log(7500)) + 5000 * np.exp(1.00 + 0.02 * np.log(5000)) + 2500 * np.exp(1.00 + 0.01 * np.log(2500))
objective 函数的定义方式存在问题。解决该问题的方法是在函数中一次使用一个 a[i]
、b[i]
、x[i]
。 Gekko 允许多个 objective 函数定义。最大化时,它转换为 min = -max
的最小化问题。因此,最大化的 objective 是 -m.options.OBJFCNVAL
.
# Log-Log model
def roi(ai, bi, xi):
return m.exp(ai + bi * m.log(xi))
# Objective function
for i,xi in enumerate(x):
m.Maximize(xi * roi(a[i], b[i], x[i]))
它还有助于定义一个新变量 sum_x
,上限 spend_max
和下限 spend_min
。
# Constraints
sum_x = m.Var(lb=spend_min,ub=spend_max)
m.Equation(sum_x==m.sum(x))
这是完整的脚本。
from gekko import GEKKO
import numpy as np
import pandas as pd
df1 = pd.DataFrame({'channel': ['fb', 'tv', 'ppc'],
'value': [5000, 5000, 5000],
'alpha': [1.00, 1.00, 1.00],
'beta': [0.03, 0.02, 0.01]
})
total_spend = df1['value'].sum()
m = GEKKO()
# Constraint parameters
spend_inc = 0.00
spend_dec = 0.00
channel_inc = 0.50
channel_dec = 0.50
channels = len(df1)
# Initialise decision variables
x = m.Array(m.Var, (channels), integer=True)
i = 0
for xi in x:
xi.value = df1['value'][i]
xi.lower = df1['value'][i] * (1 - channel_dec)
xi.upper = df1['value'][i] * (1 + channel_inc)
i += 1
# Initialise alpha
a = m.Array(m.Param, (channels))
i = 0
for ai in a:
ai.value = df1['alpha'][i]
i += 1
# Initialise beta
b = m.Array(m.Param, (channels))
i = 0
for bi in b:
bi.value = df1['beta'][i]
i += 1
# Initial global contraints
spend_min = total_spend * (1 - spend_dec)
spend_max = total_spend * (1 + spend_dec)
# Print out variabales
print(f'spend min: {spend_min}')
print(f'spend max: {spend_max}')
print('')
for i in range(0, channels):
print(f'x{i+1} value: {str(x[i].value)}')
print(f'x{i+1} lower: {str(x[i].lower)}')
print(f'x{i+1} upper: {str(x[i].upper)}')
print(f'x{i+1} alpha: {str(a[i].value)}')
print(f'x{i+1} beta: {str(b[i].value)}')
print('')
# Constraints
sum_x = m.Var(lb=spend_min,ub=spend_max)
m.Equation(sum_x==m.sum(x))
# Log-Log model
def roi(ai, bi, xi):
return m.exp(ai + bi * m.log(xi))
# Objective function
for i,xi in enumerate(x):
m.Maximize(xi * roi(a[i], b[i], x[i]))
m.options.IMODE = 3
m.solve()
for i in range(0, channels):
print(f'x{i+1}: {str(x[i].value[0])}')
print('')
print(f'optimal solution: {str(-m.options.objfcnval)}')
opt = 0
for i in range(0, channels):
opt += x[i].value[0] * (np.exp((a[i].value[0] \
+ b[i].value[0] \
* np.log(x[i].value[0]))))
print(f'optimal solution: {opt}')
# THIS IS THE EXPECTED SOLUTION
# IT NOW MATCHES THE OBJECTIVE FUNCTION VALUE SUGGESTED BY GEKKO
sol = 7500 * np.exp(1.00 + 0.03 * np.log(7500)) \
+ 5000 * np.exp(1.00 + 0.02 * np.log(5000)) \
+ 2500 * np.exp(1.00 + 0.01 * np.log(2500))
print(f'expected optimal solution: {sol}')
解决方法如下图
EXIT: Optimal Solution Found.
The solution was found.
The final value of the objective function is -50108.7613900549
---------------------------------------------------
Solver : IPOPT (v3.12)
Solution time : 9.499999985564500E-003 sec
Objective : -50108.7611889392
Successful solution
---------------------------------------------------
x1: 7500.0
x2: 4999.9999497
x3: 2500.0
optimal solution: 50108.761189
optimal solution: 50108.7611890521
expected optimal solution: 50108.76135441651