RDKit
Open-source cheminformatics and machine learning.
Loading...
Searching...
No Matches
new_canon.h
Go to the documentation of this file.
1//
2// Copyright (C) 2014 Greg Landrum
3// Adapted from pseudo-code from Roger Sayle
4//
5// @@ All Rights Reserved @@
6// This file is part of the RDKit.
7// The contents are covered by the terms of the BSD license
8// which is included in the file license.txt, found at the root
9// of the RDKit source tree.
10//
11
12#ifndef RD_NEW_CANON_H
13#define RD_NEW_CANON_H
14
15#include <RDGeneral/export.h>
16#include <RDGeneral/hanoiSort.h>
17#include <GraphMol/ROMol.h>
18#include <GraphMol/RingInfo.h>
21#include <cstdint>
22#include <boost/dynamic_bitset.hpp>
24#include <cstring>
25#include <cassert>
26#include <cstring>
27#include <vector>
28
29// #define VERBOSE_CANON 1
30
31namespace RDKit {
32namespace Canon {
33struct canon_atom;
34
37 unsigned int bondStereo{
38 static_cast<unsigned int>(Bond::BondStereo::STEREONONE)};
39 unsigned int nbrSymClass{0};
40 unsigned int nbrIdx{0};
42 const canon_atom *controllingAtoms[4]{nullptr, nullptr, nullptr, nullptr};
43 const std::string *p_symbol{
44 nullptr}; // if provided, this is used to order bonds
45 unsigned int bondIdx{0};
46
49 unsigned int nsc, unsigned int bidx)
50 : bondType(bt),
51 bondStereo(static_cast<unsigned int>(bs)),
52 nbrSymClass(nsc),
53 nbrIdx(ni),
54 bondIdx(bidx) {}
55 bondholder(Bond::BondType bt, unsigned int bs, unsigned int ni,
56 unsigned int nsc, unsigned int bidx)
57 : bondType(bt),
58 bondStereo(bs),
59 nbrSymClass(nsc),
60 nbrIdx(ni),
61 bondIdx(bidx) {}
62
63 int compareStereo(const bondholder &o) const;
64
65 bool operator<(const bondholder &o) const { return compare(*this, o) < 0; }
66 static bool greater(const bondholder &lhs, const bondholder &rhs) {
67 return compare(lhs, rhs) > 0;
68 }
69
70 static int compare(const bondholder &x, const bondholder &y,
71 unsigned int div = 1) {
72 if (x.p_symbol && y.p_symbol) {
73 auto symbolCompare = x.p_symbol->compare(*y.p_symbol);
74 if (symbolCompare != 0) {
75 return symbolCompare;
76 }
77 }
78 if (x.bondType < y.bondType) {
79 return -1;
80 } else if (x.bondType > y.bondType) {
81 return 1;
82 }
83 if (x.bondStereo < y.bondStereo) {
84 return -1;
85 } else if (x.bondStereo > y.bondStereo) {
86 return 1;
87 }
88 auto scdiv = x.nbrSymClass / div - y.nbrSymClass / div;
89 if (scdiv) {
90 return scdiv;
91 }
92 if (x.bondStereo && y.bondStereo) {
93 auto cs = x.compareStereo(y);
94 if (cs) {
95 return cs;
96 }
97 }
98 return 0;
99 }
100};
102 const Atom *atom{nullptr};
103 int index{-1};
104 unsigned int degree{0};
105 unsigned int totalNumHs{0};
106 bool hasRingNbr{false};
107 bool isRingStereoAtom{false};
108 unsigned int whichStereoGroup{0};
110 std::unique_ptr<int[]> nbrIds;
111 const std::string *p_symbol{
112 nullptr}; // if provided, this is used to order atoms
113 std::vector<int> neighborNum;
114 std::vector<int> revistedNeighbors;
115 std::vector<bondholder> bonds;
116};
117
119 canon_atom *atoms, std::vector<bondholder> &nbrs);
120
122 canon_atom *atoms, std::vector<bondholder> &nbrs, unsigned int atomIdx,
123 std::vector<std::pair<unsigned int, unsigned int>> &result);
124
125/*
126 * Different types of atom compare functions:
127 *
128 * - SpecialChiralityAtomCompareFunctor: Allows canonizing molecules exhibiting
129 *dependent chirality
130 * - SpecialSymmetryAtomCompareFunctor: Very specialized, allows canonizing
131 *highly symmetrical graphs/molecules
132 * - AtomCompareFunctor: Basic atom compare function which also allows to
133 *include neighbors within the ranking
134 */
135
137 public:
139 const ROMol *dp_mol{nullptr};
140 const boost::dynamic_bitset<> *dp_atomsInPlay{nullptr},
141 *dp_bondsInPlay{nullptr};
142
145 Canon::canon_atom *atoms, const ROMol &m,
146 const boost::dynamic_bitset<> *atomsInPlay = nullptr,
147 const boost::dynamic_bitset<> *bondsInPlay = nullptr)
148 : dp_atoms(atoms),
149 dp_mol(&m),
150 dp_atomsInPlay(atomsInPlay),
151 dp_bondsInPlay(bondsInPlay) {}
152 int operator()(int i, int j) const {
153 PRECONDITION(dp_atoms, "no atoms");
154 PRECONDITION(dp_mol, "no molecule");
155 PRECONDITION(i != j, "bad call");
156 if (dp_atomsInPlay && !((*dp_atomsInPlay)[i] || (*dp_atomsInPlay)[j])) {
157 return 0;
158 }
159
160 if (!dp_atomsInPlay || (*dp_atomsInPlay)[i]) {
162 }
163 if (!dp_atomsInPlay || (*dp_atomsInPlay)[j]) {
165 }
166 for (unsigned int ii = 0;
167 ii < dp_atoms[i].bonds.size() && ii < dp_atoms[j].bonds.size(); ++ii) {
168 int cmp =
169 bondholder::compare(dp_atoms[i].bonds[ii], dp_atoms[j].bonds[ii]);
170 if (cmp) {
171 return cmp;
172 }
173 }
174
175 std::vector<std::pair<unsigned int, unsigned int>> swapsi;
176 std::vector<std::pair<unsigned int, unsigned int>> swapsj;
177 if (!dp_atomsInPlay || (*dp_atomsInPlay)[i]) {
178 updateAtomNeighborNumSwaps(dp_atoms, dp_atoms[i].bonds, i, swapsi);
179 }
180 if (!dp_atomsInPlay || (*dp_atomsInPlay)[j]) {
181 updateAtomNeighborNumSwaps(dp_atoms, dp_atoms[j].bonds, j, swapsj);
182 }
183
184 for (unsigned int ii = 0; ii < swapsi.size() && ii < swapsj.size(); ++ii) {
185 int cmp = swapsi[ii].second - swapsj[ii].second;
186
187 if (cmp) {
188 return cmp;
189 }
190 }
191
192 return 0;
193 }
194};
195
197 public:
199 const ROMol *dp_mol{nullptr};
200 const boost::dynamic_bitset<> *dp_atomsInPlay{nullptr},
201 *dp_bondsInPlay{nullptr};
202
205 Canon::canon_atom *atoms, const ROMol &m,
206 const boost::dynamic_bitset<> *atomsInPlay = nullptr,
207 const boost::dynamic_bitset<> *bondsInPlay = nullptr)
208 : dp_atoms(atoms),
209 dp_mol(&m),
210 dp_atomsInPlay(atomsInPlay),
211 dp_bondsInPlay(bondsInPlay) {}
212 int operator()(int i, int j) const {
213 PRECONDITION(dp_atoms, "no atoms");
214 PRECONDITION(dp_mol, "no molecule");
215 PRECONDITION(i != j, "bad call");
216 if (dp_atomsInPlay && !((*dp_atomsInPlay)[i] || (*dp_atomsInPlay)[j])) {
217 return 0;
218 }
219
220 if (dp_atoms[i].neighborNum < dp_atoms[j].neighborNum) {
221 return -1;
222 } else if (dp_atoms[i].neighborNum > dp_atoms[j].neighborNum) {
223 return 1;
224 }
225
226 if (dp_atoms[i].revistedNeighbors < dp_atoms[j].revistedNeighbors) {
227 return -1;
228 } else if (dp_atoms[i].revistedNeighbors > dp_atoms[j].revistedNeighbors) {
229 return 1;
230 }
231
232 if (!dp_atomsInPlay || (*dp_atomsInPlay)[i]) {
234 }
235 if (!dp_atomsInPlay || (*dp_atomsInPlay)[j]) {
237 }
238 for (unsigned int ii = 0;
239 ii < dp_atoms[i].bonds.size() && ii < dp_atoms[j].bonds.size(); ++ii) {
240 int cmp =
241 bondholder::compare(dp_atoms[i].bonds[ii], dp_atoms[j].bonds[ii]);
242 if (cmp) {
243 return cmp;
244 }
245 }
246
247 if (dp_atoms[i].bonds.size() < dp_atoms[j].bonds.size()) {
248 return -1;
249 } else if (dp_atoms[i].bonds.size() > dp_atoms[j].bonds.size()) {
250 return 1;
251 }
252 return 0;
253 }
254};
255
256namespace {
257unsigned int getChiralRank(const ROMol *dp_mol, canon_atom *dp_atoms,
258 unsigned int i) {
259 unsigned int res = 0;
260 std::vector<unsigned int> perm;
261 perm.reserve(dp_atoms[i].atom->getDegree());
262 for (const auto nbr : dp_mol->atomNeighbors(dp_atoms[i].atom)) {
263 auto rnk = dp_atoms[nbr->getIdx()].index;
264 // make sure we don't have duplicate ranks
265 if (std::find(perm.begin(), perm.end(), rnk) != perm.end()) {
266 break;
267 } else {
268 perm.push_back(rnk);
269 }
270 }
271 if (perm.size() == dp_atoms[i].atom->getDegree()) {
272 auto ctag = dp_atoms[i].atom->getChiralTag();
275 auto sortedPerm = perm;
276 std::sort(sortedPerm.begin(), sortedPerm.end());
277 auto nswaps = countSwapsToInterconvert(perm, sortedPerm);
278 res = ctag == Atom::ChiralType::CHI_TETRAHEDRAL_CW ? 2 : 1;
279 if (nswaps % 2) {
280 res = res == 2 ? 1 : 2;
281 }
282 }
283 }
284 return res;
285}
286} // namespace
288 unsigned int getAtomRingNbrCode(unsigned int i) const {
289 if (!dp_atoms[i].hasRingNbr) {
290 return 0;
291 }
292
293 auto nbrs = dp_atoms[i].nbrIds.get();
294 unsigned int code = 0;
295 for (unsigned j = 0; j < dp_atoms[i].degree; ++j) {
296 if (dp_atoms[nbrs[j]].isRingStereoAtom) {
297 code += dp_atoms[nbrs[j]].index * 10000 + 1; // j;
298 }
299 }
300 return code;
301 }
302
303 int basecomp(int i, int j) const {
304 unsigned int ivi, ivj;
305
306 // always start with the current class:
307 ivi = dp_atoms[i].index;
308 ivj = dp_atoms[j].index;
309 if (ivi < ivj) {
310 return -1;
311 } else if (ivi > ivj) {
312 return 1;
313 }
314
316 // use the non-stereo ranks if they were assigned
317 int rankingNumber_i = 0;
318 int rankingNumber_j = 0;
319 dp_atoms[i].atom->getPropIfPresent(
321 dp_atoms[j].atom->getPropIfPresent(
323 if (rankingNumber_i < rankingNumber_j) {
324 return -1;
325 } else if (rankingNumber_i > rankingNumber_j) {
326 return 1;
327 }
328 }
329
331 // use the atom-mapping numbers if they were assigned
332 int molAtomMapNumber_i = 0;
333 int molAtomMapNumber_j = 0;
334 if (df_useAtomMaps ||
335 (df_useAtomMapsOnDummies && dp_atoms[i].atom->getAtomicNum() == 0)) {
336 dp_atoms[i].atom->getPropIfPresent(common_properties::molAtomMapNumber,
337 molAtomMapNumber_i);
338 }
339 if (df_useAtomMaps ||
340 (df_useAtomMapsOnDummies && dp_atoms[j].atom->getAtomicNum() == 0)) {
341 dp_atoms[j].atom->getPropIfPresent(common_properties::molAtomMapNumber,
342 molAtomMapNumber_j);
343 }
344 if (molAtomMapNumber_i < molAtomMapNumber_j) {
345 return -1;
346 } else if (molAtomMapNumber_i > molAtomMapNumber_j) {
347 return 1;
348 }
349 }
350 // start by comparing degree
351 ivi = dp_atoms[i].degree;
352 ivj = dp_atoms[j].degree;
353 if (ivi < ivj) {
354 return -1;
355 } else if (ivi > ivj) {
356 return 1;
357 }
358 if (dp_atoms[i].p_symbol && dp_atoms[j].p_symbol) {
359 return dp_atoms[i].p_symbol->compare(*dp_atoms[j].p_symbol);
360 }
361
362 // move onto atomic number
363 ivi = dp_atoms[i].atom->getAtomicNum();
364 ivj = dp_atoms[j].atom->getAtomicNum();
365 if (ivi < ivj) {
366 return -1;
367 } else if (ivi > ivj) {
368 return 1;
369 }
370 // isotopes if we're using them
371 if (df_useIsotopes) {
372 ivi = dp_atoms[i].atom->getIsotope();
373 ivj = dp_atoms[j].atom->getIsotope();
374 if (ivi < ivj) {
375 return -1;
376 } else if (ivi > ivj) {
377 return 1;
378 }
379 }
380
381 // nHs
382 ivi = dp_atoms[i].totalNumHs;
383 ivj = dp_atoms[j].totalNumHs;
384 if (ivi < ivj) {
385 return -1;
386 } else if (ivi > ivj) {
387 return 1;
388 }
389 // charge
390 ivi = dp_atoms[i].atom->getFormalCharge();
391 ivj = dp_atoms[j].atom->getFormalCharge();
392 if (ivi < ivj) {
393 return -1;
394 } else if (ivi > ivj) {
395 return 1;
396 }
397 // presence of specified chirality if it's being used
399 ivi =
400 dp_atoms[i].atom->getChiralTag() != Atom::ChiralType::CHI_UNSPECIFIED;
401 ivj =
402 dp_atoms[j].atom->getChiralTag() != Atom::ChiralType::CHI_UNSPECIFIED;
403 if (ivi < ivj) {
404 return -1;
405 } else if (ivi > ivj) {
406 return 1;
407 }
408 }
409 // chirality if we're using it
410 if (df_useChirality) {
411 // look at enhanced stereo - whichStereoGroup == 0 means no stereo
412 ivi = dp_atoms[i].whichStereoGroup; // can't use the index itself, but if
413 // it's set then we're in an SG
414 ivj = dp_atoms[j].whichStereoGroup;
415 if (ivi || ivj) {
416 if (ivi && !ivj) {
417 return 1;
418 } else if (ivj && !ivi) {
419 return -1;
420 } else if (ivi && ivj) {
421 auto iType = dp_atoms[i].typeOfStereoGroup;
422 auto jType = dp_atoms[j].typeOfStereoGroup;
423 if (iType < jType) {
424 return -1;
425 } else if (iType > jType) {
426 return 1;
427 }
428 if (ivi != ivj) {
429 // now check the current classes of the other members of the SG
430 std::set<unsigned int> sgi;
431 for (const auto sgat :
432 dp_mol->getStereoGroups()[ivi - 1].getAtoms()) {
433 sgi.insert(dp_atoms[sgat->getIdx()].index);
434 }
435 std::set<unsigned int> sgj;
436 for (const auto sgat :
437 dp_mol->getStereoGroups()[ivj - 1].getAtoms()) {
438 sgj.insert(dp_atoms[sgat->getIdx()].index);
439 }
440 if (sgi < sgj) {
441 return -1;
442 } else if (sgi > sgj) {
443 return 1;
444 }
445 } else { // same stereo group
447 ivi = getChiralRank(dp_mol, dp_atoms, i);
448 ivj = getChiralRank(dp_mol, dp_atoms, j);
449 if (ivi < ivj) {
450 return -1;
451 } else if (ivi > ivj) {
452 return 1;
453 }
454 }
455 }
456 }
457 } else {
458 // if there's no stereogroup, then use whatever atom stereochem is
459 // specfied:
460 ivi = 0;
461 ivj = 0;
462 // can't actually use values here, because they are arbitrary
463 ivi = dp_atoms[i].atom->getChiralTag() != 0;
464 ivj = dp_atoms[j].atom->getChiralTag() != 0;
465 if (ivi < ivj) {
466 return -1;
467 } else if (ivi > ivj) {
468 return 1;
469 }
470 // stereo set
471 if (ivi && ivj) {
472 if (ivi) {
473 ivi = getChiralRank(dp_mol, dp_atoms, i);
474 }
475 if (ivj) {
476 ivj = getChiralRank(dp_mol, dp_atoms, j);
477 }
478 if (ivi < ivj) {
479 return -1;
480 } else if (ivi > ivj) {
481 return 1;
482 }
483 }
484 }
485 }
486
488 // ring stereochemistry
489 ivi = getAtomRingNbrCode(i);
490 ivj = getAtomRingNbrCode(j);
491 if (ivi < ivj) {
492 return -1;
493 } else if (ivi > ivj) {
494 return 1;
495 } // bond stereo is taken care of in the neighborhood comparison
496 }
497 return 0;
498 }
499
500 public:
502 const ROMol *dp_mol{nullptr};
503 const boost::dynamic_bitset<> *dp_atomsInPlay{nullptr},
504 *dp_bondsInPlay{nullptr};
505 bool df_useNbrs{false};
506 bool df_useIsotopes{true};
507 bool df_useChirality{true};
509 bool df_useAtomMaps{true};
513
516 const boost::dynamic_bitset<> *atomsInPlay = nullptr,
517 const boost::dynamic_bitset<> *bondsInPlay = nullptr)
518 : dp_atoms(atoms),
519 dp_mol(&m),
520 dp_atomsInPlay(atomsInPlay),
521 dp_bondsInPlay(bondsInPlay) {}
522
523 int operator()(int i, int j) const {
524 if (dp_atomsInPlay && !((*dp_atomsInPlay)[i] || (*dp_atomsInPlay)[j])) {
525 return 0;
526 }
527 int v = basecomp(i, j);
528 if (v) {
529 return v;
530 }
531
532 if (df_useNbrs) {
533 if (!dp_atomsInPlay || (*dp_atomsInPlay)[i]) {
535 }
536 if (!dp_atomsInPlay || (*dp_atomsInPlay)[j]) {
538 }
539
540 for (unsigned int ii = 0;
541 ii < dp_atoms[i].bonds.size() && ii < dp_atoms[j].bonds.size();
542 ++ii) {
543 int cmp =
544 bondholder::compare(dp_atoms[i].bonds[ii], dp_atoms[j].bonds[ii]);
545 if (cmp) {
546 return cmp;
547 }
548 }
549
550 if (dp_atoms[i].bonds.size() < dp_atoms[j].bonds.size()) {
551 return -1;
552 } else if (dp_atoms[i].bonds.size() > dp_atoms[j].bonds.size()) {
553 return 1;
554 }
555 }
556 return 0;
557 }
558};
559
560/*
561 * A compare function to discriminate chiral atoms, similar to the CIP rules.
562 * This functionality is currently not used.
563 */
564
565const unsigned int ATNUM_CLASS_OFFSET = 10000;
567 void getAtomNeighborhood(std::vector<bondholder> &nbrs) const {
568 for (unsigned j = 0; j < nbrs.size(); ++j) {
569 unsigned int nbrIdx = nbrs[j].nbrIdx;
570 if (nbrIdx == ATNUM_CLASS_OFFSET) {
571 // Ignore the Hs
572 continue;
573 }
574 const Atom *nbr = dp_atoms[nbrIdx].atom;
575 nbrs[j].nbrSymClass =
576 nbr->getAtomicNum() * ATNUM_CLASS_OFFSET + dp_atoms[nbrIdx].index + 1;
577 }
578 std::sort(nbrs.begin(), nbrs.end(), bondholder::greater);
579 // FIX: don't want to be doing this long-term
580 }
581
582 int basecomp(int i, int j) const {
583 PRECONDITION(dp_atoms, "no atoms");
584 unsigned int ivi, ivj;
585
586 // always start with the current class:
587 ivi = dp_atoms[i].index;
588 ivj = dp_atoms[j].index;
589 if (ivi < ivj) {
590 return -1;
591 } else if (ivi > ivj) {
592 return 1;
593 }
594
595 // move onto atomic number
596 ivi = dp_atoms[i].atom->getAtomicNum();
597 ivj = dp_atoms[j].atom->getAtomicNum();
598 if (ivi < ivj) {
599 return -1;
600 } else if (ivi > ivj) {
601 return 1;
602 }
603
604 // isotopes:
605 ivi = dp_atoms[i].atom->getIsotope();
606 ivj = dp_atoms[j].atom->getIsotope();
607 if (ivi < ivj) {
608 return -1;
609 } else if (ivi > ivj) {
610 return 1;
611 }
612
613 // atom stereochem:
614 ivi = 0;
615 ivj = 0;
616 std::string cipCode;
617 if (dp_atoms[i].atom->getPropIfPresent(common_properties::_CIPCode,
618 cipCode)) {
619 ivi = cipCode == "R" ? 2 : 1;
620 }
621 if (dp_atoms[j].atom->getPropIfPresent(common_properties::_CIPCode,
622 cipCode)) {
623 ivj = cipCode == "R" ? 2 : 1;
624 }
625 if (ivi < ivj) {
626 return -1;
627 } else if (ivi > ivj) {
628 return 1;
629 }
630
631 // bond stereo is taken care of in the neighborhood comparison
632 return 0;
633 }
634
635 public:
637 const ROMol *dp_mol{nullptr};
638 bool df_useNbrs{false};
641 : dp_atoms(atoms), dp_mol(&m), df_useNbrs(false) {}
642 int operator()(int i, int j) const {
643 PRECONDITION(dp_atoms, "no atoms");
644 PRECONDITION(dp_mol, "no molecule");
645 PRECONDITION(i != j, "bad call");
646 int v = basecomp(i, j);
647 if (v) {
648 return v;
649 }
650
651 if (df_useNbrs) {
652 getAtomNeighborhood(dp_atoms[i].bonds);
653 getAtomNeighborhood(dp_atoms[j].bonds);
654
655 // we do two passes through the neighbor lists. The first just uses the
656 // atomic numbers (by passing the optional 10000 to bondholder::compare),
657 // the second takes the already-computed index into account
658 for (unsigned int ii = 0;
659 ii < dp_atoms[i].bonds.size() && ii < dp_atoms[j].bonds.size();
660 ++ii) {
661 int cmp = bondholder::compare(
662 dp_atoms[i].bonds[ii], dp_atoms[j].bonds[ii], ATNUM_CLASS_OFFSET);
663 if (cmp) {
664 return cmp;
665 }
666 }
667 for (unsigned int ii = 0;
668 ii < dp_atoms[i].bonds.size() && ii < dp_atoms[j].bonds.size();
669 ++ii) {
670 int cmp =
671 bondholder::compare(dp_atoms[i].bonds[ii], dp_atoms[j].bonds[ii]);
672 if (cmp) {
673 return cmp;
674 }
675 }
676 if (dp_atoms[i].bonds.size() < dp_atoms[j].bonds.size()) {
677 return -1;
678 } else if (dp_atoms[i].bonds.size() > dp_atoms[j].bonds.size()) {
679 return 1;
680 }
681 }
682 return 0;
683 }
684};
685
686/*
687 * Basic canonicalization function to organize the partitions which will be
688 * sorted next.
689 * */
690
691template <typename CompareFunc>
692void RefinePartitions(const ROMol &mol, canon_atom *atoms, CompareFunc compar,
693 int mode, std::vector<int> &order,
694 std::vector<int> &count, int &activeset,
695 std::vector<int> &next, std::vector<int> &changed,
696 std::vector<char> &touchedPartitions,
697 std::vector<int> *hanoiTemp = nullptr) {
698 unsigned int nAtoms = mol.getNumAtoms();
699 std::vector<int> localHanoiTemp;
700 if (!hanoiTemp) {
701 localHanoiTemp.resize(nAtoms);
702 hanoiTemp = &localHanoiTemp;
703 }
704 int partition;
705 int symclass = 0;
706 int offset;
707 int index;
708 int len;
709 int i;
710 PRECONDITION(hanoiTemp->size() >= nAtoms, "hanoi scratch is too small");
711 // std::vector<char> touchedPartitions(mol.getNumAtoms(),0);
712
713 // std::cerr<<"&&&&&&&&&&&&&&&& RP"<<std::endl;
714 while (activeset != -1) {
715 // std::cerr<<"ITER: "<<activeset<<" next: "<<next[activeset]<<std::endl;
716 // std::cerr<<" next: ";
717 // for(unsigned int ii=0;ii<nAtoms;++ii){
718 // std::cerr<<ii<<":"<<next[ii]<<" ";
719 // }
720 // std::cerr<<std::endl;
721 // for(unsigned int ii=0;ii<nAtoms;++ii){
722 // std::cerr<<order[ii]<<" count: "<<count[order[ii]]<<" index:
723 // "<<atoms[order[ii]].index<<std::endl;
724 // }
725
726 partition = activeset;
727 activeset = next[partition];
728 next[partition] = -2;
729
730 len = count[partition];
731 offset = atoms[partition].index;
732 auto start = std::span<int>(&order[offset], len);
733 // std::cerr<<"\n\n**************************************************************"<<std::endl;
734 // std::cerr<<" sort - class:"<<atoms[partition].index<<" len:
735 // "<<len<<":"; for(unsigned int ii=0;ii<len;++ii){
736 // std::cerr<<" "<<order[offset+ii]+1;
737 // }
738 // std::cerr<<std::endl;
739 // for(unsigned int ii=0;ii<nAtoms;++ii){
740 // std::cerr<<order[ii]+1<<" count: "<<count[order[ii]]<<" index:
741 // "<<atoms[order[ii]].index<<std::endl;
742 // }
743 if (RDKit::detail::hanoi(start.data(), len, hanoiTemp->data(), count.data(),
744 changed.data(), compar)) {
745 std::copy_n(hanoiTemp->begin(), len, start.begin());
746 }
747 // std::cerr<<"*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*"<<std::endl;
748 // std::cerr<<" result:";
749 // for(unsigned int ii=0;ii<nAtoms;++ii){
750 // std::cerr<<order[ii]+1<<" count: "<<count[order[ii]]<<" index:
751 // "<<atoms[order[ii]].index<<std::endl;
752 // }
753 for (int k = 0; k < len; ++k) {
754 changed[start[k]] = 0;
755 }
756
757 index = start[0];
758 // std::cerr<<" len:"<<len<<" index:"<<index<<"
759 // count:"<<count[index]<<std::endl;
760 for (i = count[index]; i < len; i++) {
761 index = start[i];
762 if (count[index]) {
763 symclass = offset + i;
764 }
765 atoms[index].index = symclass;
766 // std::cerr<<" "<<index+1<<"("<<symclass<<")";
767 // if(mode && (activeset<0 || count[index]>count[activeset]) ){
768 // activeset=index;
769 //}
770 for (unsigned j = 0; j < atoms[index].degree; ++j) {
771 changed[atoms[index].nbrIds[j]] = 1;
772 }
773 }
774 // std::cerr<<std::endl;
775
776 if (mode) {
777 index = start[0];
778 for (i = count[index]; i < len; i++) {
779 index = start[i];
780 for (unsigned j = 0; j < atoms[index].degree; ++j) {
781 unsigned int nbor = atoms[index].nbrIds[j];
782 touchedPartitions[atoms[nbor].index] = 1;
783 }
784 }
785 for (unsigned int ii = 0; ii < nAtoms; ++ii) {
786 if (touchedPartitions[ii]) {
787 partition = order[ii];
788 if ((count[partition] > 1) && (next[partition] == -2)) {
789 next[partition] = activeset;
790 activeset = partition;
791 }
792 touchedPartitions[ii] = 0;
793 }
794 }
795 }
796 }
797} // end of RefinePartitions()
798
799template <typename CompareFunc>
800void BreakTies(const ROMol &mol, canon_atom *atoms, CompareFunc compar,
801 int mode, std::vector<int> &order, std::vector<int> &count,
802 int &activeset, std::vector<int> &next,
803 std::vector<int> &changed,
804 std::vector<char> &touchedPartitions,
805 std::vector<int> *hanoiTemp = nullptr) {
806 unsigned int nAtoms = mol.getNumAtoms();
807 int partition;
808 int offset;
809 int index;
810 int len;
811 int oldPart = 0;
812
813 for (unsigned int i = 0; i < nAtoms; i++) {
814 partition = order[i];
815 oldPart = atoms[partition].index;
816 while (count[partition] > 1) {
817 len = count[partition];
818 offset = atoms[partition].index + len - 1;
819 index = order[offset];
820 atoms[index].index = offset;
821 count[partition] = len - 1;
822 count[index] = 1;
823
824 // test for ions, water molecules with no
825 if (atoms[index].degree < 1) {
826 continue;
827 }
828 for (unsigned j = 0; j < atoms[index].degree; ++j) {
829 unsigned int nbor = atoms[index].nbrIds[j];
830 touchedPartitions[atoms[nbor].index] = 1;
831 changed[nbor] = 1;
832 }
833
834 for (unsigned int ii = 0; ii < nAtoms; ++ii) {
835 if (touchedPartitions[ii]) {
836 int npart = order[ii];
837 if ((count[npart] > 1) && (next[npart] == -2)) {
838 next[npart] = activeset;
839 activeset = npart;
840 }
841 touchedPartitions[ii] = 0;
842 }
843 }
844 RefinePartitions(mol, atoms, compar, mode, order, count, activeset, next,
845 changed, touchedPartitions, hanoiTemp);
846 }
847 // not sure if this works each time
848 if (atoms[partition].index != oldPart) {
849 i -= 1;
850 }
851 }
852} // end of BreakTies()
853
855 std::vector<int> &order,
856 std::vector<int> &count,
857 canon_atom *atoms);
858
860 unsigned int nAtoms, std::vector<int> &order, std::vector<int> &count,
861 int &activeset, std::vector<int> &next, std::vector<int> &changed);
862
863//! Note that atom maps on dummy atoms will always be used
865 const ROMol &mol, std::vector<unsigned int> &res, bool breakTies = true,
866 bool includeChirality = true, bool includeIsotopes = true,
867 bool includeAtomMaps = true, bool includeChiralPresence = false,
868 bool includeStereoGroups = true, bool useNonStereoRanks = false,
869 bool includeRingStereo = true);
870
871//! Note that atom maps on dummy atoms will always be used
873 const ROMol &mol, std::vector<unsigned int> &res,
874 const boost::dynamic_bitset<> &atomsInPlay,
875 const boost::dynamic_bitset<> &bondsInPlay,
876 const std::vector<std::string> *atomSymbols,
877 const std::vector<std::string> *bondSymbols, bool breakTies,
878 bool includeChirality, bool includeIsotope, bool includeAtomMaps,
879 bool includeChiralPresence, bool includeRingStereo = true);
880
881//! Note that atom maps on dummy atoms will always be used
883 const ROMol &mol, std::vector<unsigned int> &res,
884 const boost::dynamic_bitset<> &atomsInPlay,
885 const boost::dynamic_bitset<> &bondsInPlay,
886 const std::vector<std::string> *atomSymbols = nullptr,
887 bool breakTies = true, bool includeChirality = true,
888 bool includeIsotopes = true, bool includeAtomMaps = true,
889 bool includeChiralPresence = false, bool includeRingStereo = true) {
890 rankFragmentAtoms(mol, res, atomsInPlay, bondsInPlay, atomSymbols, nullptr,
891 breakTies, includeChirality, includeIsotopes,
892 includeAtomMaps, includeChiralPresence, includeRingStereo);
893};
894
896 std::vector<unsigned int> &res);
897
899 std::vector<Canon::canon_atom> &atoms,
900 bool includeChirality = true,
901 bool includeStereoGroups = true);
902
903namespace detail {
905 std::vector<Canon::canon_atom> &atoms,
906 bool includeChirality,
907 const std::vector<std::string> *atomSymbols,
908 const std::vector<std::string> *bondSymbols,
909 const boost::dynamic_bitset<> &atomsInPlay,
910 const boost::dynamic_bitset<> &bondsInPlay,
911 bool needsInit);
912template <typename T>
913void rankWithFunctor(T &ftor, bool breakTies, std::vector<int> &order,
914 bool useSpecial = false, bool useChirality = false,
915 bool includeRingStereo = true,
916 const boost::dynamic_bitset<> *atomsInPlay = nullptr,
917 const boost::dynamic_bitset<> *bondsInPlay = nullptr);
918
919} // namespace detail
920
921} // namespace Canon
922} // namespace RDKit
923
924#endif // RD_NEW_CANON_H
#define PRECONDITION(expr, mess)
Definition Invariant.h:108
Defines the primary molecule class ROMol as well as associated typedefs.
Defines the class StereoGroup which stores relationships between the absolute configurations of atoms...
The class for representing atoms.
Definition Atom.h:74
int getAtomicNum() const
returns our atomic number
Definition Atom.h:145
@ CHI_TETRAHEDRAL_CW
tetrahedral: clockwise rotation (SMILES @@)
Definition Atom.h:107
@ CHI_UNSPECIFIED
chirality that hasn't been specified
Definition Atom.h:106
@ CHI_TETRAHEDRAL_CCW
tetrahedral: counter-clockwise rotation (SMILES
Definition Atom.h:108
BondType
the type of Bond
Definition Bond.h:55
@ UNSPECIFIED
Definition Bond.h:56
BondStereo
the nature of the bond's stereochem (for cis/trans)
Definition Bond.h:94
@ STEREONONE
Definition Bond.h:95
const boost::dynamic_bitset * dp_bondsInPlay
Definition new_canon.h:504
AtomCompareFunctor(Canon::canon_atom *atoms, const ROMol &m, const boost::dynamic_bitset<> *atomsInPlay=nullptr, const boost::dynamic_bitset<> *bondsInPlay=nullptr)
Definition new_canon.h:515
int operator()(int i, int j) const
Definition new_canon.h:523
const boost::dynamic_bitset * dp_atomsInPlay
Definition new_canon.h:503
Canon::canon_atom * dp_atoms
Definition new_canon.h:501
ChiralAtomCompareFunctor(Canon::canon_atom *atoms, const ROMol &m)
Definition new_canon.h:640
int operator()(int i, int j) const
Definition new_canon.h:642
const boost::dynamic_bitset * dp_atomsInPlay
Definition new_canon.h:140
const boost::dynamic_bitset * dp_bondsInPlay
Definition new_canon.h:141
SpecialChiralityAtomCompareFunctor(Canon::canon_atom *atoms, const ROMol &m, const boost::dynamic_bitset<> *atomsInPlay=nullptr, const boost::dynamic_bitset<> *bondsInPlay=nullptr)
Definition new_canon.h:144
const boost::dynamic_bitset * dp_bondsInPlay
Definition new_canon.h:201
const boost::dynamic_bitset * dp_atomsInPlay
Definition new_canon.h:200
SpecialSymmetryAtomCompareFunctor(Canon::canon_atom *atoms, const ROMol &m, const boost::dynamic_bitset<> *atomsInPlay=nullptr, const boost::dynamic_bitset<> *bondsInPlay=nullptr)
Definition new_canon.h:204
unsigned int getNumAtoms() const
returns our number of atoms
Definition ROMol.h:618
CXXAtomIterator< const MolGraph, Atom *const, MolGraph::adjacency_iterator > atomNeighbors(Atom const *at) const
Definition ROMol.h:440
#define RDKIT_GRAPHMOL_EXPORT
Definition export.h:321
void rankWithFunctor(T &ftor, bool breakTies, std::vector< int > &order, bool useSpecial=false, bool useChirality=false, bool includeRingStereo=true, const boost::dynamic_bitset<> *atomsInPlay=nullptr, const boost::dynamic_bitset<> *bondsInPlay=nullptr)
void initFragmentCanonAtoms(const ROMol &mol, std::vector< Canon::canon_atom > &atoms, bool includeChirality, const std::vector< std::string > *atomSymbols, const std::vector< std::string > *bondSymbols, const boost::dynamic_bitset<> &atomsInPlay, const boost::dynamic_bitset<> &bondsInPlay, bool needsInit)
RDKIT_GRAPHMOL_EXPORT void initCanonAtoms(const ROMol &mol, std::vector< Canon::canon_atom > &atoms, bool includeChirality=true, bool includeStereoGroups=true)
void RefinePartitions(const ROMol &mol, canon_atom *atoms, CompareFunc compar, int mode, std::vector< int > &order, std::vector< int > &count, int &activeset, std::vector< int > &next, std::vector< int > &changed, std::vector< char > &touchedPartitions, std::vector< int > *hanoiTemp=nullptr)
Definition new_canon.h:692
const unsigned int ATNUM_CLASS_OFFSET
Definition new_canon.h:565
RDKIT_GRAPHMOL_EXPORT void CreateSinglePartition(unsigned int nAtoms, std::vector< int > &order, std::vector< int > &count, canon_atom *atoms)
RDKIT_GRAPHMOL_EXPORT void updateAtomNeighborNumSwaps(canon_atom *atoms, std::vector< bondholder > &nbrs, unsigned int atomIdx, std::vector< std::pair< unsigned int, unsigned int > > &result)
RDKIT_GRAPHMOL_EXPORT void ActivatePartitions(unsigned int nAtoms, std::vector< int > &order, std::vector< int > &count, int &activeset, std::vector< int > &next, std::vector< int > &changed)
void BreakTies(const ROMol &mol, canon_atom *atoms, CompareFunc compar, int mode, std::vector< int > &order, std::vector< int > &count, int &activeset, std::vector< int > &next, std::vector< int > &changed, std::vector< char > &touchedPartitions, std::vector< int > *hanoiTemp=nullptr)
Definition new_canon.h:800
RDKIT_GRAPHMOL_EXPORT void chiralRankMolAtoms(const ROMol &mol, std::vector< unsigned int > &res)
RDKIT_GRAPHMOL_EXPORT void rankMolAtoms(const ROMol &mol, std::vector< unsigned int > &res, bool breakTies=true, bool includeChirality=true, bool includeIsotopes=true, bool includeAtomMaps=true, bool includeChiralPresence=false, bool includeStereoGroups=true, bool useNonStereoRanks=false, bool includeRingStereo=true)
Note that atom maps on dummy atoms will always be used.
RDKIT_GRAPHMOL_EXPORT void updateAtomNeighborIndex(canon_atom *atoms, std::vector< bondholder > &nbrs)
RDKIT_GRAPHMOL_EXPORT void rankFragmentAtoms(const ROMol &mol, std::vector< unsigned int > &res, const boost::dynamic_bitset<> &atomsInPlay, const boost::dynamic_bitset<> &bondsInPlay, const std::vector< std::string > *atomSymbols, const std::vector< std::string > *bondSymbols, bool breakTies, bool includeChirality, bool includeIsotope, bool includeAtomMaps, bool includeChiralPresence, bool includeRingStereo=true)
Note that atom maps on dummy atoms will always be used.
constexpr std::string_view _CanonicalRankingNumber
Definition types.h:75
constexpr std::string_view _CIPCode
Definition types.h:71
constexpr std::string_view molAtomMapNumber
Definition types.h:160
bool hanoi(int *base, int nel, int *temp, int *count, int *changed, CompareFunc compar)
Definition hanoiSort.h:29
Std stuff.
StereoGroupType
Definition StereoGroup.h:30
unsigned int countSwapsToInterconvert(const T &ref, T probe)
Definition utils.h:54
const std::string * p_symbol
Definition new_canon.h:43
Bond::BondType bondType
Definition new_canon.h:36
static bool greater(const bondholder &lhs, const bondholder &rhs)
Definition new_canon.h:66
bool operator<(const bondholder &o) const
Definition new_canon.h:65
Bond::BondStereo stype
Definition new_canon.h:41
const canon_atom * controllingAtoms[4]
Definition new_canon.h:42
int compareStereo(const bondholder &o) const
bondholder(Bond::BondType bt, unsigned int bs, unsigned int ni, unsigned int nsc, unsigned int bidx)
Definition new_canon.h:55
bondholder(Bond::BondType bt, Bond::BondStereo bs, unsigned int ni, unsigned int nsc, unsigned int bidx)
Definition new_canon.h:48
unsigned int bondStereo
Definition new_canon.h:37
static int compare(const bondholder &x, const bondholder &y, unsigned int div=1)
Definition new_canon.h:70
unsigned int nbrSymClass
Definition new_canon.h:39
std::vector< bondholder > bonds
Definition new_canon.h:115
StereoGroupType typeOfStereoGroup
Definition new_canon.h:109
std::unique_ptr< int[]> nbrIds
Definition new_canon.h:110
std::vector< int > revistedNeighbors
Definition new_canon.h:114
std::vector< int > neighborNum
Definition new_canon.h:113
unsigned int totalNumHs
Definition new_canon.h:105
const std::string * p_symbol
Definition new_canon.h:111
unsigned int whichStereoGroup
Definition new_canon.h:108