Efficient setting of properties of multiple vertices in Graph

The functions SetProperty and PropertyValue can only set a single property of a single vertex, as far as I know. How do I set a property for multiple vertices simultaneously?

In the documentation, they recommend using Do to set multiple properties, but I've heard that Do is not the most efficient way to do things in Mathematica. Are there any alternatives?

EDIT : The only approach seems to make several calls to SetProperty or PropertyValue (which is faster), one call for each distinct vertex property I want to set. From the answers so far it seems that there is no Mathematica equivalent to SetProperty or PropertyValue that sets several properties of distinct vertices at once. But I'd still hope that something could be done directly on the Graph object, bypassing SetProperty and PropertyValue.

-
A meta question: Is the version-8 tag necessary here? PropertyValue is new in version 8, but when mma 9 comes along this tag will become redundant... – Ajasja May 20 '12 at 20:27
@Ajasja I think the version-8 tag is important here. Especially because in Mathematica 8 there are three distinct ways of dealing with graphs: two are the Combinatorica package and the GraphPlot et al, which have been there at least since version 6; the other is the Graph object, which is new to version 8. The tag graphs-and-networks alone would not make this distinction. – becko May 20 '12 at 23:32

It may be not clear from documentation but SetProperty and PropertyValue can take "global to graph" specifications to apply simultaneously to all objects

Row@{g = CompleteGraph[4, PlotRangePadding -> .2],
PropertyValue[g, VertexLabels] = "Name"; g}


and even sets of "global to graph" specifications put in a list:

Row@{g = CompleteGraph[12, PlotRangePadding -> .3, ImageSize -> 300],
SetProperty[
g, {VertexSize -> .4, VertexLabels -> "Name",
VertexStyle -> Directive[Opacity[.4], Green],
EdgeStyle -> Directive[Thickness[.02], Opacity[.2], Red]}]}


Also, if you have unique-to-object options you can specify them by other than Do means. The documentation has examples of functional programming. For instance, slightly modifying this example using Fold:

Row@{g = CompleteGraph[8, VertexSize -> Large],
Fold[SetProperty[{#1, #2}, VertexStyle -> ColorData[45, #2]] &, g, Range[1, 8]]}


Or this example with Do that you've seen, can be done with Map:

Row@{g = CompleteGraph[15, VertexSize ->Large],
(PropertyValue[{g, #[[1]]}, VertexStyle] = #[[2]]) & /@
Transpose[{Range[15], ColorData[1, "ColorList"]}]; g}


-
I don't think this will improve efficiency, in this paricular case. FP offers speed advantage when array indexing or similar frequent operations are involved, since it pushes more of that into the kernel. This does not seem to be the case here. – Leonid Shifrin May 20 '12 at 18:56
@LeonidShifrin The question has reference to Do, and for now my sole intent was to show other functions usage. – Vitaliy Kaurov May 20 '12 at 19:15
The question's title implies that efficiency is central here, which is why I made this comment. – Leonid Shifrin May 20 '12 at 19:16
Scan might be even better than Map, since the only use if map is for the side effects. – Ajasja May 20 '12 at 19:50
I had tried similar approaches. What I was looking for was an alternative that didn't involve several distinct calls to SetProperty or PropertyValue, perhaps by avoiding those functions altogether and doing some rule-replacement combo on the Graph directly. Why isn't something like that implemented in Mathematica? – becko May 21 '12 at 23:20

I don't think you will get much better performance using functional approaches here, since the call to SetProperty is the bottleneck instead of the Do loop. Anyway it says in the documentation that PropertyValue is much faster than SetProperty. We can check both assumptions:

g = CycleGraph[1000];
Timing@Do[
PropertyValue[{g, v}, VertexWeight] = RandomReal[], {v,
VertexList[g]}]

(* ==> {0.016, Null} *)

g = CycleGraph[1000];
Timing@Do[
SetProperty[{g, v}, VertexWeight -> RandomReal[]], {v,
VertexList[g]}]

(* ==> {2.25, Null} *)

(*Try scan*)
g = CycleGraph[1000];
Timing@Scan[(PropertyValue[{g, #}, VertexWeight] = RandomReal[]) &,
VertexList[g]]

(* ==> {0.031, Null} *)

g = CycleGraph[1000];
Timing@Scan[(SetProperty[{g, #}, VertexWeight -> RandomReal[]]) &,
VertexList[g]]

(* ==> {2.625, Null} *)


From this we see that for this example PropertyValue is indeed much better and that using Scan has no benefits.

-

The information concerning SetProperty and PropertyValue timings aren't the full story! The SetProperty invokes no persistent side-effect (one must assign with it to keep the edited graph!) where as PropertyValue does edit the object (in-place).

Given that this performance testing is to create an graph object one wants to do more with I'm going to present the following timing results (from my crappy work laptop):

g = CycleGraph[1000];
Timing@Do[
PropertyValue[{g, v}, VertexWeight] = RandomReal[], {v,
VertexList[g]}] (*{24.109375,Null} *)
MemberQ[PropertyList[g], VertexWeight] (*True*)

g = CycleGraph[1000];
Timing@Do[
SetProperty[{g, v}, VertexWeight -> RandomReal[]], {v,
VertexList[g]}] (*{0.1875,Null}*)
MemberQ[PropertyList[g], VertexWeight] (*False*)

g = CycleGraph[1000];
Timing@Do[
g = SetProperty[{g, v}, VertexWeight -> RandomReal[]], {v,
VertexList[g]}] (*{23.859375,Null}*)
MemberQ[PropertyList[g], VertexWeight] (*True*)


Note the PropertyList in the second case left no side effect of a graph with VertexWeights assigned!

I've noticed the same timings irrespective of the method (Scan, Map, Fold, Do) used to invoke list processing. The performance timings appear commensurate and I'm guessing because of the underlying implementation one just can't avoid the performance hit of assigning a new graph each time any single VertexWeight is updated.

I'm yet to see what impact this has when one wants to pack more complex objects into VertexWeights such as EmpiricalDistribution (my current goal). My guess though is that providing a graph with everything it needs to be endowed with at instantiation is the only way to avoid performance problems on large graphs.

-

It just got even more interesting. I tried assigning a list to a VertexWeight and couldn't. I could however assign an EmpiricalDistribution (which is odd as it has a lot of structure inside it).

Check this the following timings though:

Clear[g]
g = CycleGraph[1000];
Timing@Do[
PropertyValue[{g, v}, VertexWeight] =
EmpiricalDistribution[RandomReal[{0, 1}, {1000}]], {v,
VertexList[g]}]
MemberQ[PropertyList[g], VertexWeight]

(* Out[3]= {28.7813, Null} *)

(* Out[4]= True *)

Clear[g]
g = CycleGraph[1000];
Timing@Do[
PropertyValue[{g, v}, "MyVertexWeight"] =
EmpiricalDistribution[RandomReal[{0, 1}, {1000}]], {v,
VertexList[g]}]
MemberQ[PropertyList[{g, 1}], "MyVertexWeight"]
PropertyList[g]
PropertyList[{g, 1}]

(* Out[7]= {0.171875, Null} *)

(* Out[8]= True *)

(* Out[9]= {GraphHighlight, GraphHighlightStyle, GraphLayout, \
GraphStyle, EdgeShapeFunction, EdgeStyle, VertexCoordinates, \
VertexShapeFunction, VertexShape, VertexSize, VertexStyle} *)

(* Out[10]= {"MyVertexWeight", VertexCoordinates, VertexShape, \
VertexShapeFunction, VertexSize, VertexStyle} *)

Clear[g]
g = CycleGraph[1000];
Timing@Do[
g = SetProperty[{g, v},
"MyVertexWeight" ->
EmpiricalDistribution[RandomReal[{0, 1}, {1000}]]], {v,
VertexList[g]}]
MemberQ[PropertyList[{g, 1}], "MyVertexWeight"]
PropertyList[g]
PropertyList[{g, 1}]

(* {0.296875, Null} *)

(* True  *)

(* Out[15]= {GraphHighlight, GraphHighlightStyle, GraphLayout, \
GraphStyle, EdgeShapeFunction, EdgeStyle, VertexCoordinates, \
VertexShapeFunction, VertexShape, VertexSize, VertexStyle} *)

(* Out[16]= {"MyVertexWeight", VertexCoordinates, VertexShape, \
VertexShapeFunction, VertexSize, VertexStyle} *)


It appears that if you create your own graph properties they behave much faster. It also turned out that you CAN assign lists to them in addition to anything else.

Funny thing is now after all that PropertyValue appears to be faster than SetProperty even with the re-assignment (and modulo Scan, Map,Do`).

-