2. Generalized indices

2.1. Introduction

The main objective of xTensor` is the manipulation of indexed objects. From the mathematical point of view we shall always use the notation of abstract indices for abstract expressions (see Penrose & Rindler, or Wald), where the indices denote the type and symmetries of a tensor, and not its components in a given frame (we use a different type of indices for components). This notation is very general and powerful, though sometimes cumbersome. From the computational point of view, however, we need a more general concept of index, with several properties:

1) We define the concept of generalized index as any expression found at an index-slot. Currently index-slots are those with lower case letters in
    tensor[a,b,c]
    covd[a][...]
    Bracket[a][..., ...]
and the indexed arguments of the inert-heads, to be explained later, but nothing else. At those index-slots we can put anything, but the system has been already trained to manipulate five types of expressions, which we now review in detail. This is called the index type (these are logic types, not actual symbol types):
    - AIndex: abstract indices: a, -b, d$101
    - BIndex: basis indices: {a, polar}, {-b, -polar}
    - CIndex: component indices: {0, polar}, {2, -polar}
    - DIndex: directions: Dir[vector]
    - LIndex: labels: LI[hello]
They all give True when the function GIndexQ is applied, and False otherwise. There is a sixth type of index, not accepted by GIndexQ:
    - PIndex: patterns with head Blank, Pattern or PatternTest, but no other pattern head
It is possible to introduce new index types but then you would need to specify which values they have for the following index properties (contact JMM to discuss why a new type of index is required):

2) All indices have a character, which can be Down (covariant) or Up (contravariant). Nameless patterns like _ or a_ do not have a well-defined character. However, by convention,  they are treated by the formatting routines as if they were contravariant. The character can be detected with the functions UpIndexQ and DownIndexQ. They both always give False on all patterns. Any index can be made contravariant using the function UpIndex and covariant using DownIndex. The character of any index can be reversed using ChangeIndex.

3) Contractible indices are those which obey the Einstein convention and are detected by the function EIndexQ. Actual pairs of indices are detected with PairQ. Currently only abstract indices and basis indices belong to this special type (though in the future we might consider having non-contracted basis indices). They have a state, which can be Free or Dummy (aka contracted). Non-contractible indices are said to have state Blocked and can be detected with the function BlockedQ, which always gives opposite answers to EIndexQ.

4) Apart from those three `public´ properties (type, character and state), xTensor` uses a fourth property internally: the metric-state of an index, saying whether an index has been moved with a metric with respect to the character of the corresponding slot at definition time.

5) To avoid index collisions we use the (Mathematica recommended) method of unique variables, having indices like a$123. This is useful but produces ugly expressions. To hide away those "dollar-indices" use the function ScreenDollarIndices, either explicitly or by setting $PrePrint or $Post.

6) Finally, we shall distinguish between indices on a complex vbundle and "conjugated indices" on the complex conjugated vbundle.

As a general recommendation, manipulation of indices must be done using mathematical commands to do that, and not tinkering directly with the indices.

2.2. Abstract indices

Abstract indices are labels that indicate tensorial slots (i. e. contraction with vectors or covectors). In principle we should have indices a, b, c, ..., contravariant indices Up[a], Up[b], Up[c], ..., and covariant indices Down[a], Down[b], Down[c], ... To simplify the input/output of tensors we represent both indices and contravariant indices as a, b, c, ... and covariant indices as -a, -b, -c, ... This is the simplest choice, but the treatment of patterns becomes harder because we loose the symmetry between upindices and downindices (the former being atoms and the latter being composite expressions).

The symbol type associated to abstract indices is AbstractIndex. (This is a *symbol type* and therefore an expression like -a cannot have this  symbol type. See below.) The list of all currently defined abstract indices is given by the global variable $AbstractIndices. All of them have associated upvalues True for the function AbstractIndexQ. Abstract indices are defined using the function DefAbstractIndex and undefined using UndefAbstractIndex. Defining an abstract index just involves checking the validity of the symbol and registering the corresponding upvalue for the Q-function. This should never be done directly by the user. These functions are made public only for consistency of the symbol type management.

Do not confuse the function AbstractIndexQ, which gives True only on symbols defined as abstract indices (like a, b, ...), and the function AIndexQ, which gives True on all abstract indices(a, -b, a$125, ...).

We may consider in the future to allow for more general (composite) abstract indices, but currently this is not possible.

In xTensor` abstract indices are always associated to vector bundles through the function VBundleOfIndex, which is sort of inverse of IndicesOfVBundle. The association to manifolds is implicit through their tangent vector bundles.

A simple way to generate a list of abstract indices is provided by the function IndexRange.

2.3. Basis indices and component indices

Almost everything related to bases and components is done by the twin package xCoba`. However, the internal manipulation of basis indices is already prepared in advance in xTensor`, to avoid a slow process of overloading of functions when loading xCoba`. Following Schouten, Dodson & Poston, and Penrose & Rindler, basis-indices contain the information of the basis they belong to. This is called the marked index notation. That avoids defining different indices for different bases, but makes basis-indices a bit cumbersome: {a, basis} where a is an abstract index. There is no type associated to basis-indices because they are always composite structures.

The function BIndexQ checks whether a given expression is a valid basis-index. It has a second argument to check whether the basis index belongs to a given basis or to a given vbundle.

Component indices are defined in parallel with basis indices {a, basis} but a is now an integer, one of those c-numbers ("c" from component and coordinate; no intended connection with Quantum Mechanics) defined with the basis. The function CIndexQ checks whether a given expression is a valid component-index. It also has a second argument to check whether the component index belongs to a given basis or to a given vbundle. The index {1, basis} is contravariant and the index {1, -basis} is covariant. The integer number can be positive, negative or zero because the character is stored as the sign of the basis, and not of the integer itself. Beware that this was different in pre-0.8 versions of xTensor`.

