11

I have two tensors of arbitrary but equal rank n (and equal dimensions): A and B, and I want to get a third tensor of rank n + 1, C.

I want to do a element by element Join, so the element in A and the corresponding element in B are contracted into a list in C.

For example, with n = 2:

A = {{a[0,0],a[1,0]},{a[0,1],a[1,1]}};
B = {{b[0,0],b[1,0]},{b[0,1],b[1,1]}};

then:

C = {{ {a[0,0],b[0,0]} , {a[1,0],b[1,0]} },{ {a[0,1],b[0,1]} , {a[1,1],b[1,1]} }}

I think it might be doable with some combination of Inner and List, but I'm not sure.

Szabolcs
  • 234,956
  • 30
  • 623
  • 1,263
Andrew Spott
  • 1,581
  • 15
  • 22

5 Answers5

10

Here are a couple of possibilities.

MapThread[Riffle[{#1}, {#2}] &, {aA, bB}, 2]

(* Out[64]= {{{a[0, 0], b[0, 0]}, {a[1, 0], b[1, 0]}}, {{a[0, 1], 
   b[0, 1]}, {a[1, 1], b[1, 1]}}} *)

Transpose[ArrayFlatten[{aA, bB}], {3, 1, 2}]

(* Out[72]= {{{a[0, 0], b[0, 0]}, {a[1, 0], b[1, 0]}}, {{a[0, 1], 
   b[0, 1]}, {a[1, 1], b[1, 1]}}} *)

Another that I like less:

Map[Thread, Thread[{aA, bB}]]

--- edit ---

Actually that second one should just be

Transpose[{aA, bB}, {3, 1, 2}]

The ArrayFlatten was not needed in this case (it can be useful somethimes though). And in case it was not clear, the lists are:

aA = {{a[0, 0], a[1, 0]}, {a[0, 1], a[1, 1]}};
bB = {{b[0, 0], b[1, 0]}, {b[0, 1], b[1, 1]}};

--- end edit ---

Daniel Lichtblau
  • 58,970
  • 2
  • 101
  • 199
8

Here's a fairly simple way of doing it (ignore the front end's syntax warning):

Function[, {##}, Listable][A, B]
(* {{{a[0, 0], b[0, 0]}, {a[1, 0], b[1, 0]}}, 
    {{a[0, 1],  b[0, 1]}, {a[1, 1], b[1, 1]}}} *)
rm -rf
  • 88,781
  • 21
  • 293
  • 472
  • Why does that work? It is very interesting, but what if I want to make a[0,0] a list as well? – Andrew Spott Jan 07 '13 at 23:48
  • 1
    If you consider element-wise multiplication A B, that is possible because of the Listable attribute of Times. In other words, it threads over lists in its arguments. What I've done above is similar – it creates a pure function that "joins" the arguments ({#}) and threads it over lists (Listable). The omitted argument is taken as Null. I don't understand what you mean by wanting to make a[0,0] a list as well, as it's no longer element-wise join, as you wanted. – rm -rf Jan 07 '13 at 23:55
  • +1. You are too fast, I am running out of tricks :-) – Leonid Shifrin Jan 08 '13 at 00:01
6

Maybe a pedagogical alternative

(A + B) /. Plus -> List
Rolf Mertig
  • 17,172
  • 1
  • 45
  • 76
  • 2
    I don't think this will work in general. Think about what happens if the elements of A and B are numbers. – m_goldberg Jan 08 '13 at 01:05
  • sure. as mentioned: it is just pedagogical in that sense that it is easy to understand and you don't have to use Function with a Null argument. – Rolf Mertig Jan 08 '13 at 09:34
6

In the spirit of Rolf Mertig's answer we can abuse a system function with the attribute Listable to effect our join. By simultaneously using an invalid syntax we can avoid undesired evaluation. I chose Re:

threadJoin = Quiet[Re @ ##] /. Re -> List &;

Now:

threadJoin[
 {1, 2, 3},
 {4, 5, 6}
]
{{1, 4}, {2, 5}, {3, 6}}
threadJoin[
 {{a[0, 0], a[1, 0]}, {a[0, 1], a[1, 1]}},
 {{b[0, 0], b[1, 0]}, {b[0, 1], b[1, 1]}},
 {{c[0, 0], c[1, 0]}, {c[0, 1], c[1, 1]}}
]
{{{a[0, 0], b[0, 0], c[0, 0]}, {a[1, 0], b[1, 0], c[1, 0]}},
   {{a[0, 1], b[0, 1], c[0, 1]}, {a[1, 1], b[1, 1], c[1, 1]}}}
threadJoin[
 {{{a1, a2, a3}, {b1, b2, b3}, {c1, c2, c3}}, {11, 22, 33}},
 {{{a4, a5, a6}, {b4, b5, b6}, {c4, c5, c6}}, {44, 55, 66}}
]
{{{{a1, a4}, {a2, a5}, {a3, a6}}, {{b1, b4}, {b2, b5}, {b3, b6}}, {{c1, c4},
    {c2, c5}, {c3, c6}}}, {{11, 44}, {22, 55}, {33, 66}}}
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
2

From your comment "what if I want to make a[0,0] a list as well?" I think Daniel's Transpose method is the way to go but the second parameter was never generalized. I believe you could use:

elementJoin[a_, b_] := 
  Transpose[{a, b}, RotateRight @ Range @ ArrayDepth @ {a, b}]

elementJoin[
 {1, 2, 3},
 {4, 5, 6}
]
{{1, 4}, {2, 5}, {3, 6}}
elementJoin[
 {{1, 2, 3}, {11, 22, 33}},
 {{4, 5, 6}, {44, 55, 66}}
]
{{{1, 4}, {2, 5}, {3, 6}}, {{11, 44}, {22, 55}, {33, 66}}}
elementJoin[
 {{{a1, a2, a3}, {b1, b2, b3}, {c1, c2, c3}}, {11, 22, 33}},
 {{{a4, a5, a6}, {b4, b5, b6}, {c4, c5, c6}}, {44, 55, 66}}
]
{{{{a1, a2, a3}, {a4, a5, a6}}, {{b1, b2, b3}, {b4, b5, b6}},
    {{c1, c2, c3}, {c4, c5, c6}}}, {{11, 44}, {22, 55}, {33, 66}}}

Note that this last output is not the same as that produced by R.M's Listable function:

Function[, {##}, Listable][
 {{{a1, a2, a3}, {b1, b2, b3}, {c1, c2, c3}}, {11, 22, 33}},
 {{{a4, a5, a6}, {b4, b5, b6}, {c4, c5, c6}}, {44, 55, 66}}
]
{{{{a1, a4}, {a2, a5}, {a3, a6}}, {{b1, b4}, {b2, b5}, {b3, b6}}, {{c1, c4},
 {c2, c5}, {c3, c6}}}, {{11, 44}, {22, 55}, {33, 66}}}

Choose according to your needs.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371