Source code for pyomo.contrib.incidence_analysis.matching

#  ___________________________________________________________________________
#
#  Pyomo: Python Optimization Modeling Objects
#  Copyright (c) 2008-2024
#  National Technology and Engineering Solutions of Sandia, LLC
#  Under the terms of Contract DE-NA0003525 with National Technology and
#  Engineering Solutions of Sandia, LLC, the U.S. Government retains certain
#  rights in this software.
#  This software is distributed under the 3-clause BSD License.
#  ___________________________________________________________________________

from pyomo.common.dependencies import networkx as nx


[docs]def maximum_matching(matrix_or_graph, top_nodes=None): """Return a maximum cardinality matching of the provided matrix or bipartite graph If a matrix is provided, the matching is returned as a map from row indices to column indices. If a bipartite graph is provided, a list of "top nodes" must be provided as well. These correspond to one of the "bipartite sets". The matching is then returned as a map from "top nodes" to the other set of nodes. Parameters ---------- matrix_or_graph: SciPy sparse matrix or NetworkX Graph The matrix or graph whose maximum matching will be computed top_nodes: list Integer nodes representing a bipartite set in a graph. Must be provided if and only if a NetworkX Graph is provided. Returns ------- max_matching: dict Dict mapping from integer nodes in the first bipartite set (row indices) to nodes in the second (column indices). """ nxb = nx.algorithms.bipartite nxc = nx.algorithms.components from_biadjacency_matrix = nxb.matrix.from_biadjacency_matrix if isinstance(matrix_or_graph, nx.Graph): graph_provided = True if top_nodes is None: raise RuntimeError("top_nodes argument must be set if a graph is provided.") M = len(top_nodes) N = len(matrix_or_graph.nodes) - M bg = matrix_or_graph if not nxb.is_bipartite(bg): raise RuntimeError("Provided graph is not bipartite.") else: graph_provided = False # Assume something SciPy-sparse compatible was provided. if top_nodes is not None: raise RuntimeError( "top_nodes argument cannot be used if a matrix is provided" ) M, N = matrix_or_graph.shape top_nodes = list(range(M)) bg = from_biadjacency_matrix(matrix_or_graph) # Check assumptions regarding from_biadjacency_matrix function: for i in range(M): # First M nodes in graph correspond to rows assert bg.nodes[i]["bipartite"] == 0 for j in range(M, M + N): # Last N nodes in graph correspond to columns assert bg.nodes[j]["bipartite"] == 1 matching = nxb.maximum_matching(bg, top_nodes=top_nodes) if graph_provided: top_node_set = set(top_nodes) # If a graph was provided, we return a mapping from "top nodes" # to their matched "bottom nodes" max_matching = {n0: n1 for n0, n1 in matching.items() if n0 in top_node_set} else: # If a matrix was provided, we return a mapping from row indices # to column indices. max_matching = {n0: n1 - M for n0, n1 in matching.items() if n0 < M} return max_matching