/* $RCSfile$ * $Author$ * $Date$ * $Revision$ * * Copyright (C) 2004-2009 Ulrich Bauer <ulrich.bauer@alumni.tum.de> * * Contact: cdk-devel@lists.sourceforge.net * * 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 USA. * */ package org.openscience.cdk.ringsearch.cyclebasis; import org._3pq.jgrapht.Edge; import org._3pq.jgrapht.UndirectedGraph; import org._3pq.jgrapht.alg.DijkstraShortestPath; import org._3pq.jgrapht.graph.UndirectedSubgraph; import org.openscience.cdk.annotations.TestClass; import org.openscience.cdk.annotations.TestMethod; import org.openscience.cdk.graph.BiconnectivityInspector; import java.util.*; /** * A minimum basis of all cycles in a graph. * All cycles in a graph G can be constructed from the basis cycles by binary * addition of their invidence vectors. * * A minimum cycle basis is a Matroid. * * @author Ulrich Bauer <ulrich.bauer@alumni.tum.de> * * * @cdk.module standard * @cdk.githash * * @cdk.builddepends jgrapht-0.5.3.jar * @cdk.depends jgrapht-0.5.3.jar */ @TestClass("org.openscience.cdk.ringsearch.cyclebasis.CycleBasisTest") public class CycleBasis { //private List cycles = new Vector(); private List mulitEdgeCycles = new Vector(); private List multiEdgeList = new Vector(); private SimpleCycleBasis cachedCycleBasis; //private List edgeList = new Vector(); //private List multiEdgeList = new Vector(); private UndirectedGraph baseGraph; private List subgraphBases = new Vector(); /** * Constructs a minimum cycle basis of a graph. * * @param g the graph for the cycle basis */ public CycleBasis (UndirectedGraph g) { baseGraph = g; // We construct a simple graph out of the input (multi-)graph // as a subgraph with no multiedges. // The removed edges are collected in multiEdgeList // Moreover, shortest cycles through these edges are constructed and // collected in mulitEdgeCycles UndirectedGraph simpleGraph = new UndirectedSubgraph(g, null, null); // Iterate over the edges and discard all edges with the same source and target for (Iterator it = g.edgeSet().iterator(); it.hasNext();) { Edge edge = (Edge) it.next(); Object u = edge.getSource(); Object v = edge.getTarget(); List edges = simpleGraph.getAllEdges(u, v); if (edges.size() > 1) { // Multiple edges between u and v. // Keep the edge with the least weight Edge minEdge = edge; for (Iterator jt = edges.iterator(); jt.hasNext();) { Edge nextEdge = (Edge) jt.next(); minEdge = nextEdge.getWeight() < minEdge.getWeight() ? nextEdge : minEdge; } // ...and remove the others. for (Iterator jt = edges.iterator(); jt.hasNext();) { Edge nextEdge = (Edge) jt.next(); if (nextEdge != minEdge) { // Remove edge from the graph simpleGraph.removeEdge(nextEdge); // Create a new cycle through this edge by finding // a shortest path between the vertices of the edge Set edgesOfCycle = new HashSet(); edgesOfCycle.add(nextEdge); edgesOfCycle.addAll(DijkstraShortestPath.findPathBetween(simpleGraph, u, v)); multiEdgeList.add(nextEdge); mulitEdgeCycles.add(new SimpleCycle(baseGraph, edgesOfCycle)); } } } } List biconnectedComponents = new BiconnectivityInspector(simpleGraph).biconnectedSets(); for (Iterator it = biconnectedComponents.iterator(); it.hasNext();) { Set edges = (Set) it.next(); if (edges.size() > 1) { Set vertices = new HashSet(); for (Iterator edgeIt = edges.iterator(); edgeIt.hasNext();) { Edge edge = (Edge) edgeIt.next(); vertices.add(edge.getSource()); vertices.add(edge.getTarget()); } UndirectedGraph subgraph = new UndirectedSubgraph(simpleGraph, vertices, edges); SimpleCycleBasis cycleBasis = new SimpleCycleBasis(subgraph); subgraphBases.add(cycleBasis); } else { Edge edge = (Edge) edges.iterator().next(); multiEdgeList.add(edge); } } } @TestMethod("testWeightVector") public int[] weightVector() { SimpleCycleBasis basis = simpleBasis(); List cycles = basis.cycles(); int[] result = new int[cycles.size()]; for (int i=0; i<cycles.size(); i++) { SimpleCycle cycle = (SimpleCycle) cycles.get(i); result[i] = (int) cycle.weight(); } Arrays.sort(result); return result; } private SimpleCycleBasis simpleBasis() { if (cachedCycleBasis == null) { List cycles = new ArrayList(); List edgeList = new ArrayList(); for (Object subgraphBase1 : subgraphBases) { SimpleCycleBasis subgraphBase = (SimpleCycleBasis) subgraphBase1; cycles.addAll(subgraphBase.cycles()); edgeList.addAll(subgraphBase.edges()); } cycles.addAll(mulitEdgeCycles); edgeList.addAll(multiEdgeList); //edgeList.addAll(baseGraph.edgeSet()); cachedCycleBasis = new SimpleCycleBasis(cycles, edgeList, baseGraph); } return cachedCycleBasis; } /** * Returns the cycles that form the cycle basis. * * @return a <Code>Collection</code> of the basis cycles */ @TestMethod("testCycles") public Collection cycles() { return simpleBasis().cycles(); } /** * Returns the essential cycles of this cycle basis. * A essential cycle is contained in every minimum cycle basis of a graph. * * @return a <Code>Collection</code> of the essential cycles */ @TestMethod("testEssentialCycles") public Collection essentialCycles() { Collection result = new HashSet(); //minimize(); for (Object subgraphBase : subgraphBases) { SimpleCycleBasis cycleBasis = (SimpleCycleBasis) subgraphBase; result.addAll(cycleBasis.essentialCycles()); } return result; } /** * Returns the essential cycles of this cycle basis. * A relevant cycle is contained in some minimum cycle basis of a graph. * * @return a <Code>Map</code> mapping each relevant cycles to the corresponding * basis cycle in this basis */ @TestMethod("testRelevantCycles") public Map relevantCycles() { Map result = new HashMap(); //minimize(); for (Object subgraphBase : subgraphBases) { SimpleCycleBasis cycleBasis = (SimpleCycleBasis) subgraphBase; result.putAll(cycleBasis.relevantCycles()); } return result; } /** * Returns the connected components of this cycle basis, in regard to matroid theory. * Two cycles belong to the same commected component if there is a circuit (a minimal * dependent set) containing both cycles. * * @return a <Code>List</code> of <Code>Set</code>s consisting of the cycles in a * equivalence class. */ @TestMethod("testEquivalenceClasses") public List equivalenceClasses() { List result = new ArrayList(); //minimize(); for (Object subgraphBase : subgraphBases) { SimpleCycleBasis cycleBasis = (SimpleCycleBasis) subgraphBase; result.addAll(cycleBasis.equivalenceClasses()); } return result; } }