nevakanezzar
nevakanezzar

Reputation: 33

Is there a bsxfun type function for vectors in Matlab?

Specifically, I'm trying to create a kernel matrix from a kernel function which takes vectors as inputs and produces a scalar as an output. The kernel matrix, then, is a the 2D matrix of such outputs for each pair of vectors.

In the case of the vector inputs themselves being 1D, I can use bsxfun do to this:

x = [1;2;3;4]; kerfun = @(s,t) (s-t) % some function, doesn't matter, except that it returns a scalar kernel = bsxfun(kerfun, x, x');

The output is the kernel, as expected:

kernel =

     0    -1    -2    -3
     1     0    -1    -2
     2     1     0    -1
     3     2     1     0

However, if I change the points in x into N-D vectors, this approach fails. My question is: Is there an efficient way to construct the kernel matrix without using loops? I tried using cellfun but that didn't seem to work either. Thanks.

Edit: As an example of the expected outcomes, if I change x and kerfun as follows:

x = [1,15;23,2;13,5;4,7];
kerfun = @(s,t) norm(s-t); %some function, returns a scalar
ker = zeros(4,4);

then compute the kernel by brute force:

for i=1:size(x,1)
    for j=1:size(x,1)
        ker(i,j) = kerfun(x(i,:),x(j,:));
    end
end

I get:

ker =

         0   25.5539   15.6205    8.5440
   25.5539         0   10.4403   19.6469
   15.6205   10.4403         0    9.2195
    8.5440   19.6469    9.2195         0

Upvotes: 2

Views: 511

Answers (1)

Luis Mendo
Luis Mendo

Reputation: 112759

bsxfun can be used with N-dimensional arrays, and it will expand their singleton dimensions. But it works element-wise. If you want a function that does some "aggregating" operation along rows, as in your example, bsxfun can only do the element-wise part (with singleton expansion). You then need to complement with some other aggregating function to get the final result.

For the specific case in your example, the function you want can be decomposed as element-wise subtraction (this is where bsxfun enters), then sum along rows (the aggregating part), then element-wise square root. To do the subtraction with bsxfun you need to permute the first dimension of a copy of x onto the third dimension. That way the first and third dimensions span all combinations of i and j in your two loops.

Then sum along the second dimension of the resulting 3D array, take the square root, and permute back to have a matrix result:

ker = sqrt(permute(sum(bsxfun(@minus, x, permute(x, [3 2 1])).^2, 2), [1 3 2]));

Note that it's faster to use one of the bsxfun's builtin functions (like minus here) than a custom function.

For x = [1,15;23,2;13,5;4,7] this produces

ker =
         0   25.5539   15.6205    8.5440
   25.5539         0   10.4403   19.6469
   15.6205   10.4403         0    9.2195
    8.5440   19.6469    9.2195         0

Upvotes: 2

Related Questions