我试图通过找到一个人会使用的最佳N
单位来最大化效用函数。其中一个限制因素是他们拥有有限的资金,m
。因此,我尝试设置约束,其中长度为3的数组N
乘以价格P
也是长度为3的数组,不能大于m
。
如下例所示:
P = np.array([3,4,5])
N = np.array([1,2,1])
m = 50
sum(P*N) > m
对于此优化,P
基于先前的优化给出。现在这是我的实际代码:
cons_c = [{'type':'ineq', 'fun': lambda N: 10 - sum(np.round(N)*P)},{'type':'ineq', 'fun': lambda N: 24 - sum(N*T)}]
bnds = [(0.,None) for x in range(len(N))]
optimized_c = scipy.optimize.minimize(utility_c, N, (P,Q,T), method='SLSQP', bounds=bnds, constraints=cons_c)
功能:
def utility_c(N,P,Q,T):
print "N: {0}".format(N)
print "P: {0}".format(P)
print "Q: {0}".format(Q)
print "T: {0}".format(T)
N = np.round(N)
m = 10 - sum(N*P)
b = sum(N*Q)
t = 24 - sum(N*T)
print "m in C: {0}".format(m)
print "b: {0}".format(b)
print "t: {0}".format(t)
# if m < 0 or t < 0:
# return 0
return 1/ ((b**0.3)*(t**0.7))+(5*(m**0.5))
问题是我仍然被否定m
!很明显,我没有正确地传递约束。我猜这是因为P
没有正确使用?
输出:
N: [ 1. 1. 1.]
P: [ 5. 14. 4.]
Q: [ 1. 3. 1.]
T: [ 1. 1. 1.01]
m in C: -13.0
我尝试过的事情:
我还尝试在args中传递P
,如下所示:
cons_c = [{'type':'ineq', 'fun': lambda N,P: 10 - sum(np.round(N)*P), 'args':P},{'type':'ineq', 'fun': lambda N: 24 - sum(N*T)}]
但是它告诉我`Lambda需要2个参数并且收到4个
**更新:**
在(F,)
中使用'args'
不允许程序在不引发错误的情况下运行,但约束仍然无法阻止。
此外,nan
被定义为负值后返回m
,这当然会将整个scipy优化抛出wack。
**完整的项目代码:**
import scipy.optimize
import numpy as np
import sys
def solve_utility(P,Q,T):
"""
Here we are given the pricing already (P,Q,T), but solve for the quantities each type
would purchase in order to maximize their utility (N).
"""
def utility_a(N,P,Q,T):
N = np.round(N)
m = 50 - sum(N*P)
b = sum(N*Q)
t = 8 - sum(N*T)
return 1/ ((b**0.5)*(t**0.5))+(5*(m**0.5))
def utility_b(N,P,Q,T):
N = np.round(N)
m = 50 - sum(N*P)
b = sum(N*Q)
t = 8 - sum(N*T)
return 1/ ((b**0.7)*(t**0.3))+(5*(m**0.5))
def utility_c(N,P,Q,T):
N = np.round(N)
print "N: {0}".format(N)
print "P: {0}".format(P)
print "Q: {0}".format(Q)
print "T: {0}".format(T)
m = 10 - sum(N*P)
b = sum(N*Q)
t = 24 - sum(N*T)
print "m in C: {0}".format(m)
print "b: {0}".format(b)
print "t: {0}".format(t)
return 1/ ((b**0.3)*(t**0.7))+(5*(m**0.5))
# Establishing constraints so no negative money or time:
N = np.array([2,2,1])
cons_a = [{'type':'ineq', 'fun': lambda N, P: 50 - sum(np.round(N)*P), 'args':(P,)},{'type':'ineq', 'fun': lambda N: 8 - sum(N*T)}]
cons_b = [{'type':'ineq', 'fun': lambda N, P: 50 - sum(np.round(N)*P), 'args':(P,)},{'type':'ineq', 'fun': lambda N: 8 - sum(N*T)}]
cons_c = [{'type':'ineq', 'fun': lambda N, P: 10 - sum(np.round(N)*P), 'args':(P,)},{'type':'ineq', 'fun': lambda N: 24 - sum(N*T)}]
maxes = P/50
bnds = [(0.,None) for x in range(len(N))]
b = [()]
optimized_a = scipy.optimize.minimize(utility_a, N, (P,Q,T), method='SLSQP', constraints=cons_a)
optimized_b = scipy.optimize.minimize(utility_b, N, (P,Q,T), method='SLSQP', constraints=cons_b)
optimized_c = scipy.optimize.minimize(utility_c, N, (P,Q,T), method='SLSQP', constraints=cons_c)
if not optimized_a.success:
print "Solving Utilities A didn't work..."
return None
if not optimized_b.success:
print "Solving Utilities B didn't work..."
return None
if not optimized_c.success:
print "Solving Utilities C didn't work..."
return None
else:
print "returning N: {0}".format(np.array([optimized_a.x,optimized_b.x,optimized_c.x]))
return np.array([optimized_a.x,optimized_b.x,optimized_c.x])
# solve_utility(P,Q,T,N)
def solve_profits():
"""
Here we build the best pricing strategy to maximize solve_profits
"""
P = np.array([ 3, 10.67, 2.30]) # Pricing
Q = np.array([ 1, 4, 1]) # Quantity of beer for each unit
T = np.array([ 1, 1, 4]) # Time cost per unit
N = np.array([ 1, 0, 1]) # Quantities of unit taken by customer
def profit(X):
P,Q,T = X[0:3], X[3:6], X[6:9]
Q[1] = round(Q[1]) # needs to be an integer
N = solve_utility(P,Q,T)
print "N: {0}".format(N)
N = np.sum(N,axis=1)
# print "P: {0}".format(P)
# print "Q: {0}".format(Q)
# print "T: {0}".format(T)
denom = sum(N*P*Q) - sum(Q*N)
return 1/ (sum(N*P*Q) - sum(Q*N))
cons = [{'type':'ineq', 'fun': lambda X: X[8] - X[6] - 0.01 }, # The time expense for a coupon must be 0.01 greater than regular
{'type':'ineq', 'fun': lambda X: X[4] - 2 }, # Packs must contain at least 2 beers
{'type':'eq', 'fun': lambda X: X[3] - 1}, # Quantity has to be 1 for single beer
{'type':'eq', 'fun': lambda X: X[5] - 1}, # same with coupons
{'type':'ineq', 'fun': lambda X: X[6] - 1}, # Time cost must be at least 1
{'type':'ineq', 'fun': lambda X: X[7] - 1},
{'type':'ineq', 'fun': lambda X: X[8] - 1},
]
X = np.concatenate([P,Q,T])
optimized = scipy.optimize.minimize(profit, X, method='L-BFGS-B', constraints=cons)
if not optimized.success:
print "Solving Profits didn't work..."
else:
return optimized.x, N
X, N = solve_profits()
print "X: {0} N {1}".format(X,N)
P,Q,T = X[0:3], X[3:6], X[6:9]
rev = sum(P * Q * N)
cost = sum(Q * N)
profit = (rev-cost)*50
print "N: {0}".format(N)
print "P: {0}".format(P)
print "Q: {0}".format(Q)
print "T: {0}".format(T)
print "profit = {0}".format(profit)
答案 0 :(得分:3)
如果你隔离了for optimized_a并运行,你会看到它抛出的错误是错误8 - 这是正导数错误。
BFGS和SLSQP都是梯度搜索方法,这意味着它们会进行初步猜测,并评估梯度及其导数,并寻找最佳方向,一直走下坡路,并在价值变化时停下来低于您设定的容差或达到最小值。
错误表明(至少在您的初步猜测中),问题没有强大的衍生物。通常,SQLSP最适用于可以表示为平方和的问题。也许尝试更现实的初步猜测会有所帮助。我肯定会抛弃大部分代码并首先使用optimized_a运行一个最小的例子,一旦你完成了工作,其余的就可以了。
也许基于非梯度的求解器可以工作,或者根据问题大小和参数的实际界限,全局优化可能是可行的。
如果你没有一个很好的衍生产品,那么scipy optimize就不是很好了