Wolfram Computation Meets Knowledge

New in 13.1: Beyond Listability: Introducing Threaded

Last year we released Version 13.0 of the Wolfram Language. Here are the updates in listability since then, including the latest features in 13.1.

 

Beyond Listability: Introducing Threaded

From the very beginning of Mathematica and the Wolfram Language we’ve had the concept of listability: if you add two lists, for example, their corresponding elements will be added:

{a, b, c} + {x, y, z}
&#10005


It’s a very convenient mechanism, that typically does exactly what you’d want. And for 35 years we haven’t really considered extending it. But if we look at code that gets written, it often happens that there are parts that basically implement something very much like listability, but slightly more general. And in Version 13.1 we have a new symbolic construct, Threaded, that effectively allows you to easily generalize listability.

Consider:

{{1, 2}, {3, 4}} + {x, y}
&#10005


This uses ordinary listability, effectively computing:

{{1, 2} + x, {3, 4} + y}
&#10005


But what if you want instead to “go down a level” and thread {x,y} into the lowest parts of the first list? Well, now you can use Threaded to do that:

{{1, 2}, {3, 4}} + Threaded
&#10005


On its own, Threaded is just a symbolic wrapper:

Threaded
&#10005


But as soon as it appears in a function—like Plus—that has attribute Listable, it specifies that the listability should be applied after what’s specified inside Threaded is “threaded” at the lowest level.

Here’s another example. Create a list:

Table
&#10005


How should we then multiply each element by {1,–1}? We could do this with:

(#1*{1, -1} & ) /@ Table
&#10005


But now we’ve got Threaded, and so instead we can just say:

Table
&#10005


You can give Threaded as an argument to any listable function, not just Plus and Times:

Mod
&#10005


You can use Threaded and ordinary listability together:

{{1, 2}, {3, 4}} + Threaded
&#10005


You can have several Threadeds together as well:

{{1, 2}, {3, 4}} + Threaded
&#10005


Threaded, by the way, gets its name from the function Thread, which explicitly does “threading”, as in:

Thread
&#10005


By default, Threaded will always thread into the lowest level of a list:

{{{3, 4}, {4, 5}}, {{4, 5}, {5, 6}}} + Threaded
&#10005


{{{{4, 5}, {5, 6}}, {{5, 6}, {6, 7}}}, {{{5, 6}, {6, 7}}, {{6, 7}, {7, 8}}}} + Threaded
&#10005


Here’s a “real-life” example of using Threaded like this. The data in a 3D color image consists of a rank-3 array of triples of RGB values:

ImageData
&#10005


This multiplies every RGB triple by {0,1,2}:

Image3D
&#10005


Most of the time you either want to use ordinary listability that operates at the top level of a list, or you want to use the default form of Threaded, that operates at the lowest level of a list. But Threaded has a more general form, in which you can explicitly say what level you want it to operate at.

Here’s the default case:

{{{3, 4}, {4, 5}}, {{4, 5}, {5, 6}}} + Threaded
&#10005


Here’s level 1, which is just like ordinary listability:

{{{3, 4}, {4, 5}}, {{4, 5}, {5, 6}}} + Threaded
&#10005


And here’s threading into level 2:

{{{3, 4}, {4, 5}}, {{4, 5}, {5, 6}}} + Threaded
&#10005


Threaded provides a very convenient way to do all sorts of array-combining operations. There’s additional complexity when the object being “threaded in” itself has multiple levels. The default in this case is to align the lowest level in the thing being threaded in with the lowest level of the thing into which it’s being threaded:

{{{3, 4}, {4, 5}}, {{4, 5}, {5, 6}}} + Threaded
&#10005


Here now is “ordinary listability” behavior:

{{{3, 4}, {4, 5}}, {{4, 5}, {5, 6}}} + Threaded
&#10005


For the arrays we’re looking at here, the default behavior is equivalent to:

{{{3, 4}, {4, 5}}, {{4, 5}, {5, 6}}} + Threaded
&#10005


Sometimes it’s clearer to write this out in a form like

{{{3, 4}, {4, 5}}, {{4, 5}, {5, 6}}} + Threaded
&#10005


which says that the first level of the array inside the Threaded is to be aligned with the second level of the outside array. In general, the default case is equivalent to –1 → –1, specifying that the bottom level of the array inside the Threaded should be aligned with the bottom level of the array outside.

Comments

Join the discussion

!Please enter your comment (at least 5 characters).

!Please enter your name.

!Please enter a valid email address.