Skip to content

Commit

Permalink
Cleanup the API for min-cut.
Browse files Browse the repository at this point in the history
  • Loading branch information
renggli committed Dec 26, 2023
1 parent 8bc0750 commit 8a5615e
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 31 deletions.
44 changes: 22 additions & 22 deletions lib/src/graph/algorithms/stoer_wagner_min_cut.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:collection/collection.dart';

import '../../../collection.dart';
import '../../../graph.dart';
import '../../functional/scope.dart';
import '../../functional/types.dart';

/// Stoer–Wagner minimum cut algorithm in _O(V*E + V*log(V))_.
Expand Down Expand Up @@ -31,10 +32,10 @@ class StoerWagnerMinCut<V, E> {
// Initialize the vertices of the working graph.
final vertexMap = graph.vertexStrategy.createMap<Set<V>>();
for (final vertex in graph.vertices) {
final list = graph.vertexStrategy.createSet();
_workingGraph.addVertex(list);
vertexMap[vertex] = list;
list.add(vertex);
final set = graph.vertexStrategy.createSet();
_workingGraph.addVertex(set);
vertexMap[vertex] = set;
set.add(vertex);
}

// Initialize the edges of the working graph.
Expand All @@ -57,7 +58,7 @@ class StoerWagnerMinCut<V, E> {
}

// Internal state.
final Graph<Set<V>, num> _workingGraph =
final _workingGraph =
Graph<Set<V>, num>.undirected(vertexStrategy: StorageStrategy.identity());
Set<V> _bestPartition = const {};
num _bestWeight = double.infinity;
Expand All @@ -71,32 +72,32 @@ class StoerWagnerMinCut<V, E> {
/// The vertex strategy to store vertices of type V.
final StorageStrategy<V> vertexStrategy;

/// Returns a view of the graph onto the first side of the cut.
Graph<V, E> get first =>
graph.where(vertexPredicate: _bestPartition.contains);

/// Returns a view of the graph onto the second side of the cut.
Graph<V, E> get second {
final vertices = vertexStrategy.createSet();
vertices.addAll(graph.vertices);
vertices.removeAll(_bestPartition);
return graph.where(vertexPredicate: vertices.contains);
}
/// Returns a list with the graphs on each side of the cut.
late final List<Graph<V, E>> graphs = [
graph.where(vertexPredicate: _bestPartition.contains),
vertexStrategy.createSet().also((vertices) {
vertices.addAll(graph.vertices);
vertices.removeAll(_bestPartition);
return graph.where(vertexPredicate: vertices.contains);
}),
];

/// Returns an iterable over the edges that are cut.
///
/// Each undirected edge appears for each direction once, to de-duplicate
/// use `minCut.edges.unique()`.
Iterable<Edge<V, E>> get edges => graph.edges.where((edge) =>
_bestPartition.contains(edge.source) !=
_bestPartition.contains(edge.target));
late final List<Edge<V, E>> edges = graph.edges
.where((edge) =>
_bestPartition.contains(edge.source) !=
_bestPartition.contains(edge.target))
.toList();

/// Returns the weight of the cut vertices.
num get weight => _bestWeight;

void _minimumCutPhase(Set<V> seed) {
final queue = Heap<_State<V>>();
final states = <Set<V>, _State<V>>{};
final states = _workingGraph.vertexStrategy.createMap<_State<V>>();
var current = seed, previous = vertexStrategy.createSet();
for (final vertex in _workingGraph.vertices) {
if (vertex == seed) continue;
Expand All @@ -112,8 +113,7 @@ class StoerWagnerMinCut<V, E> {
previous = current;
current = source;
for (final edge in _workingGraph.outgoingEdgesOf(source)) {
final target = edge.target;
final state = states[target];
final state = states[edge.target];
if (state != null) {
queue.remove(state);
state.active = true;
Expand Down
22 changes: 13 additions & 9 deletions test/graph_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2486,8 +2486,9 @@ void main() {
graph.addEdge(8, 4, value: 2);
graph.addEdge(8, 7, value: 3);
final minCut = graph.minCut();
expect(minCut.first.vertices, [3, 4, 7, 8]);
expect(minCut.second.vertices, [1, 2, 5, 6]);
expect(minCut.graphs, hasLength(2));
expect(minCut.graphs.first.vertices, [3, 4, 7, 8]);
expect(minCut.graphs.last.vertices, [1, 2, 5, 6]);
expect(
minCut.edges,
unorderedEquals([
Expand All @@ -2508,8 +2509,9 @@ void main() {
graph.addEdge(2, 4);
graph.addEdge(4, 1);
final minCut = graph.minCut();
expect(minCut.first.vertices, unorderedEquals([3]));
expect(minCut.second.vertices, unorderedEquals([0, 1, 2, 4]));
expect(minCut.graphs, hasLength(2));
expect(minCut.graphs.first.vertices, unorderedEquals([3]));
expect(minCut.graphs.last.vertices, unorderedEquals([0, 1, 2, 4]));
expect(
minCut.edges,
unorderedEquals([
Expand All @@ -2535,8 +2537,9 @@ void main() {
graph.addEdge(5, 6, value: 1);
graph.addEdge(6, 7, value: 3);
final minCut = graph.minCut();
expect(minCut.first.vertices, unorderedEquals([2, 3, 6, 7]));
expect(minCut.second.vertices, unorderedEquals([0, 1, 4, 5]));
expect(minCut.graphs, hasLength(2));
expect(minCut.graphs.first.vertices, unorderedEquals([2, 3, 6, 7]));
expect(minCut.graphs.last.vertices, unorderedEquals([0, 1, 4, 5]));
expect(
minCut.edges,
unorderedEquals([
Expand All @@ -2558,9 +2561,10 @@ void main() {
graph.addEdge('c', 'y', value: 2);
graph.addEdge('e', 'y', value: 3);
final minCut = graph.minCut();
expect(minCut.first.vertices, unorderedEquals(['e', 'y']));
expect(
minCut.second.vertices, unorderedEquals(['x', 'a', 'b', 'c', 'd']));
expect(minCut.graphs, hasLength(2));
expect(minCut.graphs.first.vertices, unorderedEquals(['e', 'y']));
expect(minCut.graphs.last.vertices,
unorderedEquals(['x', 'a', 'b', 'c', 'd']));
expect(
minCut.edges,
unorderedEquals([
Expand Down

0 comments on commit 8a5615e

Please sign in to comment.