Undesired behaviour of bsxfun with user defined function
Show older comments
main.m
X = ones(5,9);
XHat = zeros(5,9);
fhandle = @(x, xhat) x - xhat;
DiffMatrix1 = arrayfun(fhandle, X, XHat);
DiffMatrix2 = bsxfun(fhandle, X, XHat);
DiffMatrix3 = arrayfun(@loss2, X, XHat);
DiffMatrix4 = bsxfun(@loss2, X, XHat); % This one is the one I
% really want to use!!!!!
DiffMatrix5 = arrayfun(@minus, X, XHat);
DiffMatrix6 = bsxfun(@minus, X, XHat);
disp(size(DiffMatrix1));
disp(size(DiffMatrix2));
disp(size(DiffMatrix3));
disp(size(DiffMatrix4)); % return 1x1 matrix instead
% of 5x9 matrix
disp(size(DiffMatrix5));
disp(size(DiffMatrix6));
Main function calls for the function loss2.m defined as below.
function L = loss2(x, xHat)
if(x == xHat)
L = 0;
elseif(x == 1 & xHat == 0)
L = 10;
elseif(x == 1 & xHat > 1)
L = 1;
elseif(x == 0 & xHat > 0)
L = 1;
else
L = -1;
end
end
Output:
>> disp(size(DiffMatrix1));
5 9
>> disp(size(DiffMatrix2));
5 9
>> disp(size(DiffMatrix3));
5 9
>> disp(size(DiffMatrix4));
1 1
>> disp(size(DiffMatrix5));
5 9
>> disp(size(DiffMatrix6));
5 9
I really want DiffMatrix4 to work, which uses a user defined function loss2 with bsxfun. But bsxfun doesn't do the desired elementwise operation in the case, it rather takes the whole matrix and put into the function loss2.
Could anyone see how we can work around this issue? Or do we have to use arrayfun?
Thanks!! Hera
Answers (1)
Walter Roberson
on 22 Jan 2013
Your loss2 function is not designed to work with vectors or arrays, but it must do so.
fun can also be a handle to any binary element-wise function not listed above. A binary element-wise function of the form C = fun(A,B) accepts arrays A and B of arbitrary but equal size and returns output of the same size. Each element in the output array C is the result of an operation on the corresponding elements of A and B only. fun must also support scalar expansion, such that if A or B is a scalar, C is the result of applying the scalar to every element in the other input array.
7 Comments
Hera
on 23 Jan 2013
arrayfun() will call 5*9=45 times loss2(), the first time with arg1=X(1,1)->x in loss2() and arg2=XHat(1,1)->xHat in loss2(). Your function will return a scalar, L, that will ultimately end up in DiffMatrix3(1,1), .. and so on.
bsxfun() will call once only loss2() with, arg1=X->x in loss2() and arg2=XHat->xHat in loss2(). Your function will return a scalar, L, that will ultimately end up in DiffMatrix4.
So the issue is with loss2(); bsxfun() doesn't choose to or not to do elementwise operations. You made loss2() return a scalar in any case, even when its args are arrays.
EDIT (because it's more subtle than what I originally thought): to solve this, you should build L in loss2() so that it has the size of the "largest" arg (to support scalar expansion). E.g.
if isscalar(x), sizeL = size(xHat) ; else sizeL = size(x) ; end
and then build L as a "sizeL" array, i.e.
L = zeros(sizeL) ;
L = 10 * ones(sizeL) ;
L = ones(sizeL) ;
L = ones(sizeL) ;
L = -ones(sizeL) ;
respectively for your five cases. You also have to update your conditional statements (still to support scalar exp.), e.g.
if all(x(:)==1) && all(xHat(:)==0), ... ; elseif ...
or equivalent adapted to your needs.
Walter Roberson
on 23 Jan 2013
minus between two arrays is the same thing as minus applied element-wise to the two arrays.
You know that the array option will be used for bsxfun because the documentation says it will.
Hera
on 23 Jan 2013
Walter Roberson
on 23 Jan 2013
Your loss2() function does comparisons between x and fixed values. When vectors or arrays are passed in to loss2() by bsxfun() then you are going to be doing scalar to array comparisons. By definition in MATLAB, "if" is only considered true if all() of the results of the comparison are non-zero. You need to convert loss2() to use logical indexing instead of "if".
Christopher
on 20 Mar 2013
This is an old thread, but I'll re-ask the portion of the question I didn't hear answered: what is the point of bsxfun, if not to deal out element by element comparisons to a function that doesn't necessarily know about vectors or arrays? Why not skip the call to bsxfun, and directly call the function?
Because you would have to MESHGRID the args first.
>> x = 1 : 3 ;
>> y = 10 : 14 ;
>> bsxfun(@plus, x.', y)
ans =
11 12 13 14 15
12 13 14 15 16
13 14 15 16 17
How do you get this using PLUS, x, and y? One solution is a nested double FOR loop; another is a meshgrid:
>> [X,Y] = meshgrid(y, x)
X =
10 11 12 13 14
10 11 12 13 14
10 11 12 13 14
Y =
1 1 1 1 1
2 2 2 2 2
3 3 3 3 3
>> plus(X, Y)
ans =
11 12 13 14 15
12 13 14 15 16
13 14 15 16 17
Categories
Find more on Matrix Indexing in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!