// finds cycles void dfsFindCycles(ROMol &mol, int atomIdx, int inBondIdx, std::vector<AtomColors> &colors, const UINT_VECT &ranks, INT_VECT &atomOrders, VECT_INT_VECT &atomRingClosures, const boost::dynamic_bitset<> *bondsInPlay, const std::vector<std::string> *bondSymbols, bool doRandom) { Atom *atom = mol.getAtomWithIdx(atomIdx); atomOrders.push_back(atomIdx); 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 *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: // (MAX_BONDTYPE - bondOrder)*MAX_NATOMS*MAX_NATOMS // - ring-closure neighbors lose a factor of: // (MAX_BONDTYPE+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 (!doRandom) { if (colors[otherIdx] == GREY_NODE) { rank -= static_cast<int>(MAX_BONDTYPE + 1) * MAX_NATOMS * MAX_NATOMS; if (!bondSymbols) { rank += static_cast<int>(MAX_BONDTYPE - theBond->getBondType()) * MAX_NATOMS; } else { const std::string &symb = (*bondSymbols)[theBond->getIdx()]; std::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>(MAX_BONDTYPE - theBond->getBondType()) * MAX_NATOMS * MAX_NATOMS; } else { const std::string &symb = (*bondSymbols)[theBond->getIdx()]; std::uint32_t hsh = gboost::hash_range(symb.begin(), symb.end()); rank += (hsh % MAX_NATOMS) * MAX_NATOMS * MAX_NATOMS; } } } else { // randomize the rank rank = std::rand(); } // std::cerr << " " << atomIdx << ": " << otherIdx << " " << // rank // << std::endl; // std::cerr<<"aIdx: "<< atomIdx <<" p: "<<otherIdx<<" Rank: // "<<ranks[otherIdx] <<" "<<colors[otherIdx]<<" // "<<theBond->getBondType()<<" "<<rank<<std::endl; possibles.push_back(PossibleType(rank, otherIdx, theBond)); } } // --------------------- // // Sort on ranks // // --------------------- std::sort(possibles.begin(), possibles.end(), _possibleCompare()); // if (possibles.size()) // std::cerr << " aIdx1: " << atomIdx // << " first: " << possibles.front().get<0>() << " " // << possibles.front().get<1>() << std::endl; // // --------------------- // // Now work the children // // --------------------- for (auto &possible : possibles) { int possibleIdx = possible.get<1>(); Bond *bond = possible.get<2>(); switch (colors[possibleIdx]) { case WHITE_NODE: // ----- // we haven't seen this node at all before, traverse // ----- dfsFindCycles(mol, possibleIdx, bond->getIdx(), colors, ranks, atomOrders, atomRingClosures, bondsInPlay, bondSymbols, doRandom); break; case GREY_NODE: // ----- // we've seen this, but haven't finished it (we're finishing a ring) // ----- atomRingClosures[possibleIdx].push_back(bond->getIdx()); atomRingClosures[atomIdx].push_back(bond->getIdx()); break; default: // ----- // this node has been finished. don't do anything. // ----- break; } } colors[atomIdx] = BLACK_NODE; } // namespace Canon