6
votes

I've noticed various cases in Matlab and octave where functions accept both matrices and vectors, but doesn't do the same thing with vectors as it does with matrices.

This can be frustrating because when you input a matrix with a variable number of rows/columns, it could be interpreted as a vector and do something you don't expect when the height/width is 1 making for difficult debugging and weird conditional edge cases.

I'll list a few I've found, but I'm curious what others people have run into

(Note: I'm only looking for cases where code accepts matrices as valid input. Anything that raises an exception when a non-vector matrix is given as an argument doesn't count)

1) "diag" can be used to mean diagonal of a matrix or turn a vector into a diagonal matrix

Since the former is generally only used for square matrices this isn't so egregious in matlab, but in Octave it can be particularly painful when Octave interperets a vector beginning with a nonzero element and everything else zeros as a "diagonal matrix" ie

t=eye(3);
size(diag(t(:,3))) == [3,3]
size(diag(t(:,2))) == [3,3]
size(diag(t(:,1))) == [1,1]

2) Indexing into a row-vector with logicals returns a row-vector

Indexing into anything else with logicals returns a column vector

a = 1:3;
b = true(1,3);
size(a(b)) == [1, 3]
a = [a; a];
b = [b; b];
size(a(b)) == [6, 1]

3) Indexing into a vector v with an index vector i returns a vector of the same (row/col) type as v. But if either v or i is a matrix, the return value has the same size as i.

a = 1:3;
b = a';
size(a(b)) == [1, 3]
b = [b,b];
size(a(b)) == [3, 2]

4) max, min, sum etc. operate on the columns of a matrix M individiually unless M is 1xn in which case they operate on M as a single row-vector

a = 1:3
size(max(a)) == [1, 1]
a = [a;a]
size(max(a)) == [1, 3]

max is particularly bad since it can't even take a dimension as an argument (unlike sum)

What other such cases should I watch out for when writing octave/matlab code?

2
just to clarify: you can specify dimension for max/min: min(rand(3),[],1) or max(rand(3),[],2)Amro

2 Answers

1
votes

Each language has its own concepts. An important point of this language is to very often think of matrices as an array of vectors, each column an entry. Things will start to make sense then. If you don't want that behavior, use matrix(:) as the argument to those functions which will pass a single vector, rather than a matrix. For example:

octave> a = magic (5);
octave> max (a)
ans =

   23   24   25   21   22

octave> max (a(:))
ans =  25

1) This is not true with at least Octave 3.6.4. I'm not 100% sure but may be related related to this bug which has already been fixed.

2) If you index with boolean values, it will considered to be a mask and treated as such. If you index with non-boolean values, then it's treated as the indexes for the values. This makes perfect sense to me.

3) This is not true. The returned has always the same size of the index, independent if it's a matrix or vector. The only exception is that if the index is a vector, the output will be a single row. The idea is that indexing with a single vector/matrix returns something of the same size:

octave> a = 4:7
a =

   4   5   6   7

octave> a([1 1])
ans =

   4   4

octave> a([1 3])
ans =

   4   6

octave> a([1 3; 3 1])
ans =

   4   6
   6   4

4) max does take dimension as argument at least in Octave. From the 3.6.4 help text of max:

For a vector argument, return the maximum value. For a matrix argument, return the maximum value from each column, as a row vector, or over the dimension DIM if defined, in which case Y should be set to the empty matrix (it's ignored otherwise).

The rest applies like I said on the intro. If you supply a matrix, it will think of each column as a dataset.

0
votes

1) As pointed out by the other user, this is not true with at Octave >= 3.6.4.

In case 2) the rule is for vectors, return always the same shape of vector, for anything else return a column vector, consider:

>> a = reshape (1:3, 1,1,3)

a(:,:,1) =

     1.0000e+000


a(:,:,2) =

     2.0000e+000


a(:,:,3) =

     3.0000e+000

>> b = true(1,3)

b =

  1×3 logical array

   1   1   1

>> a(b)

ans(:,:,1) =

     1.0000e+000


ans(:,:,2) =

     2.0000e+000


ans(:,:,3) =

     3.0000e+000

>> a = [a;a]

a(:,:,1) =

     1.0000e+000
     1.0000e+000


a(:,:,2) =

     2.0000e+000
     2.0000e+000


a(:,:,3) =

     3.0000e+000
     3.0000e+000

>> b = [b;b]

b =

  2×3 logical array

   1   1   1
   1   1   1

>> a(b)

ans =

     1.0000e+000
     1.0000e+000
     2.0000e+000
     2.0000e+000
     3.0000e+000
     3.0000e+000

You can see that this makes sense since vectors have a clear 'direction' but other shaped matrices do not when you remove elements. EDIT: actually I just checked and Octave doesn't seem work this way exactly, but probably should.

3) This is consistent with 2). Essentially if you supply a list of indices the direction of the indexed vector is preserved. If you supply indices with a shape like a matrix, the new information is the index matrix shape is used. This is more flexible, since you can always do a(b(:)) to preserve the shape of a if you so wish. You may say it is not consistent, but remember indexing with logicals may reduce the number of elements to be returned, so they cannot be reshaped in this way.

4) As pointed out in a comment, you can specify dimension for max/min to operate on: min(rand(3),[],1) or max(rand(3),[],2), but in this case there are 'legacy' issues with these functions which data back to when they were first created and now are very difficult to change without upsetting people.