5
$\begingroup$

Working on a mechanics problem, I stumbled on something peculiar:

Since Inner is a generalisation of Dot, it changes its behaviour depending upon the structure of the input lists.

Inner[f, {a, b}, {x, y}, g]
g[f[a, x], f[b, y]]

Pretty straight forward, right? Tuples drawn from the lists get inserted into f and all resulting expressions are input into g.

Now if we use two nested lists as input, the following happens:

Inner[f, {{a, b}, {c, d}}, {{w, x}, {y, z}}, g]
{{g[f[a, w], f[b, y]], g[f[a, x], f[b, z]]}, {g[f[c, w], f[d, y]], g[f[c, x], f[d, z]]}}

With some formatting applied:

Inner[f, {{a, b}, {c, d}}, {{w, x}, {y, z}}, g]//Column
{g[f[a, w], f[b, y]], g[f[a, x], f[b, z]]}
{g[f[c, w], f[d, y]], g[f[c, x], f[d, z]]}

Whereas I would expect an output like this:

Inner[f, {{a, b}, {c, d}}, {{w, x}, {y, z}}, g]
g[f[{a,b},{w,x}],f[{c,d},{y,z}]]

Is there something I'm missing? Is there a way to have Inner be agnostic about the heads of the expressions inside the lists? I already found the following workaround here:

Inner[f, Unevaluated /@ {{a, b}, {c, d}}, Unevaluated /@ {{w, x}, {y, z}}, g]
g[f[Unevaluated[{a, b}], Unevaluated[{w, x}]], f[Unevaluated[{c, d}], Unevaluated[{y, z}]]]

But, as stated, this seems like a crude workaround. To sum up my main question: Is there a function which generalizes the behaviour I'm looking for? And wouldn't it make sense for Inner to take an optional argument to control the depth on which it operates?

$\endgroup$
2
  • $\begingroup$ From the documentation for Inner, "Inner[f,Subscript[list, 1],Subscript[list, 2],g,n] contracts index n of the first tensor with the first index of the second tensor". It also states Inner[f,{{a,b},{c,d}},{x,y},g]->{g[f[a,x],f[b,y]],g[f[c,x],f[d,y]]}, so it is not unexpected behavior. $\endgroup$ Commented Nov 16, 2014 at 17:33
  • $\begingroup$ Relevant: (9028) (I specifically link to my answer because I addressed level control.) $\endgroup$
    – Mr.Wizard
    Commented Nov 16, 2014 at 18:07

3 Answers 3

5
$\begingroup$

Maybe MapThread gives the levelspec control you need:

lst = {{{a, b}, {c, d}}, {{w, x}, {y, z}}};

g @@ MapThread[f, lst, 1]  (* or just g @@ MapThread[f, lst] -- thanks: Mr.W *)
(* g[f[{a,b},{w,x}],f[{c,d},{y,z}]] *)

g @@ MapThread[f, lst, 2]
(* g[{f[a,w],f[b,x]},{f[c,y],f[d,z]}] *)

or,

g @@ Thread[f @@ lst]
(* g[f[{a,b},{w,x}],f[{c,d},{y,z}]] *)

or

innerL1F = #4 @@ Thread[#[#2, #3]] &;

innerL1F[f, {{a, b}, {c, d}}, {{w, x}, {y, z}}, g]
(* g[f[{a,b},{w,x}],f[{c,d},{y,z}]] *)
$\endgroup$
2
  • $\begingroup$ I don't believe you need the , 1 here. Or am I forgetting something? $\endgroup$
    – Mr.Wizard
    Commented Nov 16, 2014 at 17:09
  • 1
    $\begingroup$ Thanks Mr.W, you are right. I was intending to show the level spec control we get; just posted an example where it happens to be the default levelspec. $\endgroup$
    – kglr
    Commented Nov 16, 2014 at 17:14
4
$\begingroup$

This probably doesn't address the full scope of your question but for the particular example you could use MapThread and Apply:

ex1 = {{a, b}, {c, d}};
ex2 = {{w, x}, {y, z}};

g @@ MapThread[f, {ex1, ex2}]
g[f[{a, b}, {w, x}], f[{c, d}, {y, z}]]

Or Transpose and Apply:

g @@ f @@@ ({ex1, ex2}\[Transpose])
g[f[{a, b}, {w, x}], f[{c, d}, {y, z}]]
$\endgroup$
0
$\begingroup$

You probably want something that works in higher dimensions but, for your 2D example can hack Dataset with Transpose:

{{{a, b}, {c, d}}, {{w, x}, {y, z}}} // Dataset // Transpose // 
 Query[Apply@g, Apply@f] // Normal

(* g[f[{a, b}, {w, x}], f[{c, d}, {y, z}]] *) 

Maybe Inner for order tensors can be emulated with some combo of arguments to Transpose applied at the right Dataset levels.

Transpose[list,{n1,n2,…}] transposes list so that the k^(th) level in list is the ^(th) level in the result.

$\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.