2016-01-12 18 views
18

Sto cercando di ottimizzare una funzione di utilità trovando le unità N ottimali che una persona potrebbe utilizzare. Uno dei limiti è che hanno denaro finito, m. Quindi sto provando a impostare un vincolo in cui la matrice di lunghezza 3, P, anche una serie di lunghezza 3, non può essere maggiore di , non può essere maggiore di m.Limite di Scipy Optimizer utilizzando due argomenti

Come l'esempio come segue:

P = np.array([3,4,5]) 
N = np.array([1,2,1]) 
m = 50 
sum(P*N) > m 

Per questa ottimizzazione, P viene dato sulla base di un'ottimizzazione del precedente. Ora qui è il mio codice vero e proprio:

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) 

La funzione:

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

Il problema è che ancora ottenere negativo m! Quindi chiaramente non sto passando il vincolo correttamente. Immagino che sia perché lo P non viene usato correttamente?

uscita:

N: [ 1. 1. 1.] 
P: [ 5. 14. 4.] 
Q: [ 1. 3. 1.] 
T: [ 1. 1. 1.01] 
m in C: -13.0 

Quello che ho provato:

Ho anche provato passando P in args, in questo modo:

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

ma racconta me `Lambda vuole 2 argomenti e ha ricevuto 4

.210

** Aggiornamento: **

utilizzando (F,) in 'args' non consente l'esecuzione del programma senza sollevare un errore, ma il vincolo non riesce ancora a reggere.

Inoltre, nan viene restituito dopo m è definito come un valore negativo, che ovviamente getta l'intera ottimizzazione scipy di wack.

** codice di progetto completa: **

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) 
+1

C'è un motivo per cui si usa tutto nel vostro vincolo? – Moritz

+0

E penso che si lamenta perché vede la lista con tre elementi di P. Prova 'args = (P,)' – Moritz

+0

@Moritz continua a non funzionare purtroppo – thefoxrocks

risposta

3

Se si isola il per optimized_a e correre, si vede l'errore getta è errore 8 - questo è l'errore derivato positivo.

Sia BFGS che SLSQP sono metodi di ricerca con gradiente, il che significa che prendono la tua ipotesi iniziale e valutano il gradiente e la sua derivata e cercano la direzione migliore per fare un passo avanti, sempre in discesa e fermandosi quando il cambiamento il valore è inferiore alla tolleranza impostata o raggiunge il minimo.

L'errore indica che (almeno a livello di intuizione), il problema non ha un derivato forte. In generale, SQLSP viene utilizzato al meglio su problemi che possono essere formulati come somma di quadrati. Forse provare a provare ipotesi più realistiche potrebbe aiutare. Scarterei definitivamente la maggior parte del codice ed eseguirò un esempio minimale con optimized_a prima, e una volta capito, il resto può seguire.

Forse un risolutore basato su non gradiente potrebbe funzionare o, a seconda delle dimensioni del problema e dei limiti realistici che si hanno sui parametri, un'ottimizzazione globale può essere praticabile.

SciPy ottimizzare non è grande se non si dispone di un bel derivato da seguire