105 karma · joined January 14, 2019
\sum_a(A_ia x_a) + v_i + \sum_a(x_a y_a) ( w_i + \sum_b(B_ib z_b) )
My example is actually still easier without indices. (Although you lose some information, like the fact that x & y are vectors in the indexed by a,b.) Here it's clear that Ax and x⋅y each involve a sum, which never includes v:
Ax + v + (x⋅y)(w + Bz)
In your prod<2>(a, b, ... example, if p1 and p2 are in the same space, how would a*b know which one to contract? Or do they have different names from when a was created?
Although to be honest, simply writing A[n,μ,ν,c] etc. (using different letters for different spaces) makes it pretty easy to visually check that you are getting this right. This is one of the attractions of the notation, even on paper. Unfortunately np.einsum's string notation makes this harder to see, as the indices aren't adjacent to the variable name.
[1] https://github.com/mcabbott/TensorCast.jl#checking (Julia)
[2] https://github.com/ofnote/tsalib (Python)
A_ia x_a + v_i + x_a y_a ( w_i + B_ib z_b )
You should not need to check the LHS, x_a y_a should always mean the dot product of these two. At least this is the convention among the heavy addicts; among lighter users you will sometimes find other setups.
If you had reason to do so, it would be easy to bolt something onto the front of @tensor etc. which understood say A^[i]_[j] * B^[j,k] . Something similar would not be so hard to do to np.einsum either, just messing with the string... it could take exactly your "email Latex notation". In fact I'm a bit surprised that I can't find someone who's done this, in a few minutes' googling. I did find that opt-einsum has a notation in which the indices at least follow the array:
https://optimized-einsum.readthedocs.io/en/latest/input_form...
@threads for i in eachindex(object)
and let the details about how many cores be written once, correctly, elsewhere? @einsum M[i,j] := A[i,k] * B[k,j]
Or faster with @tensor from https://github.com/Jutho/TensorOperations.jl & the same notation. I'm not aware of any attempt to distinguish co-vectors; you can have g = Diagonal([-1,1,1,1]) and insert A[i,k]g[k,k′]B[k′,j] and this is handled efficiently. Rearrange('b c h w -> b (c h w)')
is literally a tensor product of the vector spaces indexed by c,h,w. The same space that np.kron maps into (possibly modulo the order of indices) rather than np.hstack (of row vectors) which maps to the direct sum, ⊕ not ⊗.Perhaps of interest, here's a quick prototype of how this could be useful. If you use an index "n"-for-number on the dimension for which you previously used an index "c"-for-colour (when performing any einsum or slicing or re-shaping operation) then you will get a warning: