Scipy.minimize 没有正确最小化

Scipy.minimize not correctly minimizing

我目前正在做一个项目,当你离开银河系中心时绘制恒星的速度(到中心的距离用 r 表示)。

本质上,我的目标是最小化模型函数和观察函数之间的距离。为此,我必须最小化函数:np.sum(((v_model-v_obs)/errors)**2) 其中 errors 是每个不同 r 值的错误数组。 v_obs 是在每个 r 值处观察到的速度(r 只是一个数字数组)。为了最小化函数,我必须操纵 v_model 这可以通过操纵方程中的两个 "fixing parameters" p0r0 来完成(被积函数如下所示):

np.sqrt(4.302*10**(-6)*quad(integrand,0,r.all(),args=(p0,r0))[0]/r.all())

在进入问题之前,我想知道 r.all() 是否合适,因为它不允许我放置 r 因为它是一个数组。我的另一种选择是通过以下方式制作一个 v_model 数组:

#am is the amount of elements in the array r 
#r, v_model,v_obs, and errors all have the same size
def integrand(r,p0,r0):
    return (p0 * r0**3)/((r+r0)*((r**2)+(r0**2)))*4*3.1415926535*r**2
integrals = []
for i in r:
     integrals.append(quad(integrand, 0 ,i,args=(p0,r0)))

v_model = []


for x in range (0,am):
    k = integrals[x][0]
    i = r[x]
    v_model.append(np.sqrt((4.302*10**(-6)*k)/i))

不管,最小化函数np.sum(((v_model-v_obs)/errors)**2) 我试着做这样的事情:

def chisqfunc(parameters):
    p0 = parameters[0]
    r0 = parameters[1]
    v_model = []
    for x in range(0,am):
        v_model.append(np.sqrt(4.302*10.0**(-6)*quad(integrand, 0, r[x], args=(p0,r0))[0]/r[x]))
    chisq = np.sum(((v_model-v_obs)/errors)**2)
    return chisq
x0 = np.array([10**6,24])
resolution = minimize(chisqfunc,x0)

但是,我得到的值根本不合适(当我绘制观察到的数据和我的模型时很明显)

总而言之,我有两个主要问题:

1.) 我的函数是否采用模型减去每个不同 r 值的观察值,如果不是,我该如何解决? (我想我搞砸了我的 v_model 等式)

2.) 为什么返回 r0p0 的错误数字?

这是我的完整代码(顺便说一句,了解最小化是否正常工作:r0 应该在 1.5 左右,p0 应该在:3.5*10**8 左右)

from scipy.optimize import*
import numpy as np
import matplotlib.pyplot as plt
import scipy.integrate as integrate
from scipy.integrate import quad

#number of measurements
am = 18


r0 = 1.8
p0 = 3.5*10**8.62

#Observed Data
v_obs = np.array([
234.00,
192.00,
212.00,
223.00,
222.00,
224.00,
224.00,
226.00,
226.00,
227.00,
227.00,
224.00,
220.00,
218.00,
217.00,
216.00,
210.00,
208.00



]
)

r = np.array([0.92, 
2.32,
3.24,   
4.17,   
5.10,   
6.03,   
6.96,   
7.89,   
8.80,   
9.73,   
10.64,
11.58,
12.52,
13.46,
14.40,
15.33,
16.27,
17.11


]
)

errors = np.array([
3.62,
4.31,
3.11,
5.5,
3.9,
3.5,
2.7,
2.5,
2.3,
2.1,
2.3,
2.6,
3.1,
3.2,
3.2,
3.1,
2.9,
2.8
    ])


#integral 
def integrand(r,p0,r0):
    return (p0 * r0**3)/((r+r0)*((r**2)+(r0**2)))*4*3.1415926535*r**2
integrals = []
for i in r:
     integrals.append(quad(integrand, 0 ,i,args=(p0,r0)))

v_model = []


for x in range (0,am):
    k = integrals[x][0]
    i = r[x]
    v_model.append(np.sqrt((4.302*10**(-6)*k)/i))


