简体   繁体   中英

Faster way to attach 2d polynomial coefficients to terms in Python?

So I am trying to create a polynomial that contains 2 independent variables by attaching the respective coefficients ( k_ij ) to the respective monomial ( x**i*y**j , where x and y are symbolic variables). My goal is to minimize computation time because my polynomial is very large, and the main thing using computation time in my program is the set of lines that generate this symbolic polynomial (and I will need to call this step multiple times). I was very surprised to realize how much time this step took in the program, considering that the rest of my program was rather lengthy/complex. I would never have guessed how much time it takes to simply create the monomials and attach the coefficients to create a polynomial.

My polynomial does not necessarily have to be saved as a polynomial, it just has to be a sum of all of the terms. By this, I mean I do not need an output like this...

Out: Poly(7*x + 2*y, x, y, domain='ZZ')

... although, I am not against that format, either, if it minimizes computation time. What I would like, but do not necessarily need, is simply an output that looks something like this (which, in said case, can be achieved by simply saying z = 7*x + 2*y if x and y are already defined symbolically):

Out: 7*x + 2*y

So, I have a coefficient matrix (which can easily be re-ordered to fit the method used to attach it to the polynomial) that contains all of the coefficients of the desired polynomial, and I have my symbolic variables. Here is a re-creation of my first attempt (which took the most time to compute):

import sympy
import time
# let's time it, see how long it takes
from time import clock as tc

# time it!
t0 = tc()

order = 33
order_x = order
order_y = order
deg_x = order - 1
deg_y = order - 1

nnn = order_x*order_y

# here is a random coefficient matrix
coefficient_matrix = numpy.random.rand(nnn)

# define symbolic variables
x = sympy.Symbol('x')
y = sympy.Symbol('y')

# now let's populate the surface polynomial
z = 0
for i2 in range(order_y):
    for i3 in range(order_x):
        z = z + coefficient_matrix[i3 + order_x*i2]*(x**(deg_x - i3))*(y**(deg_y - i2))
        # note that this returns high order to low order terms

t1 = tc()
print(t1-t0)

That was very slow (37s), presumably because of the nature of the for loop. I then tried using numpy.polynomial.polynomial.polyvander2d to generate a pseudo-vandermonde matrix and multiplying it to the coefficient matrix, but that made almost no difference in computation time. The next method I tried, shown below, was able to put a big dent in computation time:

import sympy
import time
from time import clock as tc
import numpy
from numpy.polynomial.polynomial import polyval2d as P2

# time it!
t0 = tc()

order = 33
order_x = order
order_y = order

# here is a random coefficient matrix
coefficient_matrix = numpy.random.rand(order, order)

# define symbolic variables
x = sympy.Symbol('x')
y = sympy.Symbol('y')

# create polynomial
z = P2(x, y, coefficient_matrix)

# make the polynomial a logical sequence of monomials
z = sympy.expand(z)

t1 = tc()
print(z)
print(t1-t0)

This method took 8.5 seconds (which really surprised me, I thought it would be much shorter), but without the z = sympy.expand(z) line it took about 3 seconds. The reason I have that line is that later I need to modify the function and extract the new coefficients, so I would like it in that expanded form for later use (so that it appears in the format listed above; if I did not include this line, it appears in sort of a factored format).

Is there a way to make Python match the terms with the coefficients even faster and return them as a sequence of monomials?

Constructing a sy.Poly would take a lot less time:

using_loop          : 43.56
using_P2            : 12.64
using_poly          :  0.03

from timeit import default_timer as tc
import numpy as np
import sympy as sy
from numpy.polynomial.polynomial import polyval2d as P2


def using_poly(coefficient_matrix, S=sy.S):
    order = coefficient_matrix.shape[0]
    x = sy.Symbol('x')
    y = sy.Symbol('y')
    dct = {i:S(val) for i, val in np.ndenumerate(coefficient_matrix)}
    z = sy.Poly(dct, x, y)
    return z

def using_loop(coefficient_matrix):
    order = coefficient_matrix.shape[0]
    coefficient_matrix = coefficient_matrix.T.ravel()[::-1]
    order_x = order
    order_y = order
    deg_x = order - 1
    deg_y = order - 1
    x = sy.Symbol('x')
    y = sy.Symbol('y')
    z = 0
    for i2 in range(order_y):
        for i3 in range(order_x):
            z = z + coefficient_matrix[i3 + order_x*i2]*(x**(deg_x - i3))*(y**(deg_y - i2))
    return z

def using_P2(coefficient_matrix):
    x = sy.Symbol('x')
    y = sy.Symbol('y')
    z = P2(x, y, coefficient_matrix)
    # make the polynomial a logical sequence of monomials
    z = sy.expand(z)
    return z

order = 33
np.random.seed(2015)
coefficient_matrix = np.random.rand(order, order)
# coefficient_matrix = np.arange(1, order*order+1).reshape(order,order)

for func in (using_loop, using_P2, using_poly):
    t0 = tc()
    func(coefficient_matrix)
    t1 = tc()
    print('{:15s}: {:>5.2f}'.format(func.__name__, t1-t0))

Here is an example of what the output looks like for a small coefficient_matrix:

In [277]: order = 3

In [278]: coefficient_matrix = np.arange(1, order*order+1).reshape(order,order)

In [279]: using_poly(coefficient_matrix)
Out[279]: Poly(9*x**2*y**2 + 8*x**2*y + 7*x**2 + 6*x*y**2 + 5*x*y + 4*x + 3*y**2 + 2*y + 1, x, y, domain='ZZ')

In [280]: using_P2(coefficient_matrix)
Out[280]: 9.0*x**2*y**2 + 8.0*x**2*y + 7.0*x**2 + 6.0*x*y**2 + 5.0*x*y + 4.0*x + 3.0*y**2 + 2.0*y + 1.0

In [281]: using_loop(coefficient_matrix)
Out[281]: 9*x**2*y**2 + 8*x**2*y + 7*x**2 + 6*x*y**2 + 5*x*y + 4*x + 3*y**2 + 2*y + 1

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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