The function BCIndexQ checks whether an index is a basis-index or a component-index. The function ABIndexQ checks whether an index is an abstract-index or a basis-index, currently the only two types of indices which are contractible.

2.4. Directional indices

Directional indices represent the mathematical notation that sees tensorial slots as slots for vectors and covectors. Because it is seldom used, we want to avoid xTensor` checking continuosly if a given index is directional. Hence we introduce the head Dir to pinpoint the directional indices.

The vector argument of a directional index has its own index, which is not an index of the whole expression. The index must belong to the correct vector bundle, but the name of the index itself is irrelevant; it is some kind of dummy index; we call it an ultraindex.

The function DIndexQ checks whether a given expression is a valid directional-index. Vectors can be contracted to Dir expressions using ContractDir and separated using SeparateDir.

2.5. Label indices

There are indices which are not associated to vector bundles. We call them labels or label-indices. An example could be the l, m labels of the spherical harmonics. Because they are used only every now and then, we denote them with a special head: LI. An LI expression can have any internal structure; in particular they can have several elements. It is possible to associate a character for them using LI[a] and -LI[a] (LI[-a] is not interpreted as a "covariant label") but they are defined as not obeying the Einstein convention (blocked indices).

The function LIndexQ checks whether a given expression is a valid label-index.

2.6. Patterns

At index-slots we can also find patterns for g-indices (even patterns for patterns). Not all patterns are allowed: only those with head Blank, Pattern or PatternTest. The function PIndexQ validates the allowed patterns. The function PatternIndex constructs patterns of the required form.

For a description of the patterns to be used in rules, see Section 6 below.

2.7. Finding indices

All indices of an expression, including patterns, can be extracted using the function FindIndices. This function has attribute HoldFirst to allow it getting the indices of input expressions, before they start to evaluate. FindIndices always returns a list of indices with head IndexList, to avoid confusion with the notation for basis and component indices. FindIndices[0] returns IndexList[AnyIndices].  FindIndices works recursively, checking the heads of the elements of expressions, and complaining when it finds an unknown head. New "known" heads can be added to the list $FindIndicesAcceptedHeads.

When searching for the indices of a tensor, covariant derivative of a tensor or a tensor product we check that none of the indices are repeated with the internal function CheckRepeated. When searching for the indices of a sum of tensor expressions, or a list, equation or rule of them, we check homogeneity of free indices with the internal function CheckHomogeneity.

Three related functions, based on FindIndices, are FindFreeIndices, FindDummyIndices and FindBlockedIndices. They give disjoint lists of indices. The second one returns only the up-member of the pairs of dummies.

A very friendly driver for FindIndices is IndicesOf. The general syntax is IndicesOf[selectors][expr], where selectors are one or several of the following:
    - Free: free indices
    - Dummy: dummy indices
    - Blocked: blocked indices
    - Up: contravariant indices
    - Down: covariant indices
    - AIndex: abstract indices
    - BIndex: basis indices
    - CIndex: component indices
    - DIndex: directional indices
    - LIndex: label indices:
    - vbundle: indices of the given vbundle
    - basis: indices of the given basis
    - tensor: indices on the given tensor
    - covd: indices on the given covariant derivative
    - Basis[basis]: (both) indices on Basis objects of the given basis
    - Not[any of the previous]: complement of the previous
A sequence of several selectors represents the And of all selectors (smaller result). A list of several selectors represents the Or of all selectors (bigger result).

An alternative and completely independent way of looking for indices, very useful for recursive algorithms of index contraction, is the function IsIndexOf.

2.8. Sorting indices

The canonicalization of an indexed object essentially entails to a reordering of the indices according to the symmetries of the object and a predefined ordering for the indices. The function IndexSort returns the preferred order for a list of indices. With the function SetIndexSortPriorities we can decide which particular order we want to have. Possible priorities are the strings up"/"down, free"/"dummy, lexicographic"/"antilexicographic, positional"/"antipositional.

Simple functions derived from IndexSort are IndexOrderedQ and DisorderedPairQ.

2.9. Replacing indices

The basic function for index replacement in a generic expression is ReplaceIndex. Every valid index can be changed by any other thing, not necessarily an index (though that would inmediately produce many errors). ReplaceIndex has attribute HoldFirst. The syntax is ReplaceIndex[expr, rules], where the rules are of the form index->newindex. The rule a->b and -a->-b are considered independent, and both must be specified if that is what we need.

Derived functions are:
    - ReplaceDummies: replacement of all dummies in an expression by indices in a given list, or by new dollar-indices. Indices belonging to different vbundles are not mixed up. In computations with intensive generation of dollar-indices the memory of the computer could be filled after a while and the global variable $ComputeNewDummies has been introduced to avoid this. There is also a private function RemoveDollarIndices.
    - SameDummies: returns an expression minimizing the number of different dummies used in different terms.
    - PermuteIndices: replace indices in an expression as given by a permutation or a linear combination of them.
    - SplitIndex: returns a list of expressions where a given free index has been respectively replaced by each of a list of indices. This is useful to expand component ranges or to expand sums of vbundles.
    - TraceDummy: converts an expression with a dummy pair into a sum of expressions with different dummy pairs. Log messages can be controlled with $TraceDummyVerbose.


Created by Mathematica  (May 16, 2008) Valid XHTML 1.1!