INT_LIST getShortestPath(const ROMol &mol, int aid1, int aid2) { int nats = mol.getNumAtoms(); RANGE_CHECK(0,aid1,nats-1); RANGE_CHECK(0,aid2,nats-1); CHECK_INVARIANT(aid1 != aid2, ""); INT_VECT pred, doneAtms; //pred.reserve(nats); //doneAtms.reserve(nats); pred.resize(nats); doneAtms.resize(nats); int ai; for (ai = 0; ai < nats; ++ai) { doneAtms[ai] = 0; } std::deque<int> bfsQ; bfsQ.push_back(aid1); bool done = false; ROMol::ADJ_ITER nbrIdx,endNbrs; while ((!done) && (bfsQ.size() > 0)) { int curAid = bfsQ.front(); boost::tie(nbrIdx,endNbrs) = mol.getAtomNeighbors(mol.getAtomWithIdx(curAid)); while (nbrIdx != endNbrs) { if (doneAtms[*nbrIdx] == 0) { pred[*nbrIdx] = curAid; if (static_cast<int>(*nbrIdx) == aid2) { done = true; break; } bfsQ.push_back(*nbrIdx); } nbrIdx++; } doneAtms[curAid] = 1; bfsQ.pop_front(); } INT_LIST res; if(done){ done = false; int prev = aid2; res.push_back(aid2); while (!done) { prev = pred[prev]; if (prev != aid1) { res.push_front(prev); } else { done = true; } } res.push_front(aid1); } return res; }
// Check the chirality of atoms not directly involved in the reaction void checkAndCorrectChiralityOfProduct( const std::vector<const Atom *> &chiralAtomsToCheck, RWMOL_SPTR product, ReactantProductAtomMapping *mapping) { for (auto reactantAtom : chiralAtomsToCheck) { CHECK_INVARIANT(reactantAtom->getChiralTag() != Atom::CHI_UNSPECIFIED, "missing atom chirality."); const auto reactAtomDegree = reactantAtom->getOwningMol().getAtomDegree(reactantAtom); for (unsigned i = 0; i < mapping->reactProdAtomMap[reactantAtom->getIdx()].size(); i++) { unsigned productAtomIdx = mapping->reactProdAtomMap[reactantAtom->getIdx()][i]; Atom *productAtom = product->getAtomWithIdx(productAtomIdx); CHECK_INVARIANT( reactantAtom->getChiralTag() == productAtom->getChiralTag(), "invalid product chirality."); if (reactAtomDegree != product->getAtomDegree(productAtom)) { // If the number of bonds to the atom has changed in the course of the // reaction we're lost, so remove chirality. // A word of explanation here: the atoms in the chiralAtomsToCheck set // are not explicitly mapped atoms of the reaction, so we really have // no idea what to do with this case. At the moment I'm not even really // sure how this could happen, but better safe than sorry. productAtom->setChiralTag(Atom::CHI_UNSPECIFIED); } else if (reactantAtom->getChiralTag() == Atom::CHI_TETRAHEDRAL_CW || reactantAtom->getChiralTag() == Atom::CHI_TETRAHEDRAL_CCW) { // this will contain the indices of product bonds in the // reactant order: INT_LIST newOrder; ROMol::OEDGE_ITER beg, end; boost::tie(beg, end) = reactantAtom->getOwningMol().getAtomBonds(reactantAtom); while (beg != end) { const Bond *reactantBond = reactantAtom->getOwningMol()[*beg]; unsigned int oAtomIdx = reactantBond->getOtherAtomIdx(reactantAtom->getIdx()); CHECK_INVARIANT(mapping->reactProdAtomMap.find(oAtomIdx) != mapping->reactProdAtomMap.end(), "other atom from bond not mapped."); const Bond *productBond; unsigned neighborBondIdx = mapping->reactProdAtomMap[oAtomIdx][i]; productBond = product->getBondBetweenAtoms(productAtom->getIdx(), neighborBondIdx); CHECK_INVARIANT(productBond, "no matching bond found in product"); newOrder.push_back(productBond->getIdx()); ++beg; } int nSwaps = productAtom->getPerturbationOrder(newOrder); if (nSwaps % 2) { productAtom->invertChirality(); } } else { // not tetrahedral chirality, don't do anything. } } } // end of loop over chiralAtomsToCheck }
void dfsBuildStack(ROMol &mol,int atomIdx,int inBondIdx, std::vector<AtomColors> &colors, VECT_INT_VECT &cycles, const UINT_VECT &ranks, INT_VECT &cyclesAvailable, MolStack &molStack, INT_VECT &atomOrders, INT_VECT &bondVisitOrders, VECT_INT_VECT &atomRingClosures, std::vector<INT_LIST> &atomTraversalBondOrder, const boost::dynamic_bitset<> *bondsInPlay, const std::vector<std::string> *bondSymbols ){ #if 0 std::cerr<<"traverse from atom: "<<atomIdx<<" via bond "<<inBondIdx<<" num cycles available: " <<std::count(cyclesAvailable.begin(),cyclesAvailable.end(),1)<<std::endl; #endif Atom *atom = mol.getAtomWithIdx(atomIdx); INT_LIST directTravList,cycleEndList; boost::dynamic_bitset<> seenFromHere(mol.getNumAtoms()); seenFromHere.set(atomIdx); molStack.push_back(MolStackElem(atom)); atomOrders[atom->getIdx()] = molStack.size(); colors[atomIdx] = GREY_NODE; INT_LIST travList; if(inBondIdx>=0) travList.push_back(inBondIdx); // --------------------- // // Add any ring closures // // --------------------- if(atomRingClosures[atomIdx].size()){ std::vector<unsigned int> ringsClosed; BOOST_FOREACH(int bIdx,atomRingClosures[atomIdx]){ travList.push_back(bIdx); Bond *bond = mol.getBondWithIdx(bIdx); seenFromHere.set(bond->getOtherAtomIdx(atomIdx)); unsigned int ringIdx; if(bond->getPropIfPresent(common_properties::_TraversalRingClosureBond, ringIdx)){ // this is end of the ring closure // we can just pull the ring index from the bond itself: molStack.push_back(MolStackElem(bond,atomIdx)); bondVisitOrders[bIdx]=molStack.size(); molStack.push_back(MolStackElem(ringIdx)); // don't make the ring digit immediately available again: we don't want to have the same // ring digit opening and closing rings on an atom. ringsClosed.push_back(ringIdx-1); } else { // this is the beginning of the ring closure, we need to come up with a ring index: INT_VECT::const_iterator cAIt=std::find(cyclesAvailable.begin(), cyclesAvailable.end(),1); if(cAIt==cyclesAvailable.end()){ throw ValueErrorException("Too many rings open at once. SMILES cannot be generated."); } unsigned int lowestRingIdx = cAIt-cyclesAvailable.begin(); cyclesAvailable[lowestRingIdx] = 0; ++lowestRingIdx; bond->setProp(common_properties::_TraversalRingClosureBond,lowestRingIdx); molStack.push_back(MolStackElem(lowestRingIdx)); } }
void checkAndCorrectChiralityOfMatchingAtomsInProduct( const ROMol &reactant, unsigned reactantAtomIdx, const Atom &reactantAtom, RWMOL_SPTR product, ReactantProductAtomMapping *mapping) { for (unsigned i = 0; i < mapping->reactProdAtomMap[reactantAtomIdx].size(); i++) { unsigned productAtomIdx = mapping->reactProdAtomMap[reactantAtomIdx][i]; Atom *productAtom = product->getAtomWithIdx(productAtomIdx); if (productAtom->getChiralTag() != Atom::CHI_UNSPECIFIED || reactantAtom.getChiralTag() == Atom::CHI_UNSPECIFIED || reactantAtom.getChiralTag() == Atom::CHI_OTHER || productAtom->hasProp(common_properties::molInversionFlag)) { continue; } // we can only do something sensible here if we have the same number of // bonds in the reactants and the products: if (reactantAtom.getDegree() != productAtom->getDegree()) { continue; } unsigned int nUnknown = 0; INT_LIST pOrder; ROMol::ADJ_ITER nbrIdx, endNbrs; boost::tie(nbrIdx, endNbrs) = product->getAtomNeighbors(productAtom); while (nbrIdx != endNbrs) { if (mapping->prodReactAtomMap.find(*nbrIdx) == mapping->prodReactAtomMap.end() || !reactant.getBondBetweenAtoms(reactantAtom.getIdx(), mapping->prodReactAtomMap[*nbrIdx])) { ++nUnknown; // if there's more than one bond in the product that doesn't correspond // to anything in the reactant, we're also doomed if (nUnknown > 1) break; // otherwise, add a -1 to the bond order that we'll fill in later pOrder.push_back(-1); } else { const Bond *rBond = reactant.getBondBetweenAtoms( reactantAtom.getIdx(), mapping->prodReactAtomMap[*nbrIdx]); CHECK_INVARIANT(rBond, "expected reactant bond not found"); pOrder.push_back(rBond->getIdx()); } ++nbrIdx; } if (nUnknown == 1) { // find the reactant bond that hasn't yet been accounted for: int unmatchedBond = -1; boost::tie(nbrIdx, endNbrs) = reactant.getAtomNeighbors(&reactantAtom); while (nbrIdx != endNbrs) { const Bond *rBond = reactant.getBondBetweenAtoms(reactantAtom.getIdx(), *nbrIdx); if (std::find(pOrder.begin(), pOrder.end(), rBond->getIdx()) == pOrder.end()) { unmatchedBond = rBond->getIdx(); break; } ++nbrIdx; } // what must be true at this point: // 1) there's a -1 in pOrder that we'll substitute for // 2) unmatchedBond contains the index of the substitution auto bPos = std::find(pOrder.begin(), pOrder.end(), -1); if (unmatchedBond >= 0 && bPos != pOrder.end()) { *bPos = unmatchedBond; } if (std::find(pOrder.begin(), pOrder.end(), -1) == pOrder.end()) { nUnknown = 0; } } if (!nUnknown) { productAtom->setChiralTag(reactantAtom.getChiralTag()); int nSwaps = reactantAtom.getPerturbationOrder(pOrder); if (nSwaps % 2) { productAtom->invertChirality(); } } } }
void iterateCIPRanks(const ROMol &mol, DOUBLE_VECT &invars, UINT_VECT &ranks, bool seedWithInvars) { PRECONDITION(invars.size() == mol.getNumAtoms(), "bad invars size"); PRECONDITION(ranks.size() >= mol.getNumAtoms(), "bad ranks size"); unsigned int numAtoms = mol.getNumAtoms(); CIP_ENTRY_VECT cipEntries(numAtoms); INT_LIST allIndices; for (unsigned int i = 0; i < numAtoms; ++i) { allIndices.push_back(i); } #ifdef VERBOSE_CANON BOOST_LOG(rdDebugLog) << "invariants:" << std::endl; for (unsigned int i = 0; i < numAtoms; i++) { BOOST_LOG(rdDebugLog) << i << ": " << invars[i] << std::endl; } #endif // rank those: Rankers::rankVect(invars, ranks); #ifdef VERBOSE_CANON BOOST_LOG(rdDebugLog) << "initial ranks:" << std::endl; for (unsigned int i = 0; i < numAtoms; ++i) { BOOST_LOG(rdDebugLog) << i << ": " << ranks[i] << std::endl; } #endif // Start each atom's rank vector with its atomic number: // Note: in general one should avoid the temptation to // use invariants here, those lead to incorrect answers for (unsigned int i = 0; i < numAtoms; i++) { if (!seedWithInvars) { cipEntries[i].push_back(mol[i]->getAtomicNum()); cipEntries[i].push_back(static_cast<int>(ranks[i])); } else { cipEntries[i].push_back(static_cast<int>(invars[i])); } } // Loop until either: // 1) all classes are uniquified // 2) the number of ranks doesn't change from one iteration to // the next // 3) we've gone through maxIts times // maxIts is calculated by dividing the number of atoms // by 2. That's a pessimal version of the // maximum number of steps required for two atoms to // "feel" each other (each influences one additional // neighbor shell per iteration). unsigned int maxIts = numAtoms / 2 + 1; unsigned int numIts = 0; int lastNumRanks = -1; unsigned int numRanks = *std::max_element(ranks.begin(), ranks.end()) + 1; while (numRanks < numAtoms && numIts < maxIts && (lastNumRanks < 0 || static_cast<unsigned int>(lastNumRanks) < numRanks)) { unsigned int longestEntry = 0; // ---------------------------------------------------- // // for each atom, get a sorted list of its neighbors' ranks: // for (INT_LIST_I it = allIndices.begin(); it != allIndices.end(); ++it) { CIP_ENTRY localEntry; localEntry.reserve(16); // start by pushing on our neighbors' ranks: ROMol::OEDGE_ITER beg, end; boost::tie(beg, end) = mol.getAtomBonds(mol[*it].get()); while (beg != end) { const Bond *bond = mol[*beg].get(); ++beg; unsigned int nbrIdx = bond->getOtherAtomIdx(*it); const Atom *nbr = mol[nbrIdx].get(); int rank = ranks[nbrIdx] + 1; // put the neighbor in 2N times where N is the bond order as a double. // this is to treat aromatic linkages on fair footing. i.e. at least in // the // first iteration --c(:c):c and --C(=C)-C should look the same. // this was part of issue 3009911 unsigned int count; if (bond->getBondType() == Bond::DOUBLE && nbr->getAtomicNum() == 15 && (nbr->getDegree() == 4 || nbr->getDegree() == 3)) { // a special case for chiral phophorous compounds // (this was leading to incorrect assignment of // R/S labels ): count = 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 { count = static_cast<unsigned int>( floor(2. * bond->getBondTypeAsDouble() + .1)); } CIP_ENTRY::iterator ePos = std::lower_bound(localEntry.begin(), localEntry.end(), rank); localEntry.insert(ePos, count, rank); ++nbr; } // add a zero for each coordinated H: // (as long as we're not a query atom) if (!mol[*it]->hasQuery()) { localEntry.insert(localEntry.begin(), mol[*it]->getTotalNumHs(), 0); } // we now have a sorted list of our neighbors' ranks, // copy it on in reversed order: cipEntries[*it].insert(cipEntries[*it].end(), localEntry.rbegin(), localEntry.rend()); if (cipEntries[*it].size() > longestEntry) { longestEntry = rdcast<unsigned int>(cipEntries[*it].size()); } } // ---------------------------------------------------- // // pad the entries so that we compare rounds to themselves: // for (INT_LIST_I it = allIndices.begin(); it != allIndices.end(); ++it) { unsigned int sz = rdcast<unsigned int>(cipEntries[*it].size()); if (sz < longestEntry) { cipEntries[*it].insert(cipEntries[*it].end(), longestEntry - sz, -1); } } // ---------------------------------------------------- // // sort the new ranks and update the list of active indices: // lastNumRanks = numRanks; Rankers::rankVect(cipEntries, ranks); numRanks = *std::max_element(ranks.begin(), ranks.end()) + 1; // now truncate each vector and stick the rank at the end for (unsigned int i = 0; i < numAtoms; ++i) { cipEntries[i][numIts + 1] = ranks[i]; cipEntries[i].erase(cipEntries[i].begin() + numIts + 2, cipEntries[i].end()); } ++numIts; #ifdef VERBOSE_CANON BOOST_LOG(rdDebugLog) << "strings and ranks:" << std::endl; for (unsigned int i = 0; i < numAtoms; i++) { BOOST_LOG(rdDebugLog) << i << ": " << ranks[i] << " > "; debugVect(cipEntries[i]); } #endif } }
// // Determine bond wedge state /// Bond::BondDir DetermineBondWedgeState(const Bond *bond, const INT_MAP_INT &wedgeBonds, const Conformer *conf) { PRECONDITION(bond, "no bond"); PRECONDITION(bond->getBondType() == Bond::SINGLE, "bad bond order for wedging"); const ROMol *mol = &(bond->getOwningMol()); PRECONDITION(mol, "no mol"); Bond::BondDir res = bond->getBondDir(); if (!conf) { return res; } int bid = bond->getIdx(); INT_MAP_INT_CI wbi = wedgeBonds.find(bid); if (wbi == wedgeBonds.end()) { return res; } unsigned int waid = wbi->second; Atom *atom, *bondAtom; // = bond->getBeginAtom(); if (bond->getBeginAtom()->getIdx() == waid) { atom = bond->getBeginAtom(); bondAtom = bond->getEndAtom(); } else { atom = bond->getEndAtom(); bondAtom = bond->getBeginAtom(); } Atom::ChiralType chiralType = atom->getChiralTag(); CHECK_INVARIANT(chiralType == Atom::CHI_TETRAHEDRAL_CW || chiralType == Atom::CHI_TETRAHEDRAL_CCW, ""); // if we got this far, we really need to think about it: INT_LIST neighborBondIndices; DOUBLE_LIST neighborBondAngles; RDGeom::Point3D centerLoc, tmpPt; centerLoc = conf->getAtomPos(atom->getIdx()); tmpPt = conf->getAtomPos(bondAtom->getIdx()); centerLoc.z = 0.0; tmpPt.z = 0.0; RDGeom::Point3D refVect = centerLoc.directionVector(tmpPt); neighborBondIndices.push_back(bond->getIdx()); neighborBondAngles.push_back(0.0); ROMol::OEDGE_ITER beg, end; boost::tie(beg, end) = mol->getAtomBonds(atom); while (beg != end) { Bond *nbrBond = (*mol)[*beg].get(); Atom *otherAtom = nbrBond->getOtherAtom(atom); if (nbrBond != bond) { tmpPt = conf->getAtomPos(otherAtom->getIdx()); tmpPt.z = 0.0; RDGeom::Point3D tmpVect = centerLoc.directionVector(tmpPt); double angle = refVect.signedAngleTo(tmpVect); if (angle < 0.0) angle += 2. * M_PI; INT_LIST::iterator nbrIt = neighborBondIndices.begin(); DOUBLE_LIST::iterator angleIt = neighborBondAngles.begin(); // find the location of this neighbor in our angle-sorted list // of neighbors: while (angleIt != neighborBondAngles.end() && angle > (*angleIt)) { ++angleIt; ++nbrIt; } neighborBondAngles.insert(angleIt, angle); neighborBondIndices.insert(nbrIt, nbrBond->getIdx()); } ++beg; } // at this point, neighborBondIndices contains a list of bond // indices from the central atom. They are arranged starting // at the reference bond in CCW order (based on the current // depiction). int nSwaps = atom->getPerturbationOrder(neighborBondIndices); // in the case of three-coordinated atoms we may have to worry about // the location of the implicit hydrogen - Issue 209 // Check if we have one of these situation // // 0 1 0 2 // * \*/ // 1 - C - 2 C // // here the hydrogen will be between 1 and 2 and we need to add an additional // swap if (neighborBondAngles.size() == 3) { // three coordinated DOUBLE_LIST::iterator angleIt = neighborBondAngles.begin(); ++angleIt; // the first is the 0 (or reference bond - we will ignoire that double angle1 = (*angleIt); ++angleIt; double angle2 = (*angleIt); if (angle2 - angle1 >= (M_PI - 1e-4)) { // we have the above situation nSwaps++; } } #ifdef VERBOSE_STEREOCHEM BOOST_LOG(rdDebugLog) << "--------- " << nSwaps << std::endl; std::copy(neighborBondIndices.begin(), neighborBondIndices.end(), std::ostream_iterator<int>(BOOST_LOG(rdDebugLog), " ")); BOOST_LOG(rdDebugLog) << std::endl; std::copy(neighborBondAngles.begin(), neighborBondAngles.end(), std::ostream_iterator<double>(BOOST_LOG(rdDebugLog), " ")); BOOST_LOG(rdDebugLog) << std::endl; #endif if (chiralType == Atom::CHI_TETRAHEDRAL_CCW) { if (nSwaps % 2 == 1) { // ^ reverse) { res = Bond::BEGINDASH; } else { res = Bond::BEGINWEDGE; } } else { if (nSwaps % 2 == 1) { // ^ reverse) { res = Bond::BEGINWEDGE; } else { res = Bond::BEGINDASH; } } return res; }
void canonicalDFSTraversal(ROMol &mol,int atomIdx,int inBondIdx, std::vector<AtomColors> &colors, VECT_INT_VECT &cycles, INT_VECT &ranks, INT_VECT &cyclesAvailable, MolStack &molStack, INT_VECT &atomOrders, INT_VECT &bondVisitOrders, VECT_INT_VECT &atomRingClosures, std::vector<INT_LIST> &atomTraversalBondOrder, const boost::dynamic_bitset<> *bondsInPlay, const std::vector<std::string> *bondSymbols ){ PRECONDITION(colors.size()>=mol.getNumAtoms(),"vector too small"); PRECONDITION(ranks.size()>=mol.getNumAtoms(),"vector too small"); PRECONDITION(atomOrders.size()>=mol.getNumAtoms(),"vector too small"); PRECONDITION(bondVisitOrders.size()>=mol.getNumBonds(),"vector too small"); PRECONDITION(atomRingClosures.size()>=mol.getNumAtoms(),"vector too small"); PRECONDITION(atomTraversalBondOrder.size()>=mol.getNumAtoms(),"vector too small"); PRECONDITION(!bondsInPlay || bondsInPlay->size()>=mol.getNumBonds(),"bondsInPlay too small"); PRECONDITION(!bondSymbols || bondSymbols->size()>=mol.getNumBonds(),"bondSymbols too small"); int nAttached=0; Atom *atom = mol.getAtomWithIdx(atomIdx); INT_LIST directTravList,cycleEndList; molStack.push_back(MolStackElem(atom)); atomOrders[atom->getIdx()] = molStack.size(); colors[atomIdx] = GREY_NODE; // --------------------- // // Build the list of possible destinations from here // // --------------------- std::vector< PossibleType > possibles; possibles.resize(0); ROMol::OBOND_ITER_PAIR bondsPair = mol.getAtomBonds(atom); possibles.reserve(bondsPair.second-bondsPair.first); while(bondsPair.first != bondsPair.second){ BOND_SPTR theBond = mol[*(bondsPair.first)]; bondsPair.first++; if(bondsInPlay && !(*bondsInPlay)[theBond->getIdx()]) continue; if(inBondIdx<0 || theBond->getIdx() != static_cast<unsigned int>(inBondIdx)){ int otherIdx = theBond->getOtherAtomIdx(atomIdx); long rank=ranks[otherIdx]; // --------------------- // // things are a bit more complicated if we are sitting on a // ring atom we would like to traverse first to the // ring-closure atoms, then to atoms outside the ring first, // then to atoms in the ring that haven't already been visited // (non-ring-closure atoms). // // Here's how the black magic works: // - non-ring atom neighbors have their original ranks // - ring atom neighbors have this added to their ranks: // (Bond::OTHER - bondOrder)*MAX_NATOMS*MAX_NATOMS // - ring-closure neighbors lose a factor of: // (Bond::OTHER+1)*MAX_NATOMS*MAX_NATOMS // // This tactic biases us to traverse to non-ring neighbors first, // original ordering if bond orders are all equal... crafty, neh? // // --------------------- if( colors[otherIdx] == GREY_NODE ) { rank -= static_cast<int>(Bond::OTHER+1) * MAX_NATOMS*MAX_NATOMS; if(!bondSymbols){ rank += static_cast<int>(Bond::OTHER - theBond->getBondType()) * MAX_NATOMS; } else { const std::string &symb=(*bondSymbols)[theBond->getIdx()]; boost::uint32_t hsh=gboost::hash_range(symb.begin(),symb.end()); rank += (hsh%MAX_NATOMS) * MAX_NATOMS; } } else if( theBond->getOwningMol().getRingInfo()->numBondRings(theBond->getIdx()) ){ if(!bondSymbols){ rank += static_cast<int>(Bond::OTHER - theBond->getBondType()) * MAX_NATOMS*MAX_NATOMS; } else { const std::string &symb=(*bondSymbols)[theBond->getIdx()]; boost::uint32_t hsh=gboost::hash_range(symb.begin(),symb.end()); rank += (hsh%MAX_NATOMS)*MAX_NATOMS*MAX_NATOMS; } } possibles.push_back(PossibleType(rank,otherIdx,theBond.get())); } } // --------------------- // // Sort on ranks // // --------------------- std::sort(possibles.begin(),possibles.end(),_possibleComp); // --------------------- // // Now work the children // // --------------------- std::vector<MolStack> subStacks; for(std::vector<PossibleType>::iterator possiblesIt=possibles.begin(); possiblesIt!=possibles.end(); possiblesIt++){ MolStack subStack; #if 0 int possibleIdx = possiblesIt->second.first; Bond *bond = possiblesIt->second.second; #endif int possibleIdx = possiblesIt->get<1>(); Bond *bond = possiblesIt->get<2>(); Atom *otherAtom=mol.getAtomWithIdx(possibleIdx); unsigned int lowestRingIdx; INT_VECT::const_iterator cAIt; switch(colors[possibleIdx]){ case WHITE_NODE: // ----- // we haven't seen this node at all before // ----- // it might have some residual data from earlier calls, clean that up: if(otherAtom->hasProp("_TraversalBondIndexOrder")){ otherAtom->clearProp("_TraversalBondIndexOrder"); } directTravList.push_back(bond->getIdx()); subStack.push_back(MolStackElem(bond,atomIdx)); canonicalDFSTraversal(mol,possibleIdx,bond->getIdx(),colors, cycles,ranks,cyclesAvailable,subStack, atomOrders,bondVisitOrders,atomRingClosures,atomTraversalBondOrder, bondsInPlay,bondSymbols); subStacks.push_back(subStack); nAttached += 1; break; case GREY_NODE: // ----- // we've seen this, but haven't finished it (we're finishing a ring) // ----- cycleEndList.push_back(bond->getIdx()); cAIt=std::find(cyclesAvailable.begin(), cyclesAvailable.end(),1); if(cAIt==cyclesAvailable.end()){ throw ValueErrorException("Too many rings open at once. SMILES cannot be generated."); } lowestRingIdx = cAIt-cyclesAvailable.begin(); cyclesAvailable[lowestRingIdx] = 0; cycles[possibleIdx].push_back(lowestRingIdx); ++lowestRingIdx; bond->setProp("_TraversalRingClosureBond",lowestRingIdx); molStack.push_back(MolStackElem(bond, atom->getIdx())); molStack.push_back(MolStackElem(lowestRingIdx)); // we need to add this bond (which closes the ring) to the traversal list for the // other atom as well: atomTraversalBondOrder[otherAtom->getIdx()].push_back(bond->getIdx()); atomRingClosures[otherAtom->getIdx()].push_back(bond->getIdx()); break; default: // ----- // this node has been finished. don't do anything. // ----- break; } } INT_VECT &ringClosures=atomRingClosures[atom->getIdx()]; CHECK_INVARIANT(ringClosures.size()==cycles[atomIdx].size(), "ring closure mismatch"); for(unsigned int i=0;i<ringClosures.size();i++){ int ringIdx=cycles[atomIdx][i]; ringIdx += 1; molStack.push_back(MolStackElem(ringIdx)); } cycles[atomIdx].resize(0); MolStack::const_iterator ciMS; for(int i=0;i<nAttached;i++){ if(i<nAttached-1){ int branchIdx=0; if(subStacks[i].begin()->type==MOL_STACK_ATOM){ branchIdx=subStacks[i].begin()->obj.atom->getIdx(); } else if(subStacks[i].begin()->type==MOL_STACK_BOND){ branchIdx=-1*subStacks[i].begin()->obj.bond->getIdx(); } else { ASSERT_INVARIANT(0,"branch started with something other than an atom or bond"); } molStack.push_back(MolStackElem("(",branchIdx)); for(ciMS=subStacks[i].begin();ciMS!=subStacks[i].end();ciMS++){ molStack.push_back(*ciMS); switch(ciMS->type){ case MOL_STACK_ATOM: atomOrders[ciMS->obj.atom->getIdx()] = molStack.size(); break; case MOL_STACK_BOND: bondVisitOrders[ciMS->obj.bond->getIdx()] = molStack.size(); break; default: break; } } molStack.push_back(MolStackElem(")",branchIdx)); } else { for(ciMS=subStacks[i].begin();ciMS!=subStacks[i].end();ciMS++){ molStack.push_back(*ciMS); switch(ciMS->type){ case MOL_STACK_ATOM: atomOrders[ciMS->obj.atom->getIdx()] = molStack.size(); break; case MOL_STACK_BOND: bondVisitOrders[ciMS->obj.bond->getIdx()] = molStack.size(); break; default: break; } } } } //std::cerr<<"*****>>>>>> Traversal results for atom: "<<atom->getIdx()<<"> "; INT_LIST travList; // first push on the incoming bond: if(inBondIdx >= 0){ //std::cerr<<" "<<inBondIdx; travList.push_back(inBondIdx); } // ... ring closures that end here: for(INT_LIST_CI ilci=cycleEndList.begin();ilci!=cycleEndList.end();++ilci){ //std::cerr<<" ["<<*ilci<<"]"; travList.push_back(*ilci); } // ... ring closures that start here: // if(atom->hasProp("_TraversalBondIndexOrder")){ // INT_LIST indirectTravList; // atom->getProp("_TraversalBondIndexOrder",indirectTravList); // for(INT_LIST_CI ilci=indirectTravList.begin();ilci!=indirectTravList.end();++ilci){ // //std::cerr<<" ("<<*ilci<<")"; // travList.push_back(*ilci); // } // } BOOST_FOREACH(int ili,atomTraversalBondOrder[atom->getIdx()]){ travList.push_back(ili); }