Function Repository Resource:

ArrayContract

Source Notebook

General contraction of levels of an array

Contributed by: Jose Martin-Garcia

ResourceFunction["ArrayContract"][a,{{l1,,ln}}]

contracts levels l1, …, ln of the array a using head Plus.

ResourceFunction["ArrayContract"][a,{ctr1,ctr2,}]

performs several contractions in the array a.

ResourceFunction["ArrayContract"][a,ctrs,g]

performs contractions ctrs using head g.

ResourceFunction["ArrayContract"][a,ctrs,g,d]

performs contractions assuming that only the first d levels of the array a are array levels.

Details and Options

Each contraction ctri in the contraction list must be a list of positive integers from 1 to the depth of the array. Levels cannot be repeated, neither in a given contraction nor in different contractions.
For each contraction ctri in the contraction list, all levels must have the same array dimension.
The array a must be rectangular up to the deepest level involved in the contractions.
The effective depth d in ResourceFunction["ArrayContract"][a,ctrs,g,d] can be used to indicate that array a should be treated as if it had only d levels. Therefore, d must be an integer between 0 and the array depth of a, and all contraction levels must be between 1 and d. By default, d is chosen to be the depth of the array a.

Examples

Basic Examples (4) 

Take an array of depth 4:

In[1]:=
a = Array[OperatorApplied[Subscript, 5][x], {2, 3, 3, 3}]
Out[1]=

Contract levels 2 and 3:

In[2]:=
ResourceFunction["ArrayContract"][a, {{2, 3}}]
Out[2]=

Contract levels 2, 3 and 4:

In[3]:=
ResourceFunction["ArrayContract"][a, {{2, 3, 4}}]
Out[3]=

Use head g for the contraction:

In[4]:=
ResourceFunction["ArrayContract"][a, {{2, 3, 4}}, g]
Out[4]=

Scope (8) 

Use arrays of any depth:

In[5]:=
ResourceFunction["ArrayContract"][Array[x, {2, 2, 2}], {{1, 2}}]
Out[5]=
In[6]:=
ResourceFunction["ArrayContract"][Array[x, {2, 2, 2, 2}], {{1, 2, 3}}]
Out[6]=
In[7]:=
ResourceFunction["ArrayContract"][
 Array[x, {2, 2, 2, 2, 2}], {{1, 3}, {2, 4}}]
Out[7]=

Perform contractions of any number of levels:

In[8]:=
a = Array[x, {2, 2, 2, 2}]
Out[8]=
In[9]:=
ResourceFunction["ArrayContract"][a, {}]
Out[9]=
In[10]:=
ResourceFunction["ArrayContract"][a, {{1}}]
Out[10]=
In[11]:=
ResourceFunction["ArrayContract"][a, {{1, 3}}]
Out[11]=
In[12]:=
ResourceFunction["ArrayContract"][a, {{1, 4, 3}}]
Out[12]=
In[13]:=
ResourceFunction["ArrayContract"][a, {{1, 2, 3, 4}}]
Out[13]=

Perform any number of contractions simultaneously:

In[14]:=
a = Array[x, {2, 2, 2, 2, 2, 2}];
In[15]:=
ResourceFunction["ArrayContract"][a, {{1, 2}, {3, 4}, {5, 6}}]
Out[15]=
In[16]:=
ResourceFunction["ArrayContract"][a, {{1, 2, 5}, {3, 6}}]
Out[16]=

Use any head for contractions:

In[17]:=
a = Array[x, {2, 2, 2, 2}];
In[18]:=
ResourceFunction["ArrayContract"][a, {{1, 2}}, g]
Out[18]=
In[19]:=
ResourceFunction["ArrayContract"][a, {{1, 2}, {3, 4}}, g]
Out[19]=

Heads with attribute Flat, like Plus, will then combine, eliminating nesting:

In[20]:=
ResourceFunction["ArrayContract"][a, {{1, 2}, {3, 4}}, Plus]
Out[20]=

Take an array of depth 4:

In[21]:=
a = Array[x, {2, 2, 2, 2}];

Contract levels 1 and 3 into a single level with head g, which by default is placed where level 3 was, effectively considering that the original array had depth 3:

In[22]:=
ResourceFunction["ArrayContract"][a, {{1, 3}}, g]
Out[22]=
In[23]:=
ResourceFunction["ArrayContract"][a, {{1, 3}}, g]
Out[23]=

Move the contracted level one level deeper:

In[24]:=
ResourceFunction["ArrayContract"][a, {{1, 2}}, g, 4]
Out[24]=

Listable heads automatically move as deep as possible in the array, ignoring the effective depth argument:

In[25]:=
ResourceFunction["ArrayContract"][a, {{1, 2}}, Plus]
Out[25]=
In[26]:=
ResourceFunction["ArrayContract"][a, {{1, 2}}, Plus, 2]
Out[26]=
In[27]:=
ResourceFunction["ArrayContract"][a, {{1, 2}}, Plus, 4]
Out[27]=

Arrays must be rectangular, at least up to the level given by the effective depth:

In[28]:=
a = {{{1}, 2}, {{3}, 4}};
In[29]:=
ResourceFunction["ArrayContract"][a, {{1, 2}}, g]
Out[29]=
In[30]:=
ResourceFunction["ArrayContract"][a, {{1, 2}}, g, 2]
Out[30]=
In[31]:=
ResourceFunction["ArrayContract"][a, {{1, 2}}, g, 3]
Out[31]=

As a corner case, effective depth 0 is possible when there are no contractions:

In[32]:=
a = {{1, 2}, {3, 4}, {5, 6}};
In[33]:=
ResourceFunction["ArrayContract"][a, {}, g, 0]
Out[33]=

Contract sparse arrays, quantity arrays or other types of structured arrays:

In[34]:=
ResourceFunction["ArrayContract"][
 SparseArray[{{1, 2}, {3, 4}}], {{1, 2}}, g]
Out[34]=
In[35]:=
ResourceFunction["ArrayContract"][
 QuantityArray[{{1, 2}, {3, 4}}, "Meters"], {{1, 2}}, g]
Out[35]=

Properties and Relations (7) 

Tr can contract only the higher levels of an array:

In[36]:=
a = Array[x, {2, 2, 2, 2}]
Out[36]=
In[37]:=
Tr[a, g, 3] === ResourceFunction["ArrayContract"][a, {{1, 2, 3}}, g]
Out[37]=

ArrayContract can contract any group of levels:

In[38]:=
ResourceFunction["ArrayContract"][a, {{2, 3, 4}}]
Out[38]=

Performing this operation with Tr would require transpositions:

In[39]:=
% === Tr[Transpose[a, {4, 1, 2, 3}], Plus, 3]
Out[39]=

Empty contractions are ignored:

In[40]:=
a = Array[x, {2, 2, 2, 2}];
In[41]:=
ResourceFunction["ArrayContract"][a, {{1, 3}, {}, {2, 4}, {}}]
Out[41]=
In[42]:=
% === ResourceFunction["ArrayContract"][a, {{1, 3}, {2, 4}}]
Out[42]=

The presence of a contraction head is irrelevant if all contractions are empty:

In[43]:=
ResourceFunction["ArrayContract"][a, {{}, {}, {}}, g] === a
Out[43]=

TensorContract and ArrayContract can both perform several contractions:

In[44]:=
a = Array[x, {2, 2, 2, 2}];
In[45]:=
TensorContract[a, {{1, 3}, {2, 4}}] === ResourceFunction["ArrayContract"][a, {{1, 3}, {2, 4}}]
Out[45]=

TensorContract always uses Plus as a contraction head, but ArrayContract can use any head:

In[46]:=
ResourceFunction["ArrayContract"][a, {{1, 3}, {2, 4}}, g]
Out[46]=

TensorContract can handle symbolic expressions, but ArrayContract only acts on explicit arrays:

In[47]:=
TensorContract[
  IdentityMatrix[d]\[TensorProduct]IdentityMatrix[d], {{2, 3}}] // TensorDimensions
Out[47]=
In[48]:=
ResourceFunction["ArrayContract"][
  IdentityMatrix[4]\[TensorProduct]IdentityMatrix[4], {{2, 3}}] // Dimensions
Out[48]=

ArrayContract can perform transpositions, using effectively the same notation as Flatten:

In[49]:=
a = Array[x, {2, 3, 4}];
In[50]:=
ResourceFunction["ArrayContract"][a, {{3}, {1}, {2}}, List] === Flatten[a, {{3}, {1}, {2}}] === Transpose[a, {2, 3, 1}]
Out[50]=

ArrayContract generalizes Apply on consecutive levels, with levels shifted by one:

In[51]:=
a = Array[x, {2, 2, 2, 2}];
In[52]:=
ResourceFunction["ArrayContract"][a, {{2}, {3}, {4}}, g] === Apply[g, a, {1, 3}]
Out[52]=
In[53]:=
ResourceFunction["ArrayContract"][a, {{2}, {4}}, g]
Out[53]=

Transpose can also perform simultaneous contractions with head List:

In[54]:=
a = Array[x, {2, 2, 2, 2, 2}];
In[55]:=
Transpose[a, {2, 2, 1, 3, 3}]
Out[55]=
In[56]:=
% === ResourceFunction["ArrayContract"][a, {{1, 2}, {4, 5}}, List]
Out[56]=

The order of contractions is relevant:

In[57]:=
Transpose[a, {3, 3, 1, 2, 2}]
Out[57]=
In[58]:=
% === ResourceFunction["ArrayContract"][a, {{4, 5}, {1, 2}}, List]
Out[58]=
In[59]:=
Transpose[a, {2, 2, 3, 1, 1}]
Out[59]=
In[60]:=
% === ResourceFunction["ArrayContract"][a, {{4, 5}, {1, 2}, {3}}, List]
Out[60]=
In[61]:=
Transpose[a, {1, 2, 1, 2, 1}]
Out[61]=
In[62]:=
% === ResourceFunction["ArrayContract"][a, {{1, 3, 5}, {2, 4}}, List]
Out[62]=

Publisher

Jose Martin-Garcia

Version History

  • 1.0.0 – 05 August 2020

Related Resources

Author Notes

• This function is rather slow, and normalizes everything. It is essentially a prototype to explore the generality of the possible concept.

• This function could still be generalized: 1) The third argument could take a list of heads, one per contraction. Right now all contractions in the second argument use the same head in the third argument. This will be implemented in 12.2. 2) Like Tr, it should be possible to contract to the lowest dimension, instead of requiring coinciding dimensions. 3) The levels in the contraction could be processed with some other function (say Flatten instead of Tr), as long as the result produces a single level again. This is probably a separate function.

License Information