You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
It would be nice if tensorsolve() could solve multiple tensor equations simultaneously. i.e.
a=np.random.randn((2, 2, 3, 6))
b=np.random.randn((2, 2, 3))
np.allclose(np.einsum("...ijk,...k->...ij", a, tensorsolve(a, b)), b) # tensorsolve(a, b).shape == (2, 6)
I think there are two options for the design of vectorized tensorsolve(). Let n: int be the number of degrees of freedom for a linear equation problem:
Case 1: Assume that a.shape[axis_batch:b.ndim] == b.shape[axis_batch:] and assume that a.shape[:axis_batch] and b.shape[:axis_batch] are broadcastable
In this case n = prod(a.shape[b.ndim:], then we may find axis_batch where prod(a.shape[axis_batch:b.ndim]) = n (and if not an error should be raised), and reshape a to shape a.shape[:axis_batch] + (n, n), b to shape b.shape[:axis_batch] + (n) (not a.shape to make it broadcastable)
Case 2: Do not assume that a.shape[:b.ndim] == b.shape but assume that the batch shapes a.shape[:axis_batch] and b.shape[:axis_batch] are exactly the same (not recommended?)
In this case n = prod(a.shape)/prod(b.shape) (and if n is not int an error should be raised), and for a, b. there exists axis_sol_{a,b} where prod({a,b}.shape[axis_batch:axis_sol_{a,b}]) == n(and if not an error should be raised), and reshape a to (-1,n,n) and b to (-1,n)a.shape[:axis_batch] + (n, n), b to shape b.shape[:axis_batch] + (n) (not a.shape to make it broadcastable)
Implementation for Case 1:
importnumpyasnpdefbtensorsolve(a, b, axes=None):
""" Solve the tensor equation ``a x = b`` for x. It is assumed that all indices of `x` are summed over in the product, together with the rightmost indices of `a`, as is done in, for example, ``tensordot(a, x, axes=x.ndim)``. Parameters ---------- a : array_like Coefficient tensor, of shape ``b.shape + Q``. `Q`, a tuple, equals the shape of that sub-tensor of `a` consisting of the appropriate number of its rightmost indices, and must be such that **there exists i that** ``prod(Q) == prod(b.shape[i:])`` b : array_like Right-hand tensor, which can be of any shape. axes : tuple of ints, optional Axes in `a` to reorder to the right, before inversion. If None (default), no reordering is done. Returns ------- x : ndarray, shape Q Raises ------ LinAlgError If `a` is singular or not 'square' (in the above sense). See Also -------- numpy.tensordot, tensorinv, numpy.einsum Examples -------- >>> import numpy as np >>> rng = np.random.default_rng() >>> a = rng.normal(size=(2, 2*3, 4, 2, 3, 4)) >>> b = rng.normal(size=(2, 2*3, 4)) >>> x = np.linalg.tensorsolve(a, b) >>> x.shape (2, 2, 3, 4) >>> np.allclose(np.einsum('...ijklm,...klm->...ij', a, x), b) True """# https://github.com/numpy/numpy/blob/# e7a123b2d3eca9897843791dd698c1803d9a39c2/numpy/linalg/_linalg.py#L291an=a.ndimifaxesisnotNone:
allaxes=list(range(0, an))
forkinaxes:
allaxes.remove(k)
allaxes.insert(an, k)
a=a.transpose(allaxes)
# find right dimensions# a = [2 (dim1) 2 2 3 (dim2) 2 6]# b = [2 (dim1) 2 2 3 (dim2)]axis_sol_last=b.ndimifa.shape[:axis_sol_last] !=b.shape:
raiseValueError(
f"Shapes of a and b are incompatible: "f"{a.shape} and {b.shape}"
)
# the dimention of the linear systemsol_dim=np.prod(a.shape[axis_sol_last:])
sol_dim_=1foraxis_batch_lastinrange(axis_sol_last-1, -1, -1):
sol_dim_*=a.shape[axis_batch_last]
ifsol_dim_==sol_dim:
breakelse:
raiseValueError("Unable to divide batch dimensions and solution dimensions")
a_=a.reshape(a.shape[:axis_batch_last] + (sol_dim, sol_dim))
b_=b.reshape(b.shape[:axis_batch_last] + (sol_dim, 1))
x=np.linalg.solve(a_, b_)
returnx.reshape(a.shape[:axis_batch_last] +a.shape[axis_sol_last:])
The text was updated successfully, but these errors were encountered:
I realized that it is impossible to automatically detect which axis is the "batch" axis if an axis of size 1 is included in the front. for example if a.shape == (1, 2, 2) and b.shape == (1, 2) it is impossible to infer if 0th axis is "batch" axis or not; the result shape could be either (1, 2) or (2,) 😢
Since I could not get any feedback, I temporarily created a package batch-tensorsolve. However, I hope this will one day be officially available. https://github.com/34j/batch-tensorsolve
Uh oh!
There was an error while loading. Please reload this page.
Proposed new feature or change:
It would be nice if
tensorsolve()
could solve multiple tensor equations simultaneously. i.e.I think there are two options for the design of vectorized
tensorsolve()
. Letn: int
be the number of degrees of freedom for a linear equation problem:a.shape[axis_batch:b.ndim] == b.shape[axis_batch:]
and assume thata.shape[:axis_batch]
andb.shape[:axis_batch]
are broadcastableIn this case
n = prod(a.shape[b.ndim:]
, then we may findaxis_batch
whereprod(a.shape[axis_batch:b.ndim]) = n
(and if not an error should be raised), and reshape a to shapea.shape[:axis_batch] + (n, n)
, b to shapeb.shape[:axis_batch] + (n)
(nota.shape
to make it broadcastable)a.shape[:b.ndim] == b.shape
but assume that the batch shapesa.shape[:axis_batch]
andb.shape[:axis_batch]
are exactly the same (not recommended?)In this case
n = prod(a.shape)/prod(b.shape)
(and if n is notint
an error should be raised), and for a, b. there existsaxis_sol_{a,b}
whereprod({a,b}.shape[axis_batch:axis_sol_{a,b}]) == n
(and if not an error should be raised), and reshape a to(-1,n,n)
and b to(-1,n)
a.shape[:axis_batch] + (n, n)
, b to shapeb.shape[:axis_batch] + (n)
(nota.shape
to make it broadcastable)Implementation for Case 1:
The text was updated successfully, but these errors were encountered: