SpanningTree.java :  » Science » Chemistry-Development-Kit-1.3.5 » org » openscience » cdk » graph » Java Open Source

Java Open Source » Science » Chemistry Development Kit 1.3.5 
Chemistry Development Kit 1.3.5 » org » openscience » cdk » graph » SpanningTree.java
/* $Revision$ $Author$ $Date$    
 *
 * Copyright (C) 2001-2007  Nina Jeliazkova
 *
 * 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.graph;

import org.openscience.cdk.CDKConstants;
import org.openscience.cdk.annotations.TestClass;
import org.openscience.cdk.annotations.TestMethod;
import org.openscience.cdk.exception.NoSuchAtomException;
import org.openscience.cdk.interfaces.IAtom;
import org.openscience.cdk.interfaces.IAtomContainer;
import org.openscience.cdk.interfaces.IBond;
import org.openscience.cdk.interfaces.IRing;
import org.openscience.cdk.interfaces.IRingSet;

/**
 * Spanning tree of a molecule.
 * Used to discover the number of cyclic bonds in order to prevent the 
 * inefficient AllRingsFinder to run for too long.
 *
 * @author      Nina Jeliazkova
 * @cdk.module  core
 * @cdk.githash
 * @cdk.dictref blue-obelisk:graphSpanningTree
 * @cdk.keyword spanning tree
 * @cdk.keyword ring finding
 * @cdk.bug     1817487
 */
@TestClass("org.openscience.cdk.graph.SpanningTreeTest")
public class SpanningTree {
  
  private final static String ATOM_NUMBER = "ST_ATOMNO";
  
  private int[] parent = null;
  private int[][] cb = null; // what is cb??? cyclic bonds?
  
  protected boolean[] bondsInTree;
  
  private int sptSize = 0;
  private int edrSize = 0;

  private int bondsAcyclicCount = 0,bondsCyclicCount = 0;
  
  private IAtomContainer molecule = null;
  private int totalEdgeCount=0, totalVertexCount=0;
  private boolean disconnected;
  private boolean identifiedBonds;

    @TestMethod("testIsDisconnected")
    public boolean isDisconnected() {
    return disconnected;
  }

    @TestMethod("testSpanningTree_IAtomContainer")
    public SpanningTree(IAtomContainer atomContainer) {
    identifiedBonds = false;
    buildSpanningTree(atomContainer);
  }

    private boolean fastfind(int v1,int v2, boolean union) {
    int i = v1;  while (parent[i] > 0) i = parent[i];
    int j = v2;  while (parent[j] > 0) j = parent[j];
    int t ;
    while (parent[v1] > 0) {
      t = v1; v1 = parent[v1]; parent[t] = i;
    }
    while (parent[v2] > 0) {
      t = v2; v2 = parent[v2]; parent[t] = j;
    }    
    if (union && (i!=j)) {
      if (parent[j] < parent[i]) {
        parent[j] = parent[j] + parent[i]-1;
        parent[i] = j; 
      } else {
        parent[i] = parent[i] + parent[j]-1;
        parent[j] = i;
      }
    }
    return (i != j);
  }
  
  private void fastFindInit(int V) {
    parent = new int[V+1];
    for (int i = 1; i <= V; i++) {
      parent[i] = 0;
    }
  }
  
