Expanding polynomial with variable exponent in sympy - python-3.x

I am not very experienced with sympy so sorry if this is a simple question.
How can I use sympy to expand binomial expressions? For example say I want to have sympy compute the coefficient of $x^2$ in the polynomial $(x^2 + x + 1)^n$ (where I would expect the answer to be $n + \binom{n}{2}$).
I tried the following code:
x = symbols('x')
n = symbols('n', integer=True, nonnegative = True)
expand((x**2+x+1)**n)
but the result is just $(x^2+x+1)^n$ whereas I would want the binomial expansion, i.e .
Thanks in advance.

If the exponent is not symbolic then the following gives the coefficient very quickly for the power of an arbitrary polynomial with integer coefficients, e.g.,
>>> eq
x**3 + 3*x + 2
>>> (Poly(eq)**42).coeff_monomial(x**57)
2294988464559317378977138572972
But there is currently no routine to indicate the coefficient if the exponent of the polynomial is symbolic. rsolve can also be used to express the closed form if a pattern can be seen in the coefficient, too:
>>> print([((x**2+x+1)**i).expand().coeff(x**2) for i in range(8)])
[0, 1, 3, 6, 10, 15, 21, 28]
>>> from sympy.abc import n
>>> f=Function('f') # f(n) represents the coefficient of x**2 for a given n
The coefficient for x^2 for a given n is n more than the last value:
>>> rsolve(f(n)-f(n-1)-n, f(n),{f(0):0,f(1):1})
n*(n + 1)/2
This final expression is the coefficient of x^2 for arbitrary n.
Issue 17889 gives a routine that will compute the coefficient of a term in a univariate polynomial (with arbitrary coefficients for each term) raised to the power of n:
>>> eq = 2 + x + x**2
>>> unicoeff(eq, 4).simplify()
Piecewise(
(0, n < 2),
(2**(n - 3)*n*(n - 1), n < 3),
(2**(n - 4)*n**2*(n - 1), n < 4),
(2**n*n*(n - 1)*(n**2 + 19*n + 6)/384, True))
>>> _.subs(n, 5)
210
>>> (eq**5).expand().coeff(x**4)
210
For your expression (where the constant is 1):
>>> unicoeff(1+x+x**2,2).simplify()
Piecewise((0, n < 1), (n, n < 2), (n*(n + 1)/2, True))

Related

Optimizing Numpy Operations

