7
$\begingroup$

The following doesn't display as you'd expect:

Graph[{Labeled[1 -> 2, "A"], Labeled[1 -> 2, "B"]}, VertexLabels -> "Name"]

The parallel edges are both labeled "A". Is this a bug or is it a design feature? The definition of EdgeLabels suggests that Mathematica expects an edge to be identifiable by its ends(!)

In fact, FullForm shows that it gets converted to

{{1, 2}, {1 \[DirectedEdge] 2, 1 \[DirectedEdge] 2}, {EdgeLabels -> {1 \[DirectedEdge] 2 -> "A"}, VertexLabels -> {"Name"}}}

Ugh!

Anyone have any ideas how to get distinct labels to display on parallel edges?

Specifically, I use a simple data format for my digraphs. For the example above, it would be

{{1,{{"A",2},{"B",2}}}}

though in general there's an entry in the outermost list for each source vertex, and the vertices and labels are a variety of things.

Is there some (relatively easy) way (using EdgeShapeFunction perhaps) of creating a Graph that displays correctly?

$\endgroup$
  • 2
    $\begingroup$ This has to be a bug, yes? Why would it ever make sense to label both edges "A" when you specifically request otherwise? $\endgroup$ – user6014 Jun 28 at 17:26
  • $\begingroup$ Report was submitted to Wolfram Technical Support [CASE:4276060] $\endgroup$ – Bob Hanlon Jun 28 at 20:12
  • 4
    $\begingroup$ Possible duplicate of GraphUnion but with multiple edges? $\endgroup$ – Carl Woll Jun 28 at 20:14
  • 3
    $\begingroup$ @user6014 It's not a bug, it's a limitation of the API. Edges are referred to by their endpoints, therefore parallel edges are not distinguishable. I suggest you (and also David Bevan) contact Wolfram and ask for this to be addressed. I suspect I'm the only one who keeps complaining about this again and again and they assume no one else really needs/wants this to work ... $\endgroup$ – Szabolcs Jun 28 at 20:43
  • 1
    $\begingroup$ @Szabolcs that's incredibly frustrating. I would imagine this "limitation" pretty quickly gets in the way of many real world use cases for Graph. $\endgroup$ – user6014 Jun 28 at 22:13
8
$\begingroup$

Update: An alternative method that that takes lists of vertices, edges, edge labels and edge styles.

Lexicographic sorting of the edges, labels and styles based on the vertex list seem to match the order in which edges are processed for rendering.

