Numpy product or tensor product question - python

How can I calculate this product without a loop? I think I need to use numpy.tensordot but I can't seem to set it up correctly. Here's the loop version:
import numpy as np
a = np.random.rand(5,5,3,3)
b = np.random.rand(5,5,3,3)
c = np.zeros(a.shape[:2])
for i in range(c.shape[0]):
for j in range(c.shape[1]):
c[i,j] = np.sum(a[i,j,:,:] * b[i,j,:,:])
(The result is a numpy array c of shape (5,5))

I've lost the plot. The answer is simply
c = a * b
c = np.sum(c,axis=3)
c = np.sum(c,axis=2)
or on one line
c = np.sum(np.sum(a*b,axis=2),axis=2)

May this help you with the syntax ?
>>> from numpy import *
>>> a = arange(60.).reshape(3,4,5)
>>> b = arange(24.).reshape(4,3,2)
>>> c = tensordot(a,b, axes=([1,0],[0,1])) # sum over the 1st and 2nd dimensions
>>> c.shape
(5,2)
>>> # A slower but equivalent way of computing the same:
>>> c = zeros((5,2))
>>> for i in range(5):
... for j in range(2):
... for k in range(3):
... for n in range(4):
... c[i,j] += a[k,n,i] * b[n,k,j]
...
(from http://www.scipy.org/Numpy_Example_List#head-a46c9c520bd7a7b43e0ff166c01b57ec76eb96c7)

Related

Avoiding nested for loops when multiplying vectors in Python

I am working with a list vectors A and B. Each vector has dimension n, but the size of A and B are different. For each vector in A, I would like to compute its product with all the vectors in B. Here is an example with n=2:
import numpy as np
A = np.random.rand(10,2)
B = np.random.rand(5,2)
for a in A:
PRODUCT = 1
for b in B:
PRODUCT = PRODUCT * np.matmul(a, b)
This does what I want, but I was wondering if there are faster methods that avoid using nested for-loops. One idea I had was to use cartesian products by using from itertools import product, and then somehow doing these computations every i % len(B) == 0 iterations. But I was not able to make that work.
Are there ways for improvements? Or nested for loops are the way to go?
There might be a cleaner way to do this, but it works
import numpy as np
from itertools import product
A = np.random.rand(10,2)
B = np.random.rand(5,2)
one = []
two = []
curr = A[0]
PRODUCT = 1
for a, b in product(A, B):
if np.array_equal(a, curr) is False:
curr = a
PRODUCT = 1
PRODUCT = PRODUCT * np.matmul(a, b)
one.append(PRODUCT)
for a in A:
PRODUCT = 1
for b in B:
PRODUCT = PRODUCT * np.matmul(a, b)
two.append(PRODUCT)
if one == two:
print(True)
else:
print(False)
Results
True

Finding all the roots of the function x = a*sin(x) in Python

I have to find the number of solution depending on the parameter a. While solving the equation numerically using scipy.optimize.root I get some numbers which aren't root of the function. For example for
x = 7*sin(x) i get numbers -7.71046524 and 7.71046524. My code is:
a = np.linspace(-5, 5)
def fun(x):
return x - b*np.sin(x)
for i in a:
solutions = []
b = i
c = abs(int(round(i)))
for j in range(-c, c+1):
y = root(fun, j)
if (round(y.x[0], 3) not in solutions):
solutions.append(round(y.x[0], 3))
print(len(solutions))
If you use scipy.optimize.root, the return value contains x the solution array and the success boolean flag. You need to filter out any result where success is False.
import numpy as np
from scipy.optimize import root
a = np.linspace(-7, 7)
def fun(x):
return x - b*np.sin(x)
for i in a:
solutions = []
b = i
c = abs(int(round(i)))
for j in range(-c, c+1):
y = root(fun, j)
if y.success and (round(y.x[0], 6) not in solutions):
solutions.append(round(y.x[0], 3))
print(i, solutions)

Calculation of a symbolic expression in SymPy and convert them to numeric within Julia

I am trying to convert a piece of python code to Julia 1.1.0.
The python code:
import numpy as np
import sympy as sp
x, y = sp.symbols('x y')
data = np.random.randn(1000, 2)
a,b = data[:,0], data[:,1]
M = len(data[:,0])
m = 5
n = round(m*(m+1)/2)
L = np.zeros((M,n))
l = sp.zeros(1,n)
k = 0
for i in range(1,m+1):
for j in range(1,i+1):
l[0,k]=((i-j)*(i-j-1)*x**(i-j-2)*y**(j-1))
f = sp.lambdify([x,y], l[0,k], "numpy")
L[:,k] = np.squeeze(f(a,b))
k=k+1
and my attempt of Julia code:
using SymPy
data = rand(1000,2)
a = data[:,1];
b = data[:,2];
M = length(data[:,1])
x = symbols("x");
y = symbols("y");
m = 5;
n = Int(m*(m+1)/2)
L = zeros(M,n)
l = zeros(1,n)
k = 1;
for i in 1:m
for j in 1:i
l[1,k] = ((i-j)*(i-j-1)*x^(i-j-2)*y^(j-1))
f = l[1,k]
L[:,k] = f.subs([(x, a), (y, b)])
k=k+1
end
end
when I runJulia codes, for l[1,k] I got following error
DomainError with -2:
Cannot raise an integer x to a negative power -2.
Convert input to float.
Stacktrace:
[1] throw_domerr_powbysq(::Sym, ::Int64) at ./intfuncs.jl:173
[2] power_by_squaring(::Sym, ::Int64) at ./intfuncs.jl:196
[3] ^(::Sym, ::Int64) at ./intfuncs.jl:221
[4] top-level scope at ./In[80]:14
Also, I am not sure about the following codes
f = l[1,k]
L[:,k] = f.subs([(x, a), (y, b)])
I would appreciate if someone can help me to translate python codes to julia codes.
Update:
Based on the post of #jverzani I can now convert sym values to float with following codes
using SymPy
data = rand(1000,2)
a = data[:,1];
b = data[:,2];
M = length(data[:,1])
x = symbols("x");
y = symbols("y");
m = 5;
n = Int(m*(m+1)/2)
LL = zeros(M,n)
L = zeros(Sym, M,n)
l = zeros(Sym, 1,n)
k = 1;
for i in 1:m
for j in 1:i
l[1,k] = ((i-j)*(i-j-1)*x^Sym(i-j-2)*y^Sym(j-1))
f = l[1,k]
L[:,k] .= f.subs([(x, a), (y, b)])
global k=k+1
end
end
for s in 1:M
for r in 1:n
LL[s,r] = float(subs(L[s,r],(x,a[s]),(y,b[s])))
end
end
But this time the codes are very slow. How can I optimize the codes.
SymPy just got a major change, inherited from PyCall, which makes the last bit with subs work as typed.
As for the first one, you are bumping into a Julia issue with integer bases and non-literal, negative integer powers. You can modify that line to make the powers symbolic, such as x^Sym(i-j-2) in place of x^(i-j-2). (It might be best to bypass this.)
The following edit tries to mirror the spirit of the Python code. It wraps into a function to test speed on size of N and avoid the need for global on the k assignment.
using SymPy
import PyCall
np = PyCall.pyimport("numpy") #import numpy as np
const sp = sympy
function fill_L(N=1000)
x, y = sp.symbols("x y")
offset = 1
data = np.random.randn(N, 2)
a,b = data[:,0+offset], data[:,1+offset]
M = length(data[:,0+offset])
m = 5
n = round(Int, m*(m+1)/2)
L = np.zeros((M,n))
l = sp.zeros(1,n)
k = 0
for i in 1:m
for j in 1:i
l[1,k+offset]=((i-j)*Sym(i-j-1)*x^Sym(i-j-2)*y^Sym(j-1))
f = lambdify(l[1,k+offset], (x, y))
#f = sp.lambdify([x,y], l[0,k], "numpy")
L[:,k+offset] = f.(a,b)
k=k+1
end
end
L
end
Performance is reasonable now, due to the broadcasting of f above.

np.tile to repeat an 1D array

I have an ndarray, A,
and I want to multiply this ndarray element wise by another 1D array b where I assume that A.shape[i] = len(b) for some i. I need this generality in my application.
I can do this using np.tile as follows:
A = np.random.rand(2,3,5,9)
b = np.random.rand(5)
i = 2
b_shape = np.ones(len(A.shape), dtype=np.int)
b_shape[i] = len(b)
b_reps = list(A.shape)
b_reps[i] = 1
B = np.tile(b.reshape(b_shape), b_reps)
# Here B.shape = A.shape and
# B[i,j,:,k] = b for all i,j,k
This strikes me as ugly. Is there a better way to do this?
For this particular example, the following code would do the trick:
result = A*b[:, np.newaxis]
For any value of i, try this:
A2, B = np.broadcast_arrays(A, b)
result = A2*B

How return `A**n` in python with A an matrix?

I'm using sympy. For any matrix A I want the form of A**n but when I write
>>> from __future__ import division
>>> from sympy import *
>>> import sympy
>>> from sympy.abc import *
>>> import sys
>>> sys.displayhook = pprint
>>> from sympy.matrices import *
>>> A = Matrix([[a, b], [c, d]])
>>> A**n
I get
>>> Only integer and rational values are supported
and I hope the matrix form of A**n.
The power of a matrix by a symbolic variable is not implemented yet, the error NotImplementedError is quite clear in that regard, however you may try this code:
def jordan_cell_power(jc, n):
N = jc.shape[0]
l = jc[0, 0]
for i in range(N):
for j in range(N-i):
bn = binomial(n, i)
if isinstance(bn, binomial):
bn = bn._eval_expand_func()
jc[j, i+j] = l**(n-i)*bn
def matrix_power(M, n):
P, jordan_cells = M.jordan_cells()
for j in jordan_cells:
jordan_cell_power(j, n)
return P*diag(*jordan_cells)*P.inv()
This is the implementation given on Wikipedia:
https://en.wikipedia.org/wiki/Jordan_normal_form#Powers
That is, a matrix is reduced to the jordan form, that is, you find two matrices P and J for which A == P*J*P.inv(), such that J in represented in the Jordan normal form.
At this point, each Jordan cell is evaluated its power through some simple combinatorics (following the instruction on Wikipedia's article about calculating the power of a matrix in Jordan normal form).
Given that A**n == P*J*P*P.inv()*J* ... *J*P.inv() == P*J**n*P.inv(), there's an easy way to convert it back.
You can try this with your examples:
In [3]: A = Matrix([[1, a], [0, 1]])
In [4]: A
Out[4]:
[1 a]
[ ]
[0 1]
In [5]: matrix_power(A, n)
Out[5]:
[1 a*n]
[ ]
[0 1 ]
In [6]: B = Matrix([[a, b], [c, d]])
In [7]: matrix_power(B, n)
... enormous result ...

Categories