Undesired behaviour of bsxfun with user defined function

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)

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

Hi Walter,
Thank you for your answer. But how can I know when it would work and when it wouldn't? The @minus could hypothetically also operate on two arrays, but matlab still does elementwise operation. Why wouldn't matlab do elementwise operation for loss2 as well?
Thanks, Hera
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.
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.
Hi Cedric, Thanks for your reply. Yes I agree the suggested modification would do, but it is not what I meant in the first place. The question boils down to this:
Keeping the original loss2 definition, why would arrayfun work but not bsxfun?
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".
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

Sign in to comment.

Tags

Asked:

on 22 Jan 2013

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!