How to invert volume of matrices in numpy? - python

Please assume a vector of invertible matrices:
import numpy as np
a = np.arange(120).reshape((2, 2, 5, 6))
I want to invert the matrices over their defined axes:
b = np.linalg.inv(a, axis1=0, axis2=1)
but this does not seems supported.
How to achieve this?

inv docs specifies its array input as:
a : (..., M, M) array_like
Matrix to be inverted.
You have a
a = np.arange(120).reshape((2, 2, 5, 6))
(M,M,...)
The dimensions are in the wrong order - change them!
In [44]: a = np.arange(120).reshape((2, 2, 5, 6))
Change the axes to the order that inv accepts:
In [45]: A = a.transpose(2,3,0,1)
In [46]: Ai = np.linalg.inv(A)
In [47]: Ai.shape
Out[47]: (5, 6, 2, 2)
In [48]: ai = Ai.transpose(2,3,0,1) # and back
In [49]: ai.shape
Out[49]: (2, 2, 5, 6)
I was going to test the result, but got:
In [50]: x = a#ai
Traceback (most recent call last):
File "<ipython-input-50-9dfe3616745d>", line 1, in <module>
x = a#ai
ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 5 is different from 6)
Like inv, matmul treats the last 2 dimensions as the matrix, the first 2 as 'batch':
In [51]: x = A#Ai
In [52]: x[0,0]
Out[52]:
array([[1., 0.],
[0., 1.]])
In [53]: x[0,3]
Out[53]:
array([[1.00000000e+00, 1.38777878e-17],
[4.44089210e-16, 1.00000000e+00]])
We can do the equivalent with einsum:
In [55]: x = np.einsum('ijkl,jmkl->imkl',a,ai)
In [56]: x[:,:,0,0]
Out[56]:
array([[1., 0.],
[0., 1.]])
You might want to change the original specification to match the inv and matmul usage. It could make life easier for you. Also remember that in numpy the trailing dimensions are the inner most ones.

If you know that the matrices are 2x2 you can do that easily using the standard formula for inverting such matrices; otherwise, I fear the only reasonable solution would be to do it with for loops? For example, the following works for any shape (modifying the sizes adequately):
b = np.stack([np.linalg.inv(a[:, :, i, j]) for i in range(a.shape[2]) for j in range(a.shape[3])], axis=2)
b = b.reshape(2, 2, 5, 6)
as checked by
for i in range(a.shape[2]):
for j in range(a.shape[3]):
assert np.allclose(np.dot(a[:,:,i,j], b[:,:,i,j]), np.eye(2))
In the specific 2x2 case you can do the following, which is fully vectorized hence probably faster:
determinants = a[0, 0] * a[1, 1] - a[0, 1] * a[1, 0]
b = 1 / determinants * np.stack([
np.stack([a[1, 1], -a[0, 1]]),
np.stack([-a[1, 0], a[0, 0]]),
])
On the specific (small) input size, the second solution is about 10 times faster in my tests (43us vs. 537us).

Related

NumPy: How to make a 2-dim data array containing both x inputs and f(x) outputs (using example of kinematics)

Trying to make a simple kinematics array (for fun!) where
You're prompted for an int input tmax that determines t = [0, tmax]
So this would mean that if you input tmax = 5, the time interval would be t = [0, 5] seconds
You're prompted for a float input jerkc (third derivative of position) that determines the constant used in calculation of acceleration (jerkc * t), velocity (jerkc * 1/2 * t^2) and position.
I'm trying to use NumPy (I'm very new to this, and to python) to create a two-dimensional array where:
Row 1: time (so for tmax = 5 this row would be: [0, 1, 2, 3, 4, 5])
Row 2: acceleration(t) (so for tmax = 5, Jerkc = 2 this row would be [0, 2, 4, 6, 8, 10]
Row 3: velocity(t) (so on...)
Row 4: position(t) (so on.....)
I'm still very unfamiliar with lists - and especially NumPy arrays. I'm used to Java arrays, if you're wondering why I'm treating this array the way I am. Here's a sample of what I have so far:
import numpy as np
tmax = int(input('Please enter a timeframe [0, t] [int]: '))
print('Thank you! Your timeframe will be [0, ', tmax, ']!')
jerkc = float(input('Please enter a jerk constant [float]: '))
print('Thank you! Your jerk constant will be ', jerkc, '!')
physics = np.array([[], [], [], []])
t = int(0)
i = int(0)
m = int(0)
e = int(0)
while (t <= tmax):
physics[0, t] = t
t = t + 1
while (i <= tmax):
physics[1, t] = (jerkc * t)
t = t + 1
while (m <= tmax):
physics[2, t] = ((jerkc) * (1/2) * (t ^ 2))
t = t + 1
while (e <= tmax):
physics[3, t] = ((jerkc) * (1/2) * (1/3) * (t ^ 3))
print(physics)
If anyone knows what I'm doing wrong, or can explain to me a way I can use arrays better, please let me know, and please explain yourself carefully and understandably! Thank you!
The special thing about NumPy arrays is that you can do calculations with it. This means that the if you multiply 2 arrays, the result array will contain the values of those arrays multiplied with eachother.
As in this example:
>>> array1=array([1,2,3,4])
>>> array2=array([5,6,7,8])
>>>
>>> array1 * array2
array([ 5, 12, 21, 32])
This does also work for addition, substraction, division...
Another great thing in NumPy is the arange function. It works like the range function in python, but instead returns an array. It has three arguments: start, end and step. The returned array will start with the start argument. The following item will then be added up with the step value, and so on. Untill the next item would be larger then the end value.
Here's an example:
>>> arange(1,10)
array([1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> arange(1,10,2)
array([1, 3, 5, 7, 9])
>>> arange(10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
Note: When the step argument is not provided, it is defaulted to 1. If you provide only one argument, it will start from 0 and end with this argument.
There's also a zeros function. It returns an array of the given size, filled with zeros. It is preferrable to initialise arrays in this way.
>>> zeros(10)
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
>>> zeros((2,2))
array([[0., 0.],
[0., 0.]])
Finally, just as almost all indexable objects in python, NumPy arrays support slicing. It works in the way array[start:end]. It returns the part of the array from index start to index before end. If the start is omitted, the result will be from the start of the array to index end. The same goes for the end argument. If both are omitted (:), the result is just the whole array. Additionally, a third step value can be provided, array[start:end:step]. This does works in the same way as with arange, the indexes of the result values will be counted up from start.
An example:
>>> array1=array([1,2,3,4,5,6,7,8,9,10])
>>>
>>> array1[2:4]
array([3, 4])
>>> array1[:4]
array([1, 2, 3, 4])
>>> array1[2:]
array([ 3, 4, 5, 6, 7, 8, 9, 10])
>>> array1[:]
array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
>>> array1[5:9:3] #with step value
array([6, 9])
As NumPy prefers having an index for all the dimensions, we are going to use physics[1,:] to tell NumPy that we want to change all values in the array.
If we would rewrite your code using the previously mentioned things, it would look like:
import numpy as np
tmax = int(input('Please enter a timeframe [0, t] [int]: '))
print('Thank you! Your timeframe will be [0, ', tmax, ']!')
jerkc = float(input('Please enter a jerk constant [float]: '))
print('Thank you! Your jerk constant will be ', jerkc, '!')
physics = np.zeros((4,tmax+1))
physics[0, :] = np.arange(tmax+1)#+1, as the end is not included in the array.
physics[1, :] = physics[0, :] * jerkc
physics[2, :] = ((jerkc) * (1/2) * (physics[0, :] ** 2))
physics[3, :] = ((jerkc) * (1/2) * (1/3) * (physics[0, :] ** 3))
Note: ** is the power operator in python, ^ is exclusive or and thus won't work.

For a given condition, get indices of values in 2D tensor A, use those to index a 3D tensor B

For a given 2D tensor I want to retrieve all indices where the value is 1. I expected to be able to simply use torch.nonzero(a == 1).squeeze(), which would return tensor([1, 3, 2]). However, instead, torch.nonzero(a == 1) returns a 2D tensor (that's okay), with two values per row (that's not what I expected). The returned indices should then be used to index the second dimension (index 1) of a 3D tensor, again returning a 2D tensor.
import torch
a = torch.Tensor([[12, 1, 0, 0],
[4, 9, 21, 1],
[10, 2, 1, 0]])
b = torch.rand(3, 4, 8)
print('a_size', a.size())
# a_size torch.Size([3, 4])
print('b_size', b.size())
# b_size torch.Size([3, 4, 8])
idxs = torch.nonzero(a == 1)
print('idxs_size', idxs.size())
# idxs_size torch.Size([3, 2])
print(b.gather(1, idxs))
Evidently, this does not work, leading to aRunTimeError:
RuntimeError: invalid argument 4: Index tensor must have same
dimensions as input tensor at
C:\w\1\s\windows\pytorch\aten\src\TH/generic/THTensorEvenMoreMath.cpp:453
It seems that idxs is not what I expect it to be, nor can I use it the way I thought. idxs is
tensor([[0, 1],
[1, 3],
[2, 2]])
but reading through the documentation I don't understand why I also get back the row indices in the resulting tensor. Now, I know I can get the correct idxs by slicing idxs[:, 1] but then still, I cannot use those values as indices for the 3D tensor because the same error as before is raised. Is it possible to use the 1D tensor of indices to select items across a given dimension?
You could simply slice them and pass it as the indices as in:
In [193]: idxs = torch.nonzero(a == 1)
In [194]: c = b[idxs[:, 0], idxs[:, 1]]
In [195]: c
Out[195]:
tensor([[0.3411, 0.3944, 0.8108, 0.3986, 0.3917, 0.1176, 0.6252, 0.4885],
[0.5698, 0.3140, 0.6525, 0.7724, 0.3751, 0.3376, 0.5425, 0.1062],
[0.7780, 0.4572, 0.5645, 0.5759, 0.5957, 0.2750, 0.6429, 0.1029]])
Alternatively, an even simpler & my preferred approach would be to just use torch.where() and then directly index into the tensor b as in:
In [196]: b[torch.where(a == 1)]
Out[196]:
tensor([[0.3411, 0.3944, 0.8108, 0.3986, 0.3917, 0.1176, 0.6252, 0.4885],
[0.5698, 0.3140, 0.6525, 0.7724, 0.3751, 0.3376, 0.5425, 0.1062],
[0.7780, 0.4572, 0.5645, 0.5759, 0.5957, 0.2750, 0.6429, 0.1029]])
A bit more explanation about the above approach of using torch.where(): It works based on the concept of advanced indexing. That is, when we index into the tensor using a tuple of sequence objects such as tuple of tensors, tuple of lists, tuple of tuples etc.
# some input tensor
In [207]: a
Out[207]:
tensor([[12., 1., 0., 0.],
[ 4., 9., 21., 1.],
[10., 2., 1., 0.]])
For basic slicing, we would need a tuple of integer indices:
In [212]: a[(1, 2)]
Out[212]: tensor(21.)
To achieve the same using advanced indexing, we would need a tuple of sequence objects:
# adv. indexing using a tuple of lists
In [213]: a[([1,], [2,])]
Out[213]: tensor([21.])
# adv. indexing using a tuple of tuples
In [215]: a[((1,), (2,))]
Out[215]: tensor([21.])
# adv. indexing using a tuple of tensors
In [214]: a[(torch.tensor([1,]), torch.tensor([2,]))]
Out[214]: tensor([21.])
And the dimension of the returned tensor would always be one dimension less than the dimension of the input tensor.
Assuming that b's three dimensions are batch_size x sequence_length x features (b x s x feats), the expected results can be achieved as follows.
import torch
a = torch.Tensor([[12, 1, 0, 0],
[4, 9, 21, 1],
[10, 2, 1, 0]])
b = torch.rand(3, 4, 8)
print(b.size())
# b x s x feats
idxs = torch.nonzero(a == 1)[:, 1]
print(idxs.size())
# b
c = b[torch.arange(b.size(0)), idxs]
print(c.size())
# b x feats
import torch
a = torch.Tensor([[12, 1, 0, 0],
[4, 9, 21, 1],
[10, 2, 1, 0]])
b = torch.rand(3, 4, 8)
print('a_size', a.size())
# a_size torch.Size([3, 4])
print('b_size', b.size())
# b_size torch.Size([3, 4, 8])
#idxs = torch.nonzero(a == 1, as_tuple=True)
idxs = torch.nonzero(a == 1)
#print('idxs_size', idxs.size())
print(torch.index_select(b,1,idxs[:,1]))
As a supplementary of #kmario23's solution, you can still achieve the same results like
b[torch.nonzero(a==1,as_tuple=True)]

broadcasting arrays in numpy

I got an array and reshaped it to the following dimentions: (-1,1,1,1) and (-1,1):
Array A:
[-0.888788523827 0.11842529285 0.319928774626 0.319928774626 0.378755429421 1.225877519716 3.830653798838]
A.reshape(-1,1,1,1):
[[[[-0.888788523827]]]
[[[ 0.11842529285 ]]]
[[[ 0.319928774626]]]
[[[ 0.319928774626]]]
[[[ 0.378755429421]]]
[[[ 1.225877519716]]]
[[[ 3.830653798838]]]]
A.reshape(-1,1):
[[-0.888788523827]
[ 0.11842529285 ]
[ 0.319928774626]
[ 0.319928774626]
[ 0.378755429421]
[ 1.225877519716]
[ 3.830653798838]]
Then I have done substractig and broadcasting came in, so my resulting matrix is 7x1x7x1.
I have a hard time to visualize the intermediate step what broadcasting does. I mean I cannot imagine what elements of arrays are repeated and what they look like while broadcasting.
Could somebody shed some light on this problem,please?
In [5]: arr = np.arange(4)
In [6]: A = arr.reshape(-1,1,1,1)
In [7]: B = arr.reshape(-1,1)
In [8]: C = A + B
In [9]: C.shape
Out[9]: (4, 1, 4, 1)
In [10]: A.shape
Out[10]: (4, 1, 1, 1)
In [11]: B.shape
Out[11]: (4, 1)
There are 2 basic broadcasting rules:
expand the dimensions to match - by adding size 1 dimensions at the start
adjust all size 1 dimensions to match
So in this example:
(4,1,1,1) + (4,1)
(4,1,1,1) + (1,1,4,1) # add 2 size 1's to B
(4,1,4,1) + (4,1,4,1) # adjust 2 of the 1's to 4
(4,1,4,1)
The first step is, perhaps, the most confusing. The (4,1) is expanded to (1,1,4,1), not (4,1,1,1). The rule is intended to avoid ambiguity - by expanding in a consistent manner, not necessarily what a human might intuitively want.
Imagine the case where both arrays need expansion to match, and it could add a dimension in either direction:
(4,) and (3,)
(1,4) and (3,1) or (4,1) and (1,3)
(3,4) or (4,3)
confusion
The rule requires that the programmer choose which one expands to the right (4,1) or (3,1). numpy can then unambiguously add the other.
For a simpler example:
In [22]: A=np.arange(3).reshape(-1,1)
In [23]: B=np.arange(3)
In [24]: C = A+B (3,1)+(3,) => (3,1)+(1,3) => (3,3)
In [25]: C
Out[25]:
array([[0, 1, 2],
[1, 2, 3],
[2, 3, 4]])
In [26]: C.shape
Out[26]: (3, 3)
The [0,2,4] are present, but on the diagonal of C.
When broadcasting like this, the result is a kind of outer sum:
In [27]: np.add.outer(B,B)
Out[27]:
array([[0, 1, 2],
[1, 2, 3],
[2, 3, 4]])

SciPy lfilter with initial conditions applied along any axis of N-D array

According to SciPy docs for lfilter:
zi : array_like, optional
Initial conditions for the filter delays. It is a vector (or array of vectors for an N-dimensional input) of length max(len(a),len(b))-1. If zi is None or is not given then initial rest is assumed. See lfiltic for more information.
The following code calls lfilter, and passes zi using lfilter_zi such that the length of the last dimension of zi is max(len(a),len(b))-1. However it raises an error, depending on the axis of application:
import numpy as np
import scipy.signal as sig
def apply_filter(B, A, signal, axis=-1):
# apply filter, setting proper initial state (doesn't assume rest)
filtered, zf = sig.lfilter(B, A, signal,
zi=sig.lfilter_zi(B, A) * np.take(signal, 0, axis=axis)[..., np.newaxis], axis=axis)
return filtered
B, A = sig.butter(1, 0.5)
x = np.random.randn(12, 50)
apply_filter(B, A, x, axis=1) # works without error
apply_filter(B, A, x, axis=0) # raises ValueError
ValueError: The number of initial conditions must be max([len(a),len(b)]) - 1
How can I avoid the error, and apply a filter along any axis without assuming initial rest?
The initial conditions in zi must be in the same axis as the axis given to lfilter. Change this:
np.take(signal, 0, axis=axis)[..., np.newaxis]
to
np.take(signal, [0], axis=axis)
The difference between np.take(signal, 0, axis=axis) and np.take(signal, [0], axis=axis) is that the latter preserves the number of dimensions. E.g.
In [105]: signal
Out[105]:
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
In [106]: signal.shape
Out[106]: (3, 5)
If we take the first index from axis 1, we get a 1-d array with shape (3,):
In [107]: a = np.take(signal, 0, axis=1)
In [108]: a.shape
Out[108]: (3,)
In [109]: a
Out[109]: array([ 0, 5, 10])
If, instead, we use the list [0] in the indices argument, we get an array with shape (3, 1):
In [110]: b = np.take(signal, [0], axis=1)
In [111]: b.shape
Out[111]: (3, 1)
In [112]: b
Out[112]:
array([[ 0],
[ 5],
[10]])

How to make random N-dimensions by using Numpy?

I'm currently trying to make N-dimensional matrix.
import numpy as np
fitness_landscape = np.random.uniform (0, 1, size = (N, 2., 2., 2., 2.))
In this code, I want to reduce the recursive 2 dimensions. For example, like the following array (though it doen not work):
import numpy as np
K = 4
fitness_landscape = np.random.uniform (0, 1, size = (N, 2. * K))
Is there ways that meet my needs?
Thanks.
In [9]: K = 5
In [10]: fitness_landscape = np.random.uniform (0, 1, size = (N,) + (2,) * K)
In [11]: fitness_landscape.shape
Out[11]: (3, 2, 2, 2, 2, 2)

Categories

Resources