Related
I want to write custom Pytorch loss function.
It recieves two batches:
batch of square matrices
batch of ints
For each matrix in batch 1. I want to apply torch.tril(matrix, diagonal=d), where diagonal d is corresponding element in batch 2.
However torch does not allow me to pass a vector as diagonal parameter
I can not use for loop to apply tril element-wise, because it will be impossible to calculate gradient.
So I need some vector pytorch operation, that does the same as tril, but can receive tensor as diagonal parameter
Is there any way to perform described above operation in pytorch?
Here I designed a toy example of batches of 2 element
import torch
import numpy as np
matrix = np.array([[1,2,3,4,5], [10,20,30,40,50], [100,200,300,400,500],
[31,23,33,43,53], [21,22,23,24,25]])
matrix2 = np.array([[10,20,30,40,50], [100,200,300,400,500], [100,200,300,400,500],
[31,23,33,43,53], [21,22,23,24,25]])
matrix_batch = torch.Tensor([matrix, matrix2])
diagonals = torch.Tensor([-1, -2])
I expect recieve the following tensor:
result = torch.Tensor(
[[[ 0., 0., 0., 0., 0.],
[ 10., 0., 0., 0., 0.],
[100., 200., 0., 0., 0.],
[ 31., 23., 33., 0., 0.],
[ 21., 22., 23., 24., 0.]],
[[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[100., 0., 0., 0., 0.],
[ 31., 23., 0., 0., 0.],
[ 21., 22., 23., 0., 0.]]])
I'm trying to replicate this behaviour but on a 2D array with no success
indices = [1, 4, 5, 6, 7]
zero = np.zeros(10)
zero[indices] = 42
zero
>>>array([ 0., 42., 0., 0., 42., 42., 42., 42., 0., 0.])
If i try the same with a zeros 5x5 matrix i can't figure out how to do the correct slicing
indices = np.array([1,2],[2,4],[3,1])
zero = np.zeros((5,5))
zero[indices] = 42
This is my output
>>> zero
array([[ 0., 0., 0., 0., 0.],
[ 0., 42., 0., 0., 0.],
[ 0., 0., 42., 0., 0.],
[ 0., 0., 0., 42., 0.],
[ 0., 0., 0., 0., 42.]])
My desired would be like:
>>> zero
array([[ 0., 0., 0., 0., 0.],
[ 0., 0., 42., 0., 0.],
[ 0., 0., 0., 0., 42.],
[ 0., 42., 0., 0., 0.],
[ 0., 0., 0., 0., 0.]])
I have tried slicing like zeros[indices,indices] but i just get to modify the first two elements
You can provide the two axes ("x" and "y") of your indices separately:
indices = np.array([[1,2],[2,4],[3,1]])
zero = np.zeros((5,5))
zero[indices[:,0], indices[:,1]] = 42
>>>zero
array([[ 0., 0., 0., 0., 0.],
[ 0., 0., 42., 0., 0.],
[ 0., 0., 0., 0., 42.],
[ 0., 42., 0., 0., 0.],
[ 0., 0., 0., 0., 0.]])
So I was trying to create a code to find the shortest path length of a subpath using Networkx, basically what my code does is that it takes a 3D array to create graphs and then save them in a list so I can I use this list to find the shortest path and the shortest path length using networkx.
After that, based on the information in the lists, I want to find the shortest path length of a subpath inside the graph, if the len of a path is less equal than 3, then the shortest path is between the same source node and target node (so the length will be zero) and if the len is greater than that, then it's supposed to find the shortest path length between the second node in the path and the penultimate node (something like the "center" of the path), my code is below
import networkx as nx
import numpy as np
arr= np.array([[[ 0., 191., 16., 17., 15., 18., 18.],
[ 0., 0., 0., 0., 0., 0., 0.],
[ 0., 141., 0., 0., 0., 18., 0.],
[ 0., 138., 0., 0., 0., 0., 19.],
[ 0., 80., 0., 0., 0., 0., 15.],
[ 0., 130., 11., 0., 0., 0., 19.],
[ 0., 135., 0., 12., 16., 12., 0.]],
[[ 0., 156., 17., 13., 19., 10., 11.],
[ 0., 0., 0., 0., 0., 0., 0.],
[ 0., 21., 0., 0., 0., 6., 0.],
[ 0., 147., 0., 0., 0., 0., 4.],
[ 0., 143., 0., 0., 0., 0., 6.],
[ 0., 69., 4., 0., 0., 0., 7.],
[ 0., 87., 0., 1., 5., 9., 0.]],
[[ 0., 161., 18., 16., 13., 13., 17.],
[ 0., 0., 0., 0., 0., 0., 0.],
[ 0., 138., 0., 0., 0., 21., 0.],
[ 0., 64., 0., 0., 0., 0., 29.],
[ 0., 23., 0., 0., 0., 0., 29.],
[ 0., 2., 24., 0., 0., 0., 27.],
[ 0., 61., 0., 24., 29., 26., 0.]],
[[ 0., 163., 12., 13., 17., 19., 13.],
[ 0., 0., 0., 0., 0., 0., 0.],
[ 0., 142., 0., 0., 0., 35., 0.],
[ 0., 122., 0., 0., 0., 0., 31.],
[ 0., 72., 0., 0., 0., 0., 36.],
[ 0., 50., 39., 0., 0., 0., 31.],
[ 0., 4., 0., 38., 39., 35., 0.]],
[[ 0., 180., 17., 19., 13., 18., 15.],
[ 0., 0., 0., 0., 0., 0., 0.],
[ 0., 44., 0., 0., 0., 46., 0.],
[ 0., 27., 0., 0., 0., 0., 47.],
[ 0., 81., 0., 0., 0., 0., 45.],
[ 0., 116., 48., 0., 0., 0., 45.],
[ 0., 16., 0., 42., 49., 49., 0.]]])
graphs= []
paths = []
pathlenght = []
aux = []
for i in arr :
graphs.append(nx.from_numpy_array(i, create_using = nx.DiGraph)) #List of graphs created by the 3D array
for j in graphs:
paths.append(nx.shortest_path(j, 0, 1, weight = 'weight')) #Shortest paths of every graph
pathlenght.append(nx.shortest_path_length(j, 0, 1, weight = 'weight')) #Shortest path length of every graphs
for i in graphs:
for j in paths:
if len(j) <= 3:
aux.append(nx.shortest_path_length(i, j[0], j[0], weight = 'weight'))
else:
aux.append(nx.shortest_path_length(i, j[1], j[-2], weight = 'weight'))
print(paths) # [[0, 4, 1], [0, 5, 2, 1], [0, 5, 1], [0, 6, 1], [0, 6, 1]]
print(pathlenght) # [95.0, 35.0, 15.0, 17.0, 31.0]
print(aux) #[ 0. 11. 0. 0. 0. 0. 4. 0. 0. 0. 0. 24. 0. 0. 0. 0. 39. 0. 0. 0. 0. 48. 0. 0. 0.] shape = (25,)
The paths and the path length is fine, but in the aux list I was expecting the output to be
#aux = [0, 4.0, 0, 0, 0]
I know the problem is with the double for-loop since there is 5 graphs and 5 paths, the aux list have 25 elements, but I want to use the path according with his graph (path 1 with graph 1, path 2 with graph 2 and so on and so on) so the output of aux will be the same as above.
I'm kinda new using the for-loop So I was hoping you can help me, or if there is another way of doing what I'm trying to achieve, any help will be appreciated, thank you!
You can iterate over the corresponding pairs (graph, path) by using the zip function.
Example:
for g, path in zip(graphs, paths):
if len(path) <= 3:
aux.append(nx.shortest_path_length(g, path[0], path[0], weight = 'weight'))
else:
aux.append(nx.shortest_path_length(g, path[1], path[-2], weight = 'weight'))
I am trying to find the first minimum after the first peak in a data array. Here is my code:
x = array
mins = argrelextrema(x, np.less)[0]
mins_above_zero = np.where(x[mins] > 0)[0]
ag = x[mins[mins_above_zero]].argmin()
true_minimum_index = mins[ag]
pyplot.scatter(mins, x[mins])
pyplot.plot(x)
pyplot.ylim(0, 2000)
It currently picks out too many minimums.
If I have a numpy array like this:
array([ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 1., 0., 1., 0.,
0., 0., 1., 1., 0., 0., 2., 0., 2., 1., 3.,
1., 2., 5., 8., 6., 55., 396., 608., 157., 40., 45.,
43., 51., 74., 89., 107., 121., 98., 111., 122., 170., 187.,
190., 229., 284., 372., 450., 457., 327., 328., 318., 288., 290.,
262., 235., 223., 177., 232., 217., 234., 261., 206., 192., 221.,
189., 181., 185., 162., 140., 144., 171., 176., 168., 213., 222.,
314., 397., 413., 429., 442., 352., 416., 439., 424., 480., 479.,
515., 522., 569., 543., 626., 666., 637., 680., 678., 747., 720.,
695., 674., 605., 490., 475., 332., 284., 252., 169., 140., 117.,
86., 71., 58., 55., 37., 45., 35., 25., 21., 16., 14.,
17., 12., 9., 7., 6., 0., 6., 6., 6., 3., 1.,
1., 4., 2., 1., 4., 0., 2., 2., 0., 1., 2.,
0., 0., 4., 0., 1., 1., 0., 0., 0., 0., 0.,
0., 1., 1., 0.])
That creates a plot like this except just with the minimum after the first peak:
Try playing with the order of argrelextrema
With your array data:
x = array
# Order 2 looks at more than just the immediate numbers around a variable
mins = argrelextrema(x, np.less, order=2)[0]
print(mins)
mins_above_zero = np.where(x[mins] > 0)[0]
ag = x[mins[mins_above_zero]].argmin()
true_minimum_index = mins[ag]
#Grabs the first relative minimum
mins = mins[0]
pyplot.scatter(mins, x[mins])
pyplot.plot(x)
pyplot.ylim(0, 2000)
Which creates:
This question already has an answer here:
python: Multiply slice i of a matrix stack by column i of a matrix efficiently
(1 answer)
Closed 5 years ago.
There are really similar questions here, here, here, but I don't really understand how to apply them to my case precisely.
I have an array of matrices and an array of vectors and I need element-wise dot product. Illustration:
In [1]: matrix1 = np.eye(5)
In [2]: matrix2 = np.eye(5) * 5
In [3]: matrices = np.array((matrix1,matrix2))
In [4]: matrices
Out[4]:
array([[[ 1., 0., 0., 0., 0.],
[ 0., 1., 0., 0., 0.],
[ 0., 0., 1., 0., 0.],
[ 0., 0., 0., 1., 0.],
[ 0., 0., 0., 0., 1.]],
[[ 5., 0., 0., 0., 0.],
[ 0., 5., 0., 0., 0.],
[ 0., 0., 5., 0., 0.],
[ 0., 0., 0., 5., 0.],
[ 0., 0., 0., 0., 5.]]])
In [5]: vectors = np.ones((5,2))
In [6]: vectors
Out[6]:
array([[ 1., 1.],
[ 1., 1.],
[ 1., 1.],
[ 1., 1.],
[ 1., 1.]])
In [9]: np.array([m # v for m,v in zip(matrices, vectors.T)]).T
Out[9]:
array([[ 1., 5.],
[ 1., 5.],
[ 1., 5.],
[ 1., 5.],
[ 1., 5.]])
This last line is my desired output. Unfortunately it is very inefficient, for instance doing matrices # vectors that computes unwanted dot products due to broadcasting (if I understand well, it returns the first matrix dot the 2 vectors and the second matrix dot the 2 vectors) is actually faster.
I guess np.einsum or np.tensordot might be helpful here but all my attempts have failed:
In [30]: np.einsum("i,j", matrices, vectors)
ValueError: operand has more dimensions than subscripts given in einstein sum, but no '...' ellipsis provided to broadcast the extra dimensions.
In [34]: np.tensordot(matrices, vectors, axes=(0,1))
Out[34]:
array([[[ 6., 6., 6., 6., 6.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.]],
[[ 0., 0., 0., 0., 0.],
[ 6., 6., 6., 6., 6.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.]],
[[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 6., 6., 6., 6., 6.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.]],
[[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 6., 6., 6., 6., 6.],
[ 0., 0., 0., 0., 0.]],
[[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 6., 6., 6., 6., 6.]]])
NB: my real-case scenario use more complicated matrices than matrix1 and matrix2
With np.einsum, you might use:
np.einsum("ijk,ki->ji", matrices, vectors)
#array([[ 1., 5.],
# [ 1., 5.],
# [ 1., 5.],
# [ 1., 5.],
# [ 1., 5.]])
You can use # as follows
matrices # vectors.T[..., None]
# array([[[ 1.],
# [ 1.],
# [ 1.],
# [ 1.],
# [ 1.]],
# [[ 5.],
# [ 5.],
# [ 5.],
# [ 5.],
# [ 5.]]])
As we can see it computes the right thing but arranges them wrong.
Therefore
(matrices # vectors.T[..., None]).squeeze().T
# array([[ 1., 5.],
# [ 1., 5.],
# [ 1., 5.],
# [ 1., 5.],
# [ 1., 5.]])