简体   繁体   English

Python有效地为多个多项式找到局部最大值/最小值

[英]Python finding local maxima/minima for multiple polynomials efficiently

I am looking for an efficient way to find local mins for multiple (>1 million) but independent 4th order polynomials in given/ specified ranges/ boundaries .我正在寻找一种有效的方法来找到给定/指定范围/边界中多个(> 100 万)但独立的四阶多项式的局部最小值。

I have two requirements:我有两个要求:

R1: efficient even for 1 million different polynomial equations R1:即使对 100 万个不同的多项式方程也有效

R2: the local min is accurate up to 0.01 (ie 2dp) R2:局部最小值精确到0.01(即2dp)

Here is some code I have created using scipy .这是我使用scipy创建的一些代码。 It's okay but I am wondering if there's any other better packages in performing such a task before I go for parallel programming.没关系,但我想知道在我进行并行编程之前是否有其他更好的包来执行这样的任务

To illustrate my problem, let's start with one polynomial first:为了说明我的问题,让我们先从一个多项式开始:

Below I am trying to find the local min of 4x^4 + 6x^3 + 3x^2 + x + 5 within the range (-5, 5).下面我试图在 (-5, 5) 范围内找到 4x^4 + 6x^3 + 3x^2 + x + 5 的局部最小值。

On my laptop, it takes about 2ms to find the local min (which is at ~ -0.72770502).在我的笔记本电脑上,大约需要 2 毫秒才能找到本地最小值(约为 -0.72770502)。

The time is alright for one polynomial but I would want something faster as I need to perform this operation over 1 million times regularly.一个多项式的时间还可以,但我想要更快的东西,因为我需要定期执行此操作超过 100 万次。

from scipy import optimize
import numpy as np

# Define a objective and gradient function for 4th order polynomial
# x is the value to be evaluated
# par is a numpy array of len 5 that specifies the polynomial coefficients.
def obj_grad_fun_custom(x,par):
    obj = (np.array([x**4,x**3,x**2,x**1,1]) * par).sum()
    grad = (np.array([4*x**3,3*x**2,2*x,1]) * par[:-1]).sum()
    return obj, grad

# Try minimise an example polynomial of 4x^4 + 6x^3 + 3x^2 + x + 5
# with contrainted bound
res = optimize.minimize(
    fun = obj_grad_fun_custom,
    x0 = 0,
    args=(np.array([4,6,3,1,5])), # polynomial coefficients
    jac=True ,
    bounds=[(-2, 10)],
    tol=1e-10)
print(res.x)

# Timing (this takes about 2 ms for me)
%timeit optimize.minimize(fun = obj_grad_fun_custom, x0 = 0, args=(np.array([4,6,3,1,5])), jac=True, bounds=[(-5, 5)], tol=1e-10)

Below is what I am planning to do regular with 1 million different order 4 polynomials I would want to minimise locally.下面是我打算用 100 万个不同的 4 阶多项式定期做的事情,我想在本地最小化。 Hopefully, someone could point me to a more suitable package other than scipy .希望有人能指出我比scipy更合适的包。 Or any alternative methods?或者有什么替代方法? Thanks!谢谢!

# Multiple polynomials
result = [] # saving the local minima
poly_sim_no = 1000000 #ideally 1 million or even more
np.random.seed(0)
par_set = np.random.choice(np.arange(10), size=(poly_sim_no, 5), replace=True) #generate some order 4 polynomial coefficients 

for a in par_set:
    res = optimize.minimize(obj_grad_fun_custom, 0,args=(a), jac=True ,bounds=[(-5, 5)], tol=1e-10)
    result.append(res.x)

print(result)

Since you're finding the minimum of a polynomial, you can take advantage of the fact that it's easy to take the derivative of a polynomial, and that there are many good algorithms for finding the roots of a polynomial.由于您要找到多项式的最小值,因此您可以利用以下事实:对多项式求导很容易,并且有许多很好的算法可以找到多项式的根。

Here's how it works:以下是它的工作原理:

  1. First, take the derivative.首先,取导数。 All of the points which are minimums will have a derivative of zero.所有最小值点的导数都为零。
  2. Look for those zeros, aka find the roots of the derivative.寻找那些零,也就是找到导数的根。
  3. Once we have the list of candidates, check that the solutions are real.一旦我们有了候选人名单,请检查解决方案是否真实。
  4. Check that the solutions are within the bounds you set.检查解决方案是否在您设置的范围内。 (I don't know if you added bounds because you actually want the bounds, or to make it go faster. If it's the latter, feel free to remove this step.) (我不知道您是否添加了边界,因为您实际上想要边界,或者让它更快。如果是后者,请随时删除此步骤。)
  5. Actually evaluate the candidates with the polynomial and find the smallest one.实际上用多项式评估候选者并找到最小的那个。

Here's the code:这是代码:

import numpy as np
from numpy.polynomial import Polynomial

def find_extrema(poly, bounds):
    deriv = poly.deriv()
    extrema = deriv.roots()
    # Filter out complex roots
    extrema = extrema[np.isreal(extrema)]
    # Get real part of root
    extrema = np.real(extrema)
    # Apply bounds check
    lb, ub = bounds
    extrema = extrema[(lb <= extrema) & (extrema <= ub)]
    return extrema

def find_minimum(poly, bounds):
    extrema = find_extrema(poly, bounds)
    # Note: initially I tried taking the 2nd derivative to filter out local maxima.
    # This ended up being slower than just evaluating the function.

    # Either bound could end up being the minimum. Check those too.
    extrema = np.concatenate((extrema, bounds))
    # Check every candidate by evaluating the polynomial at each possible minimum,
    # and picking the minimum.
    value_at_extrema = poly(extrema)
    minimum_index = np.argmin(value_at_extrema)
    return extrema[minimum_index]

# Warning: polynomial expects coeffients in the opposite order that you use.
poly = Polynomial([5,1,3,6,4]) 
print(find_minimum(poly, (-5, 5)))

This takes 162 microseconds on my computer, making it about 6x faster than the scipy.optimize solution.这在我的计算机上需要 162 微秒,比 scipy.optimize 解决方案快大约 6 倍。 (The solution shown in the question takes 1.12 ms on my computer.) (问题中显示的解决方案在我的计算机上需要 1.12 毫秒。)

Edit: A faster alternative编辑:更快的选择

Here's a faster approach.这是一种更快的方法。 However, it abandons bounds checking, uses a deprecated API, and is generally harder to read.但是,它放弃了边界检查,使用了已弃用的 API,并且通常更难阅读。

p = np.poly1d([4,6,3,1,5])  # Note: polynomials are opposite order of before

def find_minimum2(poly):
    roots = np.real(np.roots(poly.deriv()))
    return roots[np.argmin(poly(roots))]

print(find_minimum2(p))

This clocks in at 110 microseconds, making it roughly 10x faster than the original.它的时钟速度为 110 微秒,比原来的速度快了大约 10 倍。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM