Skip to content
This repository has been archived by the owner on Jan 30, 2023. It is now read-only.

Commit

Permalink
Modified allow_multiple_edges, and removed simplify
Browse files Browse the repository at this point in the history
  • Loading branch information
Michele Borassi committed Jul 16, 2015
1 parent 268662f commit c0a1803
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 61 deletions.
90 changes: 75 additions & 15 deletions src/sage/graphs/generic_graph.py
Expand Up @@ -2494,33 +2494,61 @@ def allows_multiple_edges(self):
"""
return self._backend.multiple_edges(None)

def allow_multiple_edges(self, new, check=True):
def allow_multiple_edges(self, new, check=True, keep_label='any'):
"""
Changes whether multiple edges are permitted in the (di)graph.

INPUT:

- ``new`` - boolean.
- ``new`` (boolean): if ``True``, the new graph will allow multiple
edges.

EXAMPLES::
- ``check`` (boolean): if ``True`` and ``new`` is ``False``, we remove
all multiple edges from the graph.

- ``keep_label`` (``'any','min','max'``): used only if ``new`` is
``False`` and ``check`` is ``True``. If there are multiple edges with
different labels, this variable defines which label should be kept:
any label (``'any'``), the smallest label (``'min'``), or the largest
(``'max'``).

EXAMPLES:

The standard behavior with undirected graphs::

sage: G = Graph(multiedges=True,sparse=True); G
Multi-graph on 0 vertices
sage: G.has_multiple_edges()
False
sage: G.allows_multiple_edges()
True
sage: G.add_edges([(0,1)]*3)
sage: G.add_edges([(0,1,1), (0,1,2), (0,1,3)])
sage: G.has_multiple_edges()
True
sage: G.multiple_edges()
[(0, 1, None), (0, 1, None), (0, 1, None)]
[(0, 1, 1), (0, 1, 2), (0, 1, 3)]
sage: G.allow_multiple_edges(False); G
Graph on 2 vertices
sage: G.has_multiple_edges()
False
sage: G.edges()
[(0, 1, None)]
[(0, 1, 1)]

If we ask for the minimum label::

sage: G = Graph([(0, 1, 1), (0, 1, 2), (0, 1, 3)], multiedges=True,sparse=True)
sage: G.allow_multiple_edges(False, keep_label='min')
sage: G.edges()
[(0, 1, 1)]

If we ask for the maximum label::

sage: G = Graph([(0, 1, 1), (0, 1, 2), (0, 1, 3)], multiedges=True,sparse=True)
sage: G.allow_multiple_edges(False, keep_label='max')
sage: G.edges()
[(0, 1, 3)]

The standard behavior with digraphs::

sage: D = DiGraph(multiedges=True,sparse=True); D
Multi-digraph on 0 vertices
Expand All @@ -2540,15 +2568,28 @@ def allow_multiple_edges(self, new, check=True):
sage: D.edges()
[(0, 1, None)]
"""
seen = set()
seen = dict()

if not keep_label in ['any','min','max']:
raise ValueError("Variable keep_label must be 'any', 'min', or 'max'")

# TODO: this should be much faster for c_graphs, but for now we just do this
if self.allows_multiple_edges() and new is False and check:
for u,v,l in self.multiple_edges():
if (u,v) in seen:
self.delete_edge(u,v,l)
if not (u,v) in seen:
# This is the first time we see this edge
seen[(u,v)] = l
else:
seen.add((u,v))
# This edge has already been seen: we have to remove
# something from the graph.
oldl = seen[(u,v)]
if ((keep_label=='min' and l<oldl) or (keep_label=='max' and l>oldl)):
# Keep the new edge, delete the old one
self.delete_edge((u,v,oldl))
seen[(u,v)] = l
else:
# Delete the new edge
self.delete_edge((u,v,l))

self._backend.multiple_edges(new)

Expand Down Expand Up @@ -14614,10 +14655,22 @@ def complement(self):
return G.copy(immutable=True)
return G

def to_simple(self):
def to_simple(self, to_undirected=True, keep_label='any'):
"""
Returns a simple version of itself (i.e., undirected and loops and
multiple edges are removed).
Returns a simple version of itself.

In particular, loops and multiple edges are removed, and the graph might
optionally be converted to an undirected graph.

INPUT:

- ``to_undirected`` - boolean - if ``True``, the graph is also converted
to an undirected graph.

- ``keep_label`` (``'any','min','max'``): if there are multiple edges
with different labels, this variable defines which label should be
kept: any label (``'any'``), the smallest label (``'min'``), or the
largest (``'max'``).

EXAMPLES::

Expand All @@ -14634,10 +14687,17 @@ def to_simple(self):
False
sage: H.allows_multiple_edges()
False
sage: G.to_simple(to_undirected=False, keep_label='min').edges()
[(2, 3, 1), (3, 2, None)]
sage: G.to_simple(to_undirected=False, keep_label='max').edges()
[(2, 3, 2), (3, 2, None)]
"""
g=self.to_undirected()
if to_undirected:
g=self.to_undirected()
else:
g=copy(self)
g.allow_loops(False)
g.allow_multiple_edges(False)
g.allow_multiple_edges(False, keep_label=keep_label)
return g

def disjoint_union(self, other, verbose_relabel=None, labels="pairs"):
Expand Down
48 changes: 2 additions & 46 deletions src/sage/graphs/spanning_tree.pyx
Expand Up @@ -73,51 +73,6 @@ Methods

include "sage/ext/interrupt.pxi"

cpdef simplify(g):
r"""
Removes all self-loops and multiple edges from the input graph ``g``.
The graph ``g`` is not copied for performance reasons. If ``g`` is labeled
and edge ``(u,v)`` has many labels, only the minimum label will be kept.
EXAMPLE::
sage: from sage.graphs.spanning_tree import simplify
sage: g = Graph([[1,1,1],[1,2,2],[1,2,4],[2,3,3],[2,3,1]], multiedges=True, loops=True)
sage: simplify(g)
sage: g.edges()
[(1, 2, 2), (2, 3, 1)]
"""
g.allow_loops(False)
# If there are multiple edges from u to v, retain the edge of
# minimum weight among all such edges.
if g.allows_multiple_edges():
# If there are multiple edges from u to v, retain only the
# start and end vertices of such edges. Let a and b be the
# start and end vertices, respectively, of a weighted edge
# (a, b, w) having weight w. Then there are multiple weighted
# edges from a to b if and only if the set uniqE has the
# tuple (a, b) as an element.
uniqE = set()
for u, v, _ in iter(g.multiple_edges(to_undirected=True)):
uniqE.add((u, v))
# Let (u, v) be an element in uniqE. Then there are multiple
# weighted edges from u to v. Let W be a list of all edge
# weights of multiple edges from u to v, sorted in
# nondecreasing order. If w is the first element in W, then
# (u, v, w) is an edge of minimum weight (there may be
# several edges of minimum weight) among all weighted edges
# from u to v. If i >= 2 is the i-th element in W, delete the
# multiple weighted edge (u, v, i).
for u, v in uniqE:
W = sorted(g.edge_label(u, v))
for w in W[1:]:
g.delete_edge(u, v, w)
# all multiple edges should now be removed; check this!
assert g.multiple_edges() == []
g.allow_multiple_edges(False)


cpdef kruskal(G, wfunction=None, bint check=False):
r"""
Minimum spanning tree using Kruskal's algorithm.
Expand Down Expand Up @@ -324,7 +279,8 @@ cpdef kruskal(G, wfunction=None, bint check=False):
# G is a tree
return G.edges()
g = G.copy()
simplify(g)
g.allow_loops(False)
g.allow_multiple_edges(False, keep_label='min')


# G is assumed to be connected, undirected, and with at least a vertex
Expand Down

0 comments on commit c0a1803

Please sign in to comment.