I would like to call a function with all combination of values from several arrays, and get the corresponding array of values. Is a convenient general way to do that? Broadcasting comes first to my mind, but I wouldn’t call it convenient for this case, e.g.:

``````array_a = 1:3
array_b = 1:5
array_c = 1:7
f.(array_a, reshape(array_b, 1, length(array_b)), reshape(array_c, 1, 1, length(array_c)))
``````
``````array_a = 1:3
array_b = [1 2; 3 4]
array_c = 1:7
f.(array_a, reshape(array_b, 1, size(array_b)...), reshape(array_c, 1, 1, 1, length(array_c)))
``````

First it is very verbose and repetitive, and second it is error-prone especially if some of those arrays have the same size. I failed to find a better way, is there any?

Consider using `Iterators.product`:

``````[f(t...) for t = Iterators.product(array_a, array_b, array_c)]
``````
2 Likes

Oh, really! I didn’t even look at that method thinking that `Iterators` are “single-dimensional” only. I guess that will work fine for me.
If only it could also transparently be applied to cases like `f.(scalar, scalar, arr, arr, scalar, a=b, c=d)`

I wrote a package for doing such things, in which your second example looks like this:

``````using TensorCast
@cast res[α,β,β′,γ] := f(array_a[α], array_b[β,β′], array_c[γ])
``````

It should be no problem to give `f` other scalar arguments.

2 Likes

Interesting package indeed, it looks very general!

You could just use a comprehension `[f(a,b) for a in A, b in B]`, depending on what shape you want for the result array.

5 Likes

A comprehension is the way to go, or maybe `Iterators.product`. But note also that you’re making `reshape` unnecessarily verbose:

``````f.(a, reshape(b, 1, :), reshape(c, 1, 1, :))
``````

You could also use `transpose(b)` or even `b'` for real data.

Also nice suggestion, thank you - didn’t know about reshape with colon.

However, one needs to be very careful with `transpose` in Julia: at first I used `a` and `b'` for two-dimensional broadcast as you say, and surprisingly got wrong results. After digging in it turned out that `transpose` is not the same transpose I got used to in other languages, but also performs conjugation. Yes, my arrays were complex in that case, but after that I’m certainly not going to use transpose for broadcasting. Broadcasting syntax should not depend on the array element type, otherwise you can easily get errors like this when some `eltype` changes.

And anyway, `transpose` solves only a single simple case of multidimensional broadcasting, it is not of much help for > 2 arrays, or when they are > 1 dimensional (see 1st post for example of both). And it doesn’t work at all with non-numeric types.

Yes, I would not use `transpose` or `'` in this case.

Be aware, though, that `transpose` does not do complex conjugation, while `'` does (`'` is the same as `adjoint`). Both of them work recursively on their elements, which is important to be aware of, if the elements of your array can themselves be transposed.

Ah, ok. Actually approach to transpose in julia feels like an inconvenience for me, and very simple stuff like “swap rows and columns in e.g. a matplotlib plot” cannot be written as `ax'` like in Python (`ax.T` there of course), but only as `permutedims(ax, 2, 1)`. However after reading the discussion I see that the majority is happy with the current state, so it will stay like this.

Are you sure? You should be able to use `'` or `transpose` in most cases. Are you talking about higher-dimensional arrays? You would have the same problem in Matlab, btw. And in Matlab `'` also complex conjugates.

Yes, sure - I tried that Minimal example:

``````arr = [:a :b; :c :d]
a'  # MethodError: no method matching adjoint(::Symbol)
arr |> transpose  # MethodError: no method matching transpose(::Symbol)
``````

Same goes for any non-numerical array.

Wow, that caught me by surprise. To be honest, I personally think that should work. The reason it’s not is that there is no method for `transpose(::Symbol)`. Might be worth opening a discussion thread on that topic specifically.

But how does this look in python?

In numpy transpose `arr.T` just swaps rows and cols of an array, without any regards to its internal structure (eltype), and without modifying elements of course, unlike `arr'` in julia.

And yes, there is not `transpose(::Symbol)`, as well as no `transpose(::anything else but for numerics)`. And as I understood the corresponding discussion, it is by design.

I mean, what does the array look like? I have only ever seen numerical arrays in numpy.

Numpy supports any kind of objects in elements - e.g. after `fig, ax = plt.subplots(2, 2)` `ax` is a 2x2 numpy array of matplotlib axes.

OK, didn’t know that. Anyway, I think it’s strange that transpose doesn’t work on non-numeric matrices. I would ask about it specifically if I were you, unless you’ve seen somewhere that it is definitely intensional.

Anyway, note that for matrices, you don’t need to specify dimension to swap, since there are only two:

``````julia> arr = [:a :b; :c :d]
2×2 Array{Symbol,2}:
:a  :b
:c  :d

julia> permutedims(arr)
2×2 Array{Symbol,2}:
:a  :c
:b  :d
``````

Cool, didn’t know about that as well (`permutedims` without explicit dims)!
And as for transpose, it is definitely intensional:

• `permutedims(m::AbstractMatrix)` is now short for `permutedims(m, (2,1))` , and is now a more convenient way of making a “shallow transpose” of a 2D array. This is the recommended approach for manipulating arrays of data, rather than the recursively defined, linear-algebra function `transpose` . Similarly, `permutedims(v::AbstractVector)` will create a row matrix (#24839).

Note that for a matrix, you don’t need to specify dimensions, just `permutedims(ax)` is fine. See `?permutedims`.

Note that `transpose(mat)` is lazy, while `permutedims(mat)` makes a copy. There is also `PermutedDimsArray(mat,(2,1))` which is lazy and not recursive.

(It would probably make sense to have `PermutedDimsArray(mat)` too, for matrices, but currently this does not exist.)