I am trying to train a multi-class classifier with multinomial logistic regression and gradient descent. Specifically, the model will have a trained weights matrix w with shape (C, D) where C is the number of classes and D is the number of features of each input. Also, we will have a bias vector b with dimension (C,). We have an (N, D) input matrix X, where N is the number of training inputs, and a vector y with shape (N,), where each entry in y is a number from 0 to C - 1, indicating which class the input belongs to. I have written the following code:
for _ in range(max_iterations):
z = np.apply_along_axis(lambda v: v - max(v), 1, X # w.T + b)
probs = np.exp(z)
denom = np.sum(probs, axis=1)
for i in range(C):
for j in range(N):
if i == y[j]:
w[i] -= (step_size / N) * ((probs[j][i] / denom[j]) - 1) * X[j]
b[i] -= (step_size / N) * ((probs[j][i] / denom[j]) - 1)
else:
w[i] -= (step_size / N) * (probs[j][i] / denom[j]) * X[j]
b[i] -= (step_size / N) * (probs[j][i] / denom[j])
This produces the correct weights and bias that I want, but clearly it doesn't take advantage of numpy's operations to speed things up. So I tried to speed some of it up with the following code:
for _ in range(max_iterations):
z = np.apply_along_axis(lambda v: v - max(v), 1, X # w.T + b)
probs = np.exp(z)
denom = np.sum(probs, axis=1)
s = np.zeros((N, C))
for i in range(N):
s[i] = probs[i] / denom[i]
for i in range(N):
s[i][y[i]] += -1
for c in range(C):
grad_w = s.T[c] # X
w[c] += (step_size / N) * grad_w
b[c] += (step_size / N) * sum(s.T[c])
I was hoping that this would produce the same results as in the previous part while being faster... and it managed to be faster, but with incorrect results.
So I have a couple of questions. First, why is my second piece of code not producing the right results, and what would be a fix for it? Second, and more importantly, how would I optimize this further? This is mainly for me to learn how to take advantage of numpy's vectorized operations.
This may help with some of the iterations.
Start with a small 2d array:
In [251]: probs = np.arange(12).reshape(3,4)
In [252]: denom = np.sum(probs, axis=1)
In [253]: denom
Out[253]: array([ 6, 22, 38])
To divide a (3,4) array by a (3,), we need to make the later (3,1):
In [254]: probs/denom[:,None]
Out[254]:
array([[0. , 0.16666667, 0.33333333, 0.5 ],
[0.18181818, 0.22727273, 0.27272727, 0.31818182],
[0.21052632, 0.23684211, 0.26315789, 0.28947368]])
Read, and reread, the numpy documentation on broadcasting if that doesn't make sense.
Another way to get the required 2d denom, is:
In [255]: denom = np.sum(probs, axis=1, keepdims=True)
In [256]: denom
Out[256]:
array([[ 6],
[22],
[38]])
In [257]: probs/denom
Out[257]:
array([[0. , 0.16666667, 0.33333333, 0.5 ],
[0.18181818, 0.22727273, 0.27272727, 0.31818182],
[0.21052632, 0.23684211, 0.26315789, 0.28947368]])
The same should work for the max subtraction that you use with apply_along_axis. apply... is not a speed tool, and not superior to simple iteration.
In [258]: np.max(probs, axis=1, keepdims=True)
Out[258]:
array([[ 3],
[ 7],
[11]])
In [259]: probs - _
Out[259]:
array([[-3, -2, -1, 0],
[-3, -2, -1, 0],
[-3, -2, -1, 0]])

Is there a way to generate correlated variable array from an existing array in Python 3? [duplicate]

I have a non-generated 1D NumPy array. For now, we will use a generated one.
import numpy as np
arr1 = np.random.uniform(0, 100, 1_000)
I need an array that will be correlated 0.3 with it:
arr2 = '?'
print(np.corrcoef(arr1, arr2))
Out[1]: 0.3
I've adapted this answer by whuber on stats.SE to NumPy. The idea is to generate a second array noise randomly, and then compute the residuals of a least-squares linear regression of noise on arr1. The residuals necessarily have a correlation of 0 with arr1, and of course arr1 has a correlation of 1 with itself, so an appropriate linear combination of a*arr1 + b*residuals will have any desired correlation.
import numpy as np
def generate_with_corrcoef(arr1, p):
n = len(arr1)
# generate noise
noise = np.random.uniform(0, 1, n)
# least squares linear regression for noise = m*arr1 + c
m, c = np.linalg.lstsq(np.vstack([arr1, np.ones(n)]).T, noise)[0]
# residuals have 0 correlation with arr1
residuals = noise - (m*arr1 + c)
# the right linear combination a*arr1 + b*residuals
a = p * np.std(residuals)
b = (1 - p**2)**0.5 * np.std(arr1)
arr2 = a*arr1 + b*residuals
# return a scaled/shifted result to have the same mean/sd as arr1
# this doesn't change the correlation coefficient
return np.mean(arr1) + (arr2 - np.mean(arr2)) * np.std(arr1) / np.std(arr2)
The last line scales the result so that the mean and standard deviation are the same as arr1's. However, arr1 and arr2 will not be identically distributed.
Usage:
>>> arr1 = np.random.uniform(0, 100, 1000)
>>> arr2 = generate_with_corrcoef(arr1, 0.3)
>>> np.corrcoef(arr1, arr2)
array([[1. , 0.3],
[0.3, 1. ]])

using numpy fromfunction to fill a matrix with a custom function

This is related to another question I have except there I am looking for a vectorized/broadcasting solution.
Here I am hoping to try using np.fromfunction but am having issues.
The MWE is
# L1 and L2 will range from 0 to 3 typically, sometimes up to 5
# all of the following are dummy values but match correct `type`
L1, L2, x1, x2, fac = 2, 3, 2.0, 4.5, 2.3
saved_values = np.random.uniform(high=75.0, size=[max(L1,L2) + 1, max(L1,L2) + 1])
facts = np.random.uniform(high=65.0, size=[L1 + L2 + 1])
val = 0
for i in range(L1+1):
sf = saved_values[L1][i] * x1 ** (L1 - i)
for j in range(L2 + 1):
m = i + j
if m % 2 == 0:
num = sf * facts[m] / (2 * fac) ** (m / 2)
val += saved_values[L2][j] * x1 ** (L1 - j) * num
rather than use a double for loop, which is not vectorized and potentially slower (being vectorized is more important for me), I would like to use np.fromfunction to fill a matrix with the necessary values
I tried the following
matrix = np.fromfunction(lambda i, j:
saved_values[L1][i] * x1 ** (L1 - i) *
saved_values[L2][j] * x2 ** (L2 - j) *
facts[i+j] / (2 * fac) **( (i+j) / 2),
(L1+1,L2+1)
)
This generates the double for loop matrix, I then need to go over it and make anything that would fail (i+j) % 2 == 0 have a zero value, and then sum all elements in the matrix.
However
I get the following error
Traceback (most recent call last):
File "main.py", line 54, in <module>
(L1+1,L2+1)
File "/usr/lib/python3/dist-packages/numpy/core/numeric.py", line 1808, in fromfunction
return function(*args,**kwargs)
File "main.py", line 53, in <lambda>
facts[i+j] / (2 * fac) **( (i+j) / 2),
IndexError: arrays used as indices must be of integer (or boolean) type
As I suppose, you think that the function passed as the first parameter
to np.fromfunction is called several times, for each combination
of i and j.
Unfortunately, this is not the case. To see the actual way how
it operates, make such experiment:
def myFun(i, j):
print(f'myFun:\n{i.shape}\n{i}\n{j.shape}\n{j}')
rv = i + j
return rv
I deliberately put a trace printout, to show what I mean.
Then run it:
np.fromfunction(myFun, (2, 3))
The result is:
myFun:
(2, 3)
[[0. 0. 0.]
[1. 1. 1.]]
(2, 3)
[[0. 1. 2.]
[0. 1. 2.]]
array([[0., 1., 2.],
[1., 2., 3.]])
The first part is the printout, occuring only once and the second - the
actual result. Note that:
shapes of both i and j are (2,3),
both i and j are actually arrays, not integers,
the whole computation is performed by addition of both arrays.
So the conclusions are that:
the passed function operates in a vectorized way,
on arrays of parameters, adding respective elements and
returning the result,
your concept is probably impossible to implement.

Compute sum of pairwise sums of two array's columns

I am looking for a way to avoid the nested loops in the following snippet, where A and B are two-dimensional arrays, each of shape (m, n) with m, n beeing arbitray positive integers:
import numpy as np
m, n = 5, 2
a = randint(0, 10, (m, n))
b = randint(0, 10, (m, n))
out = np.empty((n, n))
for i in range(n):
for j in range(n):
out[i, j] = np.sum(A[:, i] + B[:, j])
The above logic is roughly equivalent to
np.einsum('ij,ik', A, B)
with the exception that einsum computes the sum of products.
Is there a way, equivalent to einsum, that computes a sum of sums? Or do I have to write an extension for this operation?
einsum needs to perform elementwise multiplication and then it does summing (optional). As such it might not be applicable/needed to solve our case. Read on!
Approach #1
We can leverage broadcasting such that the first axes are aligned
and second axis are elementwise summed after extending dimensions to 3D. Finally, we need summing along the first axis -
(A[:,:,None] + B[:,None,:]).sum(0)
Approach #2
We can simply do outer addition of columnar summations of each -
A.sum(0)[:,None] + B.sum(0)
Approach #3
And hence, bring in einsum -
np.einsum('ij->j',A)[:,None] + np.einsum('ij->j',B)
You can also use numpy.ufunc.outer, specifically here numpy.add.outer after summing along axis 0 as #Divakar mentioned in #approach 2
In [126]: numpy.add.outer(a.sum(0), b.sum(0))
Out[126]:
array([[54, 67],
[43, 56]])

Calculate the volume of 3d plot

The data is from a measurement. The picture of the plotted data
I tried using trapz twice, but I get and error code: "ValueError: operands could not be broadcast together with shapes (1,255) (256,531)"
The x has 256 points and y has 532 points, also the Z is a 2d array that has a 256 by 532 lenght. The code is below:
import numpy as np
img=np.loadtxt('focus_x.txt')
m=0
m=np.max(img)
Z=img/m
X=np.loadtxt("pixelx.txt",float)
Y=np.loadtxt("pixely.txt",float)
[X, Y] = np.meshgrid(X, Y)
volume=np.trapz(X,np.trapz(Y,Z))
The docs state that trapz should be used like this
intermediate = np.trapz(Z, x)
result = np.trapz(intermediate, y)
trapz is reducing the dimensionality of its operand (by default on the last axis) using optionally a 1D array of abscissae to determine the sub intervals of integration; it is not using a mesh grid for its operation.
A complete example.
First we compute, using sympy, the integral of a simple bilinear function over a rectangular domain (0, 5) × (0, 7)
In [1]: import sympy as sp, numpy as np
In [2]: x, y = sp.symbols('x y')
In [3]: f = 1 + 2*x + y + x*y
In [4]: f.integrate((x, 0, 5)).integrate((y, 0, 7))
Out[4]: 2555/4
Now we compute the trapezoidal approximation to the integral (as it happens, the approximation is exact for a bilinear function) — we need coordinates arrays
In [5]: x, y = np.linspace(0, 5, 11), np.linspace(0, 7, 22)
(note that the sampling is different in the two directions and different from the defalt value used by trapz) — we need a mesh grid to compute the integrand and we need to compute the integrand
In [6]: X, Y = np.meshgrid(x, y)
In [7]: z = 1 + 2*X + Y + X*Y
and eventually we compute the integral
In [8]: 4*np.trapz(np.trapz(z, x), y)
Out[8]: 2555.0

Resources