MathGroup Archive 2009

[Date Index] [Thread Index] [Author Index]

Search the Archive

Re: Best way to do contractions (arbitrary Tables with a

  • To: mathgroup at smc.vnet.net
  • Subject: [mg105973] Re: [mg105937] Best way to do contractions (arbitrary Tables with a
  • From: Leonid Shifrin <lshifr at gmail.com>
  • Date: Mon, 28 Dec 2009 04:58:36 -0500 (EST)
  • References: <200912270724.CAA21087@smc.vnet.net>

Hi Erik,

The following function will presumably do what you want and should be
reasonably efficient:


Clear[contract];
contract[tensor_?ArrayQ, firstIndex_Integer, secondIndex_Integer] :=
  Module[{dims = Dimensions[tensor], levs, mpos, npos, len},
   len = Length[dims];
   levs = Range[len];
   Map[Tr,
     Transpose[tensor,
      ReplacePart[
       levs - UnitStep[levs - firstIndex] -
        UnitStep[levs - secondIndex], {firstIndex ->  len - 1,
        secondIndex -> len}]], {len - 2}] /;
    0 < firstIndex <= len && 0 < secondIndex <= len &&
     firstIndex != secondIndex &&
     dims[[firstIndex]] == dims[[secondIndex]]];

The main idea is this: the built-in Transpose has an optional second
argument which is a list and allows one to reorder levels in your structure
(tensor). This is equivalent to reordering the indices, so for example by
calling

Transpose[tens,{1,3,2,4}],

you go from T<i,j,k,l> to T<i,k,j,l>.

Then, what is done is the following: make you contraction indices the two
last ones by "removing" them from the tensor index list and adding to the
end, while shifting other indices without any further reordering. Using
Transpose in the above manner with a properly constructed permutation of
levels allows us to implement this strategy. Then Map the Trace (Tr)
function on the appropriate level (which is the number of tensor indices
minus two) of resulting structure (transposed tensor). What you get is the
contracted tensor.

I also inserted the error-checking conditions so that contraction will only
happen when the dimensions of the contracted indices are the same (to
clarify the code a bit - I used a rather rarely used but actually very
useful construct when you share local variables between the body and the
condition, by placing  condition inside the body. This  is helpful when the
conditions are most easily expressed by using some results which  are only
available after some part of the code in the body of the function has been
executed. The advantage is that, if the condition gets violated, the
rule-based dispatch goes on trying next available rules as if no execution
in the body took place, which means that you can reuse some parts of the
code in the body of your definitions in rule-based dispatch mechanism).

A simple test:

In[1]:=
(test = Array[a[#]*b[#2]*c[#3]*d[#4]&,{3,2,3,2}])//Short[#,10]&

Out[1]//Short= {{{{a[1] b[1] c[1] d[1],a[1] b[1] c[1] d[2]},{a[1] b[1] c[2]
d[1],a[1] b[1] c[2] d[2]},{a[1] b[1] c[3] d[1],a[1] b[1] c[3] d[2]}},{{a[1]
b[2] c[1] d[1],a[1] b[2] c[1] d[2]},{a[1] b[2] c[2] d[1],a[1] b[2] c[2]
d[2]},{a[1] b[2] c[3] d[1],a[1] b[2] c[3] d[2]}}},<<1>>,{{{a[3] b[1] c[1]
d[1],a[3] b[1] c[1] d[2]},{a[3] b[1] c[2] d[1],a[3] b[1] c[2] d[2]},{a[3]
b[1] c[3] d[1],a[3] b[1] c[3] d[2]}},{{a[3] b[2] c[1] d[1],a[3] b[2] c[1]
d[2]},{<<1>>,<<1>>},{<<1>>,<<1>>}}}}


In[2]:=
Dimensions[test]

Out[2]= {3,2,3,2}

In[3]:=
contract[test,2,4]//Simplify

Out[3]= {{a[1] c[1] (b[1] d[1]+b[2] d[2]),a[1] c[2] (b[1] d[1]+b[2]
d[2]),a[1] c[3] (b[1] d[1]+b[2] d[2])},{a[2] c[1] (b[1] d[1]+b[2] d[2]),a[2]
c[2] (b[1] d[1]+b[2] d[2]),a[2] c[3] (b[1] d[1]+b[2] d[2])},{a[3] c[1] (b[1]
d[1]+b[2] d[2]),a[3] c[2] (b[1] d[1]+b[2] d[2]),a[3] c[3] (b[1] d[1]+b[2]
d[2])}}


In[4]:=
contract[test,1,3]//Simplify

Out[4]=
{{b[1] (a[1] c[1]+a[2] c[2]+a[3] c[3]) d[1],b[1] (a[1] c[1]+a[2] c[2]+a[3]
c[3]) d[2]},{b[2] (a[1] c[1]+a[2] c[2]+a[3] c[3]) d[1],b[2] (a[1] c[1]+a[2]
c[2]+a[3] c[3]) d[2]}}


I expect <contract> to be reasonably fast since it avoids explcit looping
and uses efficient operaqtions such as Transpose, Map and Tr, on an entire
structure at once. I did not do any timing comparisons or benchmarks
however.

Hope this helps.

Regards,
Leonid





On Sat, Dec 26, 2009 at 11:24 PM, Erik Max Francis <max at alcyone.com> wrote:

> I'm trying to do arbitrary contractions with tensors, which basically
> amounts to taking an (arbitrarily) large multi-dimensional array,
> iterating over the uncontracted indices, and then summing over the two
> (and only two) indices to be contracted.  If I were dealing with a
> specific case, I'd use Table with Sum:
>
>        Table[
>            Sum[
>                a[[i1]][[i2]]...[[j]]...[[j]]...[[im]]],
>            {j, n}],
>        {i1, n}, {i2, n}, ... {im, n}]
>
> That is, iterating over the indices i1, i2, through im (all taking on
> values 1 through n) and summing over two of the indices (as j).  I'm
> trying to figure out the most elegant way to do this in Mathematica and
> I'm only coming up with ugly solutions which are basically arbitrary
> reimplementations of Table-like functionality.
>
> I figure there's probably some more elegant way to approach this.
> Anyone have any ideas?
>
> --
> Erik Max Francis && max at alcyone.com && http://www.alcyone.com/max/
>  San Jose, CA, USA && 37 18 N 121 57 W && AIM/Y!M/Skype erikmaxfrancis
>   Without love, benevolence becomes egotism.
>    -- Dr. Martin Luther King, Jr.
>
>



  • Prev by Date: Re: "automatic" piecewise function creation
  • Next by Date: Re: Re: Re: Integration of
  • Previous by thread: Re: Best way to do contractions (arbitrary Tables with a Sum)?
  • Next by thread: Re: Best way to do contractions (arbitrary Tables with a Sum)?