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