# Problems With Graph Edge Operators

The situation is even worse than you describe and happens to involve a whole handful of Mathematica quirks.

The edge operators have a unique nonstandard behavior (quirk #1).

a\[DirectedEdge]b\[UndirectedEdge]c
Syntax::tsntxi: "a\[DirectedEdge]b\[UndirectedEdge]c" is incomplete; more input is needed.

The same behavior is observed with any combination of the `*Edge`

operators. *These are the only binary operators in Mathematica that exhibit this behavior.* Inserting parentheses resolves the syntax error:

In[1]:= a\[DirectedEdge](b\[UndirectedEdge]c) //FullForm
Out[1]= DirectedEdge[a,UndirectedEdge[b,c]]
In[2]:= (a\[DirectedEdge]b)\[UndirectedEdge]c //FullForm
Out[2]= UndirectedEdge[DirectedEdge[a,b],c]

...but unfortunately, just as with your `Rule`

s notation, this will not work.

Before we move on to the next brick wall, this is a good place to mention that `TwoWayRule`

's two different notations `<->`

and `\[TwoWayRule]`

*have different operator precedence*. (Quirk #2.)

# Problems With The `Graph`

Function

The reason your use of `Rule`

s notation doesn't work is the same reason parenthesizing `*Edge`

operators doesn't work: `Graph`

isn't smart enough to understand our intended meaning of the parenthesized expression (quirk #3) and instead creates an edge with one vertex having the other edge as its *name*:

That the notation `a\[DirectedEdge]b\[UndirectedEdge]c`

is a syntax error is either a bug or else someone had to work hard to make edges work differently from every other binary operator in Mathematica in order for it to behave in this unintuitive way. Likewise, not being able to use `a->b->c`

(`Rule`

s) with `Graph`

is either an obvious bug or a deliberate but unintuitive design decision. Since they had already allowed `Rule`

to define edges, and since `Rule`

is right associative (parenthesizes to the right), they had to make a choice of whether or not they would allow a vertex to make the inner rule the name of a vertex, as it would be if it were anything else, or another edge, as we would like it to be.

Note the irony that `*Rule`

has a higher precedence than `\[*Edge]`

, and so mixing the operators *specifically introduced to define graph edges* with a `Rule`

results in the *rule* being used as an edge and the *edge* being used as a name. (Quirk #4.)

# A Better `Graph`

Function

Here is a function that uses rewrite rules to transform our preferred notation into the form `Graph`

expects:

SetAttributes[makeGraph, HoldFirst];
makeGraph[edges_,opts: OptionsPattern[]]:=
Module[{edgelist, seq, postorderReplaceRepeated},
postorderReplaceRepeated[expr_,rules_]:=
FixedPoint[Replace[#,rules,{0,-1},Heads->True]&,expr];
edgelist = If[Head[edges]===List, edges, {edges}];
edgelist = postorderReplaceRepeated[edgelist,
{h2_[h1_[x_, y_], h3_[z_, w_]]
/;SubsetQ[{Rule, TwoWayRule}, {h1,h2, h3}]
:>seq[h1[x, y],h2[y, z], h3[z, w]],
h1_[x_, h2_[y_, z_]]
/;SubsetQ[{Rule, TwoWayRule}, {h1,h2}]
:>seq[h1[x, y], h2[y, z]],
h2_[h1_[x_, y_], z_]
/;SubsetQ[{Rule, TwoWayRule}, {h1,h2}]
:>seq[h1[x, y], h2[y, z]],
h1_[x_, seq[h2_[y_, z___], w___]]
/;SubsetQ[{Rule, TwoWayRule}, {h1,h2}]
:>seq[h1[x, y], h2[y, z], w],
h2_[seq[x___, h1_[y___, z_]], w_]
/;SubsetQ[{Rule, TwoWayRule}, {h1,h2}]
:>seq[x, h1[y, z], h2[z, w]]}];
edgelist=edgelist//.seq->Sequence;
Graph[edgelist, opts]
]

You might notice that `makeGraph`

uses an auxiliary function called `postorderReplaceRepeated`

. That's because of another little quirk of Mathematica (quirk #5), described in this SE answer:

ReplaceAll and by extension ReplaceRepeated are very unusual in the context of Mathematica in that they perform a depth-first preorder traversal, whereas nearly all other functions perform a depth-first postorder traversal.

This is the first question I have seen anywhere that involves a "5 Quirk" answer.