2012-03-04 9 views
17

Usando numpy, tengo esta definición de una función:acuerdo con desbordamiento en exp usando 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 

Esta función se evalúa un gran número de veces en una rutina de optimización. A menudo plantea una excepción:

RuntimeWarning: overflow encountered in exp 

Entiendo que el operando no se puede almacenar en el espacio asignado para un flotador. ¿Pero cómo puedo superar el problema?

+2

Deberás adaptar tu algoritmo. Si el valor no encaja, no encaja. Encuentre una forma diferente de expresar el cálculo que no se desborde. –

+1

Lo único sensato que puede hacer es observar el comportamiento asintótico de su función. Si eso es razonable, entonces, por encima de un umbral, puede reemplazar el cálculo explícito por el valor asintótico. Si el valor asintótico no es sensible, entonces es más probable que el problema esté en su elección de algoritmo, no en el código. – DaveP

+1

DaveP, comportamiento asintótico de exp es exp ... –

Respuesta

16

Puede usar el paquete bigfloat. Admite operaciones de coma flotante de precisión arbitraria.

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

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

¿Está utilizando un marco de optimización de la función? Por lo general, implementan límites de valor (utilizando términos de penalización). Trata eso. ¿Son los valores relevantes realmente tan extremos? En la optimización, no es raro minimizar el registro (f). (probabilidad aproximada de registro, etc., etc.) ¿Estás seguro de que quieres optimizar en ese valor exp y no registrar (exp (f)) == f? ?

Tener un vistazo a mi respuesta a esta pregunta: logit and inverse logit functions for extreme values

Por cierto, si todo lo que hacemos es minimizar powellBadlyScaled (x, y), entonces el mínimo está en x -> + inf e y -> + inf, así que no hay necesidad de números.

+1

Exacto, en contexto de optimización, y en cuanto a la función Powell Badly Scaled se utiliza para la prueba, impongo algunas restricciones de cuadro. El script que ejecuté por primera vez dando desbordamientos tuvo restricciones en cuenta para la inicialización (algunas muestras en la caja autorizada), pero no en la rutina principal (no verifiqué las restricciones de caja más). Teniendo en cuenta las restricciones, el operando no se desborda. Voy a intentar bigfloat de todos modos algo más tarde. ¡Gracias! – octoback

1

Quizás pueda mejorar su algoritmo comprobando para qué áreas obtiene advertencias (probablemente debajo de ciertos valores para X [0], X [1]), y reemplazando el resultado con un número realmente grande. Necesitas ver cómo se comporta tu función, lo que debes comprobar es, por ejemplo, exp(-x)+exp(-y)+x*y

0

Dependiendo de sus necesidades específicas , puede ser útil recortar el argumento de entrada al exp(). Si realmente desea obtener un inf si se desborda o si desea obtener números absurdamente enormes, entonces otras respuestas serán más apropiadas.

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

Resultado:

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 

en cuenta que no lo hicieron powellBadlyScaled2 sobre/desbordamiento cuando el original powellBadlyScaled lo hicieron, pero la versión modificada da 1.79769313486e+305inf en lugar de en una de las pruebas. Me imagino que hay un montón de aplicaciones donde 1.79769313486e+305 es prácticamente inf y esto estaría bien, o incluso preferido porque 1.79769313486e+305 es un número real y inf no lo es.

Cuestiones relacionadas