// // Copyright (C) 2014 Greg Landrum // Adapted from pseudo-code from Roger Sayle // // @@ All Rights Reserved @@ // This file is part of the RDKit. // The contents are covered by the terms of the BSD license // which is included in the file license.txt, found at the root // of the RDKit source tree. // #include "new_canon.h" #include #include #include #include #include #include namespace RDKit { namespace Canon{ void CreateSinglePartition(unsigned int nAtoms, int *order, int *count, canon_atom *atoms) { for( unsigned int i=0; i 1 ) { next[j] = activeset; activeset = j; i += count[j]; } else i++; } while( i < nAtoms ); for( i=0; i neighbors; neighbors.push_back(idx); char *visited=(char *)malloc(nAtoms*sizeof(char)); memset(visited,0,nAtoms*sizeof(char)); unsigned count = 1; std::vector nextLevelNbrs; char *lastLevelNbrs=(char *)malloc(nAtoms*sizeof(char)); memset(lastLevelNbrs,0,nAtoms*sizeof(char)); char *currentLevelNbrs=(char *)malloc(nAtoms*sizeof(char)); memset(currentLevelNbrs,0,nAtoms*sizeof(char)); while(!neighbors.empty()){ unsigned int revisitedNeighbors=0; unsigned int numLevelNbrs=0; nextLevelNbrs.resize(0); while(!neighbors.empty()){ int nidx = neighbors.front(); const Canon::canon_atom &atom = atoms[nidx]; lastLevelNbrs[nidx]=1; visited[nidx]=1; neighbors.pop_front(); for(unsigned int j=0; j void rankWithFunctor(T &ftor, bool breakTies, int *order, bool useSpecial=false, bool useChirality=false, const boost::dynamic_bitset<> *atomsInPlay=NULL, const boost::dynamic_bitset<> *bondsInPlay=NULL){ const ROMol &mol=*ftor.dp_mol; canon_atom *atoms=ftor.dp_atoms; unsigned int nAts=mol.getNumAtoms(); int *count=(int *)malloc(nAts*sizeof(int)); int activeset; int *next=(int *)malloc(nAts*sizeof(int)); int *changed=(int *)malloc(nAts*sizeof(int)); char *touched=(char *)malloc(nAts*sizeof(char)); memset(touched,0,nAts*sizeof(char)); memset(changed,1,nAts*sizeof(int)); CreateSinglePartition(nAts,order,count,atoms); //ActivatePartitions(nAts,order,count,activeset,next,changed); //RefinePartitions(mol,atoms,ftor,false,order,count,activeset,next,changed,touched); #ifdef VERBOSE_CANON std::cerr<<"1--------"<isInitialized()){ ringInfo->initialize(); } for(unsigned i=0; inumAtomRings(ftor.dp_atoms[i].atom->getIdx()) > 1){ if(count[i] != 1){ symRingAtoms += 1; } } if(count[i]){ countCls++; } else{ ties=true; } } unsigned int nAts2 = atomsInPlay ? atomsInPlay->count() : nAts; if(useSpecial && ties && static_cast(countCls)/nAts2 < 0.5 && symRingAtoms>0){ SpecialSymmetryAtomCompareFunctor sftor(atoms,mol,atomsInPlay,bondsInPlay); compareRingAtomsConcerningNumNeighbors(atoms, nAts); ActivatePartitions(nAts,order,count,activeset,next,changed); RefinePartitions(mol,atoms,sftor,true,order,count,activeset,next,changed,touched); #ifdef VERBOSE_CANON std::cerr<<"2b--------"<getChiralTag()==Atom::CHI_TETRAHEDRAL_CW || nbr->getChiralTag()==Atom::CHI_TETRAHEDRAL_CCW) && nbr->hasProp(common_properties::_ringStereoAtoms)){ return true; } } return false; } void getNbrs(const ROMol &mol,const Atom *at, int *ids){ ROMol::OEDGE_ITER beg,end; boost::tie(beg,end) = mol.getAtomBonds(at); unsigned int idx=0; while(beg!=end){ const BOND_SPTR bond=(mol)[*beg]; ++beg; unsigned int nbrIdx = bond->getOtherAtomIdx(at->getIdx()); ids[idx] = nbrIdx; ++idx; } } void getBonds(const ROMol &mol,const Atom *at,std::vector &nbrs,bool includeChirality){ ROMol::OEDGE_ITER beg,end; boost::tie(beg,end) = mol.getAtomBonds(at); while(beg!=end){ const BOND_SPTR bond=(mol)[*beg]; ++beg; Bond::BondStereo stereo=Bond::STEREONONE; if(includeChirality){ stereo = bond->getStereo(); if(stereo == Bond::STEREOANY){ stereo=Bond::STEREONONE; } } unsigned int idx = bond->getOtherAtomIdx(at->getIdx()); Bond::BondType bt = bond->getIsAromatic() ? Bond::AROMATIC : bond->getBondType(); bondholder bh(bondholder(bt,stereo,idx,0)); nbrs.push_back(bh); } std::sort(nbrs.begin(),nbrs.end(),bondholder::greater); } void getChiralBonds(const ROMol &mol,const Atom *at,std::vector &nbrs){ ROMol::OEDGE_ITER beg,end; boost::tie(beg,end) = mol.getAtomBonds(at); while(beg!=end){ const BOND_SPTR bond=(mol)[*beg]; ++beg; unsigned int nbrIdx = bond->getOtherAtomIdx(at->getIdx()); const Atom* nbr = mol.getAtomWithIdx(nbrIdx); unsigned int degreeNbr = nbr->getDegree(); unsigned int nReps=1; unsigned int stereo=0; switch(bond->getStereo()){ case Bond::STEREOZ: stereo=1; break; case Bond::STEREOE: stereo=2; break; default: stereo=0; } if(bond->getBondType() == Bond::DOUBLE && nbr->getAtomicNum()==15 && (degreeNbr==4 || degreeNbr==3) ) { // a special case for chiral phophorous compounds // (this was leading to incorrect assignment of // R/S labels ): nReps=1; // general justification of this is: // Paragraph 2.2. in the 1966 article is "Valence-Bond Conventions: // Multiple-Bond Unsaturation and Aromaticity". It contains several // conventions of which convention (b) is the one applying here: // "(b) Contibutions by d orbitals to bonds of quadriligant atoms are // neglected." // FIX: this applies to more than just P } else { nReps = static_cast(floor(2.*bond->getBondTypeAsDouble())); } unsigned int symclass = nbr->getAtomicNum()*ATNUM_CLASS_OFFSET+nbrIdx+1; bondholder bh(bondholder(Bond::SINGLE,stereo,nbrIdx,symclass)); std::vector::iterator iPos=std::lower_bound(nbrs.begin(),nbrs.end(),bh); nbrs.insert(iPos,nReps,bh); } std::reverse(nbrs.begin(),nbrs.end()); if(!at->needsUpdatePropertyCache()){ for(unsigned int ii=0;iigetTotalNumHs();++ii){ nbrs.push_back(bondholder(Bond::SINGLE,Bond::STEREONONE,ATNUM_CLASS_OFFSET,ATNUM_CLASS_OFFSET)); nbrs.push_back(bondholder(Bond::SINGLE,Bond::STEREONONE,ATNUM_CLASS_OFFSET,ATNUM_CLASS_OFFSET)); } } } void basicInitCanonAtom(const ROMol &mol,Canon::canon_atom &atom,const int &idx){ atom.atom=mol.getAtomWithIdx(idx); atom.index=idx; atom.p_symbol=NULL; atom.degree=atom.atom->getDegree(); atom.nbrIds=(int *)malloc(atom.degree*sizeof(int)); getNbrs(mol, atom.atom,atom.nbrIds); } void advancedInitCanonAtom(const ROMol &mol,Canon::canon_atom &atom,const int &idx){ atom.totalNumHs=atom.atom->getTotalNumHs(); atom.isRingStereoAtom=(atom.atom->getChiralTag()==Atom::CHI_TETRAHEDRAL_CW || atom.atom->getChiralTag()==Atom::CHI_TETRAHEDRAL_CCW) && atom.atom->hasProp("_ringStereoAtoms"); atom.hasRingNbr=hasRingNbr(mol,atom.atom); } }//end anonymous namespace void initCanonAtoms(const ROMol &mol,std::vector &atoms, bool includeChirality){ for(unsigned int i=0;i &atoms, bool includeChirality, const std::vector *atomSymbols){ for(unsigned int i=0;i &atoms){ for(unsigned int i=0;i &atoms){ for(unsigned int i=0;i &nbrs) { for(unsigned j=0; j < nbrs.size(); ++j){ unsigned nbrIdx = nbrs[j].nbrIdx; unsigned newSymClass = atoms[nbrIdx].index; nbrs.at(j).nbrSymClass = newSymClass; } std::sort(nbrs.begin(),nbrs.end(),bondholder::greater); } void updateAtomNeighborNumSwaps(canon_atom* atoms, std::vector &nbrs, unsigned int atomIdx, std::vector >& result) { for(unsigned j=0; j < nbrs.size(); ++j){ unsigned nbrIdx = nbrs[j].nbrIdx; if(atoms[nbrIdx].atom->getChiralTag()!=0){ std::vector ref,probe; for(unsigned i=0; i(countSwapsToInterconvert(ref,probe)); if(atoms[nbrIdx].atom->getChiralTag() == Atom::CHI_TETRAHEDRAL_CW){ if(nSwaps%2){ result.push_back(std::make_pair(nbrs[j].nbrSymClass, 2)); } else{ result.push_back(std::make_pair(nbrs[j].nbrSymClass, 1)); } } else if(atoms[nbrIdx].atom->getChiralTag() == Atom::CHI_TETRAHEDRAL_CCW){ if(nSwaps%2){ result.push_back(std::make_pair(nbrs[j].nbrSymClass, 1)); } else{ result.push_back(std::make_pair(nbrs[j].nbrSymClass, 2)); } } } else{ result.push_back(std::make_pair(nbrs[j].nbrSymClass, 0)); } } sort(result.begin(),result.end()); } void rankMolAtoms(const ROMol &mol,std::vector &res, bool breakTies, bool includeChirality,bool includeIsotopes) { std::vector atoms(mol.getNumAtoms()); initCanonAtoms(mol,atoms,includeChirality); AtomCompareFunctor ftor(&atoms.front(),mol); ftor.df_useIsotopes=includeIsotopes; ftor.df_useChirality=includeChirality; ftor.df_useChiralityRings=includeChirality; int *order=(int *)malloc(mol.getNumAtoms()*sizeof(int)); rankWithFunctor(ftor,breakTies,order,true,includeChirality); res.resize(mol.getNumAtoms()); for(unsigned int i=0;i &res, const boost::dynamic_bitset<> &atomsInPlay, const boost::dynamic_bitset<> &bondsInPlay, const std::vector *atomSymbols, bool breakTies, bool includeChirality,bool includeIsotopes) { PRECONDITION(atomsInPlay.size()==mol.getNumAtoms(),"bad atomsInPlay size"); PRECONDITION(bondsInPlay.size()==mol.getNumBonds(),"bad bondsInPlay size"); PRECONDITION(!atomSymbols || atomSymbols->size()==mol.getNumAtoms(),"bad atomSymbols size"); std::vector atoms(mol.getNumAtoms()); initFragmentCanonAtoms(mol,atoms,includeChirality, atomSymbols); for(ROMol::ConstBondIterator bI=mol.beginBonds(); bI!=mol.endBonds();++bI){ if(!bondsInPlay[(*bI)->getIdx()]) continue; atoms[(*bI)->getBeginAtomIdx()].degree++; atoms[(*bI)->getEndAtomIdx()].degree++; } AtomCompareFunctor ftor(&atoms.front(),mol, &atomsInPlay,&bondsInPlay); ftor.df_useIsotopes=includeIsotopes; ftor.df_useChirality=includeChirality; int *order=(int *)malloc(mol.getNumAtoms()*sizeof(int)); rankWithFunctor(ftor,breakTies,order,true,includeChirality,&atomsInPlay,&bondsInPlay); res.resize(mol.getNumAtoms()); for(unsigned int i=0;i &res){ std::vector atoms(mol.getNumAtoms()); initChiralCanonAtoms(mol,atoms); ChiralAtomCompareFunctor ftor(&atoms.front(),mol); int *order=(int *)malloc(mol.getNumAtoms()*sizeof(int)); rankWithFunctor(ftor,false,order); res.resize(mol.getNumAtoms()); for(unsigned int i=0;i