Reputation: 33
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
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