Python and coding beginner here. I started learning python a couple of days ago, no prior coding experience, and I've started learning about functions. Since python is really useful for mathematical operations, I'm trying to tie this with what I'm learning in my linear algebra class. So here's the question. Beware a lot of reading ahead!
I'm trying to multiply two random matrices using python, without numpy (otherwise I can use numpy.dot and numpy.matrix). If we have 2 matrices X and Y, of dimensions axb and bxc respectively, matrix multiplication only works if the columns of X and the rows of Y are equal. In order to write a program that can do the matrix multiplication, here's what I tried.
First I defined my function as def mat.mul(A,B), with dimensions axb and bxc respectively. I then have the matrix Z as the product of the matrix A and B, which will be empty, Z = []. Here's where my thought process is a bit wobbly. I think that first I need a for loop that iterates through the columns of A, for a in range(0, len(A)):, followed by another for loop that iterates through the rows of A, for b in range(len(0, X[0])) , followed by another for loop to iterate through the columns of B, for c in range(0, len(Y)) and finally a last for loop that iterates through the rows of Y, for d in range(0, len(Y[0])). Now I should have the product matrix Z but I'm not how I should write it. Would it be Z += X[i] * Y[d]?
Sorry for the long explanation, I just thought I'd share my thought process as well.
Related
I would like to vectorize nested sums in Python, in order to speed up the process. At the moment I have nested for loops.
for ja in np.arange(0,Na):
for jb in np.arange(0,Nb):
for ma in np.arange(-ja,ja+1):
...
The end result is the sum across 2x2 matrices, each with entries dependent on the values of ja,jb,ma,mb.
The matrices look like:
[[f11(ja,jb,ma,mb),f12(ja,jb,ma,mb)],
[f21(ja,jb,ma,mb),f22(ja,jb,ma,mb)]]
where fij are functions. The functions can be applied to arrays as they will work element by element (expoenetials, square roots, trig functions etc...). I can create arrays like:
ja=[0,0,0,1,1,1,2,2,2,3,3,3]
jb=[0,1,2,3,0,1,2,3,0,1,2,3]
By using
range_a = np.arange(0,Na/2+1)
range_b = np.arange(0,Nb/2+1)
ja = np.tile(a_range,Nb/2+1)
jb = np.repeat(b_range,Na/2+1)
But my trouble is to create arrays, such that for each value in the above j we have the m structure (from -j to j):
ma=[0,0,0,-1,-1,-1,0,0,0,1,1,1,-2,-2,-2,...]
mb=[0,-1,0,1,-2,-1,0,1,2,...]
I am having trouble making those m arrays though! each time a -j,..,j structure repeats it has a different length, so I cannot use functions like numpy.tile and numpy.repeat. So, any ideas on how to do this?
My further intentions might be relevant: I hope to be able to pad them with zeros and construct 2*length matrices, so that only one entry is populated (I need 4 matrices for each vector, total of 4 vectors). Then I can apply the functions to and add these upp. To calculate the summation across the 2*2 matrices I will dot product with a matrix that is length*4 in shape. The result is 2*2. Perhaps a better strategy exists? I thought this might have occurred before, as it has a common application in physics (trace over a density operator) but I have not found it.
I'm lost when iterating over a ndarray with nditer.
Background
I am trying to compute the eigenvalues of 3x3 symmetric matrices for each point in a 3D array.
My data is a 4D array of shape [6,x,y,z] with the 6 values being the values of matrix at point x,y,z, over a ~500x500x500 cube of float32.
I first used numpy's eigvalsh, but it's optimized for large matrices, while I can use analytical simplification for 3x3 symmetric matrices.
I then implemented wikipedia's simplification , both as a function that takes a single matrix and computes eigenvalues (then iterating naively with nested for loops), and then vectorized using numpy.
The problem is that now inside my vectorization, each operation creates an internal array of my data's size, culminating in too much RAM used and PC freeze.
I tried using numexpr etc, it's still around 10G usage.
What I'm trying to do
I want to iterate (using numpy's nditer) through my array so that for each matrix, I compute my eigenvalues. This would remove the need to allocate huge intermediary arrays because we only calculate ~ 10 float numbers at a time.
Basically trying to substitute nested for loops into one iterator.
I'm looking for something like this :
for a,b,c,d,e,f in np.nditer([symMatrix,eigenOut]): # for each matrix in x,y,z
# computing my output for this matrix
eigenOut[...] = myLovelyEigenvalue(a,b,c,d,e,f)
The best I have so far is this :
for i in np.nditer([derived],[],[['readonly']],op_axes=[[1,2,3]]):
But this means that i takes all values of the 4D array instead of being a tuple of 6 length.
I can't seem to get the hang of the nditer documentation.
What am I doing wrong ? Do you have any tips and tricks as to iterating over "all but one" axis ?
The point is to have an nditer that would outperform regular nested loops on iteration (once this works i'll change function calls, buffer iteration ... but so far I just want it to work ^^)
You don't really need np.nditer for this. A simpler way of iterating over all but the first axis is just to reshape into a [6, 500 ** 3] array, transpose it to [500 ** 3, 6], then iterate over the rows:
for (a, b, c, d, e, f) in (symMatrix.reshape(6, -1).T):
# do something involving a, b, c, d, e, f...
If you really want to use np.nditer then you would do something like this:
for (a, b, c, d, e, f) in np.nditer(x, flags=['external_loop'], order='F'):
# do something involving a, b, c, d, e, f...
A potentially important thing to consider is that if symMatrix is C-order (row-major) rather than Fortran-order (column-major) then iterating over the first dimension may be significantly faster than iterating over the last 3 dimensions, since then you will be accessing adjacent blocks of memory address. You might therefore want to consider switching to Fortran-order.
I wouldn't expect a massive performance gain from either of these, since at the end of the day you're still doing all of your looping in Python and operating only on scalars rather than taking advantage of vectorization.
In Python,
I created a 10 x 20 zero-matrix, called X:
X = numpy.zeros((10, 20))
I have another 50 x 20 matrix called A.
I want to let the 4th row of matrix X take the value of the 47th row of matrix A.
How can I write this in Python?
Note: if X is a list, then I could just write X.append () However, here X is not a list...then how can I do this?
Or, if I just have a list that contains 20 numbers, how can I let the 4th row of matrix X equal to that list of 20 numbers?
Thank you!
I'll try to answer this. So the correct syntax for selecting an entire row in numpy is
M[row_number, :]
The : part just selects the entire row in a shorthand way.
There is also a possibility of letting it go from some index to the end by using m:, where m is some known index.
If you want to go between to known indices, then we will use
M[row_number, m:n]
where m < n.
You can equate the rows/columns of a 2D-array only if they are of the same dimension.
I won't give you the exact piece of code that you'll need, but hopefully now you can figure it out using the above piece of code.
I will also suggest playing around with all kinds of matrices, and their operations like replacing some elements, columns, and rows, as well as playing with matrix multiplication until you get the hang of it.
Some useful, commands include
numpy.random.rand(m, n) # will create a matrix of dimension m x n with pseudo-random numbers between 0 and 1
numpy.random.rand(m, n) # will create a matrix of dimension m x n with pseudo-random numbers between -1 and 1
numpy.eye(m) # will create a m x m identity matrix.
numpy.ones((m, n))
And make sure to read through the docs.
Good luck! And let your Python journey be a fun one. :)
I am having a small issue understanding indexing in Numpy arrays. I think a simplified example is best to get an idea of what I am trying to do.
So first I create an array of zeros of the size I want to fill:
x = range(0,10,2)
y = range(0,10,2)
a = zeros(len(x),len(y))
so that will give me an array of zeros that will be 5X5. Now, I want to fill the array with a rather complicated function that I can't get to work with grids. My problem is that I'd like to iterate as:
for i in xrange(0,10,2):
for j in xrange(0,10,2):
.........
"do function and fill the array corresponding to (i,j)"
however, right now what I would like to be a[2,10] is a function of 2 and 10 but instead the index for a function of 2 and 10 would be a[1,4] or whatever.
Again, maybe this is elementary, I've gone over the docs and find myself at a loss.
EDIT:
In the end I vectorized as much as possible and wrote the simulation loops that I could not in Cython. Further I used Joblib to Parallelize the operation. I stored the results in a list because an array was not filling right when running in Parallel. I then used Itertools to split the list into individual results and Pandas to organize the results.
Thank you for all the help
Some tips for your to get the things done keeping a good performance:
- avoid Python `for` loops
- create a function that can deal with vectorized inputs
Example:
def f(xs, ys)
return x**2 + y**2 + x*y
where you can pass xs and ys as arrays and the operation will be done element-wise:
xs = np.random.random((100,200))
ys = np.random.random((100,200))
f(xs,ys)
You should read more about numpy broadcasting to get a better understanding about how the arrays's operations work. This will help you to design a function that can handle properly the arrays.
First, you lack some parenthesis with zeros, the first argument should be a tuple :
a = zeros((len(x),len(y)))
Then, the corresponding indices for your table are i/2 and j/2 :
for i in xrange(0,10,2):
for j in xrange(0,10,2):
# do function and fill the array corresponding to (i,j)
a[i/2, j/2] = 1
But I second Saullo Castro, you should try to vectorize your computations.
I am trying to implement the following equation using scipy's sparse package:
W = x[:,1] * y[:,1].T + x[:,2] * y[:,2].T + ...
where x & y are a nxm csc_matrix. Basically I'm trying to multiply each col of x by each col of y and sum the resulting nxn matrices together. I then want to make all non-zero elements 1.
This is my current implementation:
c = sparse.csc_matrix((n, n))
for i in xrange(0,m):
tmp = bam.id2sym_thal[:,i] * bam.id2sym_cort[:,i].T
minimum(tmp.data,ones_like(tmp.data),tmp.data)
maximum(tmp.data,ones_like(tmp.data),tmp.data)
c = c + tmp
This implementation has the following problems:
Memory usage seems to explode. As I understand it, memory should only increase as c becomes less sparse, but I am seeing that the loop starts eating up >20GB of memory with a n=10,000, m=100,000 (each row of x & y only has around 60 non-zero elements).
I'm using a python loop which is not very efficient.
My question: Is there a better way to do this? Controlling memory usage is my first concern, but it would be great to make it faster!
Thank you!
Note that a sum of outer products in the manner you describe is simply the same as multiplying two matrices together. In other words,
sum_i X[:,i]*Y[:,i].T == X*Y.T
So just multiply the matrices together.
Z = X*Y.T
For n=10000 and m=100000 and where each column has one nonzero element in both X and Y, it computes almost instantly on my laptop.
In terms of memory and performance, this might be a prime candidate for using Cython.
There is a section of the following paper describing its use with sparse scipy matricies:
http://folk.uio.no/dagss/cython_cise.pdf