2

I have a sparse matrix

obj.resOp = sparse(row,col,val); 

and a vector containing the sums of each row in the matrix

sums = sparse(sum(obj.resOp,2));

Now what I want to do is

obj.resOp = obj.resOp ./ sums;

which would scale every row in the matrix so that the rowsum in each row is 1.

However in this last line, MATLAB internally seems to construct a full matrix from obj.resOp and hence I get this error:

Error using ./ Requested 38849x231827 (17.5GB) array exceeds maximum array size preference. Creation of arrays greater than this limit may take a long time and cause MATLAB to become unresponsive. See array size limit or preference panel for more information.

for sufficiently large matrices.

In theory I think that expanding to a full matrix is not necessary. Is there any MATLAB formulation of what I want to achieve while keeping the sparsity of obj.resOp?

fpnick
  • 419
  • 2
  • 6
  • 18
  • It's likely relevant to know which version of MATLAB you're using? – Wolfie Nov 22 '17 at 11:11
  • I'm using MATLAB 2017a. – fpnick Nov 22 '17 at 11:46
  • obj.resOp = inv(diag(sums)) * obj.resOp; should give me the same result, but is killed. That could be a bug in my vectors though. – fpnick Nov 22 '17 at 12:10
  • Some of the entries in `sum` were zero which is why the above didn't work. After fixing this, everything works fine! Anyway, thank you for your suggestion, @Wolfie – fpnick Nov 22 '17 at 12:17

2 Answers2

2

You can do this with a method similar to the one described in this answer.

Start with some sparse matrix

% Random sparse matrix: 10 rows, 4 cols, density 20%
S = sprand(10,4, 0.2);

Get the row sums, note that sum returns a sparse matrix from sparse inputs, so no need for your additional conversion (docs).

rowsums = sum(S,2);

Find all non-zero indices and their values

[rowidx, colidx, vals] = find(S)

Now create a sparse matrix from the element-wise division

out = sparse(rowidx, colidx, vals./rowsums(rowidx), size(S,1), size(S,2));
Wolfie
  • 27,562
  • 7
  • 28
  • 55
0

The equivalent calculation

obj.resOp = inv(diag(sums)) * obj.resOp;

works smoothly.

fpnick
  • 419
  • 2
  • 6
  • 18
  • 2
    Avoid using `inv` at all costs unless absolutely necessary. It is known to be error-prone. Because you are finding the inverse of a diagonal matrix, the result is simply another diagonal matrix whose coefficients are the inverse. Therefore: `inv(diag(sums)) --> diag(1./sums)`. This article by [John D. Cook should prove to be enlightening](https://www.johndcook.com/blog/2010/01/19/dont-invert-that-matrix/). – rayryeng Nov 22 '17 at 15:40
  • Thanks for the tip. Implemented. – fpnick Nov 23 '17 at 06:48