For any tensor product in which two of the terms pertain to dual spaces, W⊗V⊗X⊗dual(V)⊗Z, there is a natural (ie choice-independent) linear map to W⊗X⊗Z, induced by the mutual action of the dual terms. A general member of the initial space is a sum of terms each of which is of form w×v×x×u×z with w, v, x, u and z in W, V, X, dual(V) and Z respectively. The image of this under the mutual action is simply (u·v).w×x×z, in which (u·v) is a scalar. This linear map is called trace.
When none of the vector spaces W, X, Z are dual to one another or involve V, or when they are all the field and our product is just that of V with its dual (which is {linear (V:|V)}), one may fairly use the name trace without ambiguity: given that it is acting on a tensor product in which just one dual pair of factors arises, it is clear which pair it is to contract. However, when those special conditions are not met, it is necessary to have some clear notation for specifying which dual pair of factors in the tensor product is to be contracted.
It is entirely sufficient to write out a factorisation of the tensor product, such as the X,V,W,dual(V),Z one above, and mark the dual pair in some way. To this end, we could name the above linear map trace[W,V*,X,dual(V)*,Z]. It can readily be seen that two trace operations acting on separate terms in the tensor product can be combined, and commute: if Z were X-dual, we could have trace[W,V*,X!,dual(V)*,Z!]. We could legitimately employ this notation with arbitrarily many (distinct) pairs of matched markers (but, in fact, I'll be constructing a quite different denotation shortly, once I've introduced everything it can describe).
For any mapping (T:V|I) with I a well-ordered index set and T some collection of vector spaces, one can construct a vector space, the tensor product of V, bulk(⊗, V), which may be thought of as V(last)⊗...⊗V(first). For any permutation σ of I, we have likewise the vector space bulk(⊗, V&on;σ). Now, a typical member of bulk(⊗, V) is a sum of terms each of form bulk(×, v) for some (V(i): v(i) ←i |I), for which bulk(×, v&on;σ) is in bulk(⊗, V&on;σ). This gives us a natural isomorphism (bulk(⊗, V&on;σ): bulk(×, v&on;σ) ←bulk(×, v) :bulk(⊗, V)), extended linearly from its given example action. Call this shuffle(σ), thereby defining a mapping shuffle from permutations to polymorphic linear operators on tensor products.
Now, any ordered finite set is naturally isomorphic (as an ordered finite set) to some natural number and any permutation on a natural number is wholly specified by an ordering of its members. (Thus: [0,1,2,3] is the identity on 4 and [1,2,3,0] is one of the obvious cyclic permutations of 4. Any list (:g|4) = [g(0),g(1),g(2),g(3)] can be composed after this to give g&on;[1,2,3,0] = [g(1),g(2),g(3),g(0)], e.g. [a,b,c,d]&on;[1,2,3,0] = [b,c,d,a]. Thus a permutation, (:p|n), acts on other lists of the same length as g&on;p ←(:g|n).) Consequently, we could write a permutation's action on, say, U⊗V⊗W in the form:
It remains to note that trace and permute may be combined: we can, if we wish, trace out some components of a tensor product while permuting the remainder. Extending the example used for tracing, we can now define trace-permute[W1,V*,X0,dual(V)*,Z2] = (Z⊗W⊗X: (u·v).z×w×x ← w×v×x×u×z :W⊗V⊗X⊗dual(V)⊗Z). This trace-permute operator, albeit differently written, will show up plentifully in the course of my analysis of tensor bundles: it corresponds with shuffling the order of indices (permutation) and summing over repeated indices (tracing) in orthodox notations for physics.
The use of things like `W1' and `V*' as entries in lists is uncomfortably informal - they arise from the need to declare the tensor-factorisation to be applied, i.e. the rank on which trace-permute is acting. Each of our spaces X, Y, Z might itself be a tensor product of spaces, some of which might involve V and its dual; the space we've been referring to as W⊗V⊗X⊗dual(V)⊗Z could be expressible as a match to this pattern in several ways: each of which will give meaning to trace-permute([1,*,0,*,2]), and each a different meaning. To get round this informality/ambiguity, I now introduce a general mapping τ which takes two lists, of equal length, and yields an unambiguous linear map.
The first argument to τ describes the permutation and tracing to be performed; the second is a list of linear spaces describing the tensor factorisation to be used on inputs. The first argument's entries must be symbols (which can be anything that doesn't have meaning as a natural number), appearing in pairs, and natural numbers, each appearing exactly once, with every natural smaller than any natural used being also present in the first argument. Where the first argument uses a symbol, the second argument's two entries in positions corresponding to those taken by the symbol in the first must be linear spaces dual to one another. Thus:
Thus our earlier example, (Z⊗W⊗X: (u·v).z×w×x ← w×v×x×u×z :W⊗V⊗X⊗dual(V)⊗Z), is τ([1,*,0,*,2],[W,V,X,dual(V),Z]). Note that, for any suitable list s, τ(s) is a mapping from lists of linear spaces to linear maps from the tensor product of the listed spaces to a suitably derived tensor space.
Now, in many situations the relevant factorisation, i.e. τ's second argument, is implicitly evident from context. In such cases, I'll write τ[1,*,0,*,2] with a pair of (...) curved prackets elided as a short form for the result of giving τ the indicated permutation-with-symbols list and the implicit list of spaces factorising whatever the result is to be applied to. This is syntactic sugar.
Final refinement: because τ(p,V) knows that it acts on bulk(⊗,V), we can unambibuously extend its action to bulk(⊗,V)⊗Anything by mapping any sum of terms, each of form bulk(×,v)×a, to the corresponding sum of terms of form τ(p,V,bulk(×,v))×a. Of course, when bulk(×,v)×a is met as such, this is no gain: but when we are dealing with some member of bulk(⊗,V)⊗Anything for which we haven't introduced such a factorisation (or, generally, a decomposition as a sum of products), it helps to be able to apply τ(p,V) to the thing itself rather than making the decomposition explicit in order to express the action of τ(p,V). Example: the Riemann tensor is in bulk(⊗, [G,G,G,T]) and antisymmetric under the associated τ[1,0,2,3]; it can be written as a sum of terms of form (g^h)×f×t in which each g^h is antisymmetric under τ([1,0],[G,G]); and each (g^h)⊗f is antisymmetric under τ([1,0,2],[G,G,G]); since the permutation only changes the order of the leftmost factors, it seems superflous to waste space mentioning the unaffected factors; so I want to be able to write τ([0,1],[G,G])·R = -R. But, thinking about it, this is part of what · needs to be defined to say, not what τ needs to be saying.
Formally:
is a collection of lists: τ accepts a list s precisely if, for some natural n and `symbol set' I, (|s:) is the union of n and I, with ({m}:s|) a singleton for each m in n and ({i}:s|) is a doubleton for each i in I - that is, the numbers appear once each, the symbols twice each. Note that `each ({m}:s|) is a singleton' iff the restriction (n:s:) is one-to-one, which gives us r = reverse(n:s:) with s&on;r = n.
More or less anything will do as a `symbol set', so long as none of its members are natural numbers: I've mostly used symbols but one can equally use letters (so long as the letter isn't in use as a name in some pertinent context, of course).
in such a case will accept, as input, any list ({linear spaces}:V|(:s|)) for which, whenever distinct i, j in (:s|) have s(i) = s(j), the corresponding V(i) and V(j) are dual to one another. The list V&on;r consists of the V(i) for which s(i) is in n, so not in I, shuffled into the order indicated by the positions of n's members in s.
is then a linear map (bulk(⊗, V&on;r): |bulk(⊗,V)): its action on bulk(×,v) yields the product of:
Now, for any natural number n and any rank U, we can define an n-way U-antisymmetrisation operator which acts on any rank whose factorisation begins with n copies of U as: antiSym({U}:|n) = sum(: sign(s).τ(s,({U}:|n)) ←s :{iso (n:|n)})/n!, wherein ({U}:|n) is the list of length n each of whose entries is U, {iso (n:|n)} is the group of all n! permutations (aka isomorphisms) of the finite set n (that is, {n-1, ..., 0} = n) and `sign' is the function which returns the signature of any permutation: its value is +1 for any even permutation and -1 for any odd permutation. [Technically, sign is a non-trivial group homomorphism ({-1,+1}: |{iso (n:|n)}), mapping composition among the {iso (n:|n)} to multiplication on {-1,+1}.]
One might, likewise, define a symmetrisation operator (by omitting the `sign' term): I'd call it symmetrise, I guess, but I seldom want it.
Examine antiSym({U}:|n) &on; antiSym({U}:|m) = sum(: sum(: sign(s&on;t).τ(s, ({U}:|n))&on;τ(t, ({U}:|m)) ←t:{iso (m:|m)})/m! ←s :{iso (n:|n)})/n! We can extend s and t (in practice, only one of them) to have equal (:|), say N = max(n,m) = n&unite;m: say N = n+h, extend s to s&unite;(N: n+i ← n+i :N) a.k.a. `s extended with the identity'; similarly for t. This doesn't change sign(s) or sign(t), and makes τ(s, ({U}:|n))&on;τ(t, ({U}:|m)) = τ(s&on;t, ({U}:|N)). As at least one of n, m is N - say n=N, so s ranges over all of {iso (N:|N)} - and any p in {iso (N:|N)}, including our (possibly extended) t, is invertible, so that ({iso (n:|n)}| s&on;p ←s |{iso (n:|n)}) is one-to-one, making sum(: formula(s&on;p) ←s :{iso (n:|n)}) the same thing (provided addition is Abelian) as sum(: formula(s) ←s :{iso (n:|n)}) for any formula. Consequently, if n=N, our summed quantity is independent of t, so that summation over {iso (m:|m)} just gives m! copies of each value, exactly cancelling the /m! and leaving us with sum(: sign(s).τ(s, ({U}:|N)) ←s :{iso (N:|N)})/N!, which is just antiSym({U}:|N). We get exactly the same sum, using the name t in place of s, when m=N, making s redundant. Hence antiSym({U}:|n) &on; antiSym({U}:|m) = antiSym({U}:|max(n,m)).
For any (n:u:U) we have antiSym(({U}:|n), bulk(×, (n:u:U))) which (especially when n is small) is also written as u(n-1)^...^u(0), so antiSym(({U}:|2),a×b) = a^b = (a&tensor;b - b&tensor;a)/2. Similarly with n=3, antiSym(({U}:|3),a×b×c]) = a^b^c. The space of wholly antisymmetric n-tensors over U is (| antiSym({U}:|n) :bulk(⊗,({U}:|n))), which I'll also write U^U^...^U with n appearances of U in it. We've just seen that each antiSym({U}:|m) with m≤n acts as the identity on this space: each member, x, is x=antiSym(({U}:|n),w) for some w in bulk(⊗, (n|:{U})) so antiSym(({U}:|m),x) = (antiSym({U}:|m)&on;antiSym({U}:|n))(w) = antiSym({U}:|n)(w) = x.
