python - matrix multiplication of 4 dimensional array - python

I'm plotting a color map using a mesh grid for the map calculation. I have an X, Y gird of say 1000 by 1000 points, and some function H = function(a, b, c, X, Y). The size of H is [2, 3, 1000, 1000], i.e. for each grid point the size of H is [2, 3]. With mesh grid this is easy and efficient.
Now I need to find D = np.matmul(np.transpose(H), H). Unfortunately, I do that with 2 for loops scanning the entire grid, see code below. Can someone suggest a more elegant and efficient way to find D?
for j in range(x_mesh_length):
for k in range(y_mesh_length):
D[j, k] = np.matmul(H[:, :, j, k].T,H[:, :, j, k])

Use numpy einsum
D = np.einsum('ikml, kjml ->ijml', np.transpose(H, (1,0,2,3)), H)

Related

How can I form the matrix of sums of two arrays of vectors in numpy?

I have two numpy arrays a and b, of lengths n and m, containing vectors of length d, that is, a[i].shape == (d,) and likewise for b, for all i.
I want to form the matrix A of sums of these vectors, so that
A[i, j] == a[i] + b[j]
so A will be of shape (n, m, d).
I know that I can use numpy.add.outer to get a matrix M of shape (N, d, M, d) containing all possible sums of all components of all vectors in a and b, and then my matrix A would be given by the formula
A[i, j, k] = M[i, k, j, k]
However, other than writing for loops I don't know how to turn M into what I need, and I need to do this in the most efficient way possible.
Ultimately I want to end up with the matrix of sums of squares of these vectors by doing (A**2).sum(axis=2).
You can utilize broadcasting:
A = a[:, None, :] + b[None, :, :]
You could also replace None with numpy.newaxis. They are the same thing, but numpy.newaxis might be easier to understand. I will note that you can also just do
A = a[:, None, :] + b
but I think the other way makes it more clear what is going on.

Transpose 3D coordinates on a plane to a new 2D coordinate system

I have been working on a personal project to produce an image of the integer solutions to the equation x^2 + y^2 + z^2 = S where 'S' is any integer.
In other words, I am looking for all the 3D points [x,y,z] where x, y, and z are all perfect square integers and x + y + z = S
For example, S = 2809 will have solutions:
[144, 1296, 1369],
[144, 729, 1936],
[0, 0, 2809]
... plus all permutations of the above (i.e. 144+729+1936 = 1936+729+144)
Before I get to my question, here is a small tangent for some context:
All of the solutions to the general equation x + y + z = S will lie on a 2D plane defined by:
A = [S, 0, 0]
B = [0, S, 0]
C = [0, 0, S]
Here is a graph of all solutions (not just square points) to x + y + z = 50 to illustrate that all of the solutions to this equation will lie on the same plane bounded by ABC defined above. Notice that the tips of the triangle below are: [50, 0, 0], [0, 50, 0], and [0, 0, 50]
Back to my question:
After finding the square solution points, I want to transpose the 3D solution points into 2D coordinates based on the ABC plane with A as (0,0), B is the max 'x' value, and C is the max 'y' value. I then hope to output these solutions to an image file.
My linear algebra knowledge is sparse, and I have been unable to find a method to transpose 3D coordinates into 2D plane coordinates based on 3 non-colinear points.
My code is currently in python, but an algorithmic/mathematical answer is just as good!
Any help is much appreciated!
As I see it, you can already find your (x, y, z) points, and your question is about a way to project them onto a plane.
Please refer to projection matrix to learn how to project the 3d world onto an image plane of your choosing.
Specifically, you will have to express your (x, y, z) coordinates as homogeneous coordinates by refering to them as (x, y, z, 1), and to multiply them by a relevant camera matrix which is orthogonal to the plane on which you need to cast them.
This will yield 2d homogeneous coordinates of the form (x', y', f) from which you will be able to obtain the projected coordinates by (x_projected, y_projected) = (x'/f, y'/f).
OpenCV is your friend.
Recap:
Input: n (x, y, z) points
Obtain projection (camera) matrix M of size (4, 3) using opencv or calculate yourself using whatever tools.
Add last dimension 1 to all points to get them as 3d homogeneous coordinates: n points (x, y, z, 1)
Multiply all points by the matrix to obtain projected points as 2d homogeneous coordinates: M * (x, y, z, 1)^T = (x', y', f)
Get n actual 2d projected coordinates (relative to camera center as defined by the M matrix) by (x, y) = (x'/f, y'/f)
Bonus: you can stack all your (x, y, z, 1) points as columns into a (4, n) matrix, P, and the entire multiplication process will be R = M * P, a result matrix R of shape (3, n) whose columns are the resulting homogeneous coordinates.
I think Gulzar's answer is correct, but more centered around rendering (i.e. camera and homogenous coordinates). I did however figure out how to do what I wanted.
import ast
import math
import matplotlib.pyplot as plt
def dot_3d(a, b):
return (a[0]*b[0])+ (a[1]*b[1]) + (a[2]*b[2])
def minus_3d(a, b):
return [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
def midpoint_3d(a, b):
return [(a[0] + b[0])/2, (a[1] + b[1])/2, (a[2] + b[2])/2]
def normalize_3d(vec):
magnitude = math.sqrt(vec[0]**2 + vec[1]**2 + vec[2]**2)
return [vec[0]/magnitude, vec[1]/magnitude, vec[2]/magnitude]
X = 2809
A = [X, 0, 0]
B = [0, X, 0]
C = [0, 0, X]
S = set([])
for a in range(X+1):
if int(math.sqrt(a))**2 == a:
for b in range(X+1):
if int(math.sqrt(b))**2 == b:
for c in range(X+1):
if int(math.sqrt(c))**2 == c and a + b + c == X:
S.add(str([a, b, c]))
S = list(S)
origin = A
normal = normalize_3d([X/3, X/3, X/3])
ax1 = normalize_3d(minus_3d(B, A))
ax2 = normalize_3d(minus_3d(C, midpoint_3d(A, B)))
answers = []
for point_str in S:
point = ast.literal_eval(point_str)
x = dot_3d(ax1, minus_3d(point, origin))
y = dot_3d(ax2, minus_3d(point, origin))
answers.append([x, y])
plt.scatter([p[0] for p in answers], [p[1] for p in answers])
plt.xlabel('x')
plt.ylabel('y')
plt.show()
Starting 3D coordinates graphed:
"projected" coordinates on the ABC plane:

Vectorized syntax for creating a sequence of block matrices in NumPy

I have two 3D arrays A and B with shapes (k, n, n) and (k, m, m) respectively. I would like to create a matrix C of shape (k, n+m, n+m) such that for each 0 <= i < k, the 2D matrix C[i,:,:] is the block diagonal matrix obtained by putting A[i, :, :] at the upper left n x n part and B[i, :, :] at the lower right m x m part.
Currently I am using the following to achieve this is NumPy:
C = np.empty((k, n+m, n+m))
for i in range(k):
C[i, ...] = np.block([[A[i,...], np.zeros((n,m))],
[np.zeros((m,n)), B[i,...]]])
I was wondering if there is a way to do this without the for loop. I think if k is large my solution is not very efficient.
IIUC You can simply slice and assign -
C = np.zeros((k, n+m, n+m),dtype=np.result_type(A,B))
C[:,:n,:n] = A
C[:,n:,n:] = B

broadcasted lstsq (least squares)

I have a bunch of 3x2 matrices, let's say 777 of them, and just as many right-hand sides of size 3. For each of them, I would like to know the least squared solution, so I'm doing
import numpy
A = numpy.random.rand(3, 2, 777)
b = numpy.random.rand(3, 777)
for k in range(777):
numpy.linalg.lstsq(A[..., k], b[..., k])
That works, but is slow. I'd much rather compute all the solutions in one go, but upon
numpy.linalg.lstsq(A, b)
I'm getting
numpy.linalg.linalg.LinAlgError: 3-dimensional array given. Array must be two-dimensional
Any hints on how to broadcast numpy.linalg.lstsq?
One can make use of the fact that if A = U \Sigma V^T is the singular value decomposition of A,
x = V \Sigma^+ U^T b
is the least-squares solution to Ax = b. SVD is broadcasted in numpy. It now only requires a bit of fiddling with einsums to get it all right:
A = numpy.random.rand(7, 3, 2)
b = numpy.random.rand(7, 3)
for k in range(7):
x, res, rank, sigma = numpy.linalg.lstsq(A[k], b[k])
print(x)
print
u, s, v = numpy.linalg.svd(A, full_matrices=False)
uTb = numpy.einsum('ijk,ij->ik', u, b)
xx = numpy.einsum('ijk, ij->ik', v, uTb / s)
print(xx)

Function application and reduction on large arrays

I have two numpy arrays, X and Y whose shapes are X.shape == (m,d) and Y.shape == (n,d), where m, n, and d are non-trivial sizes. I need to make a third array Z whose shape is Z.shape == (m,n).
An element Z[i,j] is the result of taking f(X[i,k],Y[j,k]) for k in range(d) and then summing over all k, for some non-linear f.
The obvious way to do this is to do this:
Z = numpy.zeros((m,n), dtype = numpy.float64)
for i in range(m):
for j in range(n):
Z[i,j] += (f(X[i,:],Y[j,:])).sum() # I can compose f from ufuncs
but what I'm really asking is whether there's some kind of clever broadcasting trick that I can use to compute Z that will:
take advantage of numpy's optimizations if possible
do this without putting an array of shape (n,m,d) in memory (n*m doubles will fit in memory, but n*m*d doubles won't)
Does anyone know of a way to do this? Thanks in advance.
Here is the solution you don't want, I've included it because I believe this is the "canonical" solution to your problem.
# A simple function of x, y
def f(x, y):
return 2*x + 3*y**2
x = x.reshape((m, 1, d))
y = y.reshape((1, n, d))
temp = f(x, y)
Z = temp.sum(2)
If you want to avoid creating the temporary array temp, which is quite large, you could try looping over the d dimension. In some cases the overhead of the following loop will be quite small and you'll get almost the same performance, with much less memory usage.
Z = np.zeros((m, n))
for i in range(d):
Z += f(x[:, :, i], y[:, :, i])
Let me know if that helps.

Categories

Resources