Eu tenho um problema de otimização não linear restrito bastante simples, maximizando a receita, dadas algumas restrições de gastos (manter o gasto geral constante e aumentar / diminuir o gasto em cada canal em até 50%) e uma função de objetivo que é receita = gasto * roi (onde roi é calculado usando os coeficientes do modelo log-log para cada canal).
Em primeiro lugar, a solução não corresponde ao que acredito ser a solução ideal. Além disso, quando pegar os valores de gasto ideais sugeridos por GEKKO e colocá-los na função objetivo, isso também não corresponde ao que GEKKO fornece como o valor de função objetivo ideal.
Se o problema acima não for bem articulado, o código de exemplo abaixo deve trazer à vida o problema ...
Alguém sabe o que estou perdendo?
Código abaixo:
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))
Houve um problema com a definição da função objetivo. A correção para o problema é usar a[i]
, b[i]
, x[i]
um de cada vez nas funções. Gekko permite mais de uma definição de função objetivo. Ao maximizar, ele se converte em um problema de minimização com min = -max
. O objetivo maximizado é, portanto -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]))
Também ajuda a definir uma nova variável sum_x
com limites superior spend_max
e inferior spend_min
.
# Constraints
sum_x = m.Var(lb=spend_min,ub=spend_max)
m.Equation(sum_x==m.sum(x))
Aqui está o roteiro completo.
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}')
A solução é mostrada abaixo.
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
Este artigo é coletado da Internet.
Se houver alguma infração, entre em [email protected] Delete.
deixe-me dizer algumas palavras