6

What is the best way to multiply integer-valued matrices in MATLAB?

I was surprised to learn that the following isn't accepted behavior:

>> x = int64([1, 2])
>> x * x'
Error using  * 
MTIMES is not fully supported for integer classes. At least one input must be scalar.
To compute elementwise TIMES, use TIMES (.*) instead.

I can always convert to double and back again. Is this the best solution? I'm using R2013b.

chappjc
  • 30,359
  • 6
  • 75
  • 132
MRocklin
  • 55,641
  • 23
  • 163
  • 235
  • 2
    I was surprised too (and annoyed) when I discovered that. It's a shame you can't properly multiply integers in Matlab – Luis Mendo Oct 25 '13 at 16:05
  • 1
    There is the danger of [integer overflow](http://en.wikipedia.org/wiki/Integer_overflow) with smaller integer types, which is usually just part of the territory when dealing with integers. However, MATLAB likes to hold your hand a little too much sometimes. I suspect they want to implement some kind of warning or overflow detection before letting `mtimes` work on `uint8` for example. – chappjc Oct 25 '13 at 20:08
  • Interesting point @chappjc . I guess IEEE floating point naturally gives this assurance already. It looks like numpy, the Python equivalent, errs silently on this issue. – MRocklin Oct 25 '13 at 22:02
  • @MRocklin: does numpy allow what MATLAB does not, in this case? what does "errs silently" mean? – Nike Aug 06 '19 at 20:54

2 Answers2

5

In this simple case, you could get by with using

sum(x.*x)

It seems times (.*) is supported properly for integer matrices, although mtimes ( *) is not.

For general matrix multiplication: let A and B be two matrices with suitable sizes so that A*B exists. Since times and sum are supported for integers, you can generalize the above trick, usingbsxfun and sum to compute all entries of the product matrix as follows.

Edit: As noted by @July, you need the 'native' flag in sum in order to keep the result of integer type. Thanks also for pointing out a problem that was caused by squeeze, now corrected by using a second permute.

permute(sum(bsxfun(@times, A.', permute(B, [1 3 2])), 1, 'native'), [2 3 1])

For example:

>> A = int64([1 2; 3 4])
A =
                    1                    2
                    3                    4
>> B = int64([5 7 9; 6 8 10])
B =
                    5                    7                    9
                    6                    8                   10
>> permute(sum(bsxfun(@times, A.', permute(B, [1 3 2])), 'native'), [2 3 1])
ans =
    17    23    29
    39    53    67

Anyway, the fastest alternative seems to be double(A)*double(B).

Luis Mendo
  • 110,752
  • 13
  • 76
  • 147
  • Nice tip. My actual application is more complex than the example I posted. I'd like to do a full matrix-matrix multiply. – MRocklin Oct 25 '13 at 16:07
  • Then it seems you would need two loops (nested) to compute each entry of the product matrix using `sum(x(ii,:).*y(:,jj).')`. It's probably faster to convert to `double` (or `single`) – Luis Mendo Oct 25 '13 at 16:09
  • ... or use `bsxfun` and do it in only line! See updated answer – Luis Mendo Oct 25 '13 at 16:22
  • I like the last answer the best -- convert to `double`. If is _safest_ too! +1 – chappjc Oct 25 '13 at 20:11
  • @chappjc - what if you don't want to lose any digits of precision in your result? – Aron Ahmadia Oct 25 '13 at 20:29
  • @AronAhmadia I think `double` does not loose digits when operating with integers – Luis Mendo Oct 25 '13 at 20:32
  • 1
    @Aron Ahmadia - [Anything less than 2^53 can be stored in a double without losing precision](http://stackoverflow.com/a/1848762/2778484)... – chappjc Oct 25 '13 at 20:33
  • @chappjc Thanks! BTW: it's weird that converting to `single` instead of `double` apparently results in longer computation time. Does that happen in your version/computer too? – Luis Mendo Oct 25 '13 at 20:35
  • @chappjc - that's correct, but notice that MRocklin is using 64-bit integers. That's 11 bits that could have been used for handling larger numbers (which he probably needed if he is using 64 bit integers in the first place). Luis Mendo, you're wrong. – Aron Ahmadia Oct 25 '13 at 20:38
  • Sure, I guess it's worth noting that numbers over 9,007,199,254,740,992 will lose precision. But your assumption that he needs bits over 53 is a big one. You usually use `long long` if you just want more than 32, not 53. – chappjc Oct 25 '13 at 20:40
  • @Luis Mendo - It's the opposite on my machine (64-bit R2013b, Windows): `double` multiplication is slower than `single`, even if I take the cast out of the timing. – chappjc Oct 25 '13 at 20:49
  • 1
    I know this is an old post, so I am posting my comment purely for the sake of completeness, since I ran into the same issue. In the bsxfun solution, one needs to use the 'native' flag for the 'sum' function, otherwise 'sum' automatically returns 'double'. – M.G. Oct 20 '17 at 22:00
  • @LuisMendo: also, are you sure about the orientations of the matrices? Multiplying 2x2 matrix with a 2x1 column vector returns 1x2 row vector, which seems to be the transpose of the correct result. However, it works correctly with your example. I'm confused about what is going on. – M.G. Oct 20 '17 at 22:12
  • 1
    @July You are right. It's the usual problem with `squeeze` not removing a leading singleton dimension from a matrix. Corrected too, thanks again! – Luis Mendo Oct 20 '17 at 22:19
1
  • MATLAB does not support integer matrix-matrix multiplication due to the lack of industrial support.

    MATLAB uses BLAS (more specifically, Intel's implementation of BLAS, i.e. MKL) to do matrix multiplication. At present, BLAS does not natively support multiplication of integer matrices. However, a good news is that MKL 2018 adds initial support for integer matrices. (cf. this slides)

  • As for your question, if you aim at performance and your matrices are not too small, sum(x.*x) is inferior to int64(double(x)*double(x.')).

  • Use native only when you are sure that overflow and underflow will not happen.

    One extra note is that although native flag can keep the type of return value the same as that of input value. It may suffer from integer overflow or underflow. Check with the following snippet.

    sum(int8(-ones(300,1)), 'native') % ans = -128 (of type int8)
    sum(int8(ones(300,1)), 'native')  % ans = 127  (of type int8)
    sum(int8(ones(300,1)))            % ans = 300  (of type double)
    

    Although single and double can also overflows and underflows, they happens less frequently.

Eli4ph
  • 317
  • 2
  • 15