17

To get neighboring vertices I first tried:

gg = GridGraph[{10, 10, 10, 10}];
VertexList[NeighborhoodGraph[gg, 1, 1]] // AbsoluteTiming  

{5.539308, {1, 2, 11, 101, 1001}}

But that is really slow. This is much faster and still uses the new Graph package:

Union[VertexInComponent[gg, 1, 1], VertexOutComponent[gg, 1, 1]] // AbsoluteTiming

{0.000172, {1, 2, 11, 101, 1001}}'

I believe that this mimics the NeigborhoodGraph approach above, but is still much faster than using NeighborhoodGraph:

VertexList[Subgraph[gg, Union[VertexInComponent[gg, 1, 1], 
      VertexOutComponent[gg, 1, 1]]]] // AbsoluteTiming

{0.000306, {1, 2, 11, 101, 1001}}

The last two approaches are also faster than NeighborhoodVertices from the GraphUtilities package:

Needs["GraphUtilities`"]
NeighborhoodVertices[gg, 1, 1] // AbsoluteTiming

{0.017248, {1, 2, 11, 101, 1001}}

I'm working with large graphs that I need to manipulate quickly for several interactive information visualization tools. Any tips on why NeighborhoodGraph is so slow here and how to best use the new Graph package when speed is an issue?

Sasha
  • 7,373
  • 36
  • 47
Todd Johnson
  • 649
  • 3
  • 12
  • I had this problem myself recently. I have no idea about an answer. I ended up using NeighborhoodVertices. Just replaced it with VertexComponents. Thanks – Cameron Murray Nov 13 '12 at 11:17

2 Answers2

12

The issue is that NeighborhoodGraph is attempting to infer the layout of the original GridGraph. The layout algorithm for the parent graph is responsible for the slowness. At the cost of loosing the layout information this can be sped-up:

In[1]:= gg = GridGraph[{10, 10, 10, 10}, GraphLayout -> None];

In[2]:= VertexList[NeighborhoodGraph[gg, 1, 1]] // AbsoluteTiming

Out[2]= {0.0199998, {1, 2, 11, 101, 1001}}

The GraphLayout -> None has the effect that Graph object is not automatically rendered:

enter image description here

In version 9 and later, the simpler

NeighborhoodGraph[g, n, GraphLayout -> None]

form can be used for much faster neighbourhood graph computations.

Szabolcs
  • 234,956
  • 30
  • 623
  • 1,263
Sasha
  • 7,373
  • 36
  • 47
  • Why does NeighbourhoodGraph need to invoke the layout algorithm? Computing the layout is not necessary if the graph is never shown (like here) ... Is this behaviour intentional or is this a bug? – Szabolcs Nov 14 '12 at 14:56
  • @Szabolcs It is so that NeighborhoodGraph could be used with HighlightGraph, e.g. gr = GridGraph[{3, 3, 3, 3}]; HighlightGraph[gr, NeighborhoodGraph[gr, 1, 1]]. – Sasha Nov 14 '12 at 15:28
  • In the meantime R.M. told me in the chatroom that NeighborhoodGraph preserves layout information (original vertex coordinates), which is useful sometimes. This information isn't really necessary for HighlightGraph though, for example HighlightGraph[g, Subgraph[g, VertexList@NeighborhoodGraph[g, 1]]] works even though Subgraph doesn't seem to preserve the original vertex coordinates. – Szabolcs Nov 14 '12 at 15:33
  • Maybe in a future version NeighborhoodGraph[g, 1, GraphLayout -> None] could be made to not compute the layout of g to make it simpler to keep a good performance and still use NeighborhoodGraph? I can't wait to see how Graph was improved in v9 :-) – Szabolcs Nov 14 '12 at 15:38
  • @Szabolcs Your suggested behavior is already implemented in v9. – Sasha Nov 14 '12 at 15:39
  • I think this behaviour is a serious usability issue. It is just not easy to figure out why NeighborhoodGraph is so slow. I saw this question when it was asked and I saw your answer, I even commented on it. Yet I completely forgot about it and this afternoon I ended up reimplementing NeighborhoodGraph because I thought the built-in one was just too slow ... of course the GraphLayout -> None fix is easy, but it's just not easy to discover it, and it's also not easy to keep this strange behaviour in mind if one doesn't use the function too often ... – Szabolcs Jan 20 '13 at 01:17
  • 1
    I'm sure many people are hit by this issue and they never realise what the solution is ... they probably just conclude that Mathematica is too slow for these kinds of computations which is of course not the case ... I really wish that people in charge of developing the graph functionality would consider this. Do you suggest I send a suggestion to support about this? – Szabolcs Jan 20 '13 at 01:20
  • 2
    A question: do you know why SetOptions[NeighborhoodGraph, GraphLayout -> None] doesn't do what one would expect it to? NeighborhoodGraph is still slow unless I pass the GraphLayout -> None with each call. – Szabolcs Jan 20 '13 at 01:27
4

In my Boggle answer, I used a custom function to get the adjacent nodes for a vertex directly from the adjacency matrix . Not only is this much faster than using NeighborhoodGraph, it also gives you the adjacent nodes for all the nodes at once, so subsequent calls are instantaneous (you only need to index it). Here's the solution:

gg = GridGraph[{10, 10, 10, 10}];
adjNodes = With[{list = First /@ ArrayRules[AdjacencyMatrix@gg] // Most}, 
    Map[Last, SplitBy[list, First], {2}]]; // AbsoluteTiming
(* {0.138181, Null} *)

In comparison, NeighborhoodGraph takes around 3.38 seconds for each vertex! To get the adjacent nodes for a vertex $v$, simply take the $v$th part of adjNodes (these are instantaneous):

adjNodes[[2]]
(* {1, 3, 12, 102, 1002} *)

adjNodes[[8764]]
(* {7764, 8664, 8754, 8763, 8765, 8774, 8864, 9764} *)

You can verify that you get the same result as Rest@VertexList@NeighborhoodGraph[gg, 8764, 1]. This approach is very useful when you have a single large graph and you need to navigate it repeatedly.

rm -rf
  • 88,781
  • 21
  • 293
  • 472