Search code examples
scipy-optimize-minimize

scipy.optimize.minimize and Minuit returning initial guess value


I'm having trouble with scipy.minimize.optimize. Here is my code.

from time import process_time 
import numpy as np
from scipy.optimize import minimize
class NMin(object):
    def __init__(self, error):
        self.error=error

    def func(self, N):
        i = np.arange(1, N+1)
        f = np.abs(np.sum(4/(N*(1+((i - 0.5)/N)**2))) - np.pi)-self.error
        return(f)

    def nMin(self):
        x0 = 1
        nMin = minimize(self.func, x0)
        return(nMin.x)


def main():
    t1_start = process_time()
    error=10**(-6)
    nMin = NMin(error).nMin()
    print("the minimum value of N is: " + str(nMin))
    t1_stop = process_time() 
    print("Elapsed time during the whole program in seconds:", 
                                         t1_stop-t1_start)

main ()  

I'm trying to minimize the function func(x) with respect to N to find N minimum, but NMin(error).nMin()seems to be returning x0 = 1and not N minimum. Here is my output.

the minimum value of N is: [1.]
Elapsed time during the whole program in seconds: 0.015625

I'm really bothered by this as I can't seem to find the problem and I don't understand why scipy.optimize is not working.


Solution

  • scipy.optimize.minimize is primarily used for continuous differentiable functions. Using arange in func makes a discrete problem from it. This causes big jumps in the gradient due these discontinuities (see the picture below).

    I added some debug print:

    from time import process_time
    import numpy as np
    from scipy.optimize import minimize
    class NMin(object):
        def __init__(self, error):
            self.error=error
    
        def func(self, N):
            print("func called N = {}".format(N))
            i = np.arange(1, N+1)
            print("i = {}".format(i))
            f = np.abs(np.sum(4/(N*(1+((i - 0.5)/N)**2))) - np.pi)-self.error
            print("f = {}".format(f))
            return(f)
    
        def nMin(self):
            x0 = 1
            nMin = minimize(self.func, x0)
            return(nMin.x)
    
    
    def main():
        t1_start = process_time()
        error=10**(-6)
        nMin = NMin(error).nMin()
        print("the minimum value of N is: " + str(nMin))
        t1_stop = process_time()
        print("Elapsed time during the whole program in seconds:",
                                             t1_stop-t1_start)
    
    main()
    

    This results in:

    func called N = [1.]
    i = [1.]
    f = 0.05840634641020706
    func called N = [1.00000001]
    i = [1. 2.]
    f = 1.289175555623012
    

    Maybe you would like to use a different solver more suitable for discrete problems or change your objective to satisfy the prerequisite of continuity for gradient-based optimization.

    enter image description here