The stucture of the expression is determined and described as follows:

A conforming summation of Indexed objects is described with a dict where
the keys are summation indices and the corresponding values are sets
containing all terms for which the summation applies. All Add objects
in the SymPy expression tree are described like this.

For all nodes in the SymPy expression tree that are not of type Add, the
following applies:

If a node discovers contractions in one of it’s arguments, the node
itself will be stored as a key in the dict. For that key, the
corresponding value is a list of dicts, each of which is the result of a
recursive call to get_contraction_structure(). The list contains only
dicts for the non-trivial deeper contractions, ommitting dicts with None
as the one and only key.

Note

The presence of expressions among the dictinary keys indicates
multiple levels of index contractions. A nested dict displays nested
contractions and may itself contain dicts from a deeper level. In
practical calculations the summation in the deepest nested level must be
calculated first so that the outer expression can access the resulting
indexed object.

A parenthesized Add object is also returned as a nested dictionary. The
term containing the parenthesis is a Mul with a contraction among the
arguments, so it will be found as a key in the result. It stores the
dictionary resulting from a recursive call on the Add expression.

The concept of outer indices applies recursively, starting on the deepest
level. This implies that dummies inside parenthesis are assumed to be
summed first, so that the following expression is handled gracefully:

>>> get_indices((x[i]+A[i,j]*y[j])*x[j])(set([i, j]), {})

This is correct and may appear convenient, but you need to be careful
with this as SymPy will happily .expand() the product, if requested. The
resulting expression would mix the outer j with the dummies inside
the parenthesis, which makes it a different expression. To be on the
safe side, it is best to avoid such ambiguities by using unique indices
for all contractions that should be held separate.