5
$\begingroup$

This morning, I opened one of my previous notebooks and evaluated it. Surprisingly, the code produced an error.

This took me a whole four hours to find the problem and it is with MapThread.

I give a simplified version here

MapThread[f, {{{a, b}, {c, d, x}}, {{u, v}, {s, t, y}}}, 2]

This code produces an error in Mathematica 10.2

MapThread::mptd: Object {{a, b}, {c, d, x}} at position {2, 1} in MapThread[f, {{{a, b}, {c, d, x}}, {{u, v}, {s, t, y}}}, 2] has only 1 of required 2 dimensions. >>

It should output this result

{{f[a, u], f[b, v]}, {f[c, s], f[d, t], f[x, y]}}

But it is not. I am pretty sure, there is no such problem in the previous version. And I can't understand why this code can't work in 10.2.

Edit According to tests carried out by several users, it seems that this kind of MapThread all get the same error. Maybe I had a wrong recall. But it is still an interesting question why this kind of threading(looks quite natural) is not supported.

$\endgroup$
  • 1
    $\begingroup$ In the "Possible issues" my MapThread doc says All arguments must be lists of the same length $\endgroup$ – Dr. belisarius Oct 27 '15 at 3:11
  • 1
    $\begingroup$ What version of Mathematica was your old notebook? $\endgroup$ – Mike Honeychurch Oct 27 '15 at 4:45
  • 1
    $\begingroup$ @MikeHoneychurch probably v9 $\endgroup$ – matheorem Oct 27 '15 at 4:59
  • 1
    $\begingroup$ v9 also gives the same error $\endgroup$ – QuantumDot Oct 27 '15 at 8:18
  • 1
    $\begingroup$ @QuantumDot v8.0.4 also gives the same error. $\endgroup$ – xzczd Oct 27 '15 at 10:02
5
$\begingroup$

There seems to be a number of ways to achieve what you want. I don't have MMA 10.2 at hand right now, but the same error comes up in 10.1. The primitive way is to improve MapThread like so:

mapThread[f_, args_List, depth_Integer:1] :=
MapThread[f, PadRight[args, ConstantArray[Automatic, depth+1], $ToNothing], depth] /. f[$ToNothing..] :> Nothing

Nothing was introduced in 10.2, so in 10.1 I would use some vanishing function instead, like Unevaluated[Sequence[]].

This is a general fix, that allows for proper function even if the elements at level 2 of the arguments are lists themselves.

You can also just make f Listable:

SetAttributes[f, Listable];
f[{{a, b}, {c, d, x}}, {{u, v}, {s, t, y}}]
{{f[a, u], f[b, v]}, {f[c, s], f[d, t], f[x, y]}}

Repeated application of Thread works too:

Thread /@ Thread@f[{{a, b}, {c, d, x}}, {{u, v}, {s, t, y}}]

Modifying built-in functions can lead to problems! Don't do the following unless you know what you're doing!

If you have some code where there's a lot of threading over ragged arrays, you can overload MapThread directly, instead of creating auxiliary functions and modifying the code. This is the same fix, as at the top of my answer, but applied directly to the built-in MapThread. It includes a conditional, which checks, if it is at all necessary to apply the modified definition.

Unprotect[MapThread];
MapThread[f_, args_List, depth_Integer: 1] /; ArrayDepth[args] <= depth :=
 MapThread[f, 
   PadRight[args, ConstantArray[Automatic, depth + 1], $ToNothing], depth] /.
       f[$ToNothing ..] :> Unevaluated[Sequence[]]

EDIT
Oh yeah, you might run into a problem if the sublists in the second argument of MapThread are of different shapes.

For example:

mapThread[f, {{{a}, {c, d, x}}, {{u, v}, {s, t, y}}}, 2]
{{f[a, u], f[$ToNothing, v]}, {f[c, s], f[d, t], f[x, y]}}

I'm struggling to think of a consistent way to remedy this problem.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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