2012-03-04 10 views
17

Utilizzando NumPy, ho questa definizione di una funzione:Deal con troppopieno in exp utilizzando NumPy

def powellBadlyScaled(X): 
    f1 = 10**4 * X[0] * X[1] - 1 
    f2 = numpy.exp(-numpy.float(X[0])) + numpy.exp(-numpy.float(X[1])) - 1.0001 
    return f1 + f2 

Questa funzione viene valutata un enorme numero di volte su una routine di ottimizzazione. Spesso solleva eccezione:

RuntimeWarning: overflow encountered in exp 

comprendo che operando non possono essere immagazzinate nella spazio allocato per un galleggiante. Ma come posso superare il problema?

+2

Avrai bisogno di adattare il tuo algoritmo. Se il valore non si adatta, non si adatta. Trova un modo diverso per esprimere il calcolo che non trabocca. –

+1

L'unica cosa sensata che puoi fare è osservare il comportamento asintotico della tua funzione. Se ciò è sensato, allora al di sopra di qualche soglia puoi sostituire il calcolo esplicito con il valore asintotico. Se il valore asintotico non è sensibile, il problema è molto probabilmente nella scelta dell'algoritmo, non nel codice. – DaveP

+1

DaveP, il comportamento asintotico di exp è exp ... –

risposta

16

È possibile utilizzare il pacchetto bigfloat. Supporta operazioni di virgola mobile di precisione arbitraria.

http://packages.python.org/bigfloat/

import bigfloat 
bigfloat.exp(5000,bigfloat.precision(100)) 
# -> BigFloat.exact('2.9676283840236670689662968052896e+2171', precision=100) 

Si sta utilizzando un framework di ottimizzazione funzione? Solitamente implementano i limiti di valore (usando termini di penalità). Prova questo. I valori rilevanti sono davvero così estremi? Nell'ottimizzazione non è raro minimizzare il log (f). (verosimiglianza approssimativa del registro ecc. ecc.). Sei sicuro di voler ottimizzare su quel valore exp e non loggare (exp (f)) == f. ?

Dai un'occhiata alla mia risposta a questa domanda: logit and inverse logit functions for extreme values

Btw, se tutto ciò che fai è minimizzare powellBadlyScaled (x, y), allora il minimo è in x -> + inf e y -> + inf, quindi non c'è bisogno di numeri.

+1

Esatto, nel contesto di ottimizzazione, e per quanto riguarda la funzione di Powell Badly Scaled utilizzata per il test, impongo alcuni vincoli di casella. Lo script che ho usato per dare gli overflow ha preso in considerazione i vincoli per l'inizializzazione (alcuni campioni nella casella autorizzata), ma non nella routine principale (non ho più controllato i vincoli di casella). Tenendo conto dei vincoli, l'operando non trabocca. Proverò comunque bigfloat qualcosa dopo. Grazie! – octoback

1

Forse puoi migliorare il tuo algoritmo controllando per quali aree ricevi avvertimenti (probabilmente metterà alcuni valori per X [0], X [1]), e sostituendo il risultato con un numero veramente grande. Devi vedere come si comporta la tua funzione, cosa che dovresti controllare ad es. exp(-x)+exp(-y)+x*y

0

A seconda delle esigenze specifiche , potrebbe essere utile ritagliare l'argomento di input su exp(). Se in realtà vuoi ottenere uno fuori se esso trabocca o vuoi ottenere numeri enormi assurdamente, allora altre risposte saranno più appropriate.

def powellBadlyScaled(X): 
    f1 = 10**4 * X[0] * X[1] - 1 
    f2 = numpy.exp(-numpy.float(X[0])) + numpy.exp(-numpy.float(X[1])) - 1.0001 
    return f1 + f2 


def powellBadlyScaled2(X): 
    f1 = 10**4 * X[0] * X[1] - 1 
    arg1 = -numpy.float(X[0]) 
    arg2 = -numpy.float(X[1]) 
    too_big = log(sys.float_info.max/1000.0) # The 1000.0 puts a margin in to avoid overflow later 
    too_small = log(sys.float_info.min * 1000.0) 
    arg1 = max([min([arg1, too_big]), too_small]) 
    arg2 = max([min([arg2, too_big]), too_small]) 
    # print(' too_small = {}, too_big = {}'.format(too_small, too_big)) # Uncomment if you're curious 
    f2 = numpy.exp(arg1) + numpy.exp(arg2) - 1.0001 
    return f1 + f2 

print('\nTest against overflow: ------------') 
x = [-1e5, 0] 
print('powellBadlyScaled({}) = {}'.format(x, powellBadlyScaled(x))) 
print('powellBadlyScaled2({}) = {}'.format(x, powellBadlyScaled2(x))) 

print('\nTest against underflow: ------------') 
x = [0, 1e20] 
print('powellBadlyScaled({}) = {}'.format(x, powellBadlyScaled(x))) 
print('powellBadlyScaled2({}) = {}'.format(x, powellBadlyScaled2(x))) 

Risultato:

Test against overflow: ------------ 
*** overflow encountered in exp 
powellBadlyScaled([-100000.0, 0]) = inf 
powellBadlyScaled2([-100000.0, 0]) = 1.79769313486e+305 

Test against underflow: ------------ 
*** underflow encountered in exp  
powellBadlyScaled([0, 1e+20]) = -1.0001 
powellBadlyScaled2([0, 1e+20]) = -1.0001 

noti che powellBadlyScaled2 invece no over/underflow quando l'originale powellBadlyScaled ha fatto, ma la versione modificata dà 1.79769313486e+305 invece di inf in uno dei test. Immagino ci siano un sacco di applicazioni in cui 1.79769313486e+305 è praticamente inf e questo andrebbe bene, o addirittura preferito perché 1.79769313486e+305 è un numero reale e non lo è inf.