Implicit expansion with empty arrays

I was just idly curious why scalar expansion of an empty array seems to work here (R2018a),
>> [1,2,3;4 5 6]-zeros(2,3,0)
ans =
2×3×0 empty double array
but not here,
>> [1,2,3;4 5 6]-zeros(2,0,0)
Error using -
Array dimensions must match for binary array op.

 Accepted Answer

James Tursa
James Tursa on 21 Aug 2019
Edited: James Tursa on 21 Aug 2019
In the 1st case, you are expanding a dimension of 1 (the 3rd dimension of the first operand) to 0, so it is scalar expansion.
In the 2nd case, you are trying to expand a dimension of 3 (the 2nd dimension of the first operand) to 0, so it is not scalar expansion ... it is simply a dimension mismatch.

11 Comments

OK. It's interesting that you can "expand" from 1 to 0.
That predates implicit expansion. It worked with scalar expansion.
>> 1-[]
ans =
[]
The minus operator is being called with two operands, a 1-by-1 and a 0-by-0. The 1-by-1 is scalar "expanded" to the same size as the other operand, the 0-by-0, and so the result is 0-by-0.
I don't see why the reverse of this answer is not true. If you can expand 1 to 0, why can't you expand 3 to 0? bsxfun gives a different error: non-singleton dimensions must match. Does that mean 0 is regarded as a singleton dimension for operations like this? Because normally it isn't.
In short, can you expand a bit on your explanation? (double pun definitely intended)
"If you can expand 1 to 0, why can't you expand 3 to 0?"
Because a singleton is dimension with length 1, and expansion by definition works on singleton dimension, it take the scalar value (length 1) and fill the array along that dimension with the same value (including empty, i.e. length 0).
Dimension with length 3 is not a singleton. Granted there is no ambiguity to "contract" anything to empty, but that is not defined as expansion, and it's good that is throw an error IMO to avoid somehing that is exception and likely a mistake rather than intentional.
Also please tell us a good reason of why the dimension of (assuming possible operation)
rand(3,1) + rand(0,1)
is 0 x 1? I might argue nan(3,1) is a valid result as well!!!
Ah, I think I understand now. It is not that the 0 has a special status, just that a singleton dim can also be resized to match 0. That is just a case where the name doesn't match the operation.
As for your question: I would explain it the same as how you explain expansion. You proces each element along the non-matching dimension and contatenate the result of each element along that dimension.
A=rand(3,1,10);
B=rand(0,1,10);
clear C
for n=1:3
C{n}=A(n,:,:)+B;
end
C=cat(1,C{:})
So if you apply your method to
rand(3,10) + rand(2,10);
you would get 6 x 10 result.
Just be aware that A + B is different than B + A.
I woud say "Why not?"
Note that such result as you defined can be achieve with reshape + expansion (no loop at cat is needed). Just split the unmatches dimension to 2 adjadcent dimensions (with singleton), do the operation then merge back the dimension to obtain the result.
reshape( reshape(A,[1 3 10]) + reshape(B,[2 1 10]), [6 10])
Personally I prefer leave it like that and people who want such result, they must use RESHAPE to avoid silence bug of too flexible syntax.
I wasn't proposing my code as a real method per se, just as an illustration. But since you insist on more robust code:
A=rand(3,1,10);
B=rand(0,1,10);
clear C
for n=1:max(size(A,1),size(B,1))
if size(A,1)>1 && size(B,1)>1
C{n}=A(n,:,:)+B(n,:,:);
elseif size(A,1)>1 && size(B,1)<=1
C{n}=A(n,:,:)+B(:,:,:);
else
C{n}=A(:,:,:)+B(n,:,:);
end
end
C=cat(1,C{:})
Note: >1 instead of ==1 because you can't use 1 as an index if the dim length is 0. I also wasn't intending on A+B having a different output from B+A.
Personally, I would have voted for extending bsxfun to support more functions, instead of enabling implicit expansion for all operations.
Bruno Luong
Bruno Luong on 22 Aug 2019
Edited: Bruno Luong on 22 Aug 2019
"Personally, I would have voted for extending bsxfun to support more functions, instead of enabling implicit expansion for all operations."
Any concrete example of binary function not supported by BSXFUN?
I think we're getting a bit off topic here, but ok. I don't have an example of a binary function, which is why I didn't specify that. I was thinking more along the lines of this:
fun=@(a,b) a+b;
A=1:5;B=A';
C=arrayfun(fun,A,B);%no need for meshgrid/ndgrid
I don't have good suggestions about how that could best be implemented, but I don't work for Mathworks, so I have the luxury of expressing a wish without having to consider the feasibility.
%maybe this?
C=bsxfun(@arrayfun,fun,A,B);
Rik wrote: That is just a case where the name doesn't match the operation.
Yes, but "scalar {or implicit} expansion except when the size of the other operand in a particular dimension is 0 in which case it is scalar {or implicit} contraction" is a bit of a mouthful.
Bruno wrote: So if you apply your method to
rand(3,10) + rand(2,10);
you would get 6 x 10 result.
But how would those inputs be replicated? repmat style or repelem style? Collated or uncollated?
>> x = reshape(1:6, [2 3]);
>> x1 = repmat(x, [3 1]);
>> x2 = repelem(x, 3, 1);
>> isequal(x1, x2) % false
If you're replicating in a singleton dimension, they're the same. Collating multiple copies of a 1-page document is the same as not collating them.
>> y = 1:10;
>> isequal(repmat(y, 3, 1), repelem(y, 3, 1)) % true
Rik wrote: Personally, I would have voted for extending bsxfun to support more functions, instead of enabling implicit expansion for all operations.
You can pass a function handle that accepts two inputs into bsxfun.
>> fun=@(a,b) a+b;
>> A=1:5;B=A';
>> C1 = bsxfun(fun, A, B);
>> C2 = A + B; % Implicit expansion
>> isequal(C1, C2) % true
>> bsxfun(@besselj, A, B) % works
Steve: "But how would those inputs be replicated? "
Following Rik's method just above my post.

Sign in to comment.

More Answers (0)

Categories

Asked:

on 21 Aug 2019

Commented:

on 22 Aug 2019

Community Treasure Hunt

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

Start Hunting!