Skip to main content

evaluation - Graph does not evaluate some options when not displayed



How can I force Graph to evaluate all its options without actually displaying the graph itself?


Evaluating the following code ignores the Print statements in VertexShapeFunction, EdgeShapeFunction and possibly in other options too meaning that these options are not evaluated and I assume that the graph is not built at all, until displayed (notice the ; at the end):


g = Graph[{Print["entered Graph"]; 1 -> 2, 2 -> 3, 3 -> 1}, 
VertexShapeFunction -> (Disk[Print[#]; #, .1] &),
EdgeShapeFunction -> (Line[Print[#]; #] &),
ImageSize -> {Print["imagesize"]; 100, 100}];


entered Graph


imagesize




I assume this is because of some kind of optimization going on in the background, like when dynamic objects are only updated when they are onscreen. However, this delayed behaviour is inconsistent with other functions' policy on options (see later) and is rather dangerous, as now calling for the presumed static object g, I do not expect extra evaluations:


g


{-0.866025,-0.5}


{0.866025,-0.5}


{1.83697*10^-16,1.}



enter image description here



For comparison, a similar (partly dynamic) object is a chart, which behaves as expected (again, note ;):


BubbleChart[{{1, 0, 1}, {0, 1, 2}, {1, 2, 3}},
ChartElementFunction -> (Disk[Print[#]; First@#, .1] &)];


{{0.942265,1.05774},{-0.11547,0.11547}}


{{-0.0816497,0.0816497},{0.836701,1.1633}}


{{0.9,1.1},{1.8,2.2}}



In general, I had the impression, that functions work like BubbleChart (i.e. Plot, ListPlot, etc.) so Graph is the odd one out. Is this delayed evaluation a feature or a bug?




Answer



It seems that it was chosen to have "graphs" have as FullForm an expression that has head Graph. A plot made by Plot has a FullForm whose head is not Plot and an "expression" made by BubbleChart does not have head BubbleChart. Expressions generated by BubbleChart and Plot have Head Graphics. It seems that the "graph" is indeed only "generated" when it has to be displayed. The following code "defines a graph"/ties the symbol g to an expression with Head Graph. It shows that whenever we evaluate g (and therefore the expression with Head Graph), Mathematica indeed goes once through all the rules, but that is just normal evaluation as the attribute NHoldAll is not like HoldAll in this sense. Now consider the code


With[
{
unevGraph =
Unevaluated @@ Hold[
Graph[
graphBody,
VertexShapeFunction -> optVal1,
EdgeShapeFunction -> optVal2,

ImageSize -> optVal3
]
] /.
{
graphBody :> {Print["entered Graph"]; 1 -> 2, 2 -> 3, 3 -> 1},
optVal1 :> (Print[
"vSFOption Evaluated"]; (Disk[
Print["printed by vertex shape"]; Print[#]; #, .1] &)),
optVal2 :> (Print[
"eSFOption Evaluated"]; (Line[Print["printed by edge shape"];

Print[#]; #] &)),
optVal3 :> (Print["iSOption Evaluated"]; {100, 100})

}
}
,
g := unevGraph;
]
g//FullForm


The code prints that the Options are evaluated and then shows the FullForm of g. It does not print the other messages. A little bit more explanation about the code above can be found in a section below.


We can print the messages without displaying the graph by calling


h = ToBoxes[g];

--prints--> 'all the messages'


We can then set


g2 = ToExpression[h];

So that g2 really refers to an expression with head Graphics. Indeed it does not print any messages!


For those interested, note that even



HoldForm @@ Hold[g2] /. OwnValues[g2]

yields the image of the graph, but I guess that was to be expected.


We seem to have effectively created an expression with head Graphics corresponding to our graph g, in a (at least somewhat) analogous manner as Plot and BubbleChart create such expressions. It now seems clear that the reason Graph does not automatically make such conversions is that expressions with Graph head can also be used in calculations. I.e. we want to be able to use an expression with head Graph as an argument of for example Subgraph, rather than some expression with head Graphics.


Things that do not work


Something that looks similar can be achieved by evaluating a graph (for example g) and then setting g4 = %. However, g4 then refers to an object with head Graph. Note that copying and pasting the graph that was displayed by evaluating g will also not work.


Remarks about the first code block above


The first code block may seem a bit complicated. Especially the fact that an intermediate expression g:=Unevaluated[stuff] occurs may be a bit confusing. Note that g:=stuff is equivalent to g:=Unevaluated[stuff], so there is nothing special there. In fact I use that these expressions are equivalent, to replace the Hold with Unevaluated, which will be automatically stripped by SetDelayed. The only reason I use the Hold and RuleDelayed is because I wanted to insert the "option evaluated" messages. All of this is really not necessary to convert a Graph to its graphical representation. This can be seen in the section below.


Compact example of making the expresion with Head Graphics


Consider the following code,



g3 =
ToExpression@ToBoxes@Graph[{1 -> 2, 2 -> 3, 3 -> 1},
VertexShapeFunction -> (Disk[Print["printed by vertex shape"];
Print[#]; #, .1] &),
EdgeShapeFunction -> (Line[Print["printed by edge shape"];
jPrint[#]; #] &),
ImageSize -> {100, 100}
];

After evaluation of the code, g3 refers to an expression with head Graphics. Evaluating g3 in a new cell (and indeed in many other places, everything is as expected) shows the graph.



I think we must conclude this is a feature, not a bug :)


Less important: Non graphical options


Note that non graphical options are not "evaluated" when we display the graph. There is actually pretty strange behavior here. If we set


graph =
Graph[{1 -> 2, 2 -> 3, 3 -> 1},
VertexShapeFunction -> (Disk[Print["printed by vertex shape"];
Print[#]; #, .1] &),
EdgeShapeFunction -> (Line[Print["printed by edge shape"];
Print[#]; #] &),
ImageSize -> {100, 100},

VertexWeight :> {4, Print["vertexWeight"]; 5, 6}
];

We have


graph

--print--> messages


--shows--> a graph


But "vertexWeight" is not among the messages. Furthermore, we have


PropertyValue[{graph, 1}, VertexWeight]


-> 4


Whereas


PropertyValue[{graph, 2}, VertexWeight]

--prints--> "vertexWeight"


-> 5


The point here is that options are "evaluated" as needed. Graphical options are used when ToBoxes is used/an output cell is made. I suppose that is also the efficient way of doing things if you also want to be able to do fast calculations with graphs.


Less important: Storage and query of options


We define g7 as follows,



mmm = 8000;
nnn = 1000;
With[
{mmm = mmm,
nnn = nnn},
g7 = Graph[Append[Array[# -> # + 1 &, mmm], mmm -> 1],
EdgeShapeFunction -> Function[c++; If[
(*Mod[c,nnn]==0*)False, Print[#]]; Line[#]]
];


]

We see that defining g7 in this way takes hardly any time at all. But we have


Timing[PropertyValue[{g, 2555}, VertexCoordinates]]

--> {3.701786, {1707.87, 2453.73}} (and prints nothing)


And


Timing[ToExpression[ToBoxes[g]]][[1]]

--prints--> 10 messages



-> 6.282605


This suggests to me Mathematica has a way of building the VertexCoordinates without making the entire graphics of the graph. However, this calculation of the VertexCoordinates is not done when setting g7=Graph[...]. This makes sense, as it has to do only with graphics, so that if you are interested in subgraphs, you really don't want this to be calculated. Calculating the same PropertyValue twice does not take shorter, so I really don't think anything is stored, unless you store the graph graphics in the way I described. But then we can probably not find the values as easily as with PropertyValue.


Less important remarks


Throughout this answer my use of language may have been a bit awkward. Of course, I just want to be precise. For example it is obviously not the case that the evaluation of g3 causes a graph to be displayed, as we can simply use CompoundExpression to not display the graph. It seems that Mathematica does two things in evaluating input (or at least this helps me to talk about things). First, it evaluates an "input" to some intermediate expression that does not change if you evaluate it again. Then it builds the output cell. It seems that whenever the expression that g3 is tied to appears in the FullForm of such an intermediate expression, the graph is displayed. The example with HoldForm above is an example of this. The same is of course not true for g (defined in the first code block), evaluating a cell with the content


Hold[g] /. OwnValues[g]

Does not display a graph. As another example, note that even


OwnValues[g3]

displays a graph.



I hope somebody will have something more to say about this :).


Comments

Popular posts from this blog

plotting - Plot 4D data with color as 4th dimension

I have a list of 4D data (x position, y position, amplitude, wavelength). I want to plot x, y, and amplitude on a 3D plot and have the color of the points correspond to the wavelength. I have seen many examples using functions to define color but my wavelength cannot be expressed by an analytic function. Is there a simple way to do this? Answer Here a another possible way to visualize 4D data: data = Flatten[Table[{x, y, x^2 + y^2, Sin[x - y]}, {x, -Pi, Pi,Pi/10}, {y,-Pi,Pi, Pi/10}], 1]; You can use the function Point along with VertexColors . Now the points are places using the first three elements and the color is determined by the fourth. In this case I used Hue, but you can use whatever you prefer. Graphics3D[ Point[data[[All, 1 ;; 3]], VertexColors -> Hue /@ data[[All, 4]]], Axes -> True, BoxRatios -> {1, 1, 1/GoldenRatio}]

plotting - Filling between two spheres in SphericalPlot3D

Manipulate[ SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, Mesh -> None, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], {n, 0, 1}] I cant' seem to be able to make a filling between two spheres. I've already tried the obvious Filling -> {1 -> {2}} but Mathematica doesn't seem to like that option. Is there any easy way around this or ... Answer There is no built-in filling in SphericalPlot3D . One option is to use ParametricPlot3D to draw the surfaces between the two shells: Manipulate[ Show[SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], ParametricPlot3D[{ r {Sin[t] Cos[1.5 Pi], Sin[t] Sin[1.5 Pi], Cos[t]}, r {Sin[t] Cos[0 Pi], Sin[t] Sin[0 Pi], Cos[t]}}, {r, 1, 2 - n}, {t, 0, Pi}, PlotStyle -> Yellow, Mesh -> {2, 15}]], {n, 0, 1}]

plotting - Mathematica: 3D plot based on combined 2D graphs

I have several sigmoidal fits to 3 different datasets, with mean fit predictions plus the 95% confidence limits (not symmetrical around the mean) and the actual data. I would now like to show these different 2D plots projected in 3D as in but then using proper perspective. In the link here they give some solutions to combine the plots using isometric perspective, but I would like to use proper 3 point perspective. Any thoughts? Also any way to show the mean points per time point for each series plus or minus the standard error on the mean would be cool too, either using points+vertical bars, or using spheres plus tubes. Below are some test data and the fit function I am using. Note that I am working on a logit(proportion) scale and that the final vertical scale is Log10(percentage). (* some test data *) data = Table[Null, {i, 4}]; data[[1]] = {{1, -5.8}, {2, -5.4}, {3, -0.8}, {4, -0.2}, {5, 4.6}, {1, -6.4}, {2, -5.6}, {3, -0.7}, {4, 0.04}, {5, 1.0}, {1, -6.8}, {2, -4.7}, {3, -1....