123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- """ Functions related to graph covers."""
- from functools import partial
- from itertools import chain
- import networkx as nx
- from networkx.utils import arbitrary_element, not_implemented_for
- __all__ = ["min_edge_cover", "is_edge_cover"]
- @not_implemented_for("directed")
- @not_implemented_for("multigraph")
- def min_edge_cover(G, matching_algorithm=None):
- """Returns the min cardinality edge cover of the graph as a set of edges.
- A smallest edge cover can be found in polynomial time by finding
- a maximum matching and extending it greedily so that all nodes
- are covered. This function follows that process. A maximum matching
- algorithm can be specified for the first step of the algorithm.
- The resulting set may return a set with one 2-tuple for each edge,
- (the usual case) or with both 2-tuples `(u, v)` and `(v, u)` for
- each edge. The latter is only done when a bipartite matching algorithm
- is specified as `matching_algorithm`.
- Parameters
- ----------
- G : NetworkX graph
- An undirected graph.
- matching_algorithm : function
- A function that returns a maximum cardinality matching for `G`.
- The function must take one input, the graph `G`, and return
- either a set of edges (with only one direction for the pair of nodes)
- or a dictionary mapping each node to its mate. If not specified,
- :func:`~networkx.algorithms.matching.max_weight_matching` is used.
- Common bipartite matching functions include
- :func:`~networkx.algorithms.bipartite.matching.hopcroft_karp_matching`
- or
- :func:`~networkx.algorithms.bipartite.matching.eppstein_matching`.
- Returns
- -------
- min_cover : set
- A set of the edges in a minimum edge cover in the form of tuples.
- It contains only one of the equivalent 2-tuples `(u, v)` and `(v, u)`
- for each edge. If a bipartite method is used to compute the matching,
- the returned set contains both the 2-tuples `(u, v)` and `(v, u)`
- for each edge of a minimum edge cover.
- Examples
- --------
- >>> G = nx.Graph([(0, 1), (0, 2), (0, 3), (1, 2), (1, 3)])
- >>> sorted(nx.min_edge_cover(G))
- [(2, 1), (3, 0)]
- Notes
- -----
- An edge cover of a graph is a set of edges such that every node of
- the graph is incident to at least one edge of the set.
- The minimum edge cover is an edge covering of smallest cardinality.
- Due to its implementation, the worst-case running time of this algorithm
- is bounded by the worst-case running time of the function
- ``matching_algorithm``.
- Minimum edge cover for `G` can also be found using the `min_edge_covering`
- function in :mod:`networkx.algorithms.bipartite.covering` which is
- simply this function with a default matching algorithm of
- :func:`~networkx.algorithms.bipartite.matching.hopcraft_karp_matching`
- """
- if len(G) == 0:
- return set()
- if nx.number_of_isolates(G) > 0:
- # ``min_cover`` does not exist as there is an isolated node
- raise nx.NetworkXException(
- "Graph has a node with no edge incident on it, " "so no edge cover exists."
- )
- if matching_algorithm is None:
- matching_algorithm = partial(nx.max_weight_matching, maxcardinality=True)
- maximum_matching = matching_algorithm(G)
- # ``min_cover`` is superset of ``maximum_matching``
- try:
- # bipartite matching algs return dict so convert if needed
- min_cover = set(maximum_matching.items())
- bipartite_cover = True
- except AttributeError:
- min_cover = maximum_matching
- bipartite_cover = False
- # iterate for uncovered nodes
- uncovered_nodes = set(G) - {v for u, v in min_cover} - {u for u, v in min_cover}
- for v in uncovered_nodes:
- # Since `v` is uncovered, each edge incident to `v` will join it
- # with a covered node (otherwise, if there were an edge joining
- # uncovered nodes `u` and `v`, the maximum matching algorithm
- # would have found it), so we can choose an arbitrary edge
- # incident to `v`. (This applies only in a simple graph, not a
- # multigraph.)
- u = arbitrary_element(G[v])
- min_cover.add((u, v))
- if bipartite_cover:
- min_cover.add((v, u))
- return min_cover
- @not_implemented_for("directed")
- def is_edge_cover(G, cover):
- """Decides whether a set of edges is a valid edge cover of the graph.
- Given a set of edges, whether it is an edge covering can
- be decided if we just check whether all nodes of the graph
- has an edge from the set, incident on it.
- Parameters
- ----------
- G : NetworkX graph
- An undirected bipartite graph.
- cover : set
- Set of edges to be checked.
- Returns
- -------
- bool
- Whether the set of edges is a valid edge cover of the graph.
- Examples
- --------
- >>> G = nx.Graph([(0, 1), (0, 2), (0, 3), (1, 2), (1, 3)])
- >>> cover = {(2, 1), (3, 0)}
- >>> nx.is_edge_cover(G, cover)
- True
- Notes
- -----
- An edge cover of a graph is a set of edges such that every node of
- the graph is incident to at least one edge of the set.
- """
- return set(G) <= set(chain.from_iterable(cover))
|