I have a directed graph, which for some pairs of vertices, has a directed edge between them in each direction. I would like to replace such pairs of directed edges between the same vertices but in opposite directions, by a single undirected edge. What's a simple way to do this?
4 Answers
A brute-force method using Gather
.
Starting graph:
SeedRandom[0]
edges = Rule @@@ RandomInteger[{1, 5}, {12, 2}]
Graph[edges]
Processing and new graph (this will work with both Rule
and DirectedEdge
:
new =
Gather[edges, #[[1]] == #2[[2]] && #[[2]] == #2[[1]] &] /.
{{_[a_, b_], __} :> a <-> b, {x_} :> x};
Graph[new]
Update
Seeking a more efficient implementation, if:
All edges in the original graph are either
Rule
orUndirectedEdge
at the outsetYou do not mind losing multiple (directed) edges between vertices
I believe we can use the much more efficient GatherBy
as follows:
new2 =
Union /@ GatherBy[edges, Sort] /.
{{_[a_, b_], __} :> a <-> b, {x_} :> x};
Graph[new2]
-
3$\begingroup$ One mistake everyone has made in all the answers is that
Graph[EdgeList[g]]
will lose unconnected vertices.Graph[VertexList[g], EdgeList[g]]
will keep them. This is not nitpicking. I've been bitten hard by this mistake. I know that you are constructing the example graph based purely on an edge list, but this is something that's still worth paying attention to ... $\endgroup$– SzabolcsCommented Feb 11, 2015 at 18:28 -
$\begingroup$ Would you clarify how
/. {{_[a_, b_], __} :> a <-> b, {x_} :> x}
works? I don't fully understand_[a_, b_]
but it seems to be an opaque variation of belisarius'Rule[a_,b_]
. $\endgroup$– DavidCCommented Feb 11, 2015 at 18:52 -
$\begingroup$ @Szabolcs Thanks for the caveat. $\endgroup$ Commented Feb 11, 2015 at 18:58
-
1$\begingroup$ @David
_[a_, b_]
is a pattern for an expression with any head and two arguments, therefore it will matchRule
,DirectedEdge
,UndirectedEdge
, etc. This lets me collapse any of the sublists fromGather
into a singleUndirectedEdge
without worrying about type.{x_} :> x
strips theList
from any singlets. $\endgroup$ Commented Feb 11, 2015 at 19:00
An alternative way to use GatherBy
bidirectedToUndirected=Join@@(GatherBy[EdgeList@#,Union] /. {x_, y_} :> {UndirectedEdge @@ x}) &;
Example (from here):
words = DictionaryLookup["wol*"];
edges = Flatten[Map[(Thread[# -> DeleteCases[Nearest[words, #, 3], #]]) &, words]];
opts = {VertexLabels -> "Name", ImagePadding -> 60, ImageSize -> 500};
g = Graph[edges, opts];
Row[{g, Graph[VertexList[g], bidirectedToUndirected@g, opts]}] (* thanks: @Szabolcs *)
-
1$\begingroup$ @Mr.Wizard, right. I missed the mixed graph requirement (which version 9 does not support except through using different
EdgeShapeFunction
properties for different edge types as in the linked Q/A). $\endgroup$– kglrCommented Feb 11, 2015 at 17:47 -
$\begingroup$ Bloody hell, where did that come from? ;-p (I didn't see
GatherBy
until after posting my version.) +1 of course. *sigh* $\endgroup$ Commented Feb 11, 2015 at 19:24 -
$\begingroup$ +1 for using
Union
instead ofSort
andUnion
$\endgroup$– DavidCCommented Feb 11, 2015 at 20:16 -
$\begingroup$ @David Out of curiosity are you comparing that to my answer? I have reason for what I wrote, or at least I think I do. $\endgroup$ Commented Feb 11, 2015 at 20:21
-
$\begingroup$ Mr.Wizard You are indeed perceptive. Yes, I was. $\endgroup$– DavidCCommented Feb 11, 2015 at 22:20
One can do it quite fast with AdjacencyMatrix
graphSum[graphs__, opts___?OptionQ] /; VectorQ[{graphs}, GraphQ] :=
Graph[Union @@ VertexList /@ {graphs}, Join @@ EdgeList /@ {graphs}, opts];
pairwiseMin[a_, b_, dom_: Reals] :=
If[dom === Integers, Quotient, Divide][a + b - Abs[a - b], 2];
mixedGraph[g_Graph] := graphSum @@ Map[AdjacencyGraph[VertexList@g, #] &, {# - #2, #2}] &[#,
pairwiseMin[#, Transpose@#, Integers]] &@AdjacencyMatrix@g
Here graphSum
is analog of GraphSum
which was in old Combinatorica
package. There is built-in GraphComputation`GraphSum
in V10, but it doesn't work for mixed graphs (one can fix it, see the previous revision of this answer).
SeedRandom[0]
edges = Rule @@@ RandomInteger[{1, 5}, {12, 2}];
g = Graph[Range@7, edges]
mixedGraph@g
It also converts directed loops to undirected loops, but they have the same meaning.
Timing for a big graph:
SeedRandom[0]
edges = Rule @@@ RandomInteger[{1, 10000}, {20000, 2}];
g = Graph[edges];
mixedGraph@g; // AbsoluteTiming
(* {0.213162, Null} *)
It is comparable with Mr.Wizards new2
but it takes into account multiple edges.
Also one can do it purely with graph functions
mixedGraph[g_Graph] :=
GraphUnion[GraphDifference@##, UndirectedGraph@GraphIntersection@##] &[g, ReverseGraph@g]
However, it is slow and simplifies multiple edges.
-
$\begingroup$ +1 for use of some interesting functions, but at least in my testing this is several times slower than my naive brute-force method. I don't know if your output should be considered better or worse than mine; I purposely wanted to change the graph as little as possible but that may not be important to the OP. $\endgroup$ Commented Feb 11, 2015 at 19:08
-
$\begingroup$ @Mr.Wizard It is not fine, I change the wording a bit. Also I add another approach with graph functions which works fine with multigraphs $\endgroup$ Commented Feb 11, 2015 at 19:40
Another not-efficient alternative:
edges //. {a___, Rule[x_, y_], b___, Rule[y_, x_], c___} :> {a, b, c, UndirectedEdge[x, y]}
-
$\begingroup$ I'm afraid that's going to be significantly worse than
Gather
, based on earlier experimentation with multiple___
patterns. I'll be happy to be proven wrong but until then I'm holding my vote as an edge list may be quite long. $\endgroup$ Commented Feb 11, 2015 at 19:02 -
$\begingroup$ Seems I was wrong. The speed of this function depends greatly on the nature of the edge list. With little duplication it can be slightly faster than mine, but with more duplication it can be an order of magnitude slower. +1 for a pretty implementation. $\endgroup$ Commented Feb 11, 2015 at 19:13
-
$\begingroup$ @Mr.Wizard As almost always, a repeated replacement rule can't beat a clever implementation in the general case. Thanks for your comments/experimentation. $\endgroup$ Commented Feb 11, 2015 at 19:15
EdgeShapeFunction
so it is more formatting that actual replacement. I guess not a duplicate after all? $\endgroup$