Подтвердить что ты не робот

Ограничение Scipy Optimizer с использованием двух аргументов

Я пытаюсь максимизировать функцию полезности, найдя оптимальные единицы 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

Что я пробовал:

Я также пробовал передавать P в args, например:

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)}]

Но он говорит мне: "Лямбда хочет 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)
4b9b3361

Ответ 1

Если вы выделили for optimized_a и запустили, вы увидите, что ошибка, которую он выбрасывает, - это ошибка 8 - это положительная производная ошибка.

Оба BFGS и SLSQP - это методы поиска градиента, что означает, что они берут ваше первоначальное предположение и оценивают градиент и его производную и ищут наилучшее направление, чтобы сделать шаг вперед, всегда спускаясь вниз и останавливаясь, когда изменение стоимости либо ниже установленного вами допуска, либо достигает минимального значения.

Ошибка указывает на то, что (по крайней мере, при вашей предполагаемой догадке) проблема не имеет сильной производной. В общем случае SQLSP лучше всего использовать для проблем, которые могут быть сформулированы как сумма квадратов. Возможно, попытка более реалистичного первоначального предположения поможет. Я бы определенно отказался от большей части кода и сначала запустил минимальный пример с optimized_a, и как только вы получите эту работу, остальные могут следовать.

Возможно, будет работать решатель на основе неградиента, или в зависимости от размера проблемы и реалистичных ограничений, которые у вас есть на параметрах, глобальная оптимизация может быть работоспособной.

scipy optimize невелика, если у вас нет хорошей производной для выполнения