Multiply 2D tensor with 3D tensor in pytorch - pytorch

Suppose I have a matrix such as P = [[0,1],[1,0]] and a vector v = [a,b]. If I multiply them I have:
Pv = [b,a]
The matrix P is simply a permutation matrix, which changes the order of each element.
Now suppose that I have the same P, but I have the matrices M1 = [[1,2],[3,4]] and M2=[[5,6],[7,8]]. Now let me combine them as the 3D Tensor T= [[[1,2],[3,4]], [[5,6],[7,8]]] with dimensions (2,2,2) - (C,W,H). Suppose I multiply P by T such that:
PT = [[[5,6],[7,8]], [[1,2],[3,4]]]
Note that now M1 now equals [[5,6],[7,8]] and M2 equals [[1,2],[3,4]] as the values have been permuted across the C dimension in T (C,W,H).
How can I multiply PT (P=2D tensor,T=3D tensor) in pytorch using matmul? The following does not work:
torch.matmul(P, T)

An alternative solution to #mlucy's answer, is to use torch.einsum. This has the benefit of defining the operation yourself, without worrying about torch.matmul's requirements:
>>> torch.einsum('ij,jkl->ikl', P, T)
tensor([[[5, 6],
[7, 8]],
[[1, 2],
[3, 4]]])
Or with torch.matmul:
>>> (P # T.flatten(1)).reshape_as(T)
tensor([[[5, 6],
[7, 8]],
[[1, 2],
[3, 4]]])

You could do something like:
torch.matmul(P, X.flatten(1)).reshape(X.shape)

Related

Finding a vector that is orthogonal to n columns of a matrix

Given a matrix B with shape (M, N), where M > N. How to find a vector v (with shape of M) that is perpendicular to all columns in B.
I tried using Numpy numpy.linalg.lstsq method to solve : Bx = 0. 0 here is a vector with M zeros.
It returns a vector of zeros with (N,) shape.
You can use sympy library, like
from sympy import Matrix
B = [[2, 3, 5], [-4, 2, 3], [0, 0, 0]]
V = A.nullspace()[0]
or to find whole nullspace
N = A.nullspace()
Here is what worked for me in case someone else need the answer:
u,s,vh=np.linalg.svd(B)
v=vh[-1:1]

Numpy reduce array to a given shape

I am trying to write a function that reduces a numpy ndarray to a given shape, which effectively "unbroadcasts" the array. For example, using add as the universal function, i want the following results:
A = [1, 2, 3, 4, 5], reduced_shape = (1,) -> [1+2+3+4+5] = [15] (sum over axis=0)
A = [[1,2], [1,2]], reduced_shape = (2,) -> [1+1, 2+2] = [2, 4] (sum over axis=0)
A = [[1,2], [1,2]], reduced_shape = (1,) -> [1+2+1+2] = [6] (sum over axis=(0,1))
A = [[[1,2], [1,2]], [[1,2], [1,2]]], reduced_shape = (2,2) -> [[1+1, 2+2], [1+1, 2+2]] = [[2,4], [2,4]] (sum over axis=0)
This is the solution i came up with:
def unbroadcast(A, reduced_shape):
fill = reduced_shape[-1] if reduced_shape else None
reduced_axes = tuple(i for i, (a,b) in enumerate(itertools.zip_longest(A, shape, fillvalue=fill)) if a!=b)
return np.add.reduce(A, axis=reduced_axes).reshape(shape)
But it feels unnecessarily complex, is there way to implement this that relies on Numpy's public API?
It's not clear how this is an 'un-broadcasting'.
The straight forward way of doing your calculations is to use the axis parameter of sum:
In [124]: np.array([1,2,3,4,5]).sum()
Out[124]: 15
In [125]: np.array([[1,2],[1,2]]).sum(axis=0)
Out[125]: array([2, 4])
In [126]: np.array([[1,2],[1,2]]).sum(axis=(0,1))
Out[126]: 6
In [128]: np.array([[[1,2], [1,2]], [[1,2], [1,2]]]).sum(axis=0)
Out[128]:
array([[2, 4],
[2, 4]])
I don't use reduce as much, but looks like axis does the same:
In [130]: np.add.reduce(np.array([1,2,3,4,5]))
Out[130]: 15
In [132]: np.add.reduce(np.array([[[1,2], [1,2]], [[1,2], [1,2]]]),axis=0)
Out[132]:
array([[2, 4],
[2, 4]])
But I haven't worked out the logic of going from your reduced_shape to the necessary axis values. With shapes like (2,) and (2,2,2), there's potential ambiguity when you say reduce the shape to (2,2). It might be clearer if you worked with samples arrays like np.arange(24).reshape(2,3,4)

What is the output vector shape in pair distance (pdist)?

from numpy.spatial.distance import pdist
X = [[1, 2], [1, 3], [2, 2], [3, 1]]
Y = pdist(X)
print(Y)
# 1.0000 1.0000 2.2361 1.4142 2.8284 1.4142
I have an array X of shape (4,2) which I pass to pdist. I don't understand why the shape of the output Y is (6,).
pdist outputs all unique internal distances in your matrix X.
In your simple case, it outputs the distance between the following index pairs:
[0,1], [0,2], [0,3], [1,2], [1,3], [2,3]
The equation for the expected number of outputs is N(N-1)/2 for an input array of shape (N,M).

How to repeat tensor in a specific new dimension in PyTorch

If I have a tensor A which has shape [M, N],
I want to repeat the tensor K times so that the result B has shape [M, K, N]
and each slice B[:, k, :] should has the same data as A.
Which is the best practice without a for loop.
K might be in other dimension.
torch.repeat_interleave() and tensor.repeat() does not seem to work. Or I am using it in a wrong way.
tensor.repeat should suit your needs but you need to insert a unitary dimension first. For this we could use either tensor.unsqueeze or tensor.reshape. Since unsqueeze is specifically defined to insert a unitary dimension we will use that.
B = A.unsqueeze(1).repeat(1, K, 1)
Code Description A.unsqueeze(1) turns A from an [M, N] to [M, 1, N] and .repeat(1, K, 1) repeats the tensor K times along the second dimension.
Einops provides repeat function
import einops
einops.repeat(x, 'm n -> m k n', k=K)
repeat can add arbitrary number of axes in any order and reshuffle existing axes at the same time.
Adding to the answer provided by #Alleo. You can use following Einops function.
einops.repeat(example_tensor, 'b h w -> (repeat b) h w', repeat=b)
Where b is the number of times you want your tensor to be repeated and h, w the additional dimensions to the tensor.
Example -
example_tensor.shape -> torch.Size([1, 40, 50])
repeated_tensor = einops.repeat(example_tensor, 'b h w -> (repeat b) h w', repeat=8)
repeated_tensor.shape -> torch.Size([8, 40, 50])
More examples here - https://einops.rocks/api/repeat/
Repeated values are memory heavy, in most cases best practice is to use broadcasting. So you would use A[:, None, :] which would make A.shape==(M, 1, N).
One case where I would agree to repeating the values, is that of in place operations in the following steps.
As numpy and torch differ in their implementations I like to agnostic (A * torch.ones(K, 1, 1))) followed by a transpose.
tensor.expand might be a better choice than tensor.repeat because according to this: "Expanding a tensor does not allocate new memory, but only creates a new view on the existing tensor where a dimension of size one is expanded to a larger size by setting the stride to 0."
However, be aware that: "More than one element of an expanded tensor may refer to a single memory location. As a result, in-place operations (especially ones that are vectorized) may result in incorrect behavior. If you need to write to the tensors, please clone them first."
M = N = K = 3
A = torch.arange(0, M * N).reshape((M, N))
B = A.unsqueeze(1).expand(M, K, N)
B
'''
tensor([[[0, 1, 2],
[0, 1, 2],
[0, 1, 2]],
[[3, 4, 5],
[3, 4, 5],
[3, 4, 5]],
[[6, 7, 8],
[6, 7, 8],
[6, 7, 8]]])
'''

Clip parts of a tensor

I have a theano tensor and I would like to clip its values, but each index to a different range.
For example, if I have a vector [a,b,c] , I want to clip a to [0,1] , clip b to [2,3] and c to [3,5].
How can I do that efficiently?
Thanks!
The theano.tensor.clip operation supports symbolic minimum and maximum values so you can pass three tensors, all of the same shape, and it will perform an element-wise clip of the first with respect to the second (minimum) and third (maximum).
This code shows two variations on this theme. v1 requires the minimum and maximum values to be passed as separate vectors while v2 allows the minimum and maximum values to be passed more like a list of pairs, represented as a two column matrix.
import theano
import theano.tensor as tt
def v1():
x = tt.vector()
min_x = tt.vector()
max_x = tt.vector()
y = tt.clip(x, min_x, max_x)
f = theano.function([x, min_x, max_x], outputs=y)
print f([2, 1, 4], [0, 2, 3], [1, 3, 5])
def v2():
x = tt.vector()
min_max = tt.matrix()
y = tt.clip(x, min_max[:, 0], min_max[:, 1])
f = theano.function([x, min_max], outputs=y)
print f([2, 1, 4], [[0, 1], [2, 3], [3, 5]])
def main():
v1()
v2()
main()

Resources