All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openscience.cdk.graph.Matching Maven / Gradle / Ivy

/*
 * Copyright (c) 2014 European Bioinformatics Institute (EMBL-EBI)
 *                    John May 
 *
 * Contact: [email protected]
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at
 * your option) any later version. All we ask is that proper credit is given
 * for our work, which includes - but is not limited to - adding the above
 * copyright notice to the beginning of your source code files, and to any
 * copyright notice that you may distribute with programs based on this work.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 U
 */

package org.openscience.cdk.graph;


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
 * share no common vertices. A matching is perfect if every vertex in the graph
 * is matched. Each vertex can be matched with exactly one other vertex.

* * This class provides storage and manipulation of a matching. A new match is * added with {@link #match(int, int)}, any existing match for the newly matched * vertices is no-longer available. The status of a vertex can be queried with * {@link #matched(int)} and the matched vertex obtained with {@link * #other(int)}.

* * @author John May * @cdk.module standard * @see Matching * (graph theory), Wikipedia */ public final class Matching { /** Indicate an unmatched vertex. */ private static final int NIL = -1; /** Match storage. */ private final int[] match; /** * Create a matching of the given size. * * @param n number of items */ private Matching(final int n) { this.match = new int[n]; Arrays.fill(match, NIL); } /** * Add the edge '{u,v}' to the matched edge set. Any existing matches for * 'u' or 'v' are removed from the matched set. * * @param u a vertex * @param v another vertex */ public void match(final int u, final int v) { // set the new match, don't need to update existing - we only provide // access to bidirectional mappings match[u] = v; match[v] = u; } /** * Access the vertex matched with 'v'. * * @param v vertex * @return matched vertex * @throws IllegalArgumentException the vertex is currently unmatched */ public int other(final int v) { if (unmatched(v)) throw new IllegalArgumentException(v + " is not matched"); return match[v]; } /** * Remove a matching for the specified vertex. * * @param v vertex */ public void unmatch(final int v) { match[v] = NIL; } /** * Determine if a vertex has a match. * * @param v vertex * @return the vertex is matched */ public boolean matched(final int v) { return !unmatched(v); } /** * Determine if a vertex is not matched. * * @param v a vertex * @return the vertex has no matching */ public boolean unmatched(final int v) { return match[v] == NIL || match[match[v]] != v; } /** * 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 different size to the * matching capacity */ 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; // arbitrary 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 arbitrary 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 */ boolean arbitaryMatching(final int[][] graph, final BitSet subset) { final BitSet unmatched = new BitSet(); // indicates 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); // update 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 maximum number of vertices * @return empty matching */ public static Matching withCapacity(final int capacity) { return new Matching(capacity); } /** @inheritDoc */ @Override public String toString() { StringBuilder sb = new StringBuilder(4 * match.length); sb.append('['); for (int u = 0; u < match.length; u++) { int v = match[u]; if (v > u && match[v] == u) { if (sb.length() > 1) sb.append(", "); sb.append(u).append('=').append(v); } } sb.append(']'); return sb.toString(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy