1 /* 2 * Copyright (c) 2014 European Bioinformatics Institute (EMBL-EBI) 3 * John May <jwmay@users.sf.net> 4 * 5 * Contact: cdk-devel@lists.sourceforge.net 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU Lesser General Public License as published by 9 * the Free Software Foundation; either version 2.1 of the License, or (at 10 * your option) any later version. All we ask is that proper credit is given 11 * for our work, which includes - but is not limited to - adding the above 12 * copyright notice to the beginning of your source code files, and to any 13 * copyright notice that you may distribute with programs based on this work. 14 * 15 * This program is distributed in the hope that it will be useful, but WITHOUT 16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 18 * License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 U 23 */ 24 25 package org.openscience.cdk.graph; 26 27 28 import java.util.Arrays; 29 import java.util.BitSet; 30 31 /** 32 * A matching is an independent edge set of a graph. This is a set of edges that 33 * share no common vertices. A matching is perfect if every vertex in the graph 34 * is matched. Each vertex can be matched with exactly one other vertex. 35 * 36 * This class provides storage and manipulation of a matching. A new match is 37 * added with {@link #match(int, int)}, any existing match for the newly matched 38 * vertices is no-longer available. The status of a vertex can be queried with 39 * {@link #matched(int)} and the matched vertex obtained with {@link 40 * #other(int)}. 41 * 42 * @author John May 43 * @cdk.module standard 44 * @see <a href="http://en.wikipedia.org/wiki/Matching_(graph_theory)">Matching 45 * (graph theory), Wikipedia</a> 46 */ 47 public final class Matching { 48 49 /** Indicate an unmatched vertex. */ 50 private static final int NIL = -1; 51 52 /** Match storage. */ 53 private final int[] match; 54 55 /** 56 * Create a matching of the given size. 57 * 58 * @param n number of items 59 */ Matching(final int n)60 private Matching(final int n) { 61 this.match = new int[n]; 62 Arrays.fill(match, NIL); 63 } 64 65 /** 66 * Add the edge '{u,v}' to the matched edge set. Any existing matches for 67 * 'u' or 'v' are removed from the matched set. 68 * 69 * @param u a vertex 70 * @param v another vertex 71 */ match(final int u, final int v)72 public void match(final int u, final int v) { 73 // set the new match, don't need to update existing - we only provide 74 // access to bidirectional mappings 75 match[u] = v; 76 match[v] = u; 77 } 78 79 /** 80 * Access the vertex matched with 'v'. 81 * 82 * @param v vertex 83 * @return matched vertex 84 * @throws IllegalArgumentException the vertex is currently unmatched 85 */ other(final int v)86 public int other(final int v) { 87 if (unmatched(v)) throw new IllegalArgumentException(v + " is not matched"); 88 return match[v]; 89 } 90 91 /** 92 * Remove a matching for the specified vertex. 93 * 94 * @param v vertex 95 */ unmatch(final int v)96 public void unmatch(final int v) { 97 match[v] = NIL; 98 } 99 100 /** 101 * Determine if a vertex has a match. 102 * 103 * @param v vertex 104 * @return the vertex is matched 105 */ matched(final int v)106 public boolean matched(final int v) { 107 return !unmatched(v); 108 } 109 110 /** 111 * Determine if a vertex is not matched. 112 * 113 * @param v a vertex 114 * @return the vertex has no matching 115 */ unmatched(final int v)116 public boolean unmatched(final int v) { 117 return match[v] == NIL || match[match[v]] != v; 118 } 119 120 /** 121 * Attempt to augment the matching such that it is perfect over the subset 122 * of vertices in the provided graph. 123 * 124 * @param graph adjacency list representation of graph 125 * @param subset subset of vertices 126 * @return the matching was perfect 127 * @throws IllegalArgumentException the graph was a different size to the 128 * matching capacity 129 */ perfect(int[][] graph, BitSet subset)130 public boolean perfect(int[][] graph, BitSet subset) { 131 132 if (graph.length != match.length || subset.cardinality() > graph.length) 133 throw new IllegalArgumentException("graph and matching had different capacity"); 134 135 // and odd set can never provide a perfect matching 136 if ((subset.cardinality() & 0x1) == 0x1) return false; 137 138 // arbitrary matching was perfect 139 if (arbitaryMatching(graph, subset)) return true; 140 141 EdmondsMaximumMatching.maxamise(this, graph, subset); 142 143 // the matching is imperfect if any vertex was 144 for (int v = subset.nextSetBit(0); v >= 0; v = subset.nextSetBit(v + 1)) 145 if (unmatched(v)) return false; 146 147 return true; 148 } 149 150 /** 151 * Assign an arbitrary matching that covers the subset of vertices. 152 * 153 * @param graph adjacency list representation of graph 154 * @param subset subset of vertices in the graph 155 * @return the matching was perfect 156 */ arbitaryMatching(final int[][] graph, final BitSet subset)157 boolean arbitaryMatching(final int[][] graph, final BitSet subset) { 158 159 final BitSet unmatched = new BitSet(); 160 161 // indicates the deg of each vertex in unmatched subset 162 final int[] deg = new int[graph.length]; 163 164 // queue/stack of vertices with deg1 vertices 165 final int[] deg1 = new int[graph.length]; 166 int nd1 = 0, nMatched = 0; 167 168 for (int v = subset.nextSetBit(0); v >= 0; v = subset.nextSetBit(v + 1)) { 169 if (matched(v)) { 170 assert subset.get(other(v)); 171 nMatched++; 172 continue; 173 } 174 unmatched.set(v); 175 for (int w : graph[v]) 176 if (subset.get(w) && unmatched(w)) deg[v]++; 177 if (deg[v] == 1) deg1[nd1++] = v; 178 } 179 180 while (!unmatched.isEmpty()) { 181 182 int v = -1; 183 184 // attempt to select a vertex with degree = 1 (in matched set) 185 while (nd1 > 0) { 186 v = deg1[--nd1]; 187 if (unmatched.get(v)) break; 188 } 189 190 // no unmatched degree 1 vertex, select the first unmatched 191 if (v < 0 || unmatched.get(v)) v = unmatched.nextSetBit(0); 192 193 unmatched.clear(v); 194 195 // find a unmatched edge and match it, adjacent degrees are updated 196 for (final int w : graph[v]) { 197 if (unmatched.get(w)) { 198 match(v, w); 199 nMatched += 2; 200 unmatched.clear(w); 201 // update neighbors of w and v (if needed) 202 for (final int u : graph[w]) 203 if (--deg[u] == 1 && unmatched.get(u)) deg1[nd1++] = u; 204 205 // if deg == 1, w is the only neighbor 206 if (deg[v] > 1) { 207 for (final int u : graph[v]) 208 if (--deg[u] == 1 && unmatched.get(u)) deg1[nd1++] = u; 209 } 210 break; 211 } 212 } 213 } 214 215 return nMatched == subset.cardinality(); 216 } 217 218 /** 219 * Create an empty matching with the specified capacity. 220 * 221 * @param capacity maximum number of vertices 222 * @return empty matching 223 */ withCapacity(final int capacity)224 public static Matching withCapacity(final int capacity) { 225 return new Matching(capacity); 226 } 227 228 /**{@inheritDoc} */ 229 @Override toString()230 public String toString() { 231 StringBuilder sb = new StringBuilder(4 * match.length); 232 sb.append('['); 233 for (int u = 0; u < match.length; u++) { 234 int v = match[u]; 235 if (v > u && match[v] == u) { 236 if (sb.length() > 1) sb.append(", "); 237 sb.append(u).append('=').append(v); 238 } 239 } 240 sb.append(']'); 241 return sb.toString(); 242 } 243 } 244