com.salesforce.jgrapht.alg.StoerWagnerMinimumCut Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of AptSpringProcessor Show documentation
Show all versions of AptSpringProcessor Show documentation
This project contains the apt processor that implements all the checks enumerated in @Verify. It is a self contained, and
shaded jar.
The newest version!
/*
* (C) Copyright 2011-2018, by Robby McKilliam, Ernst de Ridder and Contributors.
*
* JGraphT : a free Java graph-theory library
*
* See the CONTRIBUTORS.md file distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the
* GNU Lesser General Public License v2.1 or later
* which is available at
* http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html.
*
* SPDX-License-Identifier: EPL-2.0 OR LGPL-2.1-or-later
*/
package com.salesforce.jgrapht.alg;
import com.salesforce.jgrapht.*;
import com.salesforce.jgrapht.graph.*;
import java.util.*;
/**
* Implements the Stoer and Wagner minimum cut
* algorithm. Deterministically computes the minimum cut in $O(|V||E| + |V| \log |V|)$ time.
* This implementation uses Java's PriorityQueue and requires $O(|V||E| \log |E|)$ time. M. Stoer
* and F. Wagner, "A Simple Min-Cut Algorithm", Journal of the ACM, volume 44, number 4. pp 585-591,
* 1997.
*
* @param the graph vertex type
* @param the graph edge type
*
* @author Robby McKilliam
* @author Ernst de Ridder
*/
public class StoerWagnerMinimumCut
{
final Graph, DefaultWeightedEdge> workingGraph;
protected double bestCutWeight = Double.POSITIVE_INFINITY;
protected Set bestCut;
/**
* Will compute the minimum cut in graph.
*
* @param graph graph over which to run algorithm
*
* @throws IllegalArgumentException if a negative weight edge is found
* @throws IllegalArgumentException if graph has less than 2 vertices
*/
public StoerWagnerMinimumCut(Graph graph)
{
GraphTests.requireUndirected(graph, "Graph must be undirected");
if (graph.vertexSet().size() < 2) {
throw new IllegalArgumentException("Graph has less than 2 vertices");
}
// get a version of this graph where each vertex is wrapped with a list
workingGraph = new SimpleWeightedGraph<>(DefaultWeightedEdge.class);
Map> vertexMap = new HashMap<>();
for (V v : graph.vertexSet()) {
Set list = new HashSet<>();
list.add(v);
vertexMap.put(v, list);
workingGraph.addVertex(list);
}
for (E e : graph.edgeSet()) {
if (graph.getEdgeWeight(e) < 0.0) {
throw new IllegalArgumentException("Negative edge weights not allowed");
}
V s = graph.getEdgeSource(e);
Set sNew = vertexMap.get(s);
V t = graph.getEdgeTarget(e);
Set tNew = vertexMap.get(t);
// For multigraphs, we sum the edge weights (either all are
// contained in a cut, or none)
DefaultWeightedEdge eNew = workingGraph.getEdge(sNew, tNew);
if (eNew == null) {
eNew = workingGraph.addEdge(sNew, tNew);
workingGraph.setEdgeWeight(eNew, graph.getEdgeWeight(e));
} else {
workingGraph
.setEdgeWeight(eNew, workingGraph.getEdgeWeight(eNew) + graph.getEdgeWeight(e));
}
}
// arbitrary vertex used to seed the algorithm.
Set a = workingGraph.vertexSet().iterator().next();
while (workingGraph.vertexSet().size() > 1) {
minimumCutPhase(a);
}
}
/**
* Implements the MinimumCutPhase function of Stoer and Wagner.
*
* @param a the vertex
*/
protected void minimumCutPhase(Set a)
{
// The last and before last vertices added to A.
Set last = a, beforelast = null;
// queue contains vertices not in A ordered by max weight of edges to A.
PriorityQueue queue = new PriorityQueue<>();
// Maps vertices to elements of queue
Map, VertexAndWeight> dmap = new HashMap<>();
// Initialize queue
for (Set v : workingGraph.vertexSet()) {
if (v == a) {
continue;
}
DefaultWeightedEdge e = workingGraph.getEdge(v, a);
Double w = (e == null) ? 0.0 : workingGraph.getEdgeWeight(e);
VertexAndWeight vandw = new VertexAndWeight(v, w, e != null);
queue.add(vandw);
dmap.put(v, vandw);
}
// Now iteratively update the queue to get the required vertex ordering
while (!queue.isEmpty()) {
Set v = queue.poll().vertex;
dmap.remove(v);
beforelast = last;
last = v;
for (DefaultWeightedEdge e : workingGraph.edgesOf(v)) {
Set vc = Graphs.getOppositeVertex(workingGraph, e, v);
VertexAndWeight vcandw = dmap.get(vc);
if (vcandw != null) {
queue.remove(vcandw); // this is O(log n) but could be O(1)?
vcandw.active = true;
vcandw.weight += workingGraph.getEdgeWeight(e);
queue.add(vcandw); // this is O(log n) but could be O(1)?
}
}
}
// Update the best cut
double w = vertexWeight(last);
if (w < bestCutWeight) {
bestCutWeight = w;
bestCut = last;
}
// merge the last added vertices
mergeVertices(beforelast, last);
}
/**
* Return the weight of the minimum cut
*
* @return the weight of the minimum cut
*/
public double minCutWeight()
{
return bestCutWeight;
}
/**
* Return a set of vertices on one side of the cut
*
* @return a set of vertices on one side of the cut
*/
public Set minCut()
{
return bestCut;
}
/**
* Merges vertex $t$ into vertex $s$, summing the weights as required. Returns the merged vertex
* and the sum of its weights
*
* @param s the first vertex
* @param t the second vertex
*
* @return the merged vertex and its weight
*/
protected VertexAndWeight mergeVertices(Set s, Set t)
{
// construct the new combinedvertex
Set set = new HashSet<>();
set.addAll(s);
set.addAll(t);
workingGraph.addVertex(set);
// add edges and weights to the combined vertex
double wsum = 0.0;
for (Set v : workingGraph.vertexSet()) {
if ((s != v) && (t != v)) {
double neww = 0.0;
DefaultWeightedEdge etv = workingGraph.getEdge(t, v);
DefaultWeightedEdge esv = workingGraph.getEdge(s, v);
if (etv != null) {
neww += workingGraph.getEdgeWeight(etv);
}
if (esv != null) {
neww += workingGraph.getEdgeWeight(esv);
}
if ((etv != null) || (esv != null)) {
wsum += neww;
workingGraph.setEdgeWeight(workingGraph.addEdge(set, v), neww);
}
}
}
// remove original vertices
workingGraph.removeVertex(t);
workingGraph.removeVertex(s);
return new VertexAndWeight(set, wsum, false);
}
/**
* Compute the sum of the weights entering a vertex
*
* @param v the vertex
* @return the sum of the weights entering a vertex
*/
public double vertexWeight(Set v)
{
double wsum = 0.0;
for (DefaultWeightedEdge e : workingGraph.edgesOf(v)) {
wsum += workingGraph.getEdgeWeight(e);
}
return wsum;
}
/**
* Class for weighted vertices
*/
protected class VertexAndWeight
implements
Comparable
{
public Set vertex;
public Double weight;
public boolean active; // active == neighbour in A
/**
* Construct a new weighted vertex.
*
* @param v the vertex
* @param w the weight of the vertex
* @param active whether it is active
*/
public VertexAndWeight(Set v, double w, boolean active)
{
this.vertex = v;
this.weight = w;
this.active = active;
}
/**
* compareTo that sorts in reverse order because we need extract-max and queue provides
* extract-min.
*/
@Override
public int compareTo(VertexAndWeight that)
{
if (this.active && that.active) {
return -Double.compare(weight, that.weight);
}
if (this.active && !that.active) {
return -1;
}
if (!this.active && that.active) {
return +1;
}
// both inactive
return 0;
}
@Override
public String toString()
{
return "(" + vertex + ", " + weight + ")";
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy