Optymalizacja w Pythonie - NaN

0

Hej mam do zminimalizowania dość skomplikowaną funkcję która zależy od wielu parametrów i która zawiera sporo funkcji matematycznych typu sinh, cos, pierwiastek itp, czyli funkcje które mają konkretną dziedzine i w momencie gdy chcemy obliczyc jej wartosc w niedozwolonym argumencie to dostajemy NaN. To samo dotyczy dzielenia przez 0 oraz gdy chcemy obliczyc bardzo duza potege jakiejs liczby. Używam funkcji brute z pakietu scipy.optimize i w tej funkcji podaje zakres wartosc dla kazdego argumentu oraz krok o jaki kazdy parametr ma sie zmieniac. W rezultacie dla niektorych wartosci argumentow dostaje wartosc minimlizowanej funkcji NaN i caly algorytm sie psuje i zamiast zwrocic najmniejsza wartosc sposrod wszystkich sprawdzanych to dostaje NaN. Co zrobic zeby NaN nie mialy wplywu na dzialanie algorytmu?

0

Wydaje mi się, że sporo by pomogło gdybyś jednak pokazał tą funkcję.

0
katakrowa napisał(a):

Wydaje mi się, że sporo by pomogło gdybyś jednak pokazał tą funkcję.

Ok oto kod:

def cf(u, T, r, alpha, beta, delta):
    ''' Funkcja charakterystyczna w modelu Kou '''
    b = r - q - 2 * delta * (np.log(np.cos(beta/2))-np.log(np.cos((alpha+beta)/2)))
    value = np.exp(1j * u * b * T)*(np.cos(beta/2)/np.cosh((alpha*u-1j*beta)/2))**(2*delta*T)
    return value

N=2048
L=10


# This class defines puts and calls

class OptionType(enum.Enum):
    CALL = 1.0
    PUT = -1.0
    
def CallPutOptionPriceCOSMthd(cf,CP,S0,r,T,K,alpha, beta, delta,N,L):

    if K is not np.array:
        K = np.array(K).reshape([len(K),1])
    
    # Assigning i=sqrt(-1)

    i = np.complex(0.0,1.0) 
    x0 = np.log(S0 / K)   
    
    # Truncation domain

#     a = 0.0 - L * np.sqrt(T)
#     b = 0.0 + L * np.sqrt(T)
    
    
    c1=T*alpha*delta*np.tan(beta/2)
    c2=0.5*T*alpha**2*delta/((np.cos(beta/2))**2)
#     c4=(3*delta*T+2-np.cos(beta))*alpha**4*delta*T/(4*(np.cos(beta/2))**4)
#     c4=-1/8*T*alpha**4*delta*(1/8*np.cos(3*beta)-9/8*np.cos(beta)-1)/(np.cos(beta/2))**8
    c4=0.25*T*alpha**4*delta*(2-np.cos(beta))/((np.cos(beta/2))**4)
    a=c1-L*np.sqrt(c2+np.sqrt(c4))
    b=c1+L*np.sqrt(c2+np.sqrt(c4))
#     Summation from k = 0 to k=N-1

    k = np.linspace(0,N-1,N).reshape([N,1])  
    u = k * np.pi / (b - a);  

    # Determine coefficients for put prices  

    H_k = CallPutCoefficients(CP,a,b,k)   
    mat = np.exp(i * np.outer((x0 - a) , u))
    temp = cf(u, T, r, alpha, beta, delta) * H_k 
    temp[0] = 0.5 * temp[0]    
    value = np.exp(-r * T) * K * np.real(mat.dot(temp))     
    return np.exp(-q*T)*S0+ value - K*np.exp(-r*T)

# Determine coefficients for put prices 

def CallPutCoefficients(CP,a,b,k):
    if CP==OptionType.CALL:                  
        c = 0.0
        d = b
        coef = Chi_Psi(a,b,c,d,k)
        Chi_k = coef["chi"]
        Psi_k = coef["psi"]
        if a < b and b < 0.0:
            H_k = np.zeros([len(k),1])
        else:
            H_k      = 2.0 / (b - a) * (Chi_k - Psi_k)  
    elif CP==OptionType.PUT:
        c = a
        d = 0.0
        coef = Chi_Psi(a,b,c,d,k)
        Chi_k = coef["chi"]
        Psi_k = coef["psi"]
        H_k      = 2.0 / (b - a) * (- Chi_k + Psi_k)               
    
    return H_k    

