# Efficiently combine anonymous functions?

48 views (last 30 days)
Ced on 6 Apr 2016
Edited: Ced on 8 Apr 2016
Hi
I have an algorithm which has several anonymous functions passed as parameters which are then combined depending on flags. Since the resulting function is called many times, this slows down my program quite significantly. Is there a way to "explicitely combine" two anonymous functions?
Mini Example:
% Single anonymous function
full1 = @(x)sin(x) + 3*x;
% split anonymous function
g1 = @(x)sin(x);
g2 = @(x)3*x;
full2 = @(x)g1(x) + g2(x);
%%Speed Test
N = 100000;
disp('Explicit combination')
tic
for i = 1:N
full1(rand);
end
toc
disp('Multilevel combination')
tic
for i = 1:N
full2(rand);
end
toc
returns
Explicit combination
Elapsed time is 0.234414 seconds.
Multilevel combination
Elapsed time is 0.434422 seconds.
What I would like is to somehow have g1(x) + g2(x) be "combined explicitely" such that I end up with a version as "full1", and not "full2". Is there a way to do that?
Thanks!

Walter Roberson on 6 Apr 2016
If you have the symbolic toolbox, then sometimes it works to call
syms x
full1 = matlabFunction( simplify(full2(x)), 'vars', x);
You can skip the simplify() for functions you do not expect will benefit from optimization such as finding common factors.
This will not always work as some functions cannot be called with symbolic parameters, or may have different results for symbolic parameters.
If you do not have the symbolic toolbox, then you can char() the individual anonymous functions, strip off the '@(x)', and combine remembering to put in parenthesis so you do not accidentally generate the wrong order of operations. Then put the @(x) back on and str2func()

Steven Lord on 6 Apr 2016
Note that both g1 and g2 can accept either a scalar (as you're calling them) or a vector. In addition, your timing test is testing not only your anonymous function call but also N calls to the rand function. I've modified your script to focus on timing the anonymous function calls and included it below. You should notice that either of the last two options are much faster than the first two, and that their times are very similar.
% Single anonymous function
full1 = @(x)sin(x) + 3*x;
% split anonymous function
g1 = @(x)sin(x);
g2 = @(x)3*x;
full2 = @(x)g1(x) + g2(x);
%%Speed Test
N = 100000;
R = rand(1, N);
disp('Explicit combination')
tic
for k = 1:N
full1(R(k));
end
toc
disp('Multilevel combination')
tic
for k = 1:N
full2(R(k));
end
toc
disp('Vectorized full1')
tic
full1(R);
toc
disp('Vectorized full2')
tic
full2(R);
toc
Ced on 8 Apr 2016
Edited: Ced on 8 Apr 2016
Thanks, that is true of course!
In my concrete case, I am using an online algorithm, i.e. the data only comes in one sample at a time, so making a new function call each time is unavoidable.
Yes, the rand call is included in the time. I figured that since I am calling the rand function the same number of times in both loops, it didn't really matter. Also, I believe pre-initializing the variable as in your case can skew the result, that's why I tend not to do it in speedtests. Matlab will somehow access R faster the second time around, although I don't know enough about the inner workings of Matlab to give a good explanation. This can be seen though when running e.g.
full1 = @(x)sin(x) + 3*x;
%%Speed Test
N = 100000;
R = rand(1, N);
disp('Explicit combination')
tic
for k = 1:N
full1(R(k));
end
toc
disp('Explicit combination')
tic
for k = 1:N
full1(R(k));
end
toc
the second loop is always faster than the first (at least for me). The cleanest way would probably be to extract the time from the profiler.
In the vectorized case, the separate function handles are only called once, so it's not surprising that the overhead is barely noticeable. But I suppose that this was your point :).