使用两个参数的Scipy优化器约束

2024-04-26 01:04:48 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图通过找到一个人会使用的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。下面是我的实际代码:

^{pr2}$

功能:

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个参数

**更新:**

'args'中使用(F,)不允许程序在不引发错误的情况下运行,但是约束仍然无法维持。在

另外,nanm定义为负值之后返回,这当然会使整个scipy优化陷入混乱。在

**完整项目代码:**

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)

Tags: lambdaformatreturndeftypenparrayutility
1条回答
网友
1楼 · 发布于 2024-04-26 01:04:48

如果隔离for optimized_a并运行,则会看到它抛出的错误是错误8-这是正导数错误。在

BFGS和SLSQP都是梯度搜索方法,这意味着它们接受您的初始猜测,评估梯度及其导数,并寻找最佳的前进方向,总是在值的变化低于您设置的公差或达到最小值时停止。在

这个错误表明(至少在你最初的猜测中),问题没有强导数。一般来说,SQLSP最好用于可以表示为平方和的问题。也许尝试更现实的初步猜测会有所帮助。我肯定会放弃大部分代码,并首先运行一个优化了的最小示例,一旦你成功了,其余的就可以继续了。在

也许一个基于非梯度的解算器可以工作,或者根据问题大小和参数的实际边界,全局优化可能是可行的。在

如果没有一个好的衍生工具来遵循,scipy优化就不是很好

相关问题 更多 >