Generate a 100000x100000 matrix that takes less time and memory

I have written a random matrix generator code that generates an adjacency matrix of any size. I am targetting larger size like 100kx100k but the problem that I face is the time to generate that (which is related to the RAM memory). It needs ~ 60 GB to do this.
I presume that there has to be a smarter way to do this, like by using a small int instead of a double word or something similar to the datatype. Any help would be appreciated. Thanks
function [a,ed] = Random_graph_genar_function(nodes, connectivity)
for it=1:3
ni = nodes;
ac= connectivity;
mi=(ni*(ni-1))/2;
no=round(mi*ac);
a=zeros(ni,ni);
in=randperm(mi,no); p=1;
for i=1:ni
for j=i+1:ni
if (any(in(:)==p))
a(i,j)=1;
a(j,i)=1;
end
p=p+1;
end
end
p=0;
for i=1:ni
for j=i+1:ni
if (a(i,j)==1)
p=p+1;
ed(1,p)=i;
ed(2,p)=j;
end
end
end
s=sum(a);
mx=max(s)
for i=1:ni
bc(i)=mx-s(i);
end
tbc=sum(bc);
end
end

4 comentarios

What is the range of values you need to store, and what percentage of your matrix is non-zero?
Jay Vaidya
Jay Vaidya el 19 de Oct. de 2020
Editada: Jay Vaidya el 19 de Oct. de 2020
The percentage of the matrix that is non-zero is random and changes with different simulations.
But, I only need to store binary values (0 or 1) in the adjacency matrix. That means it is a non-weighted graph.
I get the following error when I try running the code below
Random_graph_genar_function(1e6,0.8)
Error using zeros
Requested 1000000x1000000 (7450.6GB) 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.
The percentage of the matrix that is non-zero is random
Is it going to be 0.000378% in one run, but 82.19% in another run? You have no idea what the percentage will be, other than more than 0% and less than 100% ?
No, I meant that I would like to control the connectivity of the graph (percentage of non-zero elements) using the connectivity parameter in the above function.

Iniciar sesión para comentar.

 Respuesta aceptada

Matt J
Matt J el 20 de Oct. de 2020
Editada: Matt J el 20 de Oct. de 2020
Seems to me the whole code can be replaced by,
function [a,ed] = Random_graph_genar_function(nodes, connectivity)
a=logical(sprandsym(nodes,connectivity));
a=a-a.*speye(nodes);
G=graph(a);
ed=table2array(G.Edges).';
end
although instead of having the function return a and ed, I suspect that everything you are trying to do is more easily accomplished with the graph object G instead.

8 comentarios

