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