def Chi_Psi(a,b,c,d,k):
    psi = np.sin(k * np.pi * (d - a) / (b - a)) - np.sin(k * np.pi * (c - a)/(b - a))
    psi[1:] = psi[1:] * (b - a) / (k[1:] * np.pi)
    psi[0] = d - c
    
    chi = 1.0 / (1.0 + np.power((k * np.pi / (b - a)) , 2.0)) 
    expr1 = np.cos(k * np.pi * (d - a)/(b - a)) * np.exp(d)  - np.cos(k * np.pi 
                  * (c - a) / (b - a)) * np.exp(c)
    expr2 = k * np.pi / (b - a) * np.sin(k * np.pi * 
                        (d - a) / (b - a))   - k * np.pi / (b - a) * np.sin(k 
                        * np.pi * (c - a) / (b - a)) * np.exp(c)
    chi = chi * (expr1 + expr2)
    
    value = {"chi":chi,"psi":psi }
    return value

def Meixner_error_function(p0):
    
    global m, local_opt, opt1
    alpha, beta, delta=p0
    
    se = []
    for row, option in calls.iterrows():
        model_value = CallPutOptionPriceCOSMthd(cf,OptionType.PUT,S0,r,option['T'],(option['strike'],),alpha, beta, delta,N,L)[0][0]
        se.append((model_value - option['price']) ** 2)
    MSE = sum(se) / len(se)
    if m % 1 == 0:
        print('%4d |' % m, np.array(p0), '| %7.3f' % MSE)
        
    m += 1
#     if local_opt:
#         penalty = np.sqrt(np.sum((np.array(p0)-np.array(opt1))  ** 2))
#         return MSE + penalty
    return MSE**0.5


    opt1 = spo.brute(Meixner_error_function, ((0.001,2.51, 0.2),(-np.pi,np.pi, 0.5), (0.01,2,0.16)), finish=None)

Minimalizuje funkcje Meixner_error_function. W nawiasach sa przedzialy dla kazdego argumentu. Niestety funkcja jest na tyle skomplikowana ze po prostu w pewnym momencie liczona jest jej wartosc w nieprawidlowych punktach i w rezultacie dostaje NaN. Najlepszym rozwiazaniem byloby zeby w takim przypadku Python zamiast zwracac NaN zwracał np. 1000000, wowczas jest to na tyle wysoka wartosc ze nie przeszkadzalaby w prawidlowym dzialaniu algorytmu

0

Funkcje nie mogą Ci zwracać NaN, zdebuguj, żeby zobaczyć co tam się naprawdę dzieje.

0

No ale dla niektorych argumentow np liczymy e^2000 a taka liczba jest za duża. To samo dotyczy liczenia logarytmu z liczby ujemnej - nie da sie. Chce tak zrobic zeby takie przypadki nie psuly algorytmu a recznie nie da rady ich wykluczyc. Na zdjeciu widac kolejne parametry i wartosc minimalizowanej funkcji dla nich. W pewnym momencie parametry sa takie ze wyskakuje blad i dalej algorytm juz nie bierze pod uwage tych normalnychliczb tylko zwraca parametry dlaktorych funkcja ma wartosc NaN

0

Debugowałeś? Jak program ma liczyć poprawnie z logarytmem z liczby ujemnej?

0

Chce tak zrobic zeby takie przypadki nie psuly algorytmu

Obawiam się że tak to się nie da zrobić. Przecież masz tu jakiś algorytm optymalizacyjny. Cała idea takich algorytmów polega na tym, że maja jakiś "kierunek", czy to będzie jakiś gradient descent czy coś innego to bez różnicy. Jak nagle zmienisz wynik na coś losowego, to cała ta optymalizacja idzie w las przecież.

Zresztą "poprawnym" rozwiązaniem byłoby jednak ustalenie dziedziny twojej funkcji dokładnie.

Ad samej precyzji obliczeń, może warto pomyśleć o użyciu gmpy2 i mpfr?

1 użytkowników online, w tym zalogowanych: 0, gości: 1