Thanks, Matt, this is really faster. But actually the adj matrix 'a' should have diagonal elements as 1. That is not the case in the code that you sent. Do you have any suggestions?
Matt J
Matt J el 20 de Oct. de 2020
Editada: Matt J el 20 de Oct. de 2020
To reduce intermediate memory allocation, you can also build a iteratively,
function [a,ed] = Random_graph_genar_function(nodes, connectivity)
nPasses=10;
a=sparse(false);
for i=1:nPasses
a=a | sprandsym(nodes,connectivity/nPasses);
end
a(1:nodes+1:end)=0; %zero the diagonal
G=graph(a);
ed=table2array(G.Edges).';
end
Thanks again Matt. Using
a(1:nodes+1:end)=0
instead of
a = a + eye(size(a))
helps to speed up I guess.
Also, could you tell me what is the reason for having the for loop that you wrote? with nPasses = 10? I would like to understand the reason as I am trying to reduce the computation time. Thanks.
Matt J
Matt J el 20 de Oct. de 2020
Editada: Matt J el 20 de Oct. de 2020
But actually the adj matrix 'a' should have diagonal elements as 1. That is not the case in the code that you sent.
It's not the case in the code you posted, as far as I can tell. Your code fills the upper triangle strictly above the diagonal of a, never touching the diagonal. Regardless, you can do
a(1:nodes+1:end)=1;
or
a=a|speye(nodes);
Also, could you tell me what is the reason for having the for loop that you wrote? with nPasses = 10?
This is to reduce the density of the temporary double sparse matrix created by sprandsym(). Ultimately, we want a to be type logical, but there is no way, unfortunately, to create a random sparse logical matrix directly. I doubt it will save you much RAM, but it is something. Compare:
>> A=sprandsym(1e5,.0001);
>> L=logical(A);
>> whos A L
Name Size Kilobytes Class Attributes
A 100000x100000 16406 double sparse
L 100000x100000 9571 logical sparse
Hi Matt,
Can we ensure that if the graph is not broken (meaning if the graph doesn't contain multiple disconnected subgraphs)?
I don't think your current implementation ensures that does it?
Walter Roberson
Walter Roberson el 20 de Oct. de 2020
Editada: Walter Roberson el 20 de Oct. de 2020
If you have a fixed number of iterations to work with, then you will need to proceed by either
  1. growing the graph step by step so that at no point are there disconnected points; or
  2. imposing a maximum distance away from other existing points upon new points, and "reserving" a number of iterations to do nothing but "fix-up" the disconnected subtrees by connecting them to other sub-trees; or
  3. after the initial iterations, find all disconnected subtrees and move them to be attached to the main tree. If you have a constrained geometry, it might take some searching to find attachment points that satisfy the constraints
I suspect that the first option, growing step by step, is the easiest.
Thanks, Walter and Matt. I agree that growing step by step would be easier. I have made another question about this. It would be great if you have some time to see that. It is here. Thanks in advance.

Iniciar sesión para comentar.

Más respuestas (2)

If most of the elements equal to zero, then use sparse array: https://www.mathworks.com/help/matlab/ref/sparse.html. You can also try to create uint8 array which will only use 1/8 memory
a=zeros(ni,ni,'uint8');

7 comentarios

This is better. I just tried this. It seems to take less memory (RAM) than the other one. But, I still cannot get the error.
Random_graph_genar_function(1e6,0.8)
Error using zeros
Requested 1000000x1000000 (7450.6GB) 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.
Don't try to build a full array and then convert it to sparse. That will consume all the memory required for the full matrix plus all the memory required for the sparse matrix.
Ideally try assembling vectors where the values are going to be non-zero and passing them to sparse once all the indices have been set.
n = 20;
r = 1:n;
c = (n+1)-r;
S = sparse(r, c, true)
F = full(S)
whos S F
Got it. Works. Thank you. But, it doesn't work for n = 1e6. It works for n = 5e5.
Error in n = 1e6;
Error using full
Requested 1000000x1000000 (931.3GB) 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.
Do not do the
F = full(S)
step.
Do you need the adjacency matrix as a full matrix, or can you work on it as a sparse matrix?
That gives the matrix that is quite sparse. I needed a matrix that can have the connectivity that I would like to have. Can we control the connectivity/density in this?
My entire code used to use double datatype. I don't know using sparse can change other things. At the end of the day, I need an adjacency matrix that has 0.1 (connectivity) and 100k nodes (100k rows and 100k columns).
I changed the
a=zeros(ni,ni);
to
a=zeros(ni,ni,'uint8');
But, it is not making a big difference in the above code for n = 1e3 (1000 nodes).

Iniciar sesión para comentar.

a=zeros(ni,ni,'uint8');
You are already using only one byte per entry.
If you were to create logic that packed 8 adjacent entries into one byte, you could potentially get 8:1 compression... and would still need 116.4 gigabytes of memory.
Your only hope would be if you could use a sparse() array. See https://www.mathworks.com/matlabcentral/answers/100287-how-much-memory-a-sparse-matrix-created-using-sprand-with-given-number-of-rows-columns-and-density for a guideline to the amount of memory a sparse array uses. I suspect the 16 is 8 bytes for an offset, plus 8 bytes for storage -- so using a sparse logical array possibly only takes 8+1 = 9 bytes per entry (this could be tested.)
Which is to say that if your occupancy is more than about 1/20 then sparse would be less efficient. If you had a target of (say) 16 gigabytes then you would need to be about only 1/1000 occupied.

Categorías

Más información sobre Sparse Matrices en Centro de ayuda y File Exchange.

Productos

Versión

R2020b

Etiquetas

Preguntada:

el 19 de Oct. de 2020

Comentada:

el 20 de Oct. de 2020

Community Treasure Hunt

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

Start Hunting!

Translated by