Improving performance of interpolation (Barycentric formula)
interpolation, matlab, performance, vectorization
Solution
This is a classical case for matlab 'vectorization'. I would say - just remove the loops. It is almost that simple. First, have a look at this code:
function p = bf2(x, f, w, grid)
m = length(grid);
p = zeros(1,m);
for i = 1:m
var = grid(i)==x;
if any(var)
p(i) = f(var);
else
sum1 = sum((w.*f)./(grid(i) - x));
sum2 = sum(w./(grid(i) - x));
p(i) = sum1/sum2;
end
end
end
I have removed the inner loop over `j`. All I did here was in fact removing the `(j)` indexing and changing the arithmetic operators from `/` to `./` and from `*` to `.*` - the same, but with a dot in front to signify that the operation is performed on element by element basis. This is called array operators in contrast to ordinary matrix operators. Also note that treating the special case where the grid points fall onto `x` is very similar to what you had in the original implementation, only using a vector `var` such that `x(var)==grid(i)`.
Now, you can also remove the outermost loop. This is a bit more tricky and there are two major approaches how you can do that in MATLAB. I will do it the simpler way, which can be less efficient, but more clear to read - using `repmat`:
function p = bf3(x, f, w, grid)
% Find grid points that coincide with x.
% The below compares all grid values with all x values
% and returns a matrix of 0/1. 1 is in the (row,col)
% for which grid(row)==x(col)
var = bsxfun(@eq, grid', x);
% find the logical indexes of those x entries
varx = sum(var, 1)~=0;
% and of those grid entries
varp = sum(var, 2)~=0;
% Outer-most loop removal - use repmat to
% replicate the vectors into matrices.
% Thus, instead of having a loop over j
% you have matrices of values that would be
% referenced in the loop
ww = repmat(w, numel(grid), 1);
ff = repmat(f, numel(grid), 1);
xx = repmat(x, numel(grid), 1);
gg = repmat(grid', 1, numel(x));
% perform the calculations element-wise on the matrices
sum1 = sum((ww.*ff)./(gg - xx),2);
sum2 = sum(ww./(gg - xx),2);
p = sum1./sum2;
% fix the case where grid==x and return
p(varp) = f(varx);
end
The fully vectorized version can be implemented with `bsxfun` rather than `repmat`. This can potentially be a bit faster, since the matrices are not explicitly formed. However, the speed difference may not be large for small system sizes.
Also, the first solution with one loop is also not too bad performance-wise. I suggest you test those and see, what is better. Maybe it is not worth it to fully vectorize? The first code looks a bit more readable..
Problem
I have been given an assignment in which I am supposed to write an algorithm which performs polynomial interpolation by the barycentric formula. The formulas states that: p(x) = (SIGMA_(j=0 to n) w(j)*f(j)/(x - x(j)))/(SIGMA_(j=0 to n) w(j)/(x - x(j))) I have written an algorithm which works just fine, and I get the polynomial output I desire. However, this requires the use of some quite long loops, and for a large grid number, lots of nastly loop operations will have to be done. Thus, I would appreciate it greatly if anyone has any hints as to how I may improve this, so that I will avoid all these loops. In the algorithm, `x` and `f` stand for the given points we are supposed to interpolate. `w` stands for the barycentric weights, which have been calculated before running the algorithm. And `grid` is the linspace over which the interpolation should take place: ``` function p = barycentric_formula(x,f,w,grid) %Assert x-vectors and f-vectors have same length. if length(x) ~= length(f) sprintf('Not equal amounts of x- and y-values. Function is terminated.') return; end n = length(x); m = length(grid); p = zeros(1,m); % Loops for finding polynomial values at grid points. All values are % calculated by the barycentric formula. for i = 1:m var = 0; sum1 = 0; sum2 = 0; for j = 1:n if grid(i) == x(j) p(i) = f(j); var = 1; else sum1 = sum1 + (w(j)*f(j))/(grid(i) - x(j)); sum2 = sum2 + (w(j)/(grid(i) - x(j))); end end if var == 0 p(i) = sum1/sum2; end end ```