# How to recompute the layout of a Graph?

I asked a question about changing Graph properties in a similar way to how Show can change Graphics properties. The answer was to use SetProperty.

Unfortunately this does not work for GraphLayout. Take the following example:

g1 = RandomGraph[{10, 20}]
g2 = RandomGraph[{10, 20}, GraphLayout -> "SpringEmbedding"]

{PropertyValue[g1, GraphLayout], PropertyValue[g2, GraphLayout]}

(* ==> {Automatic, "SpringEmbedding"} *)

ng1 = SetProperty[g1, GraphLayout -> "CircularEmbedding"]
ng2 = SetProperty[g2, GraphLayout -> "CircularEmbedding"]

{PropertyValue[ng1, GraphLayout], PropertyValue[ng2, GraphLayout]}

(* ==> {Automatic, "SpringEmbedding"} *)


Note that while GraphLayout can be queried through the standard property API, it doesn't seem like it can be set. Again, a hack like HighlightGraph[g, {}, GraphLayout -> "CircularEmbedding"] will work, but it is just a hack.

One could retrieve the vertex and edge lists of the graph and build a new Graph[...], but that is both tedious and it'll discard other property values.

So, what is the best way to re-layout a graph?

Right-clicking the graph and choosing a new layout works, but I'm looking for a programmatic way. Also, right-clicking and choosing a new layout is terribly slow---much much slower than rebuilding the graph or using HighlighGraph (can anyone reproduce this problem, is it slow for you as well?)

Update

Some spelunking reveals that GraphComputationCloneGraph[g, GraphLayout -> "CircularEmbedding"] will do the job, but I'd rather not use internal and undocumented functionality for this.

-
To clarify: I'm not looking to just just visualize a graph, so GraphPlot is not appropriate here. I want to re-layout it using the new Graph functionality, and also retrieve the coordinates. – Szabolcs Apr 20 '12 at 13:18
I cannot confirm slow speed for right-click layouting. This seems to work with the same speed as a rebuild with different layout. This is on Win 7 64bit with a quadcore Intel i7 CPU with 16GB RAM. – Yves Klett Apr 20 '12 at 13:23
@Yves I have a single-core CPU here, I suspect it has to do soemthing with the slowness. I get a "spinner" that runs for a long time before the new layout is shown. – Szabolcs Apr 20 '12 at 13:27
If you have a particularly nasty graph please share. I tried with your examples and some rather larger ones - all worked speedily. Time for new hardware? – Yves Klett Apr 20 '12 at 13:33
This seems to change the "layout": PropertyValue[g1, Properties] = {"GraphProperties" -> {GraphLayout -> "CircularEmbedding"}}; But the image doesn't reconfigure itself. – amr Apr 20 '12 at 17:01

Imagine that you have a graph with quite a few complex custom properties - below every edge and vertex have their unique properties:

g = Graph[Table[Style[j, Hue[j/2^8 - 1]], {j, 0, 2^8 - 1}],
Table[Style[j \[UndirectedEdge] FromDigits[Drop[IntegerDigits[j, 2], 1], 2],
Hue[j/2^8]], {j, 0, 2^8 - 1}], GraphLayout -> "RadialDrawing",
GraphStyle -> "LargeNetwork"]


To keep these properties and change the GraphLayout:

ToExpression@StringReplace[ToString[InputForm[g]], "RadialDrawing" -> "SpringElectricalEmbedding"]


Alternatively you could just use Shift+Ctrl+E and retype the GraphLayout option.

Yet another alternative, which avoids potentially unsafe string replacements, is the following:

ReleaseHold[
ToExpression[ToString[InputForm[g]], InputForm, Hold] /.
]


This converts the Graph object to a non-atomic Mathematica expression with the head Graph. Since this expression is not atomic, standard expression manipulation functions, including ReplaceAll, can be used on it.

-
Thanks for the answer! You're working around the fact that Graph is not an atomic object by converting it to InputForm as a string. That's a nice idea though manipulating code as string makes me a little uneasy ... Are you sure that all of the properties of the graph are preserved when converting to input form (they are in my limited test)? – Szabolcs Jul 12 '12 at 9:09
@Szabolcs It is a trick, but string processing is quite robust. If you look at the InputForm output in the above case, you'll see it is not small, yet it works. This is basically programmatic code editing, which is a general approach. – Vitaliy Kaurov Jul 12 '12 at 16:01
I'm a bit uncomfortable with changing code in the string representation ... suppose for example that we need to replace Ticks with something else. When using a string repersentation, it's not so easy to tell apart Ticks from FrameTicks without starting to write a rudimentary parsers. More importantly, it's all too easy to miss these little details and not think of an edge case where string replacement may lead to undesirable results. – Szabolcs Jul 16 '12 at 13:11
Perhaps instead of doing string replacement, we could improve your approach to get the graph representation as an actual expression: ToExpression[ToString@InputForm[g], InputForm, Hold] Then ReplaceAll what needs to be replaced and ReleaseHold. What do you think? If you think it's a good idea, could you edit it into the answer (or would you mind if I did)? Then I'll accept. – Szabolcs Jul 16 '12 at 13:15
@Szabolcs Sure, if you find a more robust way - please go ahead and edit. – Vitaliy Kaurov Jul 16 '12 at 14:31

I posted a way to update options of Graph objects like Show works for Graphics here, could you please confirm whether this is a solution for your needs with recomputing the layout?

Options[showGraph] = Options@Graph;
showGraph[g_] := g;
showGraph[g_Graph, {new___}] := showGraph[g, new];
showGraph[g_Graph, new : OptionsPattern[]] := Module[{old = Options@g, opts},
opts = DeleteDuplicates[First /@ Join[old, {new}]];
Graph[VertexList@g, EdgeList@g, Thread[opts -> (opts /. {new} /. old)]]
];


It simply replaces existing option values with new ones and adds new options, while maintaining the first two arguments of Graph without change. Using Vitaliy's example:

g1 = Graph[Table[Style[j, Hue[j/2^8 - 1]], {j, 0, 2^8 - 1}],
Table[Style[
j <-> FromDigits[Drop[IntegerDigits[j, 2], 1], 2],
Hue[j/2^8]], {j, 0, 2^8 - 1}], GraphLayout -> "RadialDrawing",
GraphStyle -> "LargeNetwork", ImageSize -> 300];
g2 = showGraph[g1, GraphLayout -> "SpringElectricalEmbedding"];

Column@{{g1, PropertyValue[g1, GraphLayout]}, {g2,
PropertyValue[g2, GraphLayout]}}
PropertyValue[g1, VertexCoordinates]
PropertyValue[g2, VertexCoordinates]


-

Edit

If you know the options you want to preserve, you can explicitly list them and change the ones you want to alter through ReplaceAll:

ng1 = g1;
a = AbsoluteOptions[g1, {GraphLayout, DirectedEdges, EdgeLabelStyle, EdgeLabels,
GraphRoot, Properties, VertexLabels, VertexShape,  VertexLabelStyle}]
/. {(GraphLayout -> _) -> GraphLayout -> "CircularEmbedding"};
Graph[Join[VertexList[g1],EdgeList[g1]], a]


This is admittedly a bit clunky, but I cannot find a simpler way.

You can't simply grab all of the AbsoluteOptions. For example, in the default options, EdgeShapeFunction-> $Failed, returned by AbsoluteOptions, will cause Graph to fail. I'm not sure whether there is such a thing as a universal list of desirable graph properties. - Yep, this is the simple way I'd like to avoid because it loses other information, e.g. vertex sizes or highlighting. Obtaining the vertex coordinates is fortunately easy, as you say. It also works with PropertyValue – Szabolcs Apr 20 '12 at 13:58 @belisarius How did you discover the right hand side of the rule? AbsoluteOptions[g1, EdgeShapeFunction] returns EdgeShapeFunction ->$Failed – DavidC Apr 20 '12 at 23:24
@Szabolcs You are correct. I updated my answer aiming to recover some of the likely missing information. Take a look. – DavidC Apr 20 '12 at 23:26
@David AbsoluteOption[g1] just plain – Dr. belisarius Apr 20 '12 at 23:30
And there are SEVERAL bugs there – Dr. belisarius Apr 20 '12 at 23:30

Not an answer, but an explanation why the properties route doesn't work. I suspect that GraphLayout is an option of the Graph, and not a property. While it is possible to introduce an inert property named GraphLayout using

PropertyValue[g, Properties] = {"GraphProperties" ->
{GraphLayout -> "CircularEmbedding"}}


as amr suggests, this doesn't set the option:

g = RandomGraph[{10, 20}, GraphLayout -> "SpringEmbedding"];

FullForm[g]


PropertyValue[g, Properties] = {"GraphProperties" ->
{GraphLayout -> "CircularEmbedding"}}

FullForm[g]


-
Strange that PropertyValue` can read the option name too though. – Szabolcs Apr 21 '12 at 10:52
@Szabolcs True. I'm a bit stymied. In all examples of the properties talk is only about vertex and edge properties, I couldn't find anything about setting Graph level properties. – Sjoerd C. de Vries Apr 22 '12 at 15:50