Skip to content

# cdk/cdk

### Subversion checkout URL

You can clone with
or
.

Utility in the Matching API provides simplified procedure to assignin…

```…g a perfect matching.

Signed-off-by: Egon Willighagen <egonw@users.sourceforge.net>```
• Loading branch information...
commit 454ed8f0e1fdf27f49a0376fca6b40f97070fa6c 1 parent 7f8e345
johnmay authored committed
110 base/standard/src/main/java/org/openscience/cdk/graph/Matching.java
 @@ -28,6 +28,7 @@ import org.openscience.cdk.annotations.TestMethod; import java.util.Arrays; +import java.util.BitSet; /** * A matching is an independent edge set of a graph. This is a set of edges that @@ -121,6 +122,115 @@ private Matching(final int n) { } /** + * Attempt to augment the matching such that it is perfect over the subset + * of vertices in the provided graph. + * + * @param graph adjacency list representation of graph + * @param subset subset of vertices + * @return the matching was perfect + * @throws IllegalArgumentException the graph was a diffeent size to the + * matching capacity + */ + @TestMethod("fulvelene2") + public boolean perfect(int[][] graph, BitSet subset) { + + if (graph.length != match.length || subset.cardinality() > graph.length) + throw new IllegalArgumentException("graph and matching had different capacity"); + + // and odd set can never provide a perfect matching + if ((subset.cardinality() & 0x1) == 0x1) + return false; + + // arbitary matching was perfect + if (arbitaryMatching(graph, subset)) + return true; + + EdmondsMaximumMatching.maxamise(this, graph, subset); + + // the matching is imperfect if any vertex was + for (int v = subset.nextSetBit(0); v >= 0; v = subset.nextSetBit(v + 1)) + if (unmatched(v)) + return false; + + return true; + } + + /** + * Assign an abirary matching that covers the subset of vertices. + * + * @param graph adjacency list representation of graph + * @param subset subset of vertices in the graph + * @return the matching was perfect + */ + @TestMethod("perfectArbitaryMatching,imperfectArbitaryMatching") + boolean arbitaryMatching(final int[][] graph, final BitSet subset) { + + final BitSet unmatched = new BitSet(); + + // indiciates the deg of each vertex in unmatched subset + final int[] deg = new int[graph.length]; + + // queue/stack of vertices with deg1 vertices + final int[] deg1 = new int[graph.length]; + int nd1 = 0, nMatched = 0; + + for (int v = subset.nextSetBit(0); v >= 0; v = subset.nextSetBit(v + 1)) { + if (matched(v)) { + assert subset.get(other(v)); + nMatched++; + continue; + } + unmatched.set(v); + for (int w : graph[v]) + if (subset.get(w) && unmatched(w)) + deg[v]++; + if (deg[v] == 1) + deg1[nd1++] = v; + } + + while (!unmatched.isEmpty()) { + + int v = -1; + + // attempt to select a vertex with degree = 1 (in matched set) + while (nd1 > 0) { + v = deg1[--nd1]; + if (unmatched.get(v)) + break; + } + + // no unmatched degree 1 vertex, select the first unmatched + if (v < 0 || unmatched.get(v)) + v = unmatched.nextSetBit(0); + + unmatched.clear(v); + + // find a unmatched edge and match it, adjacent degrees are updated + for (final int w : graph[v]) { + if (unmatched.get(w)) { + match(v, w); + nMatched += 2; + unmatched.clear(w); + // upate neighbors of w and v (if needed) + for (final int u : graph[w]) + if (--deg[u] == 1 && unmatched.get(u)) + deg1[nd1++] = u; + + // if deg == 1, w is the only neighbor + if (deg[v] > 1) { + for (final int u : graph[v]) + if (--deg[u] == 1 && unmatched.get(u)) + deg1[nd1++] = u; + } + break; + } + } + } + + return nMatched == subset.cardinality(); + } + + /** * Create an empty matching with the specified capacity. * * @param capacity maxmium number of vertices
58 base/test-standard/src/test/java/org/openscience/cdk/graph/MatchingTest.java
 @@ -26,6 +26,11 @@ import org.junit.Ignore; import org.junit.Test; +import org.openscience.cdk.interfaces.IChemObjectBuilder; +import org.openscience.cdk.silent.SilentChemObjectBuilder; +import org.openscience.cdk.smiles.SmilesParser; + +import java.util.BitSet; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @@ -38,6 +43,9 @@ */ public class MatchingTest { + private IChemObjectBuilder bldr = SilentChemObjectBuilder.getInstance(); + private SmilesParser smipar = new SmilesParser(bldr); + @Ignore("no operation performed") public void nop() { } @@ -85,6 +93,56 @@ public void nop() { assertFalse(matching.matched(2)); } + @Test public void perfectArbitaryMatching() { + Matching matching = Matching.withCapacity(4); + BitSet subset = new BitSet(); + subset.flip(0, 4); + assertTrue(matching.arbitaryMatching(new int[][]{ + {1}, + {0, 2}, + {1, 3}, + {2} + }, subset)); + } + + @Test public void imperfectArbitaryMatching() { + Matching matching = Matching.withCapacity(5); + BitSet subset = new BitSet(); + subset.flip(0, 5); + assertFalse(matching.arbitaryMatching(new int[][]{ + {1}, + {0, 2}, + {1, 3}, + {2, 4}, + {3} + }, subset)); + } + + @Test public void fulvelene1() throws Exception { + int[][] graph = GraphUtil.toAdjList(smipar.parseSmiles("c1cccc1c1cccc1")); + Matching m = Matching.withCapacity(graph.length); + BitSet subset = new BitSet(); + subset.flip(0, graph.length); + // arbitary matching will assign a perfect matching here + assertTrue(m.arbitaryMatching(graph, subset)); + } + + @Test public void fulvelene2() throws Exception { + int[][] graph = GraphUtil.toAdjList(smipar.parseSmiles("c1cccc1c1cccc1")); + Matching m = Matching.withCapacity(graph.length); + BitSet subset = new BitSet(); + subset.flip(0, graph.length); + + // induced match - can't be perfected without removing this match + m.match(1, 2); + + // arbitary matching will not be able assign a perfect matching + assertFalse(m.arbitaryMatching(graph, subset)); + + // but perfect() will + assertTrue(m.perfect(graph, subset)); + } + @Test public void string() { Matching matching = Matching.withCapacity(9); matching.match(1, 3);
Please sign in to comment.
Something went wrong with that request. Please try again.