Destructuring and broadcast

question

#1

If I want to broadcast a function that returns a tuple, is there a simple way to “broadcast” the left hand side destructuring?

a = 1:10
b = 11:20

transform(a, b) = a+b, a*b, a-b # MWE, the real one is more compicated

## want something like (syntax of course does not work)
@. x, y, z = transform(a, b)

## of course I can do
w = transform.(a,b)
x = first.(w)
y = (v->v[2]).(w)
z = last.(w)

Questiosn about `where` for function definitions (while unpacking arrays of tuples)
#2

I don’t know about simple, but I had the same problem and came up with this oneliner for creating NTuples:

x,y,z = collect(zip(transform.(a,b)...))

If you absolutely need Vectors then add another collect:

x,y,z = collect.(collect(zip(transform.(a,b)...)))

But I’m still a newbie, so I’m sure there’s a better way. I’m curious to see what the pros come up with.


#3

Hi,

I have a similar issue: I don’t know how to obtain a tuple of arrays instead of an array of tuples.

The answer by @NickNack works for simple cases, but loses track of resulting shape when the broadcast results in more than one dimension.

# compute on a 2d grid
x1 = reshape(1:5,  (5,1)) 
x2 = reshape(6:11, (1,6))
typeof(collect(zip(transform.(x1,x2)...))) # Array{NTuple{30,Int64},1}

This result is different from the solution by @Tamas_Papp, which correctly preserves the shape of the resulting arrays.

Coming from python/numpy, I am used to this situation resulting in tuple of arrays instead of array of tuples. Anybody knows a way to obtain similar behavior in julia?

The equivalent numpy code:

import numpy as np

def transform(a, b):
    return a+b, a*b, a-b

x1 = np.arange(1, 6).reshape((5,1))
x2 = np.arange(6, 12).reshape((1,6))
u, v, w = transform(x1, x2)

Potentially related:
https://github.com/JuliaLang/julia/issues/13942

None of the proposed solutions seem to work here, upon a very superficial quick try.


#4

Relevant issue: https://github.com/JuliaLang/julia/issues/22129


#5

In v0.6, I used StructsOfArrays to do broadcasting with tuple output:

using StructsOfArrays

T = Float64
TV = Vector{Float64}

a = zeros(T, 10); b = copy(a)
s = StructOfArrays{NTuple{2,T}, 1, Tuple{TV, TV}}((a, b))

f(a, b) = (a + b, a * b)

s .= f.(1:10, 1:10)

A macro for the above should be simple enough. Figuring out T and TV and handling the case where a and b are not already allocated are the main challenges I can see.


Multiple assignments using broadcasting - any hacks?
#6

Why not use dot-operations inside a function? As far as I can see, it works as well for scalars.

a_ = collect(a)
b_ = collect(b)
transform(a, b) = a .+ b, a .* b, a .- b

x, y, z = transform(a_, b_)

Also, if there are only +, - and * operations, they probably can be redefined as dot-wise for properly-sized AbstractArrays ?


#7

Assume that the transformation is a black box, eg because it involves a costly, large intermediate result that you don’t want to allocate. Eg

function transform(a, b)
    m = g(a, b)
    make_x(m), make_y(m)
end

where g is costly and m is large.