为(可能令人误解)标题和可能令人困惑的问题本身道歉,我在措辞上非常费劲,尤其是将其压缩为标题的一句话。我想使用python查找带有两个变量f(w, t, some_other_args)
和w
的函数t
的根。真正的函数结构真的很长而且很复杂,您可以在本文的末尾找到它。重要的是它包含以下行:
k = 1.5 * m.sqrt((1.0 - w) / (1.0 - 0.25 * w))
这意味着w
不能超过1,因为这将导致计算负数的平方根,这当然是不可能的。我有一些算法可以使用函数中的其他值来计算w
和t
的近似值,但是它们非常不准确。
因此,我尝试使用这些近似值作为起点,使用scipy.optimize.fsolve
计算根(在尝试了我可以在线找到的每种寻根算法之后,我发现这是最适合我的函数的),看起来像这样:
solution = optimize.fsolve(f, x0=np.array([t_approx, w_approx]), args=(some_other_args))
对于大多数值,这都可以正常工作。但是,如果w
太接近1,则总是会出现fsolve
为w
尝试大于1的值的情况,这反过来会引发ValueError
(因为从数学上讲不可能计算负数的根)。这是一个打印出fsolve
使用的值的示例,其中w
应该在0.997左右:
w_approx: 0.9960090844989311
t_approx: 24.26777844720981
Values: t:24.26777844720981, w:0.9960090844989311
Values: t:24.26777844720981, w:0.9960090844989311
Values: t:24.26777844720981, w:0.9960090844989311
Values: t:24.267778808827888, w:0.9960090844989311
Values: t:24.26777844720981, w:0.996009099340623
Values: t:16.319554685876746, w:1.0096680915775516
solution = optimize.fsolve(f, x0=np.array([t_approx, w_approx]), args=(some_other_args))
File "C:\Users\...\venv\lib\site-packages\scipy\optimize\minpack.py", line 148, in fsolve
res = _root_hybr(func, x0, args, jac=fprime, **options)
File "C:\Users\...\venv\lib\site-packages\scipy\optimize\minpack.py", line 227, in _root_hybr
ml, mu, epsfcn, factor, diag)
File "C:\Users\...\algorithm.py", line 9, in f
k = 1.5 * m.sqrt((1.0 - w) / (1.0 - 0.25 * w))
ValueError: math domain error
那么,我怎么能告诉optimize.fsolve
w
不能大于1?或做这种事情的替代算法是什么(我知道brentq
等等,但是所有这些算法都需要给 both 根指定一个间隔,我不想这样做)?
测试代码(此处需要注意的重要一点:即使在理论上func
应该假设R
和T
来计算t
和w
,但我必须笨拙地使用它。这有点笨拙,但我只是不设法重写该函数,以使其接受T, R
来计算t, w
-对于我平庸的人来说有点太过分了数学专业知识;)):
import math as m
from scipy import optimize
import numpy as np
def func(t, w, r_1, r_2, r_3):
k = 1.5 * m.sqrt((1.0 - w) / (1.0 - 0.25 * w))
k23 = 2 * k / 3
z1 = 1 / (1 + k23)
z2 = 1 / (1 - k23)
z3 = 3 * ((1 / 5 + r_1 - r_2 - 1 / 5 * r_1 * r_2) / (z1 - r_2 * z2)) * m.exp(t * (k - 1))
z4 = -(z2 - r_2 * z1) / (z1 - r_2 * z2) * m.exp(2 * k * t)
z5 = -(z1 - r_2 * z2) / (z2 - r_2 * z1)
z6 = 3 * (1 - r_2 / 5) / (z2 - r_2 * z1)
beta_t = r_3 / (z2 / z1 * m.exp(2 * k * t) + z5) * (z6 - 3 / (5 * z1) * m.exp(t * (k - 1)))
alpha_t = beta_t * z5 - r_3 * z6
beta_r = (z3 - r_1 / 5 / z2 * m.exp(-2 * t) * 3 - 3 / z2) / (z1 / z2 + z4)
alpha_r = -z1 / z2 * beta_r - 3 / z2 - 3 / 5 * r_1 / z2 * m.exp(-2 * t)
It_1 = 1 / 4 * w / (1 - 8 / 5 * w) * (alpha_t * z2 * m.exp(-k * t) + beta_t * z1 * m.exp(k * t) + 3 * r_3 * m.exp(-t))
Ir_1 = (1 / 4 * w / (1 - 8 / 5 * w)) * (z1 * alpha_r + z2 * beta_r + 3 / 5 + 3 * r_1 * m.exp(-2 * t))
T = It_1 + m.exp(-t) * r_3
R = Ir_1 + m.exp(-2 * t) * r_1
return [T, R]
def calc_1(t, w, T, R, r_1, r_2, r_3):
t_begin = float(t[0])
T_new, R_new = func(t_begin, w, r_1, r_2, r_3)
a = abs(-1 + T_new/T)
b = abs(-1 + R_new/R)
return np.array([a, b])
def calc_2(x, T, R, r_1, r_2, r_3):
t = x[0]
w = x[1]
T_new, R_new = func(t, w, r_1, r_2, r_3)
a = abs(T - T_new)
b = abs(R - R_new)
return np.array([a, b])
def approximate_w(R):
k = (1 - R) / (R + 2 / 3)
w_approx = (1 - ((2 / 3 * k) ** 2)) / (1 - ((1 / 3 * k) ** 2))
return w_approx
def approximate_t(w, T, R, r_1, r_2, r_3):
t = optimize.root(calc_1, x0=np.array([10, 0]), args=(w, T, R, r_1, r_2, r_3))
return t.x[0]
def solve(T, R, r_1, r_2, r_3):
w_x = approximate_w(R)
t_x = approximate_t(w_x, T, R, r_1, r_2, r_3)
sol = optimize.fsolve(calc_2, x0=np.array([t_x, w_x]), args=(T, R, r_1, r_2, r_3))
return sol
# Values for testing:
T = 0.09986490557943692
R = 0.8918728343037964
r_1 = 0
r_2 = 0
r_3 = 1
print(solve(T, R, r_1, r_2, r_3))
答案 0 :(得分:3)
logistic输入要约束的参数怎么办?我的意思是,您可以在f
内
import numpy as np
def f(free_w, ...):
w = 1/(1 + np.exp(-free_w)) # w will always lie between 0 and 1
...
return zeros
然后,您只需要对free_w
的解决方案值应用相同的逻辑变换即可得到w
*。见
solution = optimize.fsolve(f, x0=np.array([t_approx, w_approx]), args=(some_other_args))
free_w = solution[0]
w = 1/(1 + np.exp(-free_w))
答案 1 :(得分:1)
在优化功能之前,您应该尝试明确定义函数,这样可以更轻松地检查域。
基本上,您具有T和R的功能。这对我有用:
def func_to_solve(TR_vector, r_1, r_2, r_3):
T, R = TR_vector # what you are trying to find
w_x = approximate_w(R)
t_x = approximate_t(w_x, T, R, r_1, r_2, r_3)
return (calc_2([t_x, w_x], T, R, r_1, r_2, r_3))
def solve(TR, r_1, r_2, r_3):
sol = optimize.fsolve(func_to_solve, x0=TR, args=(r_1, r_2, r_3))
return sol
另外,将m.exp
替换为np.exp
答案 2 :(得分:1)
您报告的错误发生,因为fsolve
不能处理w
到k
转换中的隐式限制。可以通过反转这种依赖性来根本解决,而使func
依赖于t
和k
。
def w2k(w): return 3 * m.sqrt((1.0 - w) / (4.0 - w))
#k = 1.5 * m.sqrt((1.0 - w) / (1.0 - 0.25 * w))
# (k/3)**2 * (4-w)= 1-w
def k2w(k): return 4 - 3/(1-(k/3)**2)
def func(t, k, r_1, r_2, r_3):
w = k2w(k)
print "t=%20.15f, k=%20.15f, w=%20.15f"%(t,k,w)
...
然后从calc1
和calc2
中的函数值中删除绝对值。这只会使您的解决方案成为不可微分的点,这对任何寻根算法都是不利的。改变符号和平滑根是牛顿式方法的好方法。
def calc_2(x, T, R, r_1, r_2, r_3):
t = x[0]
k = x[1]
T_new, R_new = func(t, k, r_1, r_2, r_3)
a = T - T_new
b = R - R_new
return np.array([a, b])
通过求解方程t
来找到w
的值,这没有多大意义。 k
已修复,它只会使计算量增加一倍。
def approximate_k(R):
k = (1 - R) / (R + 2 / 3)
return k
def solve(T, R, r_1, r_2, r_3):
k_x = approximate_k(R)
t_x = 10
sol = optimize.fsolve(calc_2, x0=np.array([t_x, k_x]), args=(T, R, r_1, r_2, r_3))
return sol
t,k = solve(T, R, r_1, r_2, r_3)
print "t=%20.15f, k=%20.15f, w=%20.15f"%(t, k, k2w(k))
通过这些修改,解决方案
t= 14.860121342410327, k= 0.026653140486605, w= 0.999763184675043
在15个功能评估中找到。