void checkAndCorrectChiralityOfProduct(const std::vector<const Atom*>& chiralAtomsToCheck,
                                       RWMOL_SPTR product, ReactantProductAtomMapping* mapping)
{
    for(std::vector<const Atom *>::const_iterator atomIt = chiralAtomsToCheck.begin();
            atomIt != chiralAtomsToCheck.end(); ++atomIt) {
        const Atom *reactantAtom = *atomIt;
        CHECK_INVARIANT(reactantAtom->getChiralTag()!=Atom::CHI_UNSPECIFIED,
                        "missing atom chirality.");
        unsigned int 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_SPTR 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
}
Beispiel #2
0
  // 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
                     ){
    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_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:
        //       (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( 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()];
            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>(MAX_BONDTYPE - 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;
          }
        }
       //std::cerr<<"aIdx: "<< atomIdx <<"   p: "<<otherIdx<<" Rank: "<<ranks[otherIdx] <<" "<<colors[otherIdx]<<" "<<theBond->getBondType()<<" "<<rank<<std::endl;
        possibles.push_back(PossibleType(rank,otherIdx,theBond.get()));
      }
    }

    // ---------------------
    //
    //  Sort on ranks
    //
    // ---------------------
    std::sort(possibles.begin(),possibles.end(),_possibleCompare());


    // ---------------------
    //
    //  Now work the children
    //
    // ---------------------
    for(std::vector<PossibleType>::iterator possiblesIt=possibles.begin();
        possiblesIt!=possibles.end();
        possiblesIt++){
      int possibleIdx = possiblesIt->get<1>();
      Bond *bond = possiblesIt->get<2>();
      Atom *otherAtom=mol.getAtomWithIdx(possibleIdx);
      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);
        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;
  }
Beispiel #3
0
 void MolDraw2D::doContinuousHighlighting( const ROMol &mol ,
                                           const vector<int> *highlight_atoms ,
                                           const vector<int> *highlight_bonds ,
                                           const map<int,DrawColour> *highlight_atom_map,
                                           const map<int,DrawColour> *highlight_bond_map,
                                           const std::map<int,double> *highlight_radii
                                           ) {
   int orig_lw=lineWidth();
   int tgt_lw=lineWidth()*8;
   // try to scale lw to reflect the overall scaling:
   tgt_lw = max(orig_lw*2,min(tgt_lw,(int)(scale_/25.*tgt_lw))); // the 25 here is extremely empirical
   bool orig_fp=fillPolys();
   ROMol::VERTEX_ITER this_at , end_at;
   if(highlight_bonds){
     boost::tie( this_at , end_at ) = mol.getVertices();
     while( this_at != end_at ) {
       int this_idx = mol[*this_at]->getIdx();
       ROMol::OEDGE_ITER nbr , end_nbr;
       boost::tie( nbr , end_nbr ) = mol.getAtomBonds( mol[*this_at].get() );
       while( nbr != end_nbr ) {
         const BOND_SPTR bond = mol[*nbr];
         ++nbr;
         int nbr_idx = bond->getOtherAtomIdx( this_idx );
         if( nbr_idx < static_cast<int>( at_cds_.size() ) && nbr_idx > this_idx ) {
           if(std::find(highlight_bonds->begin(),highlight_bonds->end(),bond->getIdx()) != highlight_bonds->end()){
             DrawColour col=drawOptions().highlightColour;
             if(highlight_bond_map &&
                highlight_bond_map->find(bond->getIdx())!=highlight_bond_map->end()){
               col = highlight_bond_map->find(bond->getIdx())->second;
             }
             setLineWidth(tgt_lw);
             Point2D at1_cds = at_cds_[this_idx];
             Point2D at2_cds = at_cds_[nbr_idx];
             drawLine( at1_cds , at2_cds , col , col);
           }
         }
       }
       ++this_at;
     }
   }
   if(highlight_atoms){
     boost::tie( this_at , end_at ) = mol.getVertices();
     while( this_at != end_at ) {
       int this_idx = mol[*this_at]->getIdx();
       if(std::find(highlight_atoms->begin(),highlight_atoms->end(),this_idx) != highlight_atoms->end()){
         DrawColour col=drawOptions().highlightColour;
         if(highlight_atom_map &&
            highlight_atom_map->find(this_idx)!=highlight_atom_map->end()){
           col = highlight_atom_map->find(this_idx)->second;
         }
         Point2D p1=at_cds_[this_idx];
         Point2D p2=at_cds_[this_idx];
         double radius=0.4;
         if(highlight_radii && highlight_radii->find(this_idx)!=highlight_radii->end()){
           radius = highlight_radii->find(this_idx)->second;
         }
         Point2D offset(radius,radius);
         p1 -= offset;
         p2 += offset;
         setColour(col);
         setFillPolys(true);
         setLineWidth(1);
         drawEllipse(p1,p2);
       }
       ++this_at;
     }
   }
   setLineWidth(orig_lw);
   setFillPolys(orig_fp);
 }
Beispiel #4
0
  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);
    }