pythonscipysolverscipy-optimizefsolve

How to improve function using fsolve?


I want to use the fsolve one by one for each iteration. Supposed I have DataFrame look like below:

PD Downturn PD TTC
0.12 0.008
0.15 0.016
0.24 0.056
0.56 0.160
1.00 1.000

Here is the code I try:

result = []
for i in range(len(df) - 1):
  def R(x):
    ZDownturn = norm.ppf(df['PD Downturn'])[i] #Exclude 'Default' class
    ZShift = np.sqrt(
        x / (1 - x)
    ) * norm.ppf(0.999)
    ZPortion = np.sqrt(
        1 / (1 - x)
    ) * norm.ppf(df['PD TTC'])[i] #Exclude 'Default' class
    target = ZShift + ZPortion
    error =  np.abs(ZDownturn - target)
    return error

  # Initial guess
  x0 = [0.01]
  
  # Solver
  Rho = fsolve(R, x0)
  result.append(Rho[0])

I want to find x variable based on some calculation logic but I need to do it one-by-one. So, I create the new function in every iteration. The results are below:

[0.19153452995548875,
 0.15906256238706026,
 0.08759684851688349,
 0.1348702069117432]

It's work but I am looking for another way maybe more pythonic way to write the code.

Thank you.


Solution

  • You're probably looking for a vectorized version of your objective function. That's exactly where numpy shines:

    import numpy as np
    from scipy.optimize import fsolve
    from scipy.stats import norm
    
    pd_downturn = np.array([0.12, 0.15, 0.24, 0.56])
    pd_ttc = np.array([0.008, 0.016, 0.056, 0.160])
    #pd_downturn = df['PD Downturn'].values[:-1]
    #pd_ttc = df['PD TTC'].values[:-1]
    
    def R(x):
        ZDownturn = norm.ppf(pd_downturn)
        ZShift = np.sqrt(x / (1 - x)) * norm.ppf(0.999)
        ZPortion = np.sqrt(1 / (1 - x)) * norm.ppf(pd_ttc)
        target = ZShift + ZPortion
        error = (ZDownturn - target)**2
        return error
    
    # Initial guess
    x0 = 0.01*np.ones(pd_downturn.size)
      
    # Solver
    result = fsolve(R, x0)
    

    Note note you should try to avoid np.abs for root-finding or optimization problems whenever possible, just square the difference instead. The absolute value function is not differentiable at the point where its argument is zero and thus you'd be violating the mathematical assumptions of most algorithms under the hood of fsolve.