Do you have in mind a way to compute the following sum
$$R_{i,j,k} = \sum_{l=0}^{2} J_{i,j,l+3k} v_{i,j,l}$$
obviously in a vectorised fashion? This may sound like an equivalent of:
c = np.einsum('ij(l + 3k),ijl->ijk', j, v)
where of course the notation 'ij(l + 3k),ijl->ijk' is not accepted by einsum.
I tried with the following, but I wish I could use one of the existing NumPy or SciPy functions:
def jacobian_product(j_input, v_input):
"""
:param j_input: jacobian m x n x (4 or 9) jacobian column major
:param v_input: matrix m x n x (2 or 3) to be multiplied by the jacobian.
:return: m x n x (2 or 3) whose each element is the result of the product of the
jacobian (i,j,:) multiplied by the corresponding element in the vector v (i,j,:).
In tensor notation: R_{i,j,k} = \sum_{l=0}^{2} J_{i,j,l+3k} v_{i,j,l}
"""
# dimensions of the problem:
d = v_input.shape[-1]
vol = list(v_input.shape[:-1])
# repeat v input 3 times, one for each row of the jacobian matrix 3x3 or 2x2 in corresponding position:
v = np.tile(v_input, [1]*d + [d])
# element-wise product:
j_times_v = np.multiply(j_input, v)
# Sum the three blocks in the third dimension:
return np.sum(j_times_v.reshape(vol + [d, d]), axis=d+1).reshape(vol + [d])
Update after Veedrac comment: here there are two tests for both 2d and 3d vectors.
The jacobians are collected into an array of shape [m,n,4] or [m,n,9] (for 2d or 3d. The first equation proposed in this thread is about the 3d case). Vectors to be multiplied by the Jacobian in the corresponding positions have shape [m,n,2] or [m,n,3] respectively.
Sorry if this was not clear, my bad I didn't state dimensions since the beginning!
from numpy.testing import assert_array_equal
def test_jacobian_product_toy_example_2d():
def function_v(t, x):
t = float(t); x = [float(y) for y in x]
return x[1], -1 * x[0]
def function_jacobian(t, x):
t = float(t); x = [float(y) for y in x]
return 0.5, 0.6, x[0], 0.8
def function_ground_product(t, x):
t = float(t); x = [float(y) for y in x]
return 0.5*x[1] - 0.6*x[0], x[0]*x[1] - 0.8*x[0]
v = np.zeros([20, 20, 2])
jac = np.zeros([20, 20, 4])
ground_jac_v = np.zeros([20, 20, 2])
for i in range(0, 20):
for j in range(0, 20):
v[i, j, :] = function_v(1, [i, j])
jac[i, j, :] = function_jacobian(1, [i, j])
ground_jac_v[i, j, :] = function_ground_product(1, [i, j])
jac_v = jacobian_product(jac, v)
assert_array_equal(jac_v, ground_jac_v)
def test_jacobian_product_toy_example_3d():
def function_v(t, x):
t = float(t); x = [float(y) for y in x]
return 2*x[1], 3*x[0], x[0] - x[2]
def function_jacobian(t, x):
t = float(t); x = [float(y) for y in x]
return 0.5*x[0], 0.5*x[1], 0.5*x[2], \
0.5, x[0], 0.3, \
0.2*x[2], 3.0, 2.1*x[2]
def function_ground_product(t, x):
t = float(t); x = [float(y) for y in x]
return 0.5*2*x[0]*x[1] + 0.5*3*x[0]*x[1] + 0.5*x[2]*(x[0] - x[2]), \
0.5*2*x[1] + 3*x[0]*x[0] + 0.3*(x[0] - x[2]), \
0.2*2*x[2]*x[1] + 3*3*x[0] + 2.1*x[2]*(x[0] - x[2])
v = np.zeros([20, 20, 20, 3])
jac = np.zeros([20, 20, 20, 9])
ground_jac_v = np.zeros([20, 20, 20, 3])
for i in range(0, 20):
for j in range(0, 20):
for k in range(0, 20):
v[i, j, k, :] = function_v(1, [i, j, k])
jac[i, j, k, :] = function_jacobian(1, [i, j, k])
ground_jac_v[i, j, k, :] = function_ground_product(1, [i, j, k])
jac_v = jacobian_product(jac, v)
assert_array_equal(jac_v, ground_jac_v)
v
of shape(m, n, 2)
(under which[i, j, 2]
is not a valid index). Also, please provide example inputs. \$\endgroup\$