# Faster way to extract partial data from AdjacencyMatrix

I am dealing with a lot of Graph objects. One task is to obtain an Id for each of them. So I first used

g = Graph[{1 -> 2, 1 -> 3, 4 -> 2, 3 -> 5, 4 -> 5, 7 -> 2, 4 -> 7, 3 -> 8}];

GraphId[g_] := Module[{}, AdjacencyMatrix@CanonicalGraph@g]
FullForm@GraphId@g


which gives

SparseArray[
Automatic,
List[7,7],
0,
List[
1,
List[
List[0,2,5,5,6,8,8,8],
List[List[5],List[7],List[4],List[6],List[7],List[7],List[3],List[6]]
],
List[1,1,1,1,1,1,1,1]
]
]


I have to reduce this result to save space. The first approach is not to including List[1,1,1,1,1,1,1,1] in the result. So I tried

ToData[g_] := (Last@ToExpression@StringReplace[ToString@FullForm@g, "Graph" -> "List"])[[1]]
GraphIdReduced[g_] := Module[{}, ToData@CanonicalGraph@g]
FullForm@GraphIdReduced@g


which gives

SparseArray[
Automatic,
List[7,7],
0,
List[
1,
List[
List[0,2,5,5,6,8,8,8],
List[List[5],List[7],List[4],List[6],List[7],List[7],List[3],List[6]]
],
Pattern
]
]


Of course I can use the same approach to reduce the result much further. But the problem is the above method is very slow.

gT=Table[g,{i,1,10^5}];
AbsoluteTiming[GraphId/@gT][[1]]
AbsoluteTiming[GraphIdReduced/@gT][[1]]


gives

1.185602
14.445625


, and for 10^6 times, the difference is 12.386422 vs 146.539146.

Maybe this is because the string operations are slow, but I don't know how to do without it. Graph and SparseArray are atomic object and I can't not use Apply to change their Head to List and then locate the contents I care about.

So how to extract some data from the above result more quickly?

In fact, the data I need is only

You can use the new-in-Version-10 properties "RowPointers" and "ColumnIndices" of a SparseArray object:

g = Graph[{1 -> 2, 1 -> 3, 4 -> 2, 3 -> 5, 4 -> 5, 7 -> 2, 4 -> 7, 3 -> 8}];

{sa["RowPointers"],Flatten[sa["ColumnIndices"]]}
(* {{0,2,5,5,6,8,8,8},{5,7,4,6,7,7,3,6}} *)


Or define a function

foo=Function[{s},{s@#,Flatten@s@#2}&@@{"RowPointers","ColumnIndices"}]
foo@sa
(* {{0,2,5,5,6,8,8,8},{5,7,4,6,7,7,3,6}} *)


Alternatively, you can use the second part of "NonzeroPositions" instead of "ColumnIndices":

{sa["RowPointers"],sa["NonzeroPositions"][[All,2]]}
(* {{0,2,5,5,6,8,8,8},{5,7,4,6,7,7,3,6}} *)


Note: There has been few additions to the still-undocumented SparseArray "Properties", including "RowPointers" and "ColumnIndices" used above.

sa["Properties"]
(* {AdjacencyLists, Background, ColumnIndices, Density, NonzeroPositions,
NonzeroValues, PatternArray, Properties, RowPointers} *)


I am not sure what you mean by "ID".

If you just need a unique signature, but you don't need to reconstruct the graph, use

Hash@CanonicalGraph[g]


If you need to reconstruct the graph, the minimal information needed is

{VertexCount[g], EdgeList@CanonicalGraph[g]}


You can then encode this into a more compact format depending on your needs. If all your graphs are connected, you can even drop the vertex count.

If you deal with undirected graphs, one compact representation is the graph6 format. Mathematica only supports the undirected version, but you can implement directed ones too.

ExportString[CanonicalGraph[g], "Graph6"]
(* ">>graph6<<FAhM_
" *)


Your graph takes only 5 ASCII characters in this representation.