11
$\begingroup$

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.

$\endgroup$
4
  • 1
    $\begingroup$ Note: C is a built-in symbol in System` and it's not wise to assign/modify it unless absolutely necessary. $\endgroup$
    – rm -rf
    Commented Jan 7, 2013 at 23:19
  • $\begingroup$ Sorry, I was using A, B and C as example variables. $\endgroup$ Commented Jan 7, 2013 at 23:20
  • 1
    $\begingroup$ This is very strongly related, although the way the two questions are worded, they're entirely different mathematica.stackexchange.com/q/17291/5 $\endgroup$
    – rm -rf
    Commented Jan 7, 2013 at 23:35
  • $\begingroup$ Related: (6102), (9012) $\endgroup$
    – Mr.Wizard
    Commented Jun 13, 2020 at 13:40

5 Answers 5

10
$\begingroup$

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 ---

$\endgroup$
8
$\begingroup$

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]}}} *)
$\endgroup$
3
  • $\begingroup$ Why does that work? It is very interesting, but what if I want to make a[0,0] a list as well? $\endgroup$ Commented Jan 7, 2013 at 23:48
  • 1
    $\begingroup$ 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. $\endgroup$
    – rm -rf
    Commented Jan 7, 2013 at 23:55
  • $\begingroup$ +1. You are too fast, I am running out of tricks :-) $\endgroup$ Commented Jan 8, 2013 at 0:01
6
$\begingroup$

Maybe a pedagogical alternative

(A + B) /. Plus -> List
$\endgroup$
2
  • 2
    $\begingroup$ I don't think this will work in general. Think about what happens if the elements of A and B are numbers. $\endgroup$
    – m_goldberg
    Commented Jan 8, 2013 at 1:05
  • $\begingroup$ 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. $\endgroup$ Commented Jan 8, 2013 at 9:34
6
$\begingroup$

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}}}
$\endgroup$
2
$\begingroup$

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.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.