17

I would like to plot an expression (like TreeForm does), but using the new TreeGraph functionality.

TreeGraph takes as input a set of edges (of the form a -> b), so it seems that to solve the problem it is necessary to turn an expression into a set of edges. For example Sin[Plus[a, b]] would need to be transformed into: {Sin -> Plus, Plus -> a, Plus -> b}.

How can this transformation be done for any Mathematica expression? Thanks!

István Zachar
  • 47,032
  • 20
  • 143
  • 291
scaramouche
  • 507
  • 2
  • 7

2 Answers2

8

Method starts off by replacing all heads in the expression with the appropriate node index, then deduces the edge list by parsing the substituted node-index-expression. Note that input expression must be wrapped in Hold (or HoldComplete) to prevent evaluation of e.g. Plus[1, 2] into 3.

(* sow node -> label replacements *)
sowLabel[x_?AtomQ] := (Sow[# -> x]; #) &[c++];
sowLabel[x_] := x;

(* sow parent -> child replacements *)
sowEdge[head_[arg__]] := (Sow[head -> #] & /@ {arg}; head);
sowEdge[x_?AtomQ] := x;

(* the expression to be parsed into a tree *)
expr = Hold[Log[Sin[1 + 2], ToExpression["1" <> "0"]]];
FullForm@expr
Hold[Log[Sin[Plus[1, 2]], ToExpression[StringJoin["1", "0"]]]]
(* replace heads with node index & collect index -> label list *)
c = 1;
{new, labels} = Reap@ReleaseHold@Map[sowLabel, expr, {2, \[Infinity]}, Heads -> True]
{
  1[2[3[4, 5]], 6[7[8, 9]]],
  {{1 -> Log, 2 -> Sin, 3 -> Plus, 4 -> 1, 5 -> 2, 6 -> ToExpression, 7 -> StringJoin, 8 -> "1", 9 -> "0"}}
}
(* create graph edges from substituted expression *)
edges = Sort@Most@First@Last@Reap@Map[sowEdge, {new}, {0, \[Infinity]}]
{1 -> 2, 1 -> 6, 2 -> 3, 3 -> 4, 3 -> 5, 6 -> 7, 7 -> 8, 7 -> 9}
(* plot results *)
{
 TreeGraph[edges,
  VertexLabels -> First@labels, ImagePadding -> {{1, 35}, {0, 10}}
  ],
 LayeredGraphPlot[edges,
  VertexLabeling -> True,
  VertexRenderingFunction :> (Inset[
      Framed[InputForm[#2 /. First@labels], Background -> Hue[.15, .3, 1]], #1] &)
  ],
 TreeForm[expr, ImageSize -> 230]
 }

Mathematica graphics

Hold[Log[Sin[Plus[1, 2]], ToExpression[StringJoin["1", "0"]]]]

Note, that TreeForm keeps the Hold wrapper.

István Zachar
  • 47,032
  • 20
  • 143
  • 291
  • Many thanks István. You solution using TreeGraph is almost what I need. But it has a problem: TreeGraph chooses by itself which node should be the root. So, for example, if you try the expression Sin[Plus[a,b]], it will place Plus at the top (when it should be Sin). Is there a way to fix the root of the tree? – scaramouche Oct 03 '12 at 21:32
  • 2
    @scaramouche: Yes, I am aware of it, that's why I suggested using LayeredGraphPlot instead. If you insist on TreeGraph, you can extract VertexCoordinates from the LayeredGraphPlot and lay out the TreeGraph using them. Further discussion here. – István Zachar Oct 03 '12 at 22:31
2

You can also use GraphComputation`ExpressionGraph and post-process the EdgeList of the graph produced by GraphComputation`ExpressionGraph

edgeList = EdgeList @ # /. PropertyValue[#, VertexLabels]&;

Examples:

eg1 = GraphComputation`ExpressionGraph[Sin[Plus[a, b, Times[c,d]]]];

edgeList @ eg1 

{Sin -> Plus, Plus -> TagBox["a",HoldForm], Plus -> TagBox["b", HoldForm], Plus-> Times, Times-> TagBox["c", HoldForm], Times-> TagBox["d", HoldForm]}

% /. TagBox -> (# &)

{Sin -> Plus, Plus -> "a", Plus -> "b", Plus -> Times, Times -> "c", Times -> "d"}

Istvan's example:

eg2 = GraphComputation`ExpressionGraph[Hold[Log[Sin[1 + 2], ToExpression["1"<>"0"]]]];

edgeList @ eg2 

{Hold->Log, Log -> Sin, Sin -> Plus, Plus -> TagBox[1, HoldForm], Plus -> TagBox[2, HoldForm], Log -> ToExpression, ToExpression -> StringJoin, StringJoin -> TagBox["1", HoldForm], StringJoin -> TagBox["0", HoldForm]}

% /. TagBox -> (# &)

{Hold -> Log, Log- > Sin, Sin -> Plus, Plus -> 1, Plus -> 2, Log -> ToExpression, ToExpression -> StringJoin, StringJoin -> "1", StringJoin -> "0"}

With a helper function to put the vertex labels in the centers:

reStyle = SetProperty[#, {GraphStyle -> "VintageDiagram", VertexLabels -> 
 (PropertyValue[# , VertexLabels]/. HoldPattern[a_-> b_]:>Rule[a, Placed[b, Center]])}]&;

reStyle @ eg1

enter image description here

reStyle @ eg2

enter image description here

kglr
  • 394,356
  • 18
  • 477
  • 896