scipy.optimize:在2D网格上快速查找根

时间:2013-11-07 15:52:40

标签: numpy scipy equation-solving

我目前使用scipy编写了一些代码来查找以下等式的根:

def equation(x, y):
     return (x / y) * np.log((a * x / b) + 1.0) - 2.0 * c * c

使用a,b和c标量。

我在矩形网格上有y值(比如Y,形状为300x200),需要找到相应的x,为每个点求解方程式。我还对每个点的x值(X0,形状300x 200)进行了初始估计

目前我已经设法通过循环遍历数组Y中的每个项目并调用:

来解决这个问题。
for index, value in np.ndenumerate(Y):
    result[index] = scipy.optimize.newton(equation, X0[index], args=(value))
    # or other scalar solvers like brentq

这有效但速度太慢,不允许我发布我的脚本。鉴于值在网格上组织并且Y和结果数组包含“连续”值,例如逐渐从阵列的边缘向中心转变我相信必须有一个很好的阵列导向/多维方式来解决这个问题,这也可以提供更好的性能。

我尝试了很多选项,但到目前为止还没有成功。有什么想法吗?

任何帮助都将不胜感激。

由于

2 个答案:

答案 0 :(得分:2)

(扩大评论) 正如@askewchan在他的回答中所示,这里的运行时间主要是通过实际求解方程来实现的。

以下是我在此处所做的事情:将2*c*c作为乘法常量吸收到y,同样地吸收a/b。剩下的是t log (1 + t) = z形式的等式,其中tz与您的xy相关。 现在将z = t log(1 + t)的值列在您需要的范围内,插入tz,并为您的等式提供解决方案。请注意,无论阵列XY的形状如何,您只需要一维插值。

对于插值,scipy has a range of interpolators.最简单的使用可能是interp1dUnivariateSpline。它们都支持矢量化操作,因此您可以对矢量化进行矢量化。如果这对于你的表现来说已经足够了,那你就已经完成了。

根据您需要的xy值的范围,您可能会或可能不想使用边界处的显式功能行为来扩充制表符号[例如t log(1 + t) t^2接近t->0等。如果你需要那个,看看https://bitbucket.org/burovski/tabulations ---这很丑陋,但它有效[有一些scipy工作有更好的选择,但现在正在进行中]。

答案 1 :(得分:1)

如果没有YX0的值进行测试很难,但使用np.frompyfunc可能会快一些。它需要一个标量函数并将其转换为ufunc,它以元素方式对数组进行操作。

import numpy as np
import scipy.optimize

a, b, c = np.random.rand(3)

def equation(x, y):
    return (x/y)*np.log((a*x/b) + 1.0) - 2.*c*c

def solver(y, x0):
    return scipy.optimize.newton(equation, x0, args=(y,))

f = np.frompyfunc(solver, 2, 1)   # 2 arrays in, 1 array out, apply solver elementwise

Y, X0 = np.random.rand(2, 300, 200)
res = f(Y, X0)

但它仍然没有真正地对过程进行矢量化,并且它不会加速它:

In [51]: timeit f(Y, X0)
1 loops, best of 3: 10.4 s per loop

In [52]: timeit OP(Y, X0, a, b, c)
1 loops, best of 3: 10.5 s per loop

但就我所知,速度问题不是来自循环,而是来自你正在解决等式Y.size次的事实。我相信如果您真正解决Y中的每个值:

,这会快得多
In [53]: timeit solver(Y[0,0], X0[0,0])
10000 loops, best of 3: 178 µs per loop

In [54]: Y.size*178e-6
Out[54]: 10.68

可能你已经意识到这一点:P但你必须通过做一些近似或假设来实际减少计算。像@ Zhenya的建议可能是必要的,但我们必须更多地了解Y, X0, a, b, c的价值。