ClearAll[multiGraph2]
multiGraph2[vl_, elist_, elabels_, estyles_, o : OptionsPattern[Graph]] := 
 Module[{esf, edges, labels, styles, 
   sorted = Transpose@ SortBy[Transpose[{elist, elabels,  estyles}], 
     {PositionIndex[vl]@#[[1, 1]] &, PositionIndex[vl]@#[[1, 2]] &}]},
  {edges, labels, styles} = {sorted[[1]], ## & @@ (RotateRight /@ sorted[[2 ;;]])};
  esf = {First[styles = RotateLeft[styles]], 
     GraphElementData["Arrow"][##] /. Arrowheads[ah_] :>
        Arrowheads[Append[ah, {.05, .5, Graphics[
          Text[Framed[First[labels = RotateLeft[labels]], 
             FrameStyle -> None, Background -> White]]]}]]} &;
  Graph[vl, edges, EdgeShapeFunction -> esf, o]]

Example:

SeedRandom[12345]
edges = RandomSample@ EdgeList[RandomGraph[{7, 10}, 
     DirectedEdges -> True]][[{1, 1, 2, 2,  2, 3, 4, 5, 6, 7, 8, 9, 10}]];
edges = Flatten@Gather[edges];
styles = ColorData[97] /@ Range[Length@edges];
labels = Flatten[MapIndexed[Row[{#, CharacterRange["A", "Z"][[#2[[1]]]]}, "-"] &, #] & /@ 
    Gather[edges]];

multiGraph2[RandomSample[Range[7]], edges, labels, styles, 
 VertexSize -> Medium, VertexLabels -> Placed["Name", Center], ImageSize -> Large]

enter image description here

Original answer:

A function that produces a multi-graph with distinct labels and styles for multi-edges given an input list of the form

{{v1, {{v11, label11, style11}, {v12, label12, style12}, ...}, ...}

The list is processed into an edge list and Associations for edge labels and styles that are used to construct a custom EdgeShapeFunction:

ClearAll[multiGraph]
multiGraph[a_, o : OptionsPattern[Graph]] := Module[{esf, 
  edges = Flatten[Thread[DirectedEdge[#[[1]], #[[2, All, 1]]]] & /@ a], 
  edgelabels = GroupBy[#, First -> Last, Flatten] &@
     Flatten[Thread[Thread[DirectedEdge[#[[1]], #[[2, All, 1]]]] -> #[[2, All, 2]]]& /@ a],
  edgestyles = GroupBy[#, First -> Last, Flatten] &@
     Flatten[Thread[Thread[DirectedEdge[#[[1]], #[[2, All, 1]]]] -> #[[2, All, 3]]]& /@ a]},
  esf = {Dashing[{}], First[edgestyles[#2] = RotateRight[edgestyles[#2] ]],
   GraphElementData["Arrow"][##] /. Arrowheads[ah_] :> Arrowheads[Append[ah, {.05, .5, 
     Graphics[Text[Framed[First[edgelabels[#2] = RotateRight[edgelabels[#2] ]], 
      FrameStyle -> None, Background -> White]]]}]]} &;
Graph[edges, EdgeShapeFunction -> esf, o]]

Examples:

data = {{1, {{2, "A", Red}, {2, "B", Blue}, {3, "C", Green}, 
        {3, "D", Directive[Thick, Orange]}}},
     {2, {{3, "E", Directive[Dashed, Thick, Purple]}, {1, "F", Gray}}}};

data2 = data /. s_String :> Style[RandomWord["Noun"], 16, Black];
multiGraph[data2, VertexSize -> Small, 
 VertexLabels -> Placed["Name", Center], ImageSize -> Large, VertexLabelStyle -> Large]

enter image description here

SeedRandom[7777]
randomdata = {#, Table[{RandomChoice[Range@4], Style[RandomWord["Noun"], 14], 
       Opacity[1, RandomColor[]]}, RandomInteger[{2, 4}]]} & /@ Range[4];
multiGraph[randomdata, VertexSize -> Small,  VertexLabels -> Placed["Name", Center],
 ImageSize -> Large, VertexLabelStyle -> Medium]

enter image description here

See also:

$\endgroup$
  • $\begingroup$ Thanks for having wrapped this up into a reusable function! $\endgroup$ – Szabolcs Jul 1 at 8:21
  • $\begingroup$ @Szabolcs, after using Print[#2] in edge shape function with many random graphs it seems that edges are processed in an order that depends on the ordering of the vertex list. I posted an alternative approach that uses this observation. $\endgroup$ – kglr Jul 1 at 14:06
1
$\begingroup$

THIS IS AN EXTENDED COMMENT RATHER THAN AN ANSWER.

$Version

(* "12.0.0 for Mac OS X x86 (64-bit) (April 7, 2019)" *)

Using Labeled the first label instance is used for both edges.

Graph[
 {Labeled[1 \[DirectedEdge] 2, "A"], Labeled[1 \[DirectedEdge] 2, "B"]},
 VertexLabels -> "Name"]

enter image description here

However, using VertexLabels the second label instance is used for both edges.

Graph[
 {1 -> 2, 1 -> 2},
 VertexLabels -> "Name",
 EdgeLabels -> {1 \[DirectedEdge] 2 -> "A", 1 \[DirectedEdge] 2 -> "B"}]

enter image description here

Either approach results in a wrong label and both are bugs.

EDIT: Report was submitted to Wolfram Technical Support [CASE:4276060]

$\endgroup$
  • 1
    $\begingroup$ Not a bug, but a (severe) limitation of the API. Which edge does 1->2 refer to? There's no way to tell. This can only be fixed by changing the API so that edges become distinguishable (e.g. they're referred to by their index, or an additional key, rather than just their end vertices). Given the slow progress on Graph recently, and the major overhaul that would be needed to fix this, I do not have much hope ... But I also think that this is the single most frustrating limitation of Graph .... $\endgroup$ – Szabolcs Jun 28 at 20:48
  • $\begingroup$ Styling is the smallest issue, more generally it's not possible to use properties with multigraphs. mathematica.stackexchange.com/q/92014/12 $\endgroup$ – Szabolcs Jun 28 at 20:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.