In my application I often have a bunch of functions that I need to evaluate on many arguments (i.e. each combination of function and argument).
I thought of extending the Base.map function as follows:
function Base.map(F::AbstractArray{Function}, A::AbstractArray)
[[f(a) for f in F] for a in A]
end
Although this produces the correct output, it is about 10 times slower than expected. On the other hand, the following
function Base.map(F::AbstractArray{Function}, A::AbstractArray)
[Base.map(f, A) for f in F]
end
is as fast as it should be, but it does not have the desired output format (for my use).
is the above extension of Base.map duplicating some existing function in julia?
how do I perform the computation in roughly the same time, while obtaining the desired output format?
You can just do what you want with the broadcasting syntax like this:
map.(F, Ref(A))
The Ref here is to make A a scalar with respect to broadcasting, so that it iterates map(f, A) over all f ∈ F.
If performance is important, you might want to consider using a tuple for the functions instead of an array, since Function is an abstract type and therefore, your function will not be type stable.
If one loop is fast and the other is slow, it makes a difference which one is the inner and which is the outer. Iterating over an array of Functions is possibly slow.
Maybe you should do the calculations with the fast inner loop, and then rearrange the output afterwards.
It’s generally bad practise to overload methods from Base for Base types, as this could have unexpected side effects.
Yes, this should happen. This also explains, why your second function was faster than the first, since map could specialize on each f individually, at least.
I see. Unfortunately there are no existing methods for Base.map for tuples of functions (there is probably a good reason for this). The tradeoff I have is that I have to separately code function evaluation on a sample depending on whether the function is scalar- or vector-valued. If I only need the function for a custom type, the overloading should be fine. In the case where I need the function on arbitrary arrays, I could define Map = deepcopy(Base.map) and then overload the Map and only work with Map in all of my code. Or would there be an even simpler way?
The way I proposed, with the dot syntax will work just fine, if F is a scalar, too.
You have to be careful with these types of function definitions, since non-const functions can negatively impact performance. If you really want to put it in its own function, I would just declare it just like any other function and give it a name, that makes the difference clear.
That is the last thing you need to worry about and the non-constness is also not related to copying. It could even be local for example.
However, that just won’t work. deepcopy only copy the value, it does not copy the type, what you’ll get is an object of the same type (typeof(x) === typeof(deepcopy(x)) should always be true) and since the method table is a property of the type, deepcopy will not change which method table you extend.
Thanks! I just noticed that the deepcopy construction is not going to work.
In the end, it seems the safest bet is to define a new function Map and call Base.map for those methods I will need and then define new methods for the function tuples on top of that.