All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgrapht.alg.MaximumWeightBipartiteMatching Maven / Gradle / Ivy

There is a newer version: 1.5.2
Show newest version
/*
 * (C) Copyright 2015-2017, by Graeme Ahokas and Contributors.
 *
 * JGraphT : a free Java graph-theory library
 *
 * 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.
 */
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
 *
 * @param  the graph vertex type
 * @param  the graph edge type
 *
 * @author Graeme Ahokas
 * @deprecated Use {@link org.jgrapht.alg.matching.MaximumWeightBipartiteMatching} instead.
 */
@Deprecated
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;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Matching computeMatching()
    {
        Set m = getMatching();
        double w = 0d;
        for (E e : m) {
            w += graph.getEdgeWeight(e);
        }
        return new MatchingImpl(m, w);
    }

    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, 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(Collections.singletonList(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 - 2024 Weber Informatics LLC | Privacy Policy