org.jgrapht.alg.MaximumWeightBipartiteMatching Maven / Gradle / Ivy
/* ==========================================
* JGraphT : a free Java graph-theory library
* ==========================================
*
* Project Info: http://jgrapht.sourceforge.net/
* Project Creator: Barak Naveh (http://sourceforge.net/users/barak_naveh)
*
* (C) Copyright 2003-2012, by Barak Naveh and Contributors.
*
* This program and the accompanying materials are dual-licensed under
* either
*
* (a) the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation, or (at your option) any
* later version.
*
* or (per the licensee's choosing)
*
* (b) the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation.
*/
/* -------------------------
* MaximumWeightBipartiteMatching.java
* -------------------------
* (C) Copyright 2015, by Graeme Ahokas and Contributors.
*
* Original Author: Graeme Ahokas
* Contributor(s):
*
* Changes
* -------
* 30-Sep-2015 : Initial revision (GA);
*
*/
package org.jgrapht.alg;
import java.util.*;
import org.jgrapht.*;
import org.jgrapht.alg.interfaces.*;
/**
* This class finds a maximum weight matching of a simple undirected weighted
* bipartite graph. The algorithm runs in O(V|E|^2). The algorithm is described
* in The LEDA Platform of Combinatorial and Geometric Computing, Cambridge
* University Press, 1999. https://people.mpi-inf.mpg.de/~mehlhorn/LEDAbook.html
* Note: the input graph must be bipartite with positive integer edge weights
*
* @author Graeme Ahokas
*/
public class MaximumWeightBipartiteMatching
implements WeightedMatchingAlgorithm
{
private final WeightedGraph graph;
private final Set partition1;
private final Set partition2;
private Map vertexWeights;
private Map hasVertexBeenProcessed;
private Map isEdgeMatched;
private Set bipartiteMatching;
/**
* Creates a new MaximumWeightBipartiteMatching algorithm instance. The
* union of vertexPartition1 and vertexParition2 should be equal to the
* vertex set of the graph Every edge in the graph must connect a vertex in
* vertexPartition1 with a vertex in vertexPartition2
*
* @param graph simple undirected weighted bipartite graph to find matching
* in, with positive integer edge weights
* @param vertexPartition1 first vertex partition of the bipartite graph,
* disjoint from vertexPartition2
* @param vertexPartition2 second vertex partition of the bipartite graph,
* disjoint from vertexPartition1
*/
public MaximumWeightBipartiteMatching(
final WeightedGraph graph,
Set vertexPartition1,
Set vertexPartition2)
{
this.graph = graph;
partition1 = vertexPartition1;
partition2 = vertexPartition2;
vertexWeights = new HashMap();
hasVertexBeenProcessed = new HashMap();
isEdgeMatched = new HashMap();
initializeVerticesAndEdges();
}
@Override public Set getMatching()
{
if (bipartiteMatching == null) {
bipartiteMatching = maximumWeightBipartiteMatching();
}
return bipartiteMatching;
}
@Override public double getMatchingWeight()
{
if (bipartiteMatching == null) {
getMatching();
}
long weight = 0;
for (E edge : bipartiteMatching) {
weight += graph.getEdgeWeight(edge);
}
return weight;
}
private void initializeVerticesAndEdges()
{
for (V vertex : graph.vertexSet()) {
if (isTargetVertex(vertex)) {
hasVertexBeenProcessed.put(vertex, true);
setVertexWeight(vertex, (long) 0);
} else {
hasVertexBeenProcessed.put(vertex, false);
setVertexWeight(
vertex,
(long) maximumWeightOfEdgeIncidentToVertex(vertex));
}
}
for (E edge : graph.edgeSet()) {
isEdgeMatched.put(edge, false);
}
}
private long maximumWeightOfEdgeIncidentToVertex(V vertex)
{
long maxWeight = 0;
for (E edge : graph.edgesOf(vertex)) {
if (graph.getEdgeWeight(edge) > maxWeight) {
maxWeight = (long) graph.getEdgeWeight(edge);
}
}
return maxWeight;
}
private boolean isSourceVertex(V vertex)
{
return partition1.contains(vertex);
}
private boolean isTargetVertex(V vertex)
{
return partition2.contains(vertex);
}
private long vertexWeight(V vertex)
{
return vertexWeights.get(vertex);
}
private void setVertexWeight(V vertex, Long weight)
{
vertexWeights.put(vertex, weight);
}
private long reducedWeight(E edge)
{
return (long) (vertexWeight(graph.getEdgeSource(edge))
+ vertexWeight(graph.getEdgeTarget(edge))
- graph.getEdgeWeight(edge));
}
private boolean isVertexMatched(V vertex, Set matchings)
{
for (E edge : matchings) {
if (graph.getEdgeSource(edge).equals(vertex)
|| graph.getEdgeTarget(edge).equals(vertex))
{
return true;
}
}
return false;
}
private void addPathToMatchings(List path, Set matchings)
{
for (int i = 0; i < path.size(); i++) {
E edge = path.get(i);
if ((i % 2) == 0) {
isEdgeMatched.put(edge, true);
matchings.add(edge);
} else {
isEdgeMatched.put(edge, false);
matchings.remove(edge);
}
}
}
private void adjustVertexWeights(Map> reachableVertices)
{
long alpha = Long.MAX_VALUE;
for (V vertex : reachableVertices.keySet()) {
if (isSourceVertex(vertex) && (vertexWeights.get(vertex) < alpha)) {
alpha = vertexWeights.get(vertex);
}
}
long beta = Long.MAX_VALUE;
for (V vertex : reachableVertices.keySet()) {
if (isTargetVertex(vertex)) {
continue;
}
for (E edge : graph.edgesOf(vertex)) {
if (hasVertexBeenProcessed.get(
Graphs.getOppositeVertex(graph, edge, vertex))
&& !reachableVertices.keySet().contains(
Graphs.getOppositeVertex(graph, edge, vertex))
&& (reducedWeight(edge) < beta))
{
beta = reducedWeight(edge);
}
}
}
assert ((alpha > 0) && (beta > 0));
long minValue = Math.min(alpha, beta);
for (V vertex : reachableVertices.keySet()) {
if (isSourceVertex(vertex)) {
vertexWeights.put(vertex, vertexWeights.get(vertex) - minValue);
} else {
vertexWeights.put(vertex, vertexWeights.get(vertex) + minValue);
}
}
}
private Map> verticesReachableByTightAlternatingEdgesFromVertex(
V vertex)
{
Map> pathsToVertices = new HashMap>();
pathsToVertices.put(vertex, new ArrayList());
findPathsToVerticesFromVertices(
Arrays.asList(vertex),
false,
pathsToVertices);
return pathsToVertices;
}
private void findPathsToVerticesFromVertices(
List verticesToProcess,
boolean needMatchedEdge,
Map> pathsToVertices)
{
if (verticesToProcess.size() == 0) {
return;
}
List nextVerticesToProcess = new ArrayList();
for (V vertex : verticesToProcess) {
for (E edge : graph.edgesOf(vertex)) {
V adjacentVertex =
Graphs.getOppositeVertex(graph, edge, vertex);
if (hasVertexBeenProcessed.get(adjacentVertex)
&& (reducedWeight(edge) == 0)
&& !pathsToVertices.keySet().contains(adjacentVertex))
{
if ((needMatchedEdge && isEdgeMatched.get(edge))
|| (!needMatchedEdge && !isEdgeMatched.get(edge)))
{
nextVerticesToProcess.add(adjacentVertex);
List pathToAdjacentVertex =
new ArrayList(pathsToVertices.get(vertex));
pathToAdjacentVertex.add(edge);
pathsToVertices.put(
adjacentVertex,
pathToAdjacentVertex);
}
}
}
}
findPathsToVerticesFromVertices(
nextVerticesToProcess,
!needMatchedEdge,
pathsToVertices);
}
private Set maximumWeightBipartiteMatching()
{
Set matchings = new HashSet();
for (V vertex : partition1) {
hasVertexBeenProcessed.put(vertex, true);
while (true) {
Map> reachableVertices =
verticesReachableByTightAlternatingEdgesFromVertex(vertex);
boolean successful = false;
for (V reachableVertex : reachableVertices.keySet()) {
if (isSourceVertex(reachableVertex)
&& (vertexWeight(reachableVertex) == 0))
{
addPathToMatchings(
reachableVertices.get(reachableVertex),
matchings);
successful = true;
break;
}
if (isTargetVertex(reachableVertex)
&& !isVertexMatched(reachableVertex, matchings))
{
addPathToMatchings(
reachableVertices.get(reachableVertex),
matchings);
successful = true;
break;
}
}
if (successful) {
break;
}
adjustVertexWeights(reachableVertices);
}
}
return matchings;
}
}
// End MaximumWeightBipartiteMatching.java
© 2015 - 2025 Weber Informatics LLC | Privacy Policy