def chisqfunc(parameters):
    p0 = parameters[0]
    r0 = parameters[1]
    v_model = np.sqrt(4.302*10**(-6)*quad(integrand,0,r.all(),args=(p0,r0))[0]/r.all())
    chisq = np.sum(((v_model-v_obs)/errors)**2)
    print(v_model)
    return chisq
x0 = np.array([10**6,24])
resolution = minimize(chisqfunc,x0)
print("This is the function",resolution)

如果我遗漏了任何数据,请告诉我,在此先感谢您!

我一直在玩你的代码。我尝试了各种 methods/solvers,它们都收敛到大致相同的答案。我认为这是它可以达到的最佳答案。为了对此进行测试,我计算了您认为正确的答案(r0 = 1.8,p0 = 3.5 * 10 ** 8.62)和计算出的答案的差异和平方根。计算出的答案似乎有更小的错误(527 对 564)。

from scipy.optimize import*
import numpy as np
import matplotlib.pyplot as plt
import scipy.integrate as integrate
from scipy.integrate import quad
#-----------------------------------------------------------------------------
#number of measurements
am = 18
r0 = 1.8
p0 = 3.5*10.0**8.62
#Observed Data
v_obs = np.array([
234.00, 192.00, 212.00, 223.00, 222.00, 
224.00, 224.00, 226.00, 226.00, 227.00, 
227.00, 224.00, 220.00, 218.00, 217.00, 
216.00, 210.00, 208.00,
])
r = np.array([
0.92, 2.32, 3.24, 4.17, 5.10,   
6.03, 6.96, 7.89, 8.80, 9.73,   
10.64, 11.58, 12.52, 13.46, 14.40,
15.33, 16.27, 17.11
])
errors = np.array([
3.62, 4.31, 3.11, 5.5, 3.9,
3.5, 2.7, 2.5, 2.3, 2.1,
2.3, 2.6, 3.1, 3.2, 3.2,
3.1, 2.9, 2.8
])
#-----------------------------------------------------------------------------
#integral 
def integrand(r,p0,r0):
    return (p0 * r0**3)/((r+r0)*((r**2)+(r0**2)))*4.0*3.1415926535*r**2
#-----------------------------------------------------------------------------
integrals = []
for i in r:
    integrals.append(quad(integrand, 0, i, args=(p0,r0)))
#print "integrals = " + str(integrals)
v_model = []
for x in range(0,am):
    k = integrals[x][0]
    i = r[x]
    v_model.append(np.sqrt((4.302*10.0**(-6)*k)/i))
#print "v_model = " + str(v_model)
#-----------------------------------------------------------------------------
def chisqfunc(parameters):
    p0 = parameters[0]
    r0 = parameters[1]
    v_model = np.sqrt(4.302*10.0**(-6)*quad(integrand, 0, r.all(), args=(p0,r0))[0]/r.all())
    chisq = np.sum(((v_model - v_obs)/errors)**2)
    #print "chisq = " + str(chisq)
    #print "v_model = " + str(v_model)
    return chisq
#-----------------------------------------------------------------------------
x0 = np.array([10.0**6,24.0]) # initial guess
# try various methods (they all give about the same answer, they all "converged")
#resolution = minimize(chisqfunc, x0, method='Nelder-Mead', tol=0.001)
#resolution = minimize(chisqfunc, x0, method='Powell', tol=0.001)
#resolution = minimize(chisqfunc, x0, method='CG', tol=0.001)
#resolution = minimize(chisqfunc, x0, method='BFGS', tol=0.001)
# Jac required - resolution = minimize(chisqfunc, x0, method='Newton-CG', tol=0.001)
#resolution = minimize(chisqfunc, x0, method='L-BFGS-B', tol=0.001)
#
resolution = minimize(chisqfunc, x0, method='TNC', tol=0.001, 
                      options={'scale':None})
# options tried
# 'gtol':1e-6, 'xtol':-1, 'rescale':0, 'ftol':0, 'accuracy':0, 'offset':None, 'scale':None
#
#resolution = minimize(chisqfunc, x0, method='COBYLA', tol=0.001)
#resolution = minimize(chisqfunc, x0, method='SLSQP', tol=0.001)
# Jac required - resolution = minimize(chisqfunc, x0, method='dogleg', tol=0.001)
# Jac required - resolution = minimize(chisqfunc, x0, method='trust-ncg', tol=0.001)
# unknown solver - resolution = minimize(chisqfunc, x0, method='trust-exact', tol=0.001)
# unknown solver - resolution = minimize(chisqfunc, x0, method='trust-krylov', tol=0.001)
print("This is the function",resolution)
print "resolution.x"
print resolution.x
print "p0 should be = " + str(3.5*10**8)
print "r0 should be = " + str(1.5)
#-----------------------------------------------------------------------------
# check answers to see which is better
for index in range(len(v_obs)):
    # declared answer---------------------------------------------------------
    p0 = 3.5*10**8
    r0 = 1.5
    integrals = []
    for i in r:
        integrals.append(quad(integrand, 0, i, args=(p0,r0)))
    #print "integrals = " + str(integrals)
    v_model_declared = []
    sum_error1 = 0.0
    for x in range(0,am):
        k = integrals[x][0]
        i = r[x]
        v_model_declared.append(np.sqrt((4.302*10.0**(-6)*k)/i))
        error1 = (v_obs[x] - v_model_declared[x])**2
        sum_error1 += error1
    #print "v_model = " + str(v_model)

    # computed answer---------------------------------------------------------
    p0 = 8.06400536e+06
    r0 = 4.27905063e+02
    integrals = []
    for i in r:
        integrals.append(quad(integrand, 0, i, args=(p0,r0)))
    #print "integrals = " + str(integrals)
    v_model_computed = []
    sum_error2 = 0.0
    for x in range(0,am):
        k = integrals[x][0]
        i = r[x]
        v_model_computed.append(np.sqrt((4.302*10.0**(-6)*k)/i))
        error2 = (v_obs[x] - v_model_computed[x])**2
        sum_error2 += error2

    print "v_obs, v_model, v_model2 = " + str(v_obs[index]) + ", " + str(v_model_declared[index])+ ", " + str(v_model_computed[index])
#
print "sum_error1 (declared answer) = " + str(sum_error1)
print "sum_error2 (computed answer) = " + str(sum_error2)
print "sqrt(sum_error1) (declared answer) = " + str(np.sqrt(sum_error1))
print "sqrt(sum_error2) (computed answer) = " + str(np.sqrt(sum_error2))
#-----------------------------------------------------------------------------

如果您在函数中更改 p0,它会降低优化程序例程中的数字。这似乎工作得很好,我猜数字太大了,导致求解器出现一些数字错误。只需记住将 p0 答案乘以 3.85e+09,就像在 chisqfunc 例程中所做的那样。

from scipy.optimize import*
import numpy as np
import matplotlib.pyplot as plt
import scipy.integrate as integrate
from scipy.integrate import quad
#-----------------------------------------------------------------------------
am = 18 # number of measurements
#Observed Data
v_obs = np.array([
234.00, 192.00, 212.00, 223.00, 222.00, 224.00, 224.00, 226.00, 226.00, 227.00, 
227.00, 224.00, 220.00, 218.00, 217.00, 216.00, 210.00, 208.00,
])
r = np.array([
 0.92,  2.32,  3.24,  4.17,  5.10,  6.03,  6.96,  7.89, 8.80, 9.73,   
10.64, 11.58, 12.52, 13.46, 14.40, 15.33, 16.27, 17.11
])
errors = np.array([
3.62, 4.31, 3.11, 5.5, 3.9, 3.5, 2.7, 2.5, 2.3, 2.1,
2.3, 2.6, 3.1, 3.2, 3.2, 3.1, 2.9, 2.8
])
grav_const = 4.302e-06
#print "np.pi, grav_const = " + str(np.pi) + ", " + str(grav_const)
#-----------------------------------------------------------------------------
def func_to_integrate(rx, p0, r0):
    rho = (p0 * r0**3) / ( (rx + r0) * (rx**2 + r0**2) )
    function_result = rho * 4.0 * np.pi * rx**2
    #print "rx, p0, r0 = " + str(rx) + ", " + str(p0) + ", " + str(r0)
    return function_result
#-----------------------------------------------------------------------------
def simpsonsRule(func, a, b, n, p0, r0):
    if n%2 == 1:
        return "Not applicable"
    else:
        h = (b - a) / float(n)
        s = func(a, p0, r0) + sum((4 if i%2 == 1 else 2) * func(a+i*h, p0, r0) for i in range(1,n)) + func(b, p0, r0)
        return s*h/3.0
#-----------------------------------------------------------------------------
def chisqfunc(iter_vars):
    global v_model
    p0 = iter_vars[0] * 3.85e+09
    r0 = iter_vars[1]
    v_model = []
    for index in range(0, am):
        integral_result = simpsonsRule(func_to_integrate, 0.0, r[index], 200, p0, r0)
        v_mod_result = np.sqrt(grav_const * integral_result / r[index])
        v_model.append(v_mod_result)
    chisq = 0.0
    for index in range(0, am):
        chisq += ((v_model[index] - v_obs[index])/errors[index])**2
        #chisq += ((v_model[index] - v_obs[index]))**2
    chisq = np.sqrt(chisq)
    #chisq = np.sum(((v_model - v_obs)/errors)**2)
    print "chisq = " + str(chisq)
    #print "iterated p0, r0 = " + str(p0) + ", " + str(r0)
    return chisq
#-----------------------------------------------------------------------------
initial_guess = np.array([1.0, 2.0])
#initial_guess = np.array([3.5*10**8.62, 1.5])
#resolution = minimize(chisqfunc, initial_guess, method='TNC') # , tol=1e-12, options={'accuracy':0})
#resolution = minimize(chisqfunc, initial_guess, method='TNC', options={'rescale':-1})
#resolution = minimize(chisqfunc, initial_guess, method='CG')
resolution = minimize(chisqfunc, initial_guess, method='Nelder-Mead')
# # options tried
# 'gtol':1e-6, 'xtol':-1, 'rescale':0, 'ftol':0, 'accuracy':0, 'offset':None, 'scale':None
print("This is the function",resolution)
#print "resolution.x"
#print resolution.x
print "iterated p0 = " + str(resolution.x[0])
print "iterated p0 * 3.85e+09 = " + str(resolution.x[0] * 3.85e+09)
print "iterated r0 = " + str(resolution.x[1])
print "p0 should be about = " + str(3.5e+08)
print "r0 should be about = " + str(1.5)
print "-----------------------------------------------"
print "iterated model errors"
for index in range(0,am):
    print "v_obs, v_model, error = " + str(v_obs[index]) + ", " + str(v_model[index]) + ", " + str(v_obs[index] - v_model[index])
chisq = 0.0
for index in range(0, am):
    chisq += ((v_model[index] - v_obs[index])/errors[index])**2
    #chisq += ((v_model[index] - v_obs[index]))**2
chisq = np.sqrt(chisq)
print "iterated chisq = " + str(chisq)
print ""
#-----------------------------------------------------------------------------
print "model errors using p0 = 3.5*10**8.62, r0 = 1.8"
p0 = 3.5*10**8.62
r0 = 1.8
# chisq = 118 for p0 = 3.5*10**8.62, r0 = 1.8
# chisq = 564 for p0 = 3.5*10**8   , r0 = 1.5
chisq = 0.0
for index in range(0, am):
    integral_result = simpsonsRule(func_to_integrate, 0.0, r[index], 200, p0, r0)
    v_mod = np.sqrt(grav_const * integral_result / r[index])
    print "v_obs, v_mod, error = " + str(v_obs[index]) + ", " + str(v_mod) + ", " + str(v_obs[index] - v_mod)
    #chisq += ((v_mod - v_obs[index]))**2
    chisq += ((v_mod - v_obs[index])/errors[index])**2
chisq = np.sqrt(chisq)
print "using p0 = 3.5*10**8.62, r0 = 1.8, the chisq = " + str(chisq)
#-----------------------------------------------------------------------------