  /*
   * Kruskal algorithm
   */
  private void buildSpanningTree(IAtomContainer atomContainer){
    disconnected = false;
    molecule = atomContainer;
    
    totalVertexCount = atomContainer.getAtomCount();
    totalEdgeCount = atomContainer.getBondCount();
    
    sptSize = 0;edrSize = 0;
    fastFindInit(totalVertexCount);
    for (int i = 0; i < totalVertexCount; i++) {
      (atomContainer.getAtom(i)).setProperty(ATOM_NUMBER, Integer.toString(i+1));
    }
    IBond bond;
    int v1,v2;
    bondsInTree = new boolean[totalEdgeCount];
    
    for (int b=0; b < totalEdgeCount; b++ ) {
      bondsInTree[b] = false;      
      bond = atomContainer.getBond(b);
      v1 = Integer.parseInt((bond.getAtom(0)).getProperty(ATOM_NUMBER).toString());
      v2 = Integer.parseInt((bond.getAtom(1)).getProperty(ATOM_NUMBER).toString());
      //this below is a little bit  slower
      //v1 = atomContainer.getAtomNumber(bond.getAtomAt(0))+1; 
      //v2 = atomContainer.getAtomNumber(bond.getAtomAt(1))+1;
      if (fastfind(v1,v2,true)) {
        bondsInTree[b] = true;
        sptSize++;
        //logger.debug("ST : includes bond between atoms "+v1+","+v2);
      }
      if (sptSize>=(totalVertexCount-1)) break;
      
    }
    // if atomcontainer is connected then the number of bonds in the spanning tree = (No atoms-1)
    //i.e.  edgesRings = new Bond[E-V+1];
    //but to hold all bonds if atomContainer was disconnected then  edgesRings = new Bond[E-sptSize]; 
    if (sptSize != (totalVertexCount-1)) disconnected = true;
    for (int b=0; b < totalEdgeCount; b++ ) if (!bondsInTree[b]){
//      edgesRings[edrSize] = atomContainer.getBondAt(b);
      edrSize++;
    }
    cb = new int[edrSize][totalEdgeCount];
    for (int i = 0; i < edrSize; i++) 
      for (int a = 0; a < totalEdgeCount; a++) 
        cb[i][a] = 0;
    
    // remove ATOM_NUMBER props again
    for (IAtom atom : atomContainer.atoms()) atom.removeProperty(ATOM_NUMBER);
  }

    @TestMethod("testGetSpanningTree")
    public IAtomContainer getSpanningTree() {
    IAtomContainer ac = molecule.getBuilder().newInstance(IAtomContainer.class);
    for (int a=0 ; a < totalVertexCount; a++) ac.addAtom(molecule.getAtom(a));
    for (int b=0; b < totalEdgeCount; b++ ) if (bondsInTree[b])
      ac.addBond(molecule.getBond(b));
    return ac;
  }

    @TestMethod("testGetPath_IAtomContainer_IAtom_IAtom")
    public IAtomContainer getPath(IAtomContainer spt,IAtom a1, IAtom a2) throws NoSuchAtomException {
    IAtomContainer path = spt.getBuilder().newInstance(IAtomContainer.class);
    PathTools.resetFlags(spt);
    path.addAtom(a1);
    PathTools.depthFirstTargetSearch(spt,a1,a2,path);
    if (path.getAtomCount() == 1) path.removeAtom(a1); // no path found: remove initial atom
    return path;
  }

  private IRing getRing(IAtomContainer spt, IBond bond) {
    IRing ring = spt.getBuilder().newInstance(IRing.class);
    PathTools.resetFlags(spt);
    ring.addAtom(bond.getAtom(0));    
    PathTools.depthFirstTargetSearch(spt,bond.getAtom(0),bond.getAtom(1),ring);    
    ring.addBond(bond);
    return ring;
  }
  
  private void getBondsInRing(IAtomContainer mol, IRing ring, int[] bonds) {
    for (int i=0; i < ring.getBondCount(); i++ ) {
      int m = mol.getBondNumber(ring.getBond(i));
      bonds[m] = 1;
    }
  }

    @TestMethod("testGetBasicRings")
    public IRingSet getBasicRings() throws NoSuchAtomException {
    IRingSet ringset = molecule.getBuilder().newInstance(IRingSet.class);
    IAtomContainer spt = getSpanningTree();
    for (int i = 0; i < totalEdgeCount; i++) if (!bondsInTree[i])  
      ringset.addAtomContainer(getRing(spt,molecule.getBond(i)));
    return ringset;
  }

  /**
   * Returns an IAtomContainer which contains all the atoms and bonds which
   * are involved in ring systems.
   * 
   * @see #getAllRings()
   * @see #getBasicRings()
     * @return the IAtomContainer as described above
   */
    @TestMethod("testGetCyclicFragmentsContainer")
    public IAtomContainer getCyclicFragmentsContainer()  {
        IAtomContainer fragContainer = this.molecule.getBuilder().newInstance(IAtomContainer.class);
        IAtomContainer spt = getSpanningTree();

        for (int i = 0; i < totalEdgeCount; i++)
            if (!bondsInTree[i]) {
                IRing ring = getRing(spt, molecule.getBond(i));
                for (int b = 0; b < ring.getBondCount(); b++) {
                    IBond ringBond = ring.getBond(b);
                    if (!fragContainer.contains(ringBond)) {
                        fragContainer.addBond(ringBond);
                        for (int atomCount = 0; atomCount < ringBond.getAtomCount(); atomCount++) {
                            IAtom atom = ringBond.getAtom(atomCount);
                            if (!fragContainer.contains(atom)) {
                                atom.setFlag(CDKConstants.ISINRING, true);
                                fragContainer.addAtom(atom);
                            }
                        }
                    }
                }
            }
        return fragContainer;
    }

