1

I am building a tool to manipulate Feynman diagrams and depict them using the Graph functionality. A diagram might have the following syntax

f=diag[g[a,i[1]],g[b,i[2]],g[i[1],c],g[i[2],d],v[i[1],i[2]]]

This is just an example. Now we would like to represent it as a graph.

First way (using RuleDelayed):

Clear[pic]
pic[diag[x__]]:=Module[{i},Graph[List[x]/.{g[i_,j_]:> DirectedEdge[j,i],v[j_,k_]:> UndirectedEdge[j,k]},VertexLabels->"Name",PlotTheme->"Business",ImageSize->300,ImagePadding->10,VertexLabelStyle->Directive[Black,11],GraphHighlight->Cases[List[x],v[i_,j_]:> UndirectedEdge[i,j]]]]

Using it

pic[f]
i=5;
pic[f]

produces

enter image description here enter image description here

Thus we have an expected result and a result with wrong labels.

Second way (using Rule)

Clear[i,g,v,f]
f=diag[g[a,i[1]],g[b,i[2]],g[i[1],c],g[i[2],d],v[i[1],i[2]]];
Clear[pic]
pic[diag[x__]]:=Module[{i,j,k},Graph[List[x]/.{g[i_,j_]->DirectedEdge[j,i],v[j_,k_]->UndirectedEdge[j,k]},VertexLabels->"Name",PlotTheme->"Business",ImageSize->300,ImagePadding->10,VertexLabelStyle->Directive[Black,11],GraphHighlight->Cases[List[x],v[i_,j_]->UndirectedEdge[i,j]]]]

Now using it

pic[f]
i=5;
pic[f]

Produces the correct picture only the first time, and does not work at all second time.

yarchik
  • 18,202
  • 2
  • 28
  • 66
  • RuleDelayed solves your problem in the second snippet. It is true that Rule treats named pattern as local to the rule. However in the second snippet, the right-hand side gets evaluated to Times[2, 5] before any replacing takes place. RuleDelayed first performs replacements, and then evaluates the expression on the right-hand side of the rule. I am still confused about the first snippet, though. – Natas Oct 30 '20 at 10:55
  • I think SetDelayed is not relevant here. The confusing part is this: Clear[i, x]; i = n; Module[{i}, x /. {i_ -> i}] (*n*). To be honest, if this is expected behavior I have understood nothing about Module. – Natas Oct 30 '20 at 11:03
  • OK, the relevant documentation for Module is this "Before evaluating expr, Module substitutes new symbols for each of the local variables that appear anywhere in expr except as local variables in scoping constructs." The issue now is that Rule is also a scoping construct (with named patterns). Therefore Module does not replace the i in the Rule. – Natas Oct 30 '20 at 11:10
  • As a general rule I suggest: Any time you write a rule where the name of the pattern appears on the right-hand side, use RuleDelayed. This would be the "canonical way" in my opinion. – Natas Oct 30 '20 at 11:13
  • Ok, in the second one the problem is of course again using Rule instead of RuleDelayed. In the first the problem for the label is that f is defined using i. Of course, if you later set i it will be used for the value of f. Mathematica usually evaluates expressions unless being told to hold them. – Natas Oct 30 '20 at 11:29
  • @Natas Right, but I am not sure how to hold i in the first way. – yarchik Oct 30 '20 at 11:31
  • Well, once you define it, there is no going back. Of course a more flexible definition of f would use DownValues and SetDelayed. – Natas Oct 30 '20 at 11:34
  • The problem is that you are using global variables. Either be very careful that there aren't any conflicts (yeah, right...) or don't use global variables at all. There is no way around this problem. – Natas Oct 30 '20 at 11:36
  • @Natas Hmm.. But can you elaborate (possibly as an answer) on what do you mean with a more flexible definition of f . The thing is I am doing something similar like you said, but it does not change anything. – yarchik Oct 30 '20 at 11:39
  • I fear that anything addressing your issue would result again in a wall of text. Perhaps a solution for you would be to Protect the special symbols to prevent any assignment to them. There just isn't any easy solution too scoping in programming. If you want to do it right, it takes a lot of time and patience. – Natas Oct 30 '20 at 11:46
  • I can identify at least 2 issues in the Graph example: 1. 2. Module only sees explicit variable: Clear[a, b, c]; b = 2 a; Module[{a = 2}, b], so Module just won't help here. Block may be a choice for circumventing. 2. Function without Hold* attribute evaluates its argument before it's passed into the function. Example: Clear[f, i]; f[x_] := Block[{i}, Hold[x]]; i = 2; {f[i], f[Unevaluated@i]}, so your i has already evaluated to 5 before passing into the function. – xzczd Oct 30 '20 at 12:16
  • One possible solution: Clear[i, pic]; f = diag[g[a, i[1]], g[b, i[2]], g[i[1], c], g[i[2], d], v[i[1], i[2]]];pic[diag[x__]] := Graph[{x} /. {g[i_, j_] :> DirectedEdge[j, i], v[j_, k_] :> UndirectedEdge[j, k]}, VertexLabels -> "Name"];i = 5;Block[{i = "i"}, pic[f]] – xzczd Oct 30 '20 at 12:21

1 Answers1

5

Your question boils down to: "How can I prevent my special symbols to conflict with other global symbols?"

Solution 1: Block

With Block you can localize your symbols, protecting them from any global assignments. This is the recommended solution. (Note that Block is also used in the definition of pic.)

Clear[pic]
pic[diag[x__]] := 
 Block[{g, v}, 
  Graph[List[x] /. {g[i_, j_] :> DirectedEdge[j, i], 
     v[j_, k_] :> UndirectedEdge[j, k]}, VertexLabels -> "Name", 
   PlotTheme -> "Business", ImageSize -> 300, ImagePadding -> 10, 
   VertexLabelStyle -> Directive[Black, 11], 
   GraphHighlight -> 
    Cases[List[x], v[i_, j_] :> UndirectedEdge[i, j]]]]
Block[{i, g, v, a, b, c, d},
 f = diag[g[a, i[1]], g[b, i[2]], g[i[1], c], g[i[2], d], 
   v[i[1], i[2]]];
 pic[f]
 ]

Solution 2: Protect

This is a quick and easy solution to your problem. Protecting a symbol means that no one can assign to it. This should probably appear at the top of your notebook.

Clear[i, g, v, a, b, c, d];
Protect[i, g, v, a, b, c, d];
f = diag[g[a, i[1]], g[b, i[2]], g[i[1], c], g[i[2], d], v[i[1], i[2]]];

Solution 3: Formal symbols

This is probably a more cumbersome solution, since special symbols are hard to type in Mathematica. However, Mathematica features Formal variants of many symbols which cannot be assigned to.

Clear[pic]
pic[diag[x__]] := 
 Graph[List[
    x] /. {\[FormalG][i_, j_] :> 
     DirectedEdge[j, i], \[FormalV][j_, k_] :> UndirectedEdge[j, k]}, 
  VertexLabels -> "Name", PlotTheme -> "Business", ImageSize -> 300, 
  ImagePadding -> 10, VertexLabelStyle -> Directive[Black, 11], 
  GraphHighlight -> 
   Cases[List[x], \[FormalV][i_, j_] :> UndirectedEdge[i, j]]]
f = diag[\[FormalG][\[FormalA], \[FormalI][
     1]], \[FormalG][\[FormalB], \[FormalI][
     2]], \[FormalG][\[FormalI][
     1], \[FormalC]], \[FormalG][\[FormalI][
     2], \[FormalD]], \[FormalV][\[FormalI][1], \[FormalI][2]]];
pic[f]

(Another possible issue is that now the labels of the graph are the formal symbols.)

Natas
  • 2,310
  • 4
  • 14