Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python scipy.optimize: Using fsolve with multiple first guesses

Scipy version 0.10.0

Consider the following:

>>> import math
>>> from scipy.optimize import fsolve
>>> import numpy as np
>>> def p(s, l, k, q):
    p = q * np.maximum(s - k, 0.0)
    return (p + math.copysign(l, -q)) * math.fabs(q) * 100.0

>>> x0 = fsolve(p, np.arange(33.86, 50.86, 1.0), args=(1.42, 41.0, -1.0), xtol=1e-06, maxfev=500)
Warning (from warnings module):
  File "C:\Python27\lib\site-packages\scipy\optimize\minpack.py", line 152
    warnings.warn(msg, RuntimeWarning)
RuntimeWarning: The iteration is not making good progress, as measured by the 
   improvement from the last ten iterations.
>>> print x0
[ -4.87169392e+05  -4.87168392e+05  -4.87167392e+05  -4.87166392e+05
  -4.87165392e+05  -4.87164392e+05  -4.87163392e+05  -4.87162392e+05
   4.24200000e+01   4.24200000e+01   4.24200000e+01   4.24200000e+01
   4.24200000e+01   4.24200000e+01   4.24200000e+01   4.24200000e+01
   4.24200000e+01]

First question is how one might suppress the warning message that's being returned?

Second, why might this error be generated in the first place (other than the obvious, that the iteration is not making good progress :) )?

Finally, the root of this function is 42.42 (which is found). Why is fzero returning -4.87e+05 as well?

like image 760
Jason Strimpel Avatar asked Oct 24 '12 20:10

Jason Strimpel


1 Answers

Doing this might make you miss something important, but, to silence the warning message you could use warnings.filterwarnings:

import warnings
warnings.filterwarnings('ignore', 'The iteration is not making good progress')
import math
from scipy.optimize import fsolve
import numpy as np
def p(s, l, k, q):
    p = q * np.maximum(s - k, 0.0)
    return (p + math.copysign(l, -q)) * math.fabs(q) * 100.0

x0 = fsolve(p, np.arange(33.86, 50.86, 1.0),
            args=(1.42, 41.0, -1.0), xtol=1e-06, maxfev=500)
print(x0)

In fact, p(x0, 1.42, 41.0, -1) is not close to zero, so fsolve is correctly warning you that it failed to find a solution.


PS. When you say

fsolve(p, np.arange(33.86, 50.86, 1.0),...)

you are telling fsolve that your initial guess for s is the numpy array np.arange(33.86, 50.86, 1.0). The whole array is being passed in to p at once.

Notice that np.arange(33.86, 50.86, 1.0) has length 17 and so does x0. That is because fsolve thinks it is looking for an array of length 17 that solves p.

I think perhaps you meant s to be a float? In that case, you can only pass in one float value for your initial guess:

fsolve(p, 41.0, args = (1.42, 41.0, -1.0), xtol=1e-06, maxfev=500)

For example,

import math
import scipy.optimize as optimize
import numpy as np

def p(s, l, k, q):
    p = q * np.maximum(s - k, 0.0)
    return (p + math.copysign(l, -q)) * math.fabs(q) * 100.0

args = (1.42, 41.0, -1.0)
result = optimize.fsolve(p, 41.0, args=args, xtol=1e-06, maxfev=500)
print(result)

yields

[ 42.42]

fsolve does a decent job of zeroing-in on the root if the initial guess is >= 41.0 (the value of k) but fails when the initial guess is < 41.0.

My guess is that this is due to np.maximum not changing for many guesses for s. So fsolve does not know whether to increase or decrease s and is apt to guess wrong and move s farther and farther from the root.

like image 79
unutbu Avatar answered Oct 08 '22 11:10

unutbu