I am given a quadratic matrix and have to do as follows:
For each entry (i,j) in the matrix
If i = j:
set y[i,j] = x[i,j].
Else:
set y[i,j] = x[i,j] + x[j,i]
I have made the following script:
def symmetrize(x):
## The symmetrized matrix that is returned
y = np.zeros(np.shape(x))
## For loop for each element (i,j) in the matrix
for i in range (np.size(x)):
for j in range (np.size(x)):
if i == j:
y[i,j] = x[i,j]
else:
y[i,j] = x[i,j] + x[j,i]
return y
I get this error message whenever I want to run the code with the following matrix:
np.array([[1.2, 2.3, 3.4],[4.5, 5.6, 6.7], [7.8, 8.9, 10.0]])
Error message:
y[i,j] = x[i,j] + x[j,i]
IndexError: index 3 is out of bounds for axis 1 with size 3
Do someone know what the problem is?
np.size(), without an axis, gives you the total number of elements in the matrix. So your range()s are going to go from 0 - 8, not from 0 - 2.
You don't need to use np.size() or np.shape() for that matter; these functions aren't even listed in the documentation any more. Just use the .shape attribute of a matrix:
y = np.zeros(x.shape)
for i in range(x.shape[0]):
for j in range(x.shape[1]):
There are better ways of producing your output. You could use:
def symmetrize(x):
return x + x.T - np.diag(x.diagonal())
instead. x.T is the transposed matrix, so rows and columns swapped. x + x.T is the sum of the original matrix and the transposition matrix, so the numbers on the diagonal are doubled. x.diagonal() is an array of just those numbers on the diagonal, which can be subtracted once you created a matrix of those numbers on the diagonal, which is what np.diag() does for you.
You are using np.size() wrong way, it doesn't tell you how many rows or columns your list has, but number of elements in the array, in your case - 9. You could use shape of your list like so:
def symmetrize(x):
## The symmetrized matrix that is returned
y = np.zeros(np.shape(x))
## For loop for each element (i,j) in the matrix
for i in range(x.shape[0]):
for j in range(x.shape[1]):
if i == j:
y[i,j] = x[i,j]
else:
y[i,j] = x[i,j] + x[j,i]
return y
Related
I'm trying to implement a differential in python via numpy that can accept a scalar, a vector, or a matrix.
import numpy as np
def foo_scalar(x):
f = x * x
df = 2 * x
return f, df
def foo_vector(x):
f = x * x
n = x.size
df = np.zeros((n, n))
for mu in range(n):
for i in range(n):
if mu == i:
df[mu, i] = 2 * x[i]
return f, df
def foo_matrix(x):
f = x * x
m, n = x.shape
df = np.zeros((m, n, m, n))
for mu in range(m):
for nu in range(n):
for i in range(m):
for j in range(n):
if (mu == i) and (nu == j):
df[mu, nu, i, j] = 2 * x[i, j]
return f, df
This works fine, but it seems like there should be a way to do this in a single function, and let numpy "figure out" the correct dimensions. I could force everything into a 2-D array form with something like
x = np.array(x)
if len(x.shape) == 0:
x = x.reshape(1, 1)
elif len(x.shape) == 1:
x = x.reshape(-1, 1)
if len(f.shape) == 0:
f = f.reshape(1, 1)
elif len(f.shape) == 1:
f = f.reshape(-1, 1)
and always have 4 nested for loops, but this doesn't scale if I need to generalize to higher-order tensors.
Is what I'm trying to do possible, and if so, how?
I highly doubt there is a function to generate the second parameter returned by the function in Numpy. That being said you can play with the feature of Numpy and Python so to vectorize this and make the function faster. You first need to generate the indices and, then generate the target matrix and set it. Note that operating with N-dimensional generic arrays tends to be slow and tricky in non-trivial cases. The magic * unrolling operator is used to generate N parameters.
def foo_generic(x):
f = x ** 2
idx = np.stack(np.meshgrid(*[np.arange(e) for e in x.shape], indexing='ij'))
idx = tuple(np.concatenate((idx, idx)).reshape(2*x.ndim, -1))
df = np.zeros([*x.shape, *x.shape])
df[idx] = 2 * x.ravel()
return f, df
Note that foo_generic does not support scalar and it would be very inefficient to use it for that anyway, but you can add a condition in it to support this special case apart.
The df matrix will very quickly be huge for higher order so I strongly advise you not to use dense matrices for that since the number of zeros is huge compared to the number of values in the matrix case already. Sparse matrices fix this. In fact, for a 5x5 matrix, there are >95% of zeros. Not to mention the matrix becomes quickly huge and willing a huge matrix full of zeros is not efficient.
I have an array S(i) which contains all of the index values of the x coordinates j which are close to i, i.e. if S(1) = {2,3} this means that x2 and x3 are close to x1. In total I have S(1), ..., S(N) sets.
So this part of the code works fine:
arr = np.array([[1,3], [2,8],[3,1],[6,18], [9,8]])
arr = [item[0] for item in arr] #Extract x-coordinates
def Si(x): #This is the set i want to use
return [[j for j in range(len(x)) if np.abs(x[j] - x[i]) < 2] for i in range(len(x))]
Now i have a subscript of j's, I want to calculate the euclidean distance between (x_i,y_i) to each (x_j,y_j) in S(i), e.g for i=1, if S(1) = {7}, find distance between (x_1, y_1) and (x_7, y_7), for i=2, if S(2) = {3,9}, find distance between (x_2,y_2) and (x_3,y_3) and (x_2,y_2) and (x_9,y_9) and repeat for each i.
I don't know how to implement this, i'm really confused! Here is a Euclidean distance code which finds it for ALL values in the array, but not in the set which i want.
def euc_dist(arr):
arr_x = (arr[:,0,np.newaxis].T - arr[:,0,np.newaxis])**2 ##x-coordinates
arr_y = (arr[:,1,np.newaxis].T - arr[:,1,np.newaxis])**2 ##y-coordinates
arr = np.sqrt(arr_x + arr_y)
return arr
This should work:
S = Si(arr) # get the array
def my_fn(i): # take the value of i
euc_dists = []
for j in S[i]: # iterate over j's in S[i]
if i!= j:
dist = np.linalg.norm(arr[i]-arr[j]) # euclidean distance
euc_dists.append(dist)
return euc_dists
Let say we have 2 matrices:
mat = torch.randn([20, 7]) * 100
mat2 = torch.randn([7, 20]) * 100
n, m = mat.shape
The simplest usual matrix multiplication looks like this:
def mat_vec_dot_product(mat, vect):
n, m = mat.shape
res = torch.zeros([n])
for i in range(n):
for j in range(m):
res[i] += mat[i][j] * vect[j]
return res
res = torch.zeros([n, n])
for k in range(n):
res[:, k] = mat_vec_dot_product(mat, mat2[:, k])
But what if I need to apply L2 norm instead of dot product? The code is next:
def mat_vec_l2_mult(mat, vect):
n, m = mat.shape
res = torch.zeros([n])
for i in range(n):
for j in range(m):
res[i] += (mat[i][j] - vect[j]) ** 2
res = res.sqrt()
return res
for k in range(n):
res[:, k] = mat_vec_l2_mult(mat, mat2[:, k])
Can we do this somehow in an optimal way using Torch or any other libraries? Cause naive O(n^3) Python code works really slow.
Use torch.cdist for L2 norm - euclidean distance
res = torch.cdist(mat, mat2.permute(1,0), p=2)
Here, I have used permute to swap dim of mat2 from 7,20 to 20,7
First of all, matrix multiplication in PyTorch has a built-in operator: #.
So, to multiply mat and mat2 you simply do:
mat # mat2
(should work, assuming dimensions agree).
Now, to compute the Sum of Squared Differences(SSD, or L2-norm of differences) which you seem to compute in your second block, you can do a simple trick.
Since the squared L2-norm ||m_i - v||^2 (where m_i is the i'th row of matrix M and v is the vector) is equal to the dot product <m_i - v, m_i-v> - from linearity of the dot product you obtain: <m_i,m_i> - 2<m_i,v> + <v,v> so you can compute the SSD of each row in M from vector v by computing once the squared L2-norm of each row, once the dot product between each row and the vector and once the L2-norm of the vector. This can be done in O(n^2).
However, for the SSD between 2 matrices you will still get O(n^3). Improvements can be made though by vectorizing the operations instead of using loops.
Here is a simple implementation for 2 matrices:
def mat_mat_l2_mult(mat,mat2):
rows_norm = (torch.norm(mat, dim=1, p=2, keepdim=True)**2).repeat(1,mat2.shape[1])
cols_norm = (torch.norm(mat2, dim=0, p=2, keepdim=True)**2).repeat(mat.shape[0], 1)
rows_cols_dot_product = mat # mat2
ssd = rows_norm -2*rows_cols_dot_product + cols_norm
return ssd.sqrt()
mat = torch.randn([20, 7])
mat2 = torch.randn([7,20])
print(mat_mat_l2_mult(mat, mat2))
The resulting matrix will have at each cell i,j the L2-norm of the difference between each row i in mat and each column j in mat2.
I have a fairly non trivial function I want to differentiate with autograd but I'm not quite enough of a numpy wizard to figure our how to do it without array assingment.
I also apologize that I had to make this example incredibly contrived and meaningless to be able to run standalone. The actual code I'm working with is for non linear finite elements and is trying to compute the jacobian for a complex non linear system.
import autograd.numpy as anp
from autograd import jacobian
def alpha(x):
return anp.exp(-(x - 10) ** 2) / (x + 1)
def f(x):
# Matrix getting constructed
k = anp.zeros((x.shape[0], x.shape[0]))
# loop over some random 3 dimensional vectors
for element in anp.random.randint(0, x.shape[0], (x.shape[0], 3)):
# select 3 values from x
x_ijk = anp.array([[x[i] for i in element]])
norm = anp.linalg.norm(
x_ijk # anp.vstack((element, element)).transpose()
)
# make some matrix from the element
m = element.reshape(3, 1) # element.reshape(1, 3)
# alpha is an arbitrary differentiable function R -> R
alpha_value = alpha(norm)
# combine m matricies into k scaling by alpha_value
n = m.shape[0]
for i in range(n):
for j in range(n):
k[element[i], element[j]] += m[i, j] * alpha_value
return k # x
print(jacobian(f)(anp.random.rand(10)))
# And course we get an error
# k[element[i], element[j]] += m[i, j] * alpha_value
# ValueError: setting an array element with a sequence.
I don't really understand this message since no type error is happening. I assume it must be from assignment.
After writting the above I made a trivial switch to PyTorch and the code runs just fine. But I would still prefer to use autograd
#pytorch version
import torch
from torch.autograd.gradcheck import zero_gradients
def alpha(x):
return torch.exp(x)
def f(x):
# Matrix getting constructed
k = torch.zeros((x.shape[0], x.shape[0]))
# loop over some random 3 dimensional vectors
for element in torch.randint(0, x.shape[0], (x.shape[0], 3)):
# select 3 values from x
x_ijk = torch.tensor([[1. if n == e else 0 for n in range(len(x))] for e in element]) # x
norm = torch.norm(
x_ijk # torch.stack((torch.tanh(element.float() + 4), element.float() - 4)).t()
)
m = torch.rand(3, 3)
# alpha is an arbitrary differentiable function R -> R
alpha_value = alpha(norm)
n = m.shape[0]
for i in range(n):
for j in range(n):
k[element[i], element[j]] += m[i, j] * alpha_value
print(k)
return k # x
x = torch.rand(4, requires_grad=True)
print(x, '\n')
y = f(x)
print(y, '\n')
grads = []
for val in y:
val.backward(retain_graph=True)
grads.append(x.grad.clone())
zero_gradients(x)
if __name__ == '__main__':
print(torch.stack(grads))
In Autograd, and in JAX, you are not allowed to perform array indexing assignments. See the JAX gotchas for a partial explanation of this.
PyTorch allows this functionality. If you want to run your code in autograd, you'll have to find a way to remove the offending line k[element[i], element[j]] += m[i, j] * alpha_value. If you are okay with running your code in JAX (which has essentially the same syntax as autograd, but more features), then it looks like jax.ops could be helpful for performing this sort of indexing assignment.
I've been looking for a way to create a custom h5py array that is in the end symmetric. Ideally it would be an array such that when it was created had a single value that a[i][j] and a[j][i] pointed to. The reason for this is that I will be writing a large distance vector into a square form matrix. The vector and square matrix are too large to hold in memory, so I would like a relatively fast solution to create the square matrix.
I would suggest doing this with a bit of extra logic use a 1D array to store just the upper triangle of the matrix. Map the index in the 1D array <-> the 2D array with a mapping like this:
[[0 1 2 3 ]
[x 4 5 6 ]
[x x 7 8 ]
[x x x 9 ]]
You can write a function for this as:
from __future__ import division
def tri_ravel_factory(n_cols):
def tri_ravel(j, k):
assert j < n_cols, 'j out of range'
assert k < n_cols, 'j out of range'
assert j >= 0, 'j out of range'
assert k >= 0, 'j out of range'
if k < j:
j, k = k, j
#return sum(n_cols - tmp for tmp in range(0, j)) + (k - j)
return j * n_cols - (j * (j-1))//2 + (k-j)
return tri_ravel
test_ravel = tri_ravel_factory(4)
indx = test_ravel(1, 0)
This only gets you a factor of two. You might be better off with a sparse array, computing the distances you need on the fly, or finding a way to not have to compute most of the distances (like if you only care about pair with distance < r).