    /**
   * Identifies whether bonds are cyclic or not. It is used by several other methods.
   */
  private void identifyBonds() {
    IAtomContainer spt = getSpanningTree();
    IRing ring;
    int nBasicRings = 0;
    for (int i = 0; i < totalEdgeCount; i++) {
      if (!bondsInTree[i]) {  
        ring = getRing(spt,molecule.getBond(i));
        for (int b=0; b < ring.getBondCount(); b++ ) {
          int m = molecule.getBondNumber(ring.getBond(b));
          cb[nBasicRings][m] = 1;
        }
        nBasicRings++;
      }
    }
    bondsAcyclicCount = 0; bondsCyclicCount = 0;
    for (int i = 0; i < totalEdgeCount; i++) {
      int s = 0;
      for (int j = 0; j < nBasicRings; j++) {
        s+= cb[j][i];
      }
      switch(s) {
        case(0): { bondsAcyclicCount++; break; }
        case(1): { bondsCyclicCount ++; break; }
        default: { bondsCyclicCount ++; }
      }        
    }
    identifiedBonds = true;
  }


    @TestMethod("testGetAllRings")
    public IRingSet getAllRings() throws NoSuchAtomException {
    IRingSet ringset = getBasicRings();
    IRing newring;
    
    int nBasicRings = ringset.getAtomContainerCount();
    for (int i = 0; i < nBasicRings; i++) 
      getBondsInRing(molecule,(IRing) ringset.getAtomContainer(i), cb[i]);
    
    for (int i= 0; i < nBasicRings; i++) {
      for (int j= i+1; j < nBasicRings; j++) {
        //logger.debug("combining rings "+(i+1)+","+(j+1));
        newring = combineRings(ringset, i, j);        
        //newring = combineRings((Ring)ringset.get(i),(Ring)ringset.get(j));
        if (newring != null) ringset.addAtomContainer(newring);
      }
    }
      
    return ringset;
  }  

    @TestMethod("testGetSpanningTreeSize")
    public int getSpanningTreeSize() {
    return sptSize;
  }

  private IRing combineRings(IRingSet ringset, int i, int j) {
    int c = 0;
    for (int b= 0; b < cb[i].length; b++) {
      c = cb[i][b] + cb[j][b];
      if (c > 1) break;  //at least one common bond
    }
    if (c < 2) return null;
    IRing ring = molecule.getBuilder().newInstance(IRing.class);
    IRing ring1 = (IRing) ringset.getAtomContainer(i);
    IRing ring2 = (IRing) ringset.getAtomContainer(j);
    for (int b= 0; b < cb[i].length; b++) {
      c = cb[i][b] + cb[j][b];
      if ((c == 1) && (cb[i][b] == 1)) ring.addBond(molecule.getBond(b));
      else
      if ((c == 1) && (cb[j][b] == 1)) ring.addBond(molecule.getBond(b));      
    }
    for (int a = 0; a < ring1.getAtomCount(); a++) 
      ring.addAtom(ring1.getAtom(a));
    for (int a = 0; a < ring2.getAtomCount(); a++) 
      ring.addAtom(ring2.getAtom(a));
    
    return ring;
  }

  /**
   * @return Returns the bondsAcyclicCount.
   */
    @TestMethod("testGetBondsAcyclicCount")
    public int getBondsAcyclicCount() {
    if (!identifiedBonds) identifyBonds();
    return bondsAcyclicCount;
  }

  /**
   * @return Returns the bondsCyclicCount.
   */
    @TestMethod("testGetBondsCyclicCount")
    public int getBondsCyclicCount()  {
    if (!identifiedBonds) identifyBonds();
    return bondsCyclicCount;
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.