com.salesforce.jgrapht.alg.matching.EdmondsBlossomShrinking 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.
/*
* (C) Copyright 2012-2017, by Alejandro Ramon Lopez del Huerto 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 com.salesforce.jgrapht.alg.matching;
import java.util.*;
import com.salesforce.jgrapht.*;
import com.salesforce.jgrapht.alg.interfaces.*;
import com.salesforce.jgrapht.util.*;
/**
* An implementation of Edmonds Blossom Shrinking algorithm for constructing maximum matchings on
* graphs. The algorithm runs in time O(V^4).
*
* @param the graph vertex type
* @param the graph edge type
*
* @author Alejandro R. Lopez del Huerto
* @since Jan 24, 2012
*/
public class EdmondsBlossomShrinking
implements MatchingAlgorithm
{
private final UndirectedGraph graph;
private Map match;
private Map path;
private Map contracted;
/**
* Construct an instance of the Edmonds blossom shrinking algorithm.
*
* @param graph the input graph
* @throws IllegalArgumentException if the graph is not undirected
*/
public EdmondsBlossomShrinking(Graph graph)
{
if (graph == null) {
throw new IllegalArgumentException("Input graph cannot be null");
}
if (!(graph instanceof UndirectedGraph)) {
throw new IllegalArgumentException("Only undirected graphs supported");
}
this.graph = TypeUtil.uncheckedCast(graph, null);
}
/**
* {@inheritDoc}
*/
@Override
public Matching computeMatching()
{
Set edges = findMatch();
return new MatchingImpl<>(edges, edges.size());
}
/**
* Runs the algorithm on the input graph and returns the match edge set.
*
* @return set of edges
*/
private Set findMatch()
{
Set result = new ArrayUnenforcedSet<>();
match = new HashMap<>();
path = new HashMap<>();
contracted = new HashMap<>();
for (V i : graph.vertexSet()) {
// Any augmenting path should start with _exposed_ vertex
// (vertex may not escape match-set being added once)
if (!match.containsKey(i)) {
// Match is maximal iff graph G contains no more augmenting paths
V v = findPath(i);
while (v != null) {
V pv = path.get(v);
V ppv = match.get(pv);
match.put(v, pv);
match.put(pv, v);
v = ppv;
}
}
}
Set seen = new HashSet<>();
graph.vertexSet().stream().filter(v -> !seen.contains(v) && match.containsKey(v)).forEach(
v -> {
seen.add(v);
seen.add(match.get(v));
result.add(graph.getEdge(v, match.get(v)));
});
return result;
}
private V findPath(V root)
{
Set used = new HashSet<>();
Queue q = new ArrayDeque<>();
// Expand graph back from its contracted state
path.clear();
contracted.clear();
graph.vertexSet().forEach(vertex -> contracted.put(vertex, vertex));
used.add(root);
q.add(root);
while (!q.isEmpty()) {
V v = q.remove();
for (E e : graph.edgesOf(v)) {
V to = graph.getEdgeSource(e);
if (to.equals(v)) {
to = graph.getEdgeTarget(e);
}
if ((contracted.get(v).equals(contracted.get(to))) || to.equals(match.get(v))) {
continue;
}
// Check whether we've hit a 'blossom'
if ((to.equals(root))
|| ((match.containsKey(to)) && (path.containsKey(match.get(to)))))
{
V stem = lowestCommonAncestor(v, to);
Set blossom = new HashSet<>();
markPath(v, to, stem, blossom);
markPath(to, v, stem, blossom);
graph
.vertexSet().stream()
.filter(
i -> contracted.containsKey(i) && blossom.contains(contracted.get(i)))
.forEach(i -> {
contracted.put(i, stem);
if (!used.contains(i)) {
used.add(i);
q.add(i);
}
});
// Check whether we've had hit a loop (of even length (!) presumably)
} else if (!path.containsKey(to)) {
path.put(to, v);
if (!match.containsKey(to)) {
return to;
}
to = match.get(to);
used.add(to);
q.add(to);
}
}
}
return null;
}
private void markPath(V v, V child, V stem, Set blossom)
{
while (!contracted.get(v).equals(stem)) {
blossom.add(contracted.get(v));
blossom.add(contracted.get(match.get(v)));
path.put(v, child);
child = match.get(v);
v = path.get(match.get(v));
}
}
private V lowestCommonAncestor(V a, V b)
{
Set seen = new HashSet<>();
for (;;) {
a = contracted.get(a);
seen.add(a);
if (!match.containsKey(a)) {
break;
}
a = path.get(match.get(a));
}
for (;;) {
b = contracted.get(b);
if (seen.contains(b)) {
return b;
}
b = path.get(match.get(b));
}
}
}
// End EdmondsBlossomShrinking.java
© 2015 - 2025 Weber Informatics LLC | Privacy Policy