Coverage Report

Created: 2026-11-12 07:33

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