Files
rdkit/Code/GraphMol/MolAlign/O3AAlignMolecules.cpp
Greg Landrum 0147cd8201 Fixes #5210 (#5408)
* revert duplicate chunk in release notes

* replace deprecated ifdefs
This one gets rid of USE_BUILTIN_POPCNT and RDK_THREADSAFE_SS
use RDK_OPTIMIZE_POPCNT or RDK_BUILD_THREADSAFE_SSS instead

* get rid of BUILD_COORDGEN_SUPPORT from ROMol.i

* fix a stupid typo

* update release notes
2022-07-11 11:20:03 +02:00

1721 lines
80 KiB
C++

//
// Copyright (C) 2013-2018 Paolo Tosco, Greg Landrum
//
// @@ All Rights Reserved @@
// This file is part of the RDKit.
// The contents are covered by the terms of the BSD license
// which is included in the file license.txt, found at the root
// of the RDKit source tree.
//
#include "O3AAlignMolecules.h"
#include <RDGeneral/utils.h>
#include <Numerics/Vector.h>
#include <GraphMol/Substruct/SubstructMatch.h>
#include <GraphMol/Conformer.h>
#include <GraphMol/ROMol.h>
#include <GraphMol/MolOps.h>
#include <GraphMol/AtomIterators.h>
#include <Numerics/Alignment/AlignPoints.h>
#include <GraphMol/MolTransforms/MolTransforms.h>
#include <boost/dynamic_bitset.hpp>
#include <boost/algorithm/string.hpp>
#include <RDGeneral/RDThreads.h>
#ifdef RDK_BUILD_THREADSAFE_SSS
#include <thread>
#include <future>
#endif
double square(double x) { return x * x; }
namespace RDKit {
namespace MolAlign {
static std::uint8_t mmffSimMatrix[99][99] = {
{1, 3, 4, 3, 0, 4, 6, 7, 5, 6, 7, 4, 5, 7, 3, 5, 6, 7, 3, 2,
0, 2, 0, 0, 6, 7, 0, 0, 0, 3, 0, 10, 0, 10, 10, 0, 3, 6, 7, 6,
8, 7, 8, 4, 7, 6, 8, 7, 10, 0, 10, 0, 6, 10, 12, 14, 12, 10, 4, 4,
6, 10, 3, 3, 6, 6, 8, 8, 8, 8, 0, 10, 10, 4, 5, 10, 10, 3, 6, 10,
12, 8, 0, 0, 0, 0, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
{3, 1, 2, 2, 0, 5, 6, 7, 3, 5, 7, 4, 5, 7, 3, 4, 5, 6, 3, 2,
0, 2, 0, 0, 5, 8, 0, 0, 0, 2, 0, 10, 0, 8, 10, 0, 2, 4, 5, 5,
6, 4, 7, 4, 6, 5, 7, 6, 10, 0, 8, 0, 4, 10, 12, 14, 12, 10, 4, 3,
5, 10, 2, 2, 4, 4, 7, 7, 7, 8, 0, 10, 8, 3, 4, 10, 10, 2, 4, 10,
12, 7, 0, 0, 0, 0, 8, 8, 10, 10, 10, 8, 8, 8, 8, 8, 8, 8, 8},
{4, 2, 1, 2, 0, 7, 7, 8, 2, 5, 7, 4, 5, 7, 5, 5, 3, 4, 4, 4,
0, 4, 0, 0, 4, 8, 0, 0, 0, 2, 0, 10, 0, 8, 12, 0, 2, 4, 6, 6,
4, 8, 8, 6, 6, 3, 8, 7, 8, 0, 6, 0, 6, 8, 10, 12, 10, 8, 6, 6,
3, 12, 2, 2, 6, 6, 4, 4, 4, 10, 0, 12, 5, 3, 6, 12, 12, 2, 4, 7,
10, 5, 0, 0, 0, 0, 6, 6, 12, 12, 12, 6, 6, 6, 6, 6, 6, 6, 6},
{3, 2, 2, 1, 0, 5, 6, 7, 4, 5, 7, 4, 5, 7, 3, 4, 5, 6, 3, 2,
0, 2, 0, 0, 5, 8, 0, 0, 0, 2, 0, 10, 0, 8, 10, 0, 2, 4, 5, 5,
6, 4, 7, 4, 6, 5, 7, 6, 10, 0, 8, 0, 4, 10, 12, 14, 12, 10, 4, 3,
5, 10, 2, 2, 4, 4, 7, 7, 7, 8, 0, 10, 8, 3, 4, 10, 10, 2, 4, 10,
12, 7, 0, 0, 0, 0, 8, 8, 10, 10, 10, 8, 8, 8, 8, 8, 8, 8, 8},
{0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{4, 5, 7, 5, 0, 1, 3, 5, 4, 5, 6, 5, 5, 6, 2, 4, 7, 7, 5, 4,
0, 4, 0, 0, 7, 6, 0, 0, 0, 4, 0, 5, 0, 12, 5, 0, 6, 6, 4, 4,
6, 4, 4, 4, 7, 6, 6, 6, 4, 0, 6, 0, 5, 12, 14, 16, 14, 12, 2, 4,
8, 6, 6, 6, 4, 4, 8, 8, 8, 3, 0, 6, 6, 7, 6, 6, 8, 5, 4, 12,
10, 8, 0, 0, 0, 0, 14, 14, 8, 8, 8, 14, 14, 14, 14, 14, 14, 14, 14},
{6, 6, 7, 6, 0, 3, 1, 6, 3, 5, 6, 5, 5, 6, 4, 2, 7, 7, 7, 6,
0, 6, 0, 0, 7, 6, 0, 0, 0, 4, 0, 4, 0, 12, 4, 0, 6, 6, 4, 4,
7, 4, 4, 5, 10, 8, 6, 5, 10, 0, 10, 0, 5, 12, 14, 16, 14, 12, 3, 4,
8, 6, 6, 6, 4, 4, 8, 8, 8, 4, 0, 6, 6, 8, 5, 6, 8, 6, 6, 12,
10, 8, 0, 0, 0, 0, 14, 14, 8, 8, 8, 14, 14, 14, 14, 14, 14, 14, 14},
{7, 7, 8, 7, 0, 5, 6, 1, 2, 6, 8, 7, 7, 8, 6, 7, 7, 7, 7, 7,
0, 7, 0, 0, 7, 5, 0, 0, 0, 7, 0, 12, 0, 4, 12, 0, 7, 2, 4, 2,
12, 3, 6, 7, 8, 8, 7, 7, 10, 0, 10, 0, 5, 4, 5, 5, 8, 4, 6, 6,
6, 5, 7, 7, 4, 4, 7, 7, 7, 6, 0, 10, 10, 10, 6, 6, 10, 7, 4, 7,
5, 7, 0, 0, 0, 0, 12, 12, 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12},
{5, 3, 2, 4, 0, 4, 3, 2, 1, 5, 8, 7, 7, 8, 6, 6, 7, 7, 7, 5,
0, 5, 0, 0, 7, 6, 0, 0, 0, 5, 0, 10, 0, 5, 10, 0, 5, 2, 4, 2,
10, 2, 5, 6, 8, 7, 5, 4, 10, 0, 8, 0, 4, 3, 3, 3, 6, 3, 5, 5,
5, 4, 5, 5, 4, 4, 7, 7, 7, 7, 0, 10, 10, 10, 3, 7, 10, 5, 4, 6,
4, 7, 0, 0, 0, 0, 12, 12, 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12},
{6, 5, 5, 5, 0, 5, 5, 6, 5, 1, 8, 7, 7, 8, 5, 7, 8, 8, 8, 6,
0, 6, 0, 0, 8, 6, 0, 0, 0, 6, 0, 6, 0, 8, 6, 0, 6, 3, 3, 5,
8, 3, 2, 6, 8, 7, 4, 5, 12, 0, 10, 0, 5, 6, 6, 8, 10, 5, 6, 6,
6, 3, 6, 6, 4, 4, 7, 7, 7, 8, 0, 8, 8, 10, 6, 4, 10, 6, 4, 8,
6, 7, 0, 0, 0, 0, 12, 12, 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12},
{7, 7, 7, 7, 0, 6, 6, 8, 8, 8, 1, 3, 4, 6, 6, 6, 10,
10, 10, 8, 0, 8, 0, 0, 8, 10, 0, 0, 0, 8, 0, 3, 0, 12,
3, 0, 7, 7, 7, 8, 4, 5, 6, 6, 10, 8, 8, 6, 12, 0, 12,
0, 10, 12, 12, 14, 12, 12, 5, 6, 8, 5, 7, 7, 6, 6, 10, 10,
10, 5, 0, 4, 6, 10, 6, 3, 5, 7, 6, 12, 14, 10, 0, 0, 0,
0, 14, 14, 3, 4, 5, 14, 14, 14, 14, 14, 14, 14, 14},
{4, 4, 4, 4, 0, 5, 5, 7, 7, 7, 3, 1, 2, 3, 4, 4, 8, 8, 6, 6,
0, 6, 0, 0, 7, 8, 0, 0, 0, 6, 0, 4, 0, 10, 4, 0, 5, 5, 5, 6,
5, 4, 5, 4, 8, 7, 7, 6, 10, 0, 10, 0, 10, 12, 12, 14, 12, 12, 4, 5,
8, 6, 5, 5, 5, 5, 10, 10, 10, 6, 0, 4, 6, 10, 6, 4, 4, 5, 5, 12,
14, 10, 0, 0, 0, 0, 12, 12, 4, 3, 3, 12, 12, 12, 12, 12, 12, 12, 12},
{5, 5, 5, 5, 0, 5, 5, 7, 7, 7, 4, 2, 1, 2, 4, 4, 8, 8, 6, 6,
0, 6, 0, 0, 7, 8, 0, 0, 0, 6, 0, 4, 0, 10, 4, 0, 5, 5, 5, 6,
5, 4, 5, 4, 8, 7, 7, 6, 10, 0, 10, 0, 10, 12, 12, 14, 12, 12, 4, 5,
8, 6, 5, 5, 5, 5, 10, 10, 10, 6, 0, 4, 6, 10, 6, 4, 4, 5, 5, 12,
14, 10, 0, 0, 0, 0, 12, 12, 5, 3, 3, 12, 12, 12, 12, 12, 12, 12, 12},
{7, 7, 7, 7, 0, 6, 6, 8, 8, 8, 6, 3, 2, 1, 4, 4, 7,
7, 8, 8, 0, 8, 0, 0, 8, 10, 0, 0, 0, 8, 0, 6, 0, 12,
6, 0, 7, 7, 7, 8, 5, 6, 6, 4, 10, 8, 8, 6, 10, 0, 10,
0, 10, 12, 12, 14, 14, 14, 6, 6, 10, 6, 7, 7, 7, 7, 10, 10,
10, 10, 0, 5, 7, 8, 8, 5, 5, 7, 7, 12, 14, 10, 0, 0, 0,
0, 14, 14, 8, 5, 4, 14, 14, 14, 14, 14, 14, 14, 14},
{3, 3, 5, 3, 0, 2, 4, 6, 6, 5, 6, 4, 4, 4, 1, 2, 5, 4, 5, 4,
0, 4, 0, 0, 7, 6, 0, 0, 0, 4, 0, 6, 0, 12, 6, 0, 6, 6, 5, 5,
6, 6, 5, 2, 10, 8, 6, 6, 5, 0, 7, 0, 8, 12, 12, 14, 12, 12, 3, 5,
7, 5, 6, 6, 5, 5, 10, 10, 10, 5, 0, 3, 5, 3, 6, 7, 7, 6, 5, 12,
14, 10, 0, 0, 0, 0, 14, 14, 10, 8, 7, 14, 14, 14, 14, 14, 14, 14, 14},
{5, 4, 5, 4, 0, 4, 2, 7, 6, 7, 6, 4, 4, 4, 2, 1, 5, 5, 5, 5,
0, 5, 0, 0, 7, 6, 0, 0, 0, 4, 0, 5, 0, 12, 5, 0, 5, 6, 5, 5,
3, 5, 6, 3, 10, 8, 6, 4, 10, 0, 8, 0, 8, 12, 12, 14, 12, 12, 4, 4,
8, 7, 5, 5, 5, 5, 10, 10, 10, 6, 0, 4, 7, 5, 6, 7, 8, 5, 5, 12,
14, 10, 0, 0, 0, 0, 14, 14, 10, 8, 7, 14, 14, 14, 14, 14, 14, 14, 14},
{6, 5, 3, 5, 0, 7, 7, 7, 7, 8, 10, 8, 8, 7, 5, 5, 1, 2, 5, 5,
0, 5, 0, 0, 4, 5, 0, 0, 0, 4, 0, 10, 0, 8, 12, 0, 5, 6, 7, 7,
5, 8, 8, 4, 6, 4, 8, 7, 7, 0, 6, 0, 6, 8, 10, 12, 10, 8, 6, 7,
4, 10, 5, 5, 7, 7, 6, 6, 6, 8, 0, 10, 3, 2, 6, 12, 12, 5, 7, 7,
8, 5, 0, 0, 0, 0, 6, 6, 12, 12, 12, 10, 8, 8, 8, 8, 8, 8, 8},
{7, 6, 4, 6, 0, 7, 7, 7, 7, 8, 10, 8, 8, 7, 4, 5, 2, 1, 5, 6,
0, 6, 0, 0, 3, 7, 0, 0, 0, 5, 0, 12, 0, 7, 12, 0, 5, 6, 8, 8,
3, 10, 10, 5, 3, 4, 8, 8, 6, 0, 4, 0, 4, 10, 12, 14, 6, 7, 7, 8,
5, 12, 5, 5, 8, 8, 3, 3, 3, 10, 0, 10, 2, 2, 7, 12, 12, 5, 8, 6,
8, 3, 0, 0, 0, 0, 5, 5, 12, 12, 12, 10, 8, 8, 8, 8, 8, 8, 8},
{3, 3, 4, 3, 0, 5, 7, 7, 7, 8, 10, 6, 6, 8, 5, 5, 5, 5, 1, 3,
0, 3, 0, 0, 5, 6, 0, 0, 0, 3, 0, 10, 0, 10, 10, 0, 4, 6, 7, 7,
8, 8, 10, 4, 8, 7, 8, 8, 10, 0, 10, 0, 6, 10, 12, 14, 12, 10, 4, 6,
8, 12, 4, 4, 7, 7, 8, 8, 8, 8, 0, 10, 8, 5, 5, 10, 10, 4, 7, 10,
12, 8, 0, 0, 0, 0, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
{2, 2, 4, 2, 0, 4, 6, 7, 5, 6, 8, 6, 6, 8, 4, 5, 5, 6, 3, 1,
0, 2, 0, 0, 6, 7, 0, 0, 0, 3, 0, 10, 0, 10, 10, 0, 3, 6, 7, 6,
8, 7, 8, 4, 7, 6, 8, 7, 10, 0, 10, 0, 6, 10, 12, 14, 12, 10, 4, 4,
6, 10, 3, 3, 6, 6, 8, 8, 8, 8, 0, 10, 10, 4, 5, 10, 10, 3, 6, 10,
12, 8, 0, 0, 0, 0, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{2, 2, 4, 2, 0, 4, 6, 7, 5, 6, 8, 6, 6, 8, 4, 5, 5, 6, 3, 2,
0, 1, 0, 0, 6, 7, 0, 0, 0, 3, 0, 10, 0, 10, 10, 0, 3, 6, 7, 6,
8, 7, 8, 4, 7, 6, 8, 7, 10, 0, 10, 0, 6, 10, 12, 14, 12, 10, 4, 4,
6, 10, 3, 3, 6, 6, 8, 8, 8, 8, 0, 10, 10, 4, 5, 10, 10, 3, 6, 10,
12, 8, 0, 0, 0, 0, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{6, 5, 4, 5, 0, 7, 7, 7, 7, 8, 8, 7, 7, 8, 7, 7, 4, 3, 5, 6,
0, 6, 0, 0, 1, 3, 0, 0, 0, 4, 0, 10, 0, 6, 12, 0, 4, 4, 6, 5,
4, 5, 7, 7, 4, 4, 8, 6, 8, 0, 7, 0, 5, 8, 10, 12, 10, 7, 7, 7,
5, 12, 4, 4, 6, 6, 4, 4, 4, 10, 0, 12, 5, 4, 3, 12, 12, 4, 6, 6,
8, 4, 0, 0, 0, 0, 6, 6, 12, 12, 12, 6, 6, 6, 6, 6, 6, 6, 6},
{7, 8, 8, 8, 0, 6, 6, 5, 6, 6, 10, 8, 8, 10, 6, 6, 5, 7, 6, 7,
0, 7, 0, 0, 3, 1, 0, 0, 0, 8, 0, 10, 0, 8, 10, 0, 7, 7, 6, 7,
12, 4, 7, 7, 10, 8, 5, 10, 12, 0, 12, 0, 7, 6, 7, 7, 10, 6, 6, 6,
8, 7, 7, 7, 6, 6, 8, 8, 8, 7, 0, 10, 10, 7, 4, 6, 8, 7, 6, 8,
6, 8, 0, 0, 0, 0, 12, 12, 8, 8, 8, 12, 12, 12, 12, 12, 12, 12, 12},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{3, 2, 2, 2, 0, 4, 4, 7, 5, 6, 8, 6, 6, 8, 4, 4, 4, 5, 3, 3,
0, 3, 0, 0, 4, 8, 0, 0, 0, 1, 0, 10, 0, 8, 10, 0, 2, 4, 5, 5,
6, 4, 7, 4, 6, 5, 7, 7, 10, 0, 8, 0, 4, 10, 12, 14, 12, 10, 4, 3,
5, 10, 2, 2, 4, 4, 7, 7, 7, 8, 0, 10, 8, 3, 4, 10, 10, 2, 4, 10,
12, 7, 0, 0, 0, 0, 8, 8, 10, 10, 10, 8, 8, 8, 8, 8, 8, 8, 8},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{10, 10, 10, 10, 0, 5, 4, 12, 10, 6, 3, 4, 4, 6, 6, 5, 10,
12, 10, 10, 0, 10, 0, 0, 10, 10, 0, 0, 0, 10, 0, 1, 0, 20,
2, 0, 10, 12, 8, 10, 10, 4, 6, 8, 16, 14, 6, 6, 20, 0, 20,
0, 12, 20, 20, 20, 20, 20, 7, 8, 14, 2, 10, 10, 8, 8, 16, 16,
16, 5, 0, 2, 10, 8, 10, 2, 8, 10, 8, 20, 20, 16, 0, 0, 0,
0, 20, 20, 4, 4, 4, 20, 20, 20, 20, 20, 20, 20, 20},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{10, 8, 8, 8, 0, 12, 12, 4, 5, 8, 12, 10, 10, 12, 12, 12, 8,
7, 10, 10, 0, 10, 0, 0, 6, 8, 0, 0, 0, 8, 0, 20, 0, 1,
20, 0, 14, 4, 7, 5, 16, 12, 12, 16, 6, 7, 10, 12, 3, 0, 4,
0, 4, 2, 2, 2, 4, 2, 12, 14, 6, 20, 14, 14, 16, 16, 6, 6,
6, 12, 0, 20, 16, 10, 8, 20, 20, 14, 16, 4, 2, 6, 0, 0, 0,
0, 4, 4, 20, 20, 20, 4, 4, 4, 4, 4, 4, 4, 4},
{10, 10, 12, 10, 0, 5, 4, 12, 10, 6, 3, 4, 4, 6, 6, 5, 12,
12, 10, 10, 0, 10, 0, 0, 12, 10, 0, 0, 0, 10, 0, 2, 0, 20,
1, 0, 10, 12, 8, 10, 10, 4, 6, 8, 16, 14, 6, 8, 20, 0, 20,
0, 12, 20, 20, 20, 20, 20, 7, 10, 14, 2, 10, 10, 8, 8, 16, 16,
16, 5, 0, 2, 10, 8, 10, 2, 8, 10, 8, 20, 20, 16, 0, 0, 0,
0, 20, 20, 4, 4, 4, 20, 20, 20, 20, 20, 20, 20, 20},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{3, 2, 2, 2, 0, 6, 6, 7, 5, 6, 7, 5, 5, 7, 6, 5, 5, 5, 4, 3,
0, 3, 0, 0, 4, 7, 0, 0, 0, 2, 0, 10, 0, 14, 10, 0, 1, 3, 5, 5,
5, 5, 7, 4, 7, 5, 6, 8, 14, 0, 8, 0, 7, 8, 10, 12, 10, 8, 4, 7,
7, 10, 1, 1, 5, 5, 7, 7, 7, 12, 0, 10, 6, 6, 7, 10, 10, 1, 5, 7,
12, 7, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14},
{6, 4, 4, 4, 0, 6, 6, 2, 2, 3, 7, 5, 5, 7, 6, 6, 6, 6, 6, 6,
0, 6, 0, 0, 4, 7, 0, 0, 0, 4, 0, 12, 0, 4, 12, 0, 3, 1, 5, 3,
8, 3, 8, 6, 7, 6, 7, 7, 7, 0, 6, 0, 4, 4, 5, 8, 8, 2, 6, 8,
6, 12, 3, 3, 6, 6, 6, 6, 6, 6, 0, 12, 8, 8, 6, 12, 12, 3, 5, 8,
6, 6, 0, 0, 0, 0, 12, 12, 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12},
{7, 5, 6, 5, 0, 4, 4, 4, 4, 3, 7, 5, 5, 7, 5, 5, 7, 8, 7, 7,
0, 7, 0, 0, 6, 6, 0, 0, 0, 5, 0, 8, 0, 7, 8, 0, 5, 5, 1, 3,
10, 5, 5, 4, 8, 7, 5, 10, 12, 0, 12, 0, 10, 12, 12, 14, 14, 12, 4, 8,
8, 8, 4, 4, 2, 2, 8, 8, 8, 10, 0, 10, 10, 10, 6, 8, 10, 12, 4, 7,
4, 8, 0, 0, 0, 0, 12, 12, 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12},
{6, 5, 6, 5, 0, 4, 4, 2, 2, 5, 8, 6, 6, 8, 5, 5, 7, 8, 7, 6,
0, 6, 0, 0, 5, 7, 0, 0, 0, 5, 0, 10, 0, 5, 10, 0, 5, 3, 3, 1,
10, 8, 8, 8, 10, 8, 5, 8, 10, 0, 10, 0, 8, 6, 5, 7, 8, 5, 8, 10,
8, 8, 7, 7, 5, 5, 7, 7, 7, 8, 0, 10, 10, 10, 8, 10, 12, 7, 5, 6,
5, 7, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
{8, 6, 4, 6, 0, 6, 7, 12, 10, 8, 4, 5, 5, 5, 6, 3, 5, 3, 8, 8,
0, 8, 0, 0, 4, 12, 0, 0, 0, 6, 0, 10, 0, 16, 10, 0, 5, 8, 10, 10,
1, 8, 8, 10, 4, 5, 8, 10, 12, 0, 12, 0, 8, 12, 12, 14, 10, 12, 10, 8,
8, 10, 5, 5, 8, 8, 4, 4, 4, 8, 0, 8, 3, 6, 10, 4, 4, 5, 8, 10,
12, 4, 0, 0, 0, 0, 8, 8, 12, 12, 12, 8, 8, 8, 8, 8, 8, 8, 8},
{7, 4, 8, 4, 0, 4, 4, 3, 2, 3, 5, 4, 4, 6, 6, 5, 8, 10, 8, 7,
0, 7, 0, 0, 5, 4, 0, 0, 0, 4, 0, 4, 0, 12, 4, 0, 5, 3, 5, 8,
8, 1, 6, 7, 8, 7, 4, 6, 12, 0, 12, 0, 7, 8, 12, 12, 14, 12, 7, 5,
7, 8, 5, 5, 4, 4, 10, 10, 10, 5, 0, 5, 8, 8, 3, 4, 8, 5, 4, 14,
12, 10, 0, 0, 0, 0, 14, 14, 6, 6, 6, 14, 14, 14, 14, 14, 14, 14, 14},
{8, 7, 8, 7, 0, 4, 4, 6, 5, 2, 6, 5, 5, 6, 5, 6, 8, 10, 10, 8,
0, 8, 0, 0, 7, 7, 0, 0, 0, 7, 0, 6, 0, 12, 6, 0, 7, 8, 5, 8,
8, 6, 1, 8, 8, 8, 6, 8, 10, 0, 8, 0, 7, 7, 8, 10, 12, 7, 8, 8,
10, 3, 5, 5, 4, 4, 10, 10, 10, 6, 0, 7, 10, 10, 6, 4, 10, 5, 4, 12,
10, 10, 0, 0, 0, 0, 12, 12, 8, 8, 8, 12, 12, 12, 12, 12, 12, 12, 12},
{4, 4, 6, 4, 0, 4, 5, 7, 6, 6, 6, 4, 4, 4, 2, 3, 4, 5, 4, 4,
0, 4, 0, 0, 7, 7, 0, 0, 0, 4, 0, 8, 0, 16, 8, 0, 4, 6, 4, 8,
10, 7, 8, 1, 10, 8, 6, 8, 5, 0, 7, 0, 8, 10, 10, 12, 12, 10, 2, 7,
10, 8, 4, 4, 3, 3, 10, 10, 10, 6, 0, 6, 8, 8, 7, 7, 10, 4, 3, 12,
12, 10, 0, 0, 0, 0, 14, 14, 8, 8, 8, 14, 14, 14, 14, 14, 14, 14, 14},
{7, 6, 6, 6, 0, 7, 10, 8, 8, 8, 10, 8, 8, 10, 10, 10, 6, 3, 8, 7,
0, 7, 0, 0, 4, 10, 0, 0, 0, 6, 0, 16, 0, 6, 16, 0, 7, 7, 8, 10,
4, 8, 8, 10, 1, 3, 10, 10, 4, 0, 4, 0, 4, 8, 10, 12, 12, 8, 10, 12,
8, 12, 7, 7, 8, 8, 4, 4, 4, 10, 0, 12, 4, 5, 7, 10, 8, 7, 8, 12,
12, 4, 0, 0, 0, 0, 8, 8, 12, 12, 12, 8, 8, 8, 8, 8, 8, 8, 8},
{6, 5, 3, 5, 0, 6, 8, 8, 7, 7, 8, 7, 7, 8, 8, 8, 4, 4, 7, 6,
0, 6, 0, 0, 4, 8, 0, 0, 0, 5, 0, 14, 0, 7, 14, 0, 5, 6, 7, 8,
5, 7, 8, 8, 3, 1, 8, 8, 8, 0, 7, 0, 6, 6, 8, 10, 10, 8, 6, 8,
6, 8, 5, 5, 8, 8, 3, 3, 3, 10, 0, 12, 3, 4, 7, 10, 8, 5, 8, 10,
10, 3, 0, 0, 0, 0, 8, 8, 14, 14, 14, 8, 8, 8, 8, 8, 8, 8, 8},
{8, 7, 8, 7, 0, 6, 6, 7, 5, 4, 8, 7, 7, 8, 6, 6, 8, 8, 8, 8,
0, 8, 0, 0, 8, 5, 0, 0, 0, 7, 0, 6, 0, 10, 6, 0, 6, 7, 5, 5,
8, 4, 6, 6, 10, 8, 1, 4, 10, 0, 10, 0, 5, 8, 8, 10, 12, 8, 6, 5,
8, 5, 6, 6, 5, 5, 8, 8, 8, 6, 0, 6, 8, 8, 5, 4, 8, 6, 5, 12,
10, 8, 0, 0, 0, 0, 14, 14, 8, 8, 8, 14, 14, 14, 14, 14, 14, 14, 14},
{7, 6, 7, 6, 0, 6, 5, 7, 4, 5, 6, 6, 6, 6, 6, 4, 7,
8, 8, 7, 0, 7, 0, 0, 6, 10, 0, 0, 0, 7, 0, 6, 0, 12,
8, 0, 8, 7, 10, 8, 10, 6, 8, 8, 10, 8, 4, 1, 10, 0, 10,
0, 8, 10, 10, 12, 14, 10, 6, 7, 10, 7, 8, 8, 6, 6, 10, 10,
10, 8, 0, 6, 10, 10, 4, 7, 10, 8, 6, 14, 12, 10, 0, 0, 0,
0, 14, 14, 8, 8, 8, 14, 14, 14, 14, 14, 14, 14, 14},
{10, 10, 8, 10, 0, 4, 10, 10, 10, 12, 12, 10, 10, 10, 5, 10, 7,
6, 10, 10, 0, 10, 0, 0, 8, 12, 0, 0, 0, 10, 0, 20, 0, 3,
20, 0, 14, 7, 12, 10, 12, 12, 10, 5, 4, 8, 10, 10, 1, 0, 2,
0, 8, 4, 4, 5, 6, 3, 4, 8, 6, 20, 14, 14, 16, 16, 4, 4,
4, 3, 0, 20, 8, 7, 10, 20, 16, 14, 16, 6, 5, 4, 0, 0, 0,
0, 6, 6, 20, 20, 20, 6, 6, 6, 6, 6, 6, 6, 6},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{10, 8, 6, 8, 0, 6, 10, 10, 8, 10, 12, 10, 10, 10, 7, 8, 6,
4, 10, 10, 0, 10, 0, 0, 7, 12, 0, 0, 0, 8, 0, 20, 0, 4,
20, 0, 8, 6, 12, 10, 12, 12, 8, 7, 4, 7, 10, 10, 2, 0, 1,
0, 6, 2, 5, 7, 8, 5, 8, 8, 12, 20, 8, 8, 10, 10, 7, 7,
7, 6, 0, 20, 10, 10, 8, 20, 16, 8, 10, 8, 7, 7, 0, 0, 0,
0, 6, 6, 20, 20, 20, 6, 6, 6, 6, 6, 6, 6, 6},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{6, 4, 6, 4, 0, 5, 5, 5, 4, 5, 10, 10, 10, 10, 8, 8, 6, 4, 6, 6,
0, 6, 0, 0, 5, 7, 0, 0, 0, 4, 0, 12, 0, 4, 12, 0, 7, 4, 10, 8,
8, 7, 7, 8, 4, 6, 5, 8, 8, 0, 6, 0, 1, 4, 6, 8, 10, 4, 10, 8,
5, 14, 7, 7, 8, 8, 4, 4, 4, 10, 0, 12, 6, 6, 8, 12, 10, 7, 8, 10,
8, 4, 0, 0, 0, 0, 8, 8, 14, 14, 14, 8, 8, 8, 8, 8, 8, 8, 8},
{10, 10, 8, 10, 0, 12, 12, 4, 3, 6, 12, 12, 12, 12, 12, 12, 8,
10, 10, 10, 0, 10, 0, 0, 8, 6, 0, 0, 0, 10, 0, 20, 0, 2,
20, 0, 8, 4, 12, 6, 12, 8, 7, 10, 8, 6, 8, 10, 4, 0, 2,
0, 4, 1, 2, 2, 4, 2, 10, 10, 6, 20, 8, 8, 10, 10, 4, 4,
4, 10, 0, 20, 10, 10, 8, 20, 16, 8, 10, 4, 2, 4, 0, 0, 0,
0, 5, 5, 20, 20, 20, 5, 5, 5, 5, 5, 5, 5, 5},
{12, 12, 10, 12, 0, 14, 14, 5, 3, 6, 12, 12, 12, 12, 12, 12, 10,
12, 12, 12, 0, 12, 0, 0, 10, 7, 0, 0, 0, 12, 0, 20, 0, 2,
20, 0, 10, 5, 12, 5, 12, 12, 8, 10, 10, 8, 8, 10, 4, 0, 5,
0, 6, 2, 1, 2, 4, 2, 10, 10, 6, 20, 8, 8, 10, 10, 4, 4,
4, 10, 0, 20, 10, 10, 8, 20, 16, 8, 10, 4, 2, 4, 0, 0, 0,
0, 5, 5, 20, 20, 20, 5, 5, 5, 5, 5, 5, 5, 5},
{14, 14, 12, 14, 0, 16, 16, 5, 3, 8, 14, 14, 14, 14, 14, 14, 12,
14, 14, 14, 0, 14, 0, 0, 12, 7, 0, 0, 0, 14, 0, 20, 0, 2,
20, 0, 12, 8, 14, 7, 14, 12, 10, 12, 12, 10, 10, 12, 5, 0, 7,
0, 8, 2, 2, 1, 3, 3, 12, 12, 8, 20, 10, 10, 12, 12, 8, 8,
8, 12, 0, 20, 14, 14, 12, 20, 20, 10, 12, 4, 2, 8, 0, 0, 0,
0, 8, 8, 20, 20, 20, 8, 8, 8, 8, 8, 8, 8, 8},
{12, 12, 10, 12, 0, 14, 14, 8, 6, 10, 12, 12, 12, 14, 12, 12, 10,
6, 12, 12, 0, 12, 0, 0, 10, 10, 0, 0, 0, 12, 0, 20, 0, 4,
20, 0, 10, 8, 14, 8, 10, 14, 12, 12, 12, 10, 12, 14, 6, 0, 8,
0, 10, 4, 4, 3, 1, 5, 12, 12, 10, 20, 8, 8, 14, 14, 10, 10,
10, 12, 0, 20, 12, 12, 12, 20, 20, 8, 14, 2, 4, 10, 0, 0, 0,
0, 8, 8, 20, 20, 20, 8, 8, 8, 8, 8, 8, 8, 8},
{10, 10, 8, 10, 0, 12, 12, 4, 3, 5, 12, 12, 12, 14, 12, 12, 8,
7, 10, 10, 0, 10, 0, 0, 7, 6, 0, 0, 0, 10, 0, 20, 0, 2,
20, 0, 8, 2, 12, 5, 12, 12, 7, 10, 8, 8, 8, 10, 3, 0, 5,
0, 4, 2, 2, 3, 5, 1, 10, 12, 6, 20, 8, 8, 12, 12, 8, 8,
8, 12, 0, 20, 12, 12, 10, 20, 20, 8, 12, 5, 3, 8, 0, 0, 0,
0, 10, 10, 20, 20, 20, 10, 10, 10, 10, 10, 10, 10, 10},
{4, 4, 6, 4, 0, 2, 3, 6, 5, 6, 5, 4, 4, 6, 3, 4, 6, 7, 4, 4,
0, 4, 0, 0, 7, 6, 0, 0, 0, 4, 0, 7, 0, 12, 7, 0, 4, 6, 4, 8,
10, 7, 8, 2, 10, 6, 6, 6, 4, 0, 8, 0, 10, 10, 10, 12, 12, 10, 1, 7,
10, 8, 4, 4, 3, 3, 10, 10, 10, 4, 0, 7, 10, 10, 7, 7, 10, 4, 3, 12,
12, 10, 0, 0, 0, 0, 14, 14, 7, 7, 7, 14, 14, 14, 14, 14, 14, 14, 14},
{4, 3, 6, 3, 0, 4, 4, 6, 5, 6, 6, 5, 5, 6, 5, 4, 7, 8, 6, 4,
0, 4, 0, 0, 7, 6, 0, 0, 0, 3, 0, 8, 0, 14, 10, 0, 7, 8, 8, 10,
8, 5, 8, 7, 12, 8, 5, 7, 8, 0, 8, 0, 8, 10, 10, 12, 12, 12, 7, 1,
5, 7, 7, 7, 7, 7, 12, 12, 12, 8, 0, 8, 10, 10, 7, 6, 10, 7, 7, 8,
12, 12, 0, 0, 0, 0, 14, 14, 8, 8, 8, 14, 14, 14, 14, 14, 14, 14, 14},
{6, 5, 3, 5, 0, 8, 8, 6, 5, 6, 8, 8, 8, 10, 7, 8, 4, 5, 8, 6,
0, 6, 0, 0, 5, 8, 0, 0, 0, 5, 0, 14, 0, 6, 14, 0, 7, 6, 8, 8,
8, 7, 10, 10, 8, 6, 8, 10, 6, 0, 12, 0, 5, 6, 6, 8, 10, 6, 10, 5,
1, 10, 7, 7, 8, 8, 3, 3, 3, 8, 0, 14, 5, 5, 10, 10, 10, 7, 8, 10,
8, 10, 0, 0, 0, 0, 8, 8, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8},
{10, 10, 12, 10, 0, 6, 6, 5, 4, 3, 5, 6, 6, 6, 5, 7, 10,
12, 12, 10, 0, 10, 0, 0, 12, 7, 0, 0, 0, 10, 0, 2, 0, 20,
2, 0, 10, 12, 8, 8, 10, 8, 3, 8, 12, 8, 5, 7, 20, 0, 20,
0, 14, 20, 20, 20, 20, 20, 8, 7, 10, 1, 10, 10, 8, 8, 20, 20,
20, 5, 0, 2, 10, 10, 7, 2, 6, 10, 8, 20, 20, 20, 0, 0, 0,
0, 20, 20, 4, 4, 4, 20, 20, 20, 20, 20, 20, 20, 20},
{3, 2, 2, 2, 0, 6, 6, 7, 5, 6, 7, 5, 5, 7, 6, 5, 5, 5, 4, 3,
0, 3, 0, 0, 4, 7, 0, 0, 0, 2, 0, 10, 0, 14, 10, 0, 1, 3, 4, 7,
5, 5, 5, 4, 7, 5, 6, 8, 14, 0, 8, 0, 7, 8, 8, 10, 8, 8, 4, 7,
7, 10, 1, 2, 5, 5, 7, 7, 7, 12, 0, 10, 6, 6, 7, 10, 10, 2, 5, 10,
12, 7, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14},
{3, 2, 2, 2, 0, 6, 6, 7, 5, 6, 7, 5, 5, 7, 6, 5, 5, 5, 4, 3,
0, 3, 0, 0, 4, 7, 0, 0, 0, 2, 0, 10, 0, 14, 10, 0, 1, 3, 4, 7,
5, 5, 5, 4, 7, 5, 6, 8, 14, 0, 8, 0, 7, 8, 8, 10, 8, 8, 4, 7,
7, 10, 2, 1, 5, 5, 7, 7, 7, 12, 0, 10, 6, 6, 7, 10, 10, 2, 5, 10,
12, 7, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14},
{6, 4, 6, 4, 0, 4, 4, 4, 4, 4, 6, 5, 5, 7, 5, 5, 7, 8, 7, 6,
0, 6, 0, 0, 6, 6, 0, 0, 0, 4, 0, 8, 0, 16, 8, 0, 5, 6, 2, 5,
8, 4, 4, 3, 8, 8, 5, 6, 16, 0, 10, 0, 8, 10, 10, 12, 14, 12, 3, 7,
8, 8, 5, 5, 1, 2, 6, 6, 6, 10, 0, 8, 6, 6, 6, 8, 10, 5, 2, 6,
4, 6, 0, 0, 0, 0, 12, 12, 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12},
{6, 4, 6, 4, 0, 4, 4, 4, 4, 4, 6, 5, 5, 7, 5, 5, 7, 8, 7, 6,
0, 6, 0, 0, 6, 6, 0, 0, 0, 4, 0, 8, 0, 16, 8, 0, 5, 6, 2, 5,
8, 4, 4, 3, 8, 8, 5, 6, 16, 0, 10, 0, 8, 10, 10, 12, 14, 12, 3, 7,
8, 8, 5, 5, 2, 1, 6, 6, 6, 10, 0, 8, 6, 6, 6, 8, 10, 5, 2, 6,
4, 6, 0, 0, 0, 0, 12, 12, 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12},
{8, 7, 4, 7, 0, 8, 8, 7, 7, 7, 10, 10, 10, 10, 10, 10, 6,
3, 8, 8, 0, 8, 0, 0, 4, 8, 0, 0, 0, 7, 0, 16, 0, 6,
16, 0, 7, 6, 8, 7, 4, 10, 10, 10, 4, 3, 8, 10, 4, 0, 7,
0, 4, 4, 4, 8, 10, 8, 10, 12, 3, 20, 7, 7, 6, 6, 1, 2,
2, 10, 0, 20, 5, 5, 10, 20, 16, 7, 6, 8, 6, 2, 0, 0, 0,
0, 10, 10, 20, 20, 20, 10, 10, 10, 10, 10, 10, 10, 10},
{8, 7, 4, 7, 0, 8, 8, 7, 7, 7, 10, 10, 10, 10, 10, 10, 6,
3, 8, 8, 0, 8, 0, 0, 4, 8, 0, 0, 0, 7, 0, 16, 0, 6,
16, 0, 7, 6, 8, 7, 4, 10, 10, 10, 4, 3, 8, 10, 4, 0, 7,
0, 4, 4, 4, 8, 10, 8, 10, 12, 3, 20, 7, 7, 6, 6, 2, 1,
2, 10, 0, 20, 5, 5, 10, 20, 16, 7, 6, 8, 6, 2, 0, 0, 0,
0, 10, 10, 20, 20, 20, 10, 10, 10, 10, 10, 10, 10, 10},
{8, 7, 4, 7, 0, 8, 8, 7, 7, 7, 10, 10, 10, 10, 10, 10, 6,
3, 8, 8, 0, 8, 0, 0, 4, 8, 0, 0, 0, 7, 0, 16, 0, 6,
16, 0, 7, 6, 8, 7, 4, 10, 10, 10, 4, 3, 8, 10, 4, 0, 7,
0, 4, 4, 4, 8, 10, 8, 10, 12, 3, 20, 7, 7, 6, 6, 2, 2,
1, 10, 0, 20, 5, 5, 10, 20, 16, 7, 6, 8, 6, 2, 0, 0, 0,
0, 10, 10, 20, 20, 20, 10, 10, 10, 10, 10, 10, 10, 10},
{8, 8, 10, 8, 0, 3, 4, 6, 7, 8, 5, 6, 6, 10, 5, 6, 8,
10, 8, 8, 0, 8, 0, 0, 10, 7, 0, 0, 0, 8, 0, 5, 0, 12,
5, 0, 12, 6, 10, 8, 8, 5, 6, 6, 10, 10, 6, 8, 3, 0, 6,
0, 10, 10, 10, 12, 12, 12, 4, 8, 8, 5, 12, 12, 10, 10, 10, 10,
10, 1, 0, 4, 7, 7, 6, 5, 8, 12, 10, 12, 14, 10, 0, 0, 0,
0, 10, 10, 5, 4, 4, 10, 10, 10, 10, 10, 10, 10, 10},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{10, 10, 12, 10, 0, 6, 6, 10, 10, 8, 4, 4, 4, 5, 3, 4, 10,
10, 10, 10, 0, 10, 0, 0, 12, 10, 0, 0, 0, 10, 0, 2, 0, 20,
2, 0, 10, 12, 10, 10, 8, 5, 7, 6, 12, 12, 6, 6, 20, 0, 20,
0, 12, 20, 20, 20, 20, 20, 7, 8, 14, 2, 10, 10, 8, 8, 20, 20,
20, 4, 0, 1, 6, 6, 8, 2, 6, 10, 10, 20, 20, 20, 0, 0, 0,
0, 20, 20, 4, 3, 3, 20, 20, 20, 20, 20, 20, 20, 20},
{10, 8, 5, 8, 0, 6, 6, 10, 10, 8, 6, 6, 6, 7, 5, 7, 3, 2, 8, 10,
0, 10, 0, 0, 5, 10, 0, 0, 0, 8, 0, 10, 0, 16, 10, 0, 6, 8, 10, 10,
3, 8, 10, 8, 4, 3, 8, 10, 8, 0, 10, 0, 6, 10, 10, 14, 12, 12, 10, 10,
5, 10, 6, 6, 6, 6, 5, 5, 5, 7, 0, 6, 1, 3, 8, 7, 5, 6, 10, 12,
14, 5, 0, 0, 0, 0, 8, 8, 14, 14, 14, 8, 8, 8, 8, 8, 8, 8, 8},
{4, 3, 3, 3, 0, 7, 8, 10, 10, 10, 10, 10, 10, 8, 3, 5, 2, 2, 5, 4,
0, 4, 0, 0, 4, 7, 0, 0, 0, 3, 0, 8, 0, 10, 8, 0, 6, 8, 10, 10,
6, 8, 10, 8, 5, 4, 8, 10, 7, 0, 10, 0, 6, 10, 10, 14, 12, 12, 10, 10,
5, 10, 6, 6, 6, 6, 5, 5, 5, 7, 0, 6, 3, 1, 7, 10, 7, 6, 10, 7,
8, 5, 0, 0, 0, 0, 8, 8, 14, 14, 14, 8, 8, 8, 8, 8, 8, 8, 8},
{5, 4, 6, 4, 0, 6, 5, 6, 3, 6, 6, 6, 6, 8, 6, 6, 6, 7, 5, 5,
0, 5, 0, 0, 3, 4, 0, 0, 0, 4, 0, 10, 0, 8, 10, 0, 7, 6, 6, 8,
10, 3, 6, 7, 7, 7, 5, 4, 10, 0, 8, 0, 8, 8, 8, 12, 12, 10, 7, 7,
10, 7, 7, 7, 6, 6, 10, 10, 10, 6, 0, 8, 8, 7, 1, 7, 8, 7, 6, 10,
8, 10, 0, 0, 0, 0, 10, 10, 7, 7, 7, 10, 10, 10, 10, 10, 10, 10, 10},
{10, 10, 12, 10, 0, 6, 6, 6, 7, 4, 3, 4, 4, 5, 7, 7, 12,
12, 10, 10, 0, 10, 0, 0, 12, 6, 0, 0, 0, 10, 0, 2, 0, 20,
2, 0, 10, 12, 8, 10, 4, 4, 4, 7, 10, 10, 4, 7, 20, 0, 20,
0, 12, 20, 20, 20, 20, 20, 7, 6, 10, 2, 10, 10, 8, 8, 20, 20,
20, 5, 0, 2, 7, 10, 7, 1, 8, 10, 8, 20, 20, 20, 0, 0, 0,
0, 20, 20, 4, 4, 4, 20, 20, 20, 20, 20, 20, 20, 20},
{10, 10, 12, 10, 0, 8, 8, 10, 10, 10, 5, 4, 4, 5, 7, 8, 12,
12, 10, 10, 0, 10, 0, 0, 12, 8, 0, 0, 0, 10, 0, 8, 0, 20,
8, 0, 10, 12, 10, 12, 4, 8, 10, 10, 8, 8, 8, 10, 16, 0, 16,
0, 10, 16, 16, 20, 20, 20, 10, 10, 10, 6, 10, 10, 10, 10, 16, 16,
16, 8, 0, 6, 5, 7, 8, 8, 1, 10, 10, 20, 20, 16, 0, 0, 0,
0, 10, 10, 8, 6, 7, 10, 10, 10, 10, 10, 10, 10, 10},
{3, 2, 2, 2, 0, 5, 6, 7, 5, 6, 7, 5, 5, 7, 6, 5, 5, 5, 4, 3,
0, 3, 0, 0, 4, 7, 0, 0, 0, 2, 0, 10, 0, 14, 10, 0, 1, 3, 12, 7,
5, 5, 5, 4, 7, 5, 6, 8, 14, 0, 8, 0, 7, 8, 8, 10, 8, 8, 4, 7,
7, 10, 2, 2, 5, 5, 7, 7, 7, 12, 0, 10, 6, 6, 7, 10, 10, 1, 5, 3,
6, 7, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14},
{6, 4, 4, 4, 0, 4, 6, 4, 4, 4, 6, 5, 5, 7, 5, 5, 7, 8, 7, 6,
0, 6, 0, 0, 6, 6, 0, 0, 0, 4, 0, 8, 0, 16, 8, 0, 5, 5, 4, 5,
8, 4, 4, 3, 8, 8, 5, 6, 16, 0, 10, 0, 8, 10, 10, 12, 14, 12, 3, 7,
8, 8, 5, 5, 2, 2, 6, 6, 6, 10, 0, 10, 10, 10, 6, 8, 10, 5, 1, 6,
4, 6, 0, 0, 0, 0, 12, 12, 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12},
{10, 10, 7, 10, 0, 12, 12, 7, 6, 8, 12, 12, 12, 12, 12, 12, 7,
6, 10, 10, 0, 10, 0, 0, 6, 8, 0, 0, 0, 10, 0, 20, 0, 4,
20, 0, 7, 8, 7, 6, 10, 14, 12, 12, 12, 10, 12, 14, 6, 0, 8,
0, 10, 4, 4, 4, 2, 5, 12, 8, 10, 20, 10, 10, 6, 6, 8, 8,
8, 12, 0, 20, 12, 7, 10, 20, 20, 3, 6, 1, 3, 8, 0, 0, 0,
0, 8, 8, 20, 20, 20, 8, 8, 8, 8, 8, 8, 8, 8},
{12, 12, 10, 12, 0, 10, 10, 5, 4, 6, 14, 14, 14, 14, 14, 14, 8,
8, 12, 12, 0, 12, 0, 0, 8, 6, 0, 0, 0, 12, 0, 20, 0, 2,
20, 0, 12, 6, 4, 5, 12, 12, 10, 12, 12, 10, 10, 12, 5, 0, 7,
0, 8, 2, 2, 2, 4, 3, 12, 12, 8, 20, 12, 12, 4, 4, 6, 6,
6, 14, 0, 20, 14, 8, 8, 20, 20, 6, 4, 3, 1, 6, 0, 0, 0,
0, 5, 5, 20, 20, 20, 5, 5, 5, 5, 5, 5, 5, 5},
{8, 7, 5, 7, 0, 8, 8, 7, 7, 7, 10, 10, 10, 10, 10, 10, 5,
3, 8, 8, 0, 8, 0, 0, 4, 8, 0, 0, 0, 7, 0, 16, 0, 6,
16, 0, 7, 6, 8, 7, 4, 10, 10, 10, 4, 3, 8, 10, 4, 0, 7,
0, 4, 4, 4, 8, 10, 8, 10, 12, 10, 20, 7, 7, 6, 6, 2, 2,
2, 10, 0, 20, 5, 5, 10, 20, 16, 7, 6, 8, 6, 1, 0, 0, 0,
0, 10, 10, 20, 20, 20, 10, 10, 10, 10, 10, 10, 10, 10},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{10, 8, 6, 8, 0, 14, 14, 12, 12, 12, 14, 12, 12, 14, 14, 14, 6,
5, 10, 10, 0, 10, 0, 0, 6, 12, 0, 0, 0, 8, 0, 20, 0, 4,
20, 0, 14, 12, 12, 12, 8, 14, 12, 14, 8, 8, 14, 14, 6, 0, 6,
0, 8, 5, 5, 8, 8, 10, 14, 14, 8, 20, 14, 14, 12, 12, 10, 10,
10, 10, 0, 20, 8, 8, 10, 20, 10, 14, 12, 8, 5, 10, 0, 0, 0,
0, 1, 2, 20, 20, 20, 6, 5, 4, 4, 4, 4, 4, 4},
{10, 8, 6, 8, 0, 14, 14, 12, 12, 12, 14, 12, 12, 14, 14, 14, 6,
5, 10, 10, 0, 10, 0, 0, 6, 12, 0, 0, 0, 8, 0, 20, 0, 4,
20, 0, 14, 12, 12, 12, 8, 14, 12, 14, 8, 8, 14, 14, 6, 0, 6,
0, 8, 5, 5, 8, 8, 10, 14, 14, 8, 20, 14, 14, 12, 12, 10, 10,
10, 10, 0, 20, 8, 8, 10, 20, 10, 14, 12, 8, 5, 10, 0, 0, 0,
0, 2, 1, 20, 20, 20, 7, 6, 5, 5, 5, 5, 5, 5},
{10, 10, 12, 10, 0, 8, 8, 10, 10, 10, 3, 4, 5, 8, 10, 10, 12,
12, 10, 10, 0, 10, 0, 0, 12, 8, 0, 0, 0, 10, 0, 4, 0, 20,
4, 0, 14, 10, 10, 12, 12, 6, 8, 8, 12, 14, 8, 8, 20, 0, 20,
0, 14, 20, 20, 20, 20, 20, 7, 8, 16, 4, 14, 14, 10, 10, 20, 20,
20, 5, 0, 4, 14, 14, 7, 4, 8, 14, 10, 20, 20, 20, 0, 0, 0,
0, 20, 20, 1, 3, 4, 20, 20, 20, 20, 20, 20, 20, 20},
{10, 10, 12, 10, 0, 8, 8, 10, 10, 10, 4, 3, 3, 5, 8, 8, 12,
12, 10, 10, 0, 10, 0, 0, 12, 8, 0, 0, 0, 10, 0, 4, 0, 20,
4, 0, 14, 10, 10, 12, 12, 6, 8, 8, 12, 14, 8, 8, 20, 0, 20,
0, 14, 20, 20, 20, 20, 20, 7, 8, 16, 4, 14, 14, 10, 10, 20, 20,
20, 4, 0, 3, 14, 14, 7, 4, 6, 14, 10, 20, 20, 20, 0, 0, 0,
0, 20, 20, 3, 1, 2, 20, 20, 20, 20, 20, 20, 20, 20},
{10, 10, 12, 10, 0, 8, 8, 10, 10, 10, 5, 3, 3, 4, 7, 7, 12,
12, 10, 10, 0, 10, 0, 0, 12, 8, 0, 0, 0, 10, 0, 4, 0, 20,
4, 0, 14, 10, 10, 12, 12, 6, 8, 8, 12, 14, 8, 8, 20, 0, 20,
0, 14, 20, 20, 20, 20, 20, 7, 8, 16, 4, 14, 14, 10, 10, 20, 20,
20, 4, 0, 3, 14, 14, 7, 4, 7, 14, 10, 20, 20, 20, 0, 0, 0,
0, 20, 20, 4, 2, 1, 20, 20, 20, 20, 20, 20, 20, 20},
{10, 8, 6, 8, 0, 14, 14, 12, 12, 12, 14, 12, 12, 14, 14, 14, 10,
10, 10, 10, 0, 10, 0, 0, 6, 12, 0, 0, 0, 8, 0, 20, 0, 4,
20, 0, 14, 12, 12, 12, 8, 14, 12, 14, 8, 8, 14, 14, 6, 0, 6,
0, 8, 5, 5, 8, 8, 10, 14, 14, 8, 20, 14, 14, 12, 12, 10, 10,
10, 10, 0, 20, 8, 8, 10, 20, 10, 14, 12, 8, 5, 10, 0, 0, 0,
0, 6, 7, 20, 20, 20, 1, 2, 3, 5, 5, 5, 5, 5},
{10, 8, 6, 8, 0, 14, 14, 12, 12, 12, 14, 12, 12, 14, 14, 14, 8,
8, 10, 10, 0, 10, 0, 0, 6, 12, 0, 0, 0, 8, 0, 20, 0, 4,
20, 0, 14, 12, 12, 12, 8, 14, 12, 14, 8, 8, 14, 14, 6, 0, 6,
0, 8, 5, 5, 8, 8, 10, 14, 14, 8, 20, 14, 14, 12, 12, 10, 10,
10, 10, 0, 20, 8, 8, 10, 20, 10, 14, 12, 8, 5, 10, 0, 0, 0,
0, 5, 6, 20, 20, 20, 2, 1, 2, 4, 4, 4, 4, 4},
{10, 8, 6, 8, 0, 14, 14, 12, 12, 12, 14, 12, 12, 14, 14, 14, 8,
8, 10, 10, 0, 10, 0, 0, 6, 12, 0, 0, 0, 8, 0, 20, 0, 4,
20, 0, 14, 12, 12, 12, 8, 14, 12, 14, 8, 8, 14, 14, 6, 0, 6,
0, 8, 5, 5, 8, 8, 10, 14, 14, 8, 20, 14, 14, 12, 12, 10, 10,
10, 10, 0, 20, 8, 8, 10, 20, 10, 14, 12, 8, 5, 10, 0, 0, 0,
0, 4, 5, 20, 20, 20, 3, 2, 1, 4, 4, 4, 4, 4},
{10, 8, 6, 8, 0, 14, 14, 12, 12, 12, 14, 12, 12, 14, 14, 14, 8,
8, 10, 10, 0, 10, 0, 0, 6, 12, 0, 0, 0, 8, 0, 20, 0, 4,
20, 0, 14, 12, 12, 12, 8, 14, 12, 14, 8, 8, 14, 14, 6, 0, 6,
0, 8, 5, 5, 8, 8, 10, 14, 14, 8, 20, 14, 14, 12, 12, 10, 10,
10, 10, 0, 20, 8, 8, 10, 20, 10, 14, 12, 8, 5, 10, 0, 0, 0,
0, 4, 5, 20, 20, 20, 5, 4, 4, 1, 3, 3, 3, 3},
{10, 8, 6, 8, 0, 14, 14, 12, 12, 12, 14, 12, 12, 14, 14, 14, 8,
8, 10, 10, 0, 10, 0, 0, 6, 12, 0, 0, 0, 8, 0, 20, 0, 4,
20, 0, 14, 12, 12, 12, 8, 14, 12, 14, 8, 8, 14, 14, 6, 0, 6,
0, 8, 5, 5, 8, 8, 10, 14, 14, 8, 20, 14, 14, 12, 12, 10, 10,
10, 10, 0, 20, 8, 8, 10, 20, 10, 14, 12, 8, 5, 10, 0, 0, 0,
0, 4, 5, 20, 20, 20, 5, 4, 4, 3, 1, 5, 4, 2},
{10, 8, 6, 8, 0, 14, 14, 12, 12, 12, 14, 12, 12, 14, 14, 14, 8,
8, 10, 10, 0, 10, 0, 0, 6, 12, 0, 0, 0, 8, 0, 20, 0, 4,
20, 0, 14, 12, 12, 12, 8, 14, 12, 14, 8, 8, 14, 14, 6, 0, 6,
0, 8, 5, 5, 8, 8, 10, 14, 14, 8, 20, 14, 14, 12, 12, 10, 10,
10, 10, 0, 20, 8, 8, 10, 20, 10, 14, 12, 8, 5, 10, 0, 0, 0,
0, 4, 5, 20, 20, 20, 5, 4, 4, 3, 5, 1, 3, 4},
{10, 8, 6, 8, 0, 14, 14, 12, 12, 12, 14, 12, 12, 14, 14, 14, 8,
8, 10, 10, 0, 10, 0, 0, 6, 12, 0, 0, 0, 8, 0, 20, 0, 4,
20, 0, 14, 12, 12, 12, 8, 14, 12, 14, 8, 8, 14, 14, 6, 0, 6,
0, 8, 5, 5, 8, 8, 10, 14, 14, 8, 20, 14, 14, 12, 12, 10, 10,
10, 10, 0, 20, 8, 8, 10, 20, 10, 14, 12, 8, 5, 10, 0, 0, 0,
0, 4, 5, 20, 20, 20, 5, 4, 4, 3, 4, 3, 1, 3},
{10, 8, 6, 8, 0, 14, 14, 12, 12, 12, 14, 12, 12, 14, 14, 14, 8,
8, 10, 10, 0, 10, 0, 0, 6, 12, 0, 0, 0, 8, 0, 20, 0, 4,
20, 0, 14, 12, 12, 12, 8, 14, 12, 14, 8, 8, 14, 14, 6, 0, 6,
0, 8, 5, 5, 8, 8, 10, 14, 14, 8, 20, 14, 14, 12, 12, 10, 10,
10, 10, 0, 20, 8, 8, 10, 20, 10, 14, 12, 8, 5, 10, 0, 0, 0,
0, 4, 5, 20, 20, 20, 5, 4, 4, 3, 2, 4, 3, 1},
};
MolHistogram::MolHistogram(const ROMol &mol, const double *dmat,
bool cleanupDmat)
: d_h(boost::extents[mol.getNumHeavyAtoms()][O3_MAX_H_BINS]) {
PRECONDITION(dmat, "empty distance matrix");
unsigned int nAtoms = mol.getNumAtoms();
unsigned int y = 0;
for (unsigned int i = 0; i < nAtoms; ++i) {
// skip hydrogens
if ((mol.getAtomWithIdx(i))->getAtomicNum() == 1) {
continue;
}
// initialize h row(y) to all zeros
for (unsigned int j = 0; j < O3_MAX_H_BINS; ++j) {
d_h[y][j] = 0;
}
for (unsigned int j = 0; j < nAtoms; ++j) {
auto dist = static_cast<unsigned int>(floor(dmat[i * nAtoms + j]));
if (dist < O3_MAX_H_BINS) {
++d_h[y][dist];
}
}
++y;
}
if (cleanupDmat) {
delete[] dmat;
}
}
int o3aMMFFCostFunc(const unsigned int prbIdx, const unsigned int refIdx,
double hSum, void *data) {
std::uint8_t mmffSim =
mmffSimMatrix[(static_cast<MMFF::MMFFMolProperties *>(
(static_cast<O3AFuncData *>(data))->refProp))
->getMMFFAtomType(refIdx) -
1][(static_cast<MMFF::MMFFMolProperties *>(
(static_cast<O3AFuncData *>(data))->prbProp))
->getMMFFAtomType(prbIdx) -
1];
return std::lround(
(static_cast<double>(((O3AFuncData *)data)->coeff) * O3_CHARGE_WEIGHT *
fabs((static_cast<MMFF::MMFFMolProperties *>(
(static_cast<O3AFuncData *>(data))->refProp))
->getMMFFPartialCharge(refIdx) -
(static_cast<MMFF::MMFFMolProperties *>(
(static_cast<O3AFuncData *>(data))->prbProp))
->getMMFFPartialCharge(prbIdx)) +
(((O3AFuncData *)data)->useMMFFSim
? static_cast<double>(O3_MAX_WEIGHT_COEFF -
((O3AFuncData *)data)->coeff) *
static_cast<double>(mmffSim)
: 0.0) +
hSum) *
1.0e03);
}
int o3aCrippenCostFunc(const unsigned int prbIdx, const unsigned int refIdx,
double hSum, void *data) {
return std::lround(
(static_cast<double>((static_cast<O3AFuncData *>(data))->coeff) *
O3_CHARGE_WEIGHT *
fabs((*(static_cast<std::vector<double> *>(
(static_cast<O3AFuncData *>(data))->refProp)))[refIdx] -
(*(static_cast<std::vector<double> *>(
(static_cast<O3AFuncData *>(data))->prbProp)))[prbIdx]) +
hSum) *
1.0e03);
}
void LAP::computeCostMatrix(const ROMol &prbMol, const MolHistogram &prbHist,
const ROMol &refMol, const MolHistogram &refHist,
O3AConstraintVect *o3aConstraintVect,
int (*costFunc)(const unsigned int,
const unsigned int, double, void *),
void *data, const unsigned int n_bins) {
unsigned int i;
unsigned int j;
unsigned int k;
unsigned int x;
unsigned int y;
unsigned int largestNHeavyAtoms =
std::max(refMol.getNumHeavyAtoms(), prbMol.getNumHeavyAtoms());
unsigned int mapIdx = 0;
double hSum = 0.0;
// ref is on rows, prb is on columns
for (i = 0; i < largestNHeavyAtoms; ++i) {
for (j = 0; j < largestNHeavyAtoms; ++j) {
d_cost[i][j] = O3_DUMMY_COST;
}
}
for (i = 0, y = 0; i < refMol.getNumAtoms(); ++i) {
if (refMol[i]->getAtomicNum() == 1) {
continue;
}
for (j = 0, x = 0; j < prbMol.getNumAtoms(); ++j) {
if (prbMol[j]->getAtomicNum() == 1) {
continue;
}
// if this pair is constrained, make sure that it is
// associated with a low cost so it is picked up
// in the minimum cost path
// first pair element is prb, second is ref
if (o3aConstraintVect && (mapIdx < o3aConstraintVect->size()) &&
(j == (*o3aConstraintVect)[mapIdx]->getPrbIdx()) &&
(i == (*o3aConstraintVect)[mapIdx]->getRefIdx())) {
d_cost[y][x] = O3_LARGE_NEGATIVE_WEIGHT;
++mapIdx;
} else {
for (k = 0, hSum = 0.0; k < n_bins; ++k) {
int rhyk = refHist.get(y, k);
int phxk = prbHist.get(x, k);
if ((!rhyk) && (!phxk)) {
continue;
}
hSum += square(rhyk - phxk) / (rhyk + phxk);
}
d_cost[y][x] = (*costFunc)(j, i, hSum, data);
}
++x;
}
++y;
}
}
//----------------------------------------------------
// Taken from Jonker, R.; Volgenant, A.
// A Shortest Augmenting Path Algorithm for Dense
// and Sparse Linear Assignment Problems
// Computing 1987, 38, 325-340
//----------------------------------------------------
void LAP::computeMinCostPath(const int dim) {
// input:
// dim - problem size
// cost - cost matrix
// output:
// rowsol - column assigned to row in solution
// colsol - row assigned to column in solution
// v - dual variables, column reduction numbers
int loopCnt;
int unassignedFound;
// row vars
int i;
int imin;
int numFree = 0;
int prvNumFree;
int f;
int i0;
int k;
int freeRow;
// col vars
int j;
int j1;
int j2 = 0;
int endOfPath;
int last = 0;
int low;
int up;
// cost vars
int min = 0;
int h;
int uMin;
int uSubMin;
int v2;
// init how many times a row will be assigned in the column reduction
for (int n = 0; n < dim; ++n) {
d_matches[n] = 0;
}
// COLUMN REDUCTION
// reverse order gives better results
for (j = dim - 1; j >= 0; --j) {
// find minimum cost over rows
min = d_cost[0][j];
imin = 0;
for (i = 1; i < dim; ++i) {
if (d_cost[i][j] < min) {
min = d_cost[i][j];
imin = i;
}
}
d_v[j] = min;
if (++(d_matches[imin]) == 1) {
// init assignment if minimum row assigned for first time
d_rowSol[imin] = j;
d_colSol[j] = imin;
} else {
// row already assigned, column not assigned
d_colSol[j] = -1;
}
}
// REDUCTION TRANSFER
for (i = 0; i < dim; ++i) {
if (!(d_matches[i])) {
// fill list of unassigned 'd_free' rows
d_free[numFree++] = i;
} else {
if (d_matches[i] == 1) {
// transfer reduction from rows that are assigned once
j1 = d_rowSol[i];
min = O3_DUMMY_COST;
for (j = 0; j < dim; ++j) {
if ((j != j1) && (d_cost[i][j] - d_v[j] < min)) {
min = d_cost[i][j] - d_v[j];
}
}
d_v[j1] -= min;
}
}
}
// AUGMENTING ROW REDUCTION
// do-loop to be done twice
for (loopCnt = 0; loopCnt < 2; ++loopCnt) {
// scan all d_free rows
// in some cases, a d_free row may be replaced
// with another one to be scanned next
k = 0;
prvNumFree = numFree;
numFree = 0;
// start list of rows still d_free after augmenting row reduction
while (k < prvNumFree) {
i = d_free[k];
++k;
// find minimum and second minimum reduced cost over columns
uMin = d_cost[i][0] - d_v[0];
j1 = 0;
uSubMin = O3_DUMMY_COST;
for (j = 1; j < dim; ++j) {
h = d_cost[i][j] - d_v[j];
if (h < uSubMin) {
if (h >= uMin) {
uSubMin = h;
j2 = j;
} else {
uSubMin = uMin;
uMin = h;
j2 = j1;
j1 = j;
}
}
}
i0 = d_colSol[j1];
if (uMin < uSubMin) {
// change the reduction of the minimum column to increase
// the minimum reduced cost in the row to the subminimum
d_v[j1] -= (uSubMin - uMin);
} else {
// minimum and subminimum equal
if (i0 >= 0) {
// minimum column j1 is assigned
// swap columns j1 and j2, as j2 may be unassigned
j1 = j2;
i0 = d_colSol[j2];
}
}
// (re-)assign i to j1, possibly de-assigning an i0
d_rowSol[i] = j1;
d_colSol[j1] = i;
if (i0 >= 0) {
// minimum column j1 assigned earlier
if (uMin < uSubMin) {
// put in current k, and go back to that k
// continue augmenting path i - j1 with i0
d_free[--k] = i0;
} else {
// no further augmenting reduction possible
// store i0 in list of d_free rows for next phase
d_free[numFree++] = i0;
}
}
}
}
// AUGMENT SOLUTION for each d_free row
for (f = 0; f < numFree; ++f) {
// start row of augmenting path
freeRow = d_free[f];
// Dijkstra shortest path algorithm
// runs until unassigned column added to shortest path tree
for (j = 0; j < dim; ++j) {
d_d[j] = d_cost[freeRow][j] - d_v[j];
d_pred[j] = freeRow;
// init column list
d_colList[j] = j;
}
// columns in 0..low-1 are ready, now none
low = 0;
// columns in low..up-1 are to be scanned for current minimum, now none
up = 0;
// columns in up..dim-1 are to be considered later to find new minimum,
// at this stage the list simply contains all columns
unassignedFound = 0;
while (!unassignedFound) {
if (up == low) {
// no more columns to be scanned for current minimum
last = low - 1;
// scan columns for up..dim-1 to find all indices for which new minimum
// occurs
// store these indices between low..up-1 (increasing up)
min = d_d[d_colList[up++]];
for (k = up; k < dim; ++k) {
j = d_colList[k];
h = d_d[j];
if (h <= min) {
if (h < min) {
// new minimum
// restart list at index low
up = low;
min = h;
}
// new index with same minimum, put on index up, and extend list
d_colList[k] = d_colList[up];
d_colList[up++] = j;
}
}
// check if any of the minimum columns happens to be unassigned
// if so, we have an augmenting path right away
for (k = low; k < up; ++k) {
if (d_colSol[d_colList[k]] < 0) {
endOfPath = d_colList[k];
unassignedFound = 1;
break;
}
}
}
if (!unassignedFound) {
// update 'distances' between freeRow and
// all unscanned columns, via next scanned column
j1 = d_colList[low];
++low;
i = d_colSol[j1];
h = d_cost[i][j1] - d_v[j1] - min;
for (k = up; k < dim; ++k) {
j = d_colList[k];
v2 = d_cost[i][j] - d_v[j] - h;
if (v2 < d_d[j]) {
d_pred[j] = i;
if (v2 == min) {
// new column found at same minimum value
if (d_colSol[j] < 0) {
// if unassigned, shortest augmenting path is complete
endOfPath = j;
unassignedFound = 1;
break;
} else {
// else add to list to be scanned right away
d_colList[k] = d_colList[up];
d_colList[up++] = j;
}
}
d_d[j] = v2;
}
}
}
}
// update column prices
for (k = 0; k <= last; ++k) {
j1 = d_colList[k];
d_v[j1] += (d_d[j1] - min);
}
// reset row and column assignments along the alternating path
do {
i = d_pred[endOfPath];
d_colSol[endOfPath] = i;
j1 = endOfPath;
endOfPath = d_rowSol[i];
d_rowSol[i] = j1;
} while (i != freeRow);
}
}
void SDM::fillFromLAP(LAP &lap) {
unsigned int i;
unsigned int j;
unsigned int k;
unsigned int n;
unsigned int n_atoms;
unsigned int n_equiv;
const RDGeom::POINT3D_VECT &refPos = d_refConf->getPositions();
const RDGeom::POINT3D_VECT &prbPos = d_prbConf->getPositions();
const ROMol *mol[2] = {&(d_refConf->getOwningMol()),
&(d_prbConf->getOwningMol())};
// filter out atom equivalences whose cost is > O3_DUMMY_COST
for (i = 0, n_equiv = 0; i < mol[0]->getNumHeavyAtoms(); ++i) {
if (lap.getCost(i, lap.getRowSol(i)) >= O3_DUMMY_COST) {
continue;
}
auto *sdmElement = new SDMElement;
sdmElement->idx[0] = i;
sdmElement->idx[1] = lap.getRowSol(i);
sdmElement->cost = lap.getCost(i, lap.getRowSol(i));
d_SDMPtrVect.push_back(boost::shared_ptr<SDMElement>(sdmElement));
++n_equiv;
}
boost::multi_array<double, 2> diff(boost::extents[n_equiv][n_equiv]);
// find out correspondences between heavy atom indexes
// in the original molecule and heavy atoms in the sdm matrix
// (the sdm matrix is rebuilt using original molecule
// heavy atom indexes)
for (n = 0; n < 2; ++n) {
for (i = 0; i < n_equiv; ++i) {
j = 0;
n_atoms = 0;
k = d_SDMPtrVect[i]->idx[n];
while ((n_atoms < mol[n]->getNumAtoms()) && (j <= k)) {
if (mol[n]->getAtomWithIdx(n_atoms)->getAtomicNum() > 1) {
++j;
d_SDMPtrVect[i]->idx[n] = n_atoms;
}
++n_atoms;
}
}
}
// loop over n_equiv rows
for (i = 0; i < n_equiv; ++i) {
d_SDMPtrVect[i]->o3aConstraint = nullptr;
if (d_SDMPtrVect[i]->cost < 0) {
// this is one of the constrained atom pairs, so assign it
// a pointer to the relevant O3AConstraint object
if (d_o3aConstraintVect) {
for (j = 0; j < (*d_o3aConstraintVect).size(); ++j) {
if (((*d_o3aConstraintVect)[j]->getPrbIdx() ==
d_SDMPtrVect[i]->idx[1]) &&
((*d_o3aConstraintVect)[j]->getRefIdx() ==
d_SDMPtrVect[i]->idx[0])) {
d_SDMPtrVect[i]->o3aConstraint = (*d_o3aConstraintVect)[j];
break;
}
}
}
}
// initialize to 0 the i-th row of the diff matrix
for (j = 0; j < n_equiv; ++j) {
diff[i][j] = 0.0;
}
for (j = 0; j < n_equiv; ++j) {
// if i == j then the distance will be 0,
// no need to spend time computing it
if (i == j) {
continue;
}
// - the euclidean distance between atoms i,j is computed for ref_conf
// - the euclidean distance between the corresponding atoms is computed
// for moved_conf
// - the absolute value of the difference between the two distances is
// computed
// and placed in diff[i][j]
diff[i][j] = fabs(
(refPos[d_SDMPtrVect[i]->idx[0]] - refPos[d_SDMPtrVect[j]->idx[0]])
.length() -
(prbPos[d_SDMPtrVect[i]->idx[1]] - prbPos[d_SDMPtrVect[j]->idx[1]])
.length());
}
}
for (i = 0; i < n_equiv; ++i) {
for (j = 0, d_SDMPtrVect[i]->score = 0; j < n_equiv; ++j) {
if ((diff[i][j] > O3_THRESHOLD_DIFF_DISTANCE) &&
(d_SDMPtrVect[i]->cost >= 0)) {
++(d_SDMPtrVect[i]->score);
}
}
}
// sort the SDM matrix by increasing scores
std::sort(d_SDMPtrVect.begin(), d_SDMPtrVect.end(), this->compareSDMScore);
}
void SDM::fillFromDist(double threshold,
const boost::dynamic_bitset<> &refHvyAtoms,
const boost::dynamic_bitset<> &prbHvyAtoms) {
unsigned int n = 0;
unsigned int pairs = 0;
const RDGeom::POINT3D_VECT &refPos = d_refConf->getPositions();
const RDGeom::POINT3D_VECT &prbPos = d_prbConf->getPositions();
unsigned int mapIdx = 0;
d_SDMPtrVect.clear();
double sqThreshold = square(threshold);
unsigned int refNAtoms = d_refConf->getNumAtoms();
unsigned int prbNAtoms = d_prbConf->getNumAtoms();
unsigned int largestNAtoms = std::max(prbNAtoms, refNAtoms);
boost::dynamic_bitset<> refUsed(largestNAtoms);
boost::dynamic_bitset<> prbUsed(largestNAtoms);
// loop over ref atoms
for (unsigned int i = 0; i < refNAtoms; ++i) {
if (!refHvyAtoms[i]) {
continue;
}
// loop over prb atoms
for (unsigned int j = 0; j < prbNAtoms; ++j) {
if (!prbHvyAtoms[j]) {
continue;
}
double sqDist = (refPos[i] - prbPos[j]).lengthSq();
// if the distance between these two atoms is lower
// than threshold, then include this pair in the SDM matrix
bool isConstraint =
(d_o3aConstraintVect && (mapIdx < (*d_o3aConstraintVect).size()) &&
(j == (*d_o3aConstraintVect)[mapIdx]->getPrbIdx()) &&
(i == (*d_o3aConstraintVect)[mapIdx]->getRefIdx()));
if ((sqDist < sqThreshold) || isConstraint) {
auto *sdmElement = new SDMElement();
sdmElement->idx[0] = i;
sdmElement->idx[1] = j;
sdmElement->sqDist = sqDist;
sdmElement->o3aConstraint = nullptr;
if (isConstraint) {
sdmElement->o3aConstraint = (*d_o3aConstraintVect)[mapIdx];
// in case there are duplicate constraints referring to the same
// atom pair, apply only the one with the highest weight
while ((mapIdx < (*d_o3aConstraintVect).size()) &&
(j == (*d_o3aConstraintVect)[mapIdx]->getPrbIdx()) &&
(i == (*d_o3aConstraintVect)[mapIdx]->getRefIdx())) {
++mapIdx;
}
}
d_SDMPtrVect.push_back(boost::shared_ptr<SDMElement>(sdmElement));
++n;
}
}
}
// sort the SDM matrix by decreasing constraint weights and,
// as a second criterion, increasing distances
std::sort(d_SDMPtrVect.begin(), d_SDMPtrVect.end(), this->compareSDMDist);
// increase the number of pairs which will be used
// by rmsAlgorithm until an atom which has been
// included in a previous pair is found
for (unsigned int i = 0; (i < n) && (!(refUsed[d_SDMPtrVect[i]->idx[0]])) &&
(!(prbUsed[d_SDMPtrVect[i]->idx[1]]));
++i) {
++pairs;
refUsed[d_SDMPtrVect[i]->idx[0]] = 1;
prbUsed[d_SDMPtrVect[i]->idx[1]] = 1;
}
d_SDMPtrVect.resize(pairs);
}
double o3aMMFFWeightFunc(const unsigned int prbIdx, const unsigned int refIdx,
void *data) {
std::uint8_t mmffSim =
mmffSimMatrix[(static_cast<MMFF::MMFFMolProperties *>(
(static_cast<O3AFuncData *>(data))->refProp))
->getMMFFAtomType(refIdx) -
1][(static_cast<MMFF::MMFFMolProperties *>(
(static_cast<O3AFuncData *>(data))->prbProp))
->getMMFFAtomType(prbIdx) -
1];
return static_cast<double>(O3_MAX_WEIGHT_COEFF -
(static_cast<O3AFuncData *>(data))->weight) *
((1.0 + O3_CHARGE_COEFF *
fabs((static_cast<MMFF::MMFFMolProperties *>(
(static_cast<O3AFuncData *>(data))->refProp))
->getMMFFPartialCharge(refIdx) +
(static_cast<MMFF::MMFFMolProperties *>(
(static_cast<O3AFuncData *>(data))->prbProp))
->getMMFFPartialCharge(prbIdx))) /
(1.0 + fabs((static_cast<MMFF::MMFFMolProperties *>(
(static_cast<O3AFuncData *>(data))->refProp))
->getMMFFPartialCharge(refIdx) -
(static_cast<MMFF::MMFFMolProperties *>(
(static_cast<O3AFuncData *>(data))->prbProp))
->getMMFFPartialCharge(prbIdx)))) +
(double)((static_cast<O3AFuncData *>(data))->weight) /
static_cast<double>(mmffSim);
}
double o3aCrippenWeightFunc(const unsigned int prbIdx,
const unsigned int refIdx, void *data) {
return (
0.01 +
fabs((*(static_cast<std::vector<double> *>(
(static_cast<O3AFuncData *>(data))->refProp)))[refIdx] +
(*(static_cast<std::vector<double> *>(
(static_cast<O3AFuncData *>(data))->prbProp)))[prbIdx]) /
(1.0 +
fabs((*(static_cast<std::vector<double> *>(
(static_cast<O3AFuncData *>(data))->refProp)))[refIdx] -
(*(static_cast<std::vector<double> *>(
(static_cast<O3AFuncData *>(data))->prbProp)))[prbIdx])));
}
void SDM::prepareMatchWeightsVect(
RDKit::MatchVectType &matchVect, RDNumeric::DoubleVector &weights,
double (*weightFunc)(const unsigned int, const unsigned int, void *),
void *data) {
PRECONDITION(matchVect.size() == weights.size(),
"matchVect/weights size mismatch");
double min = MAX_DOUBLE;
for (unsigned int i = 0; i < matchVect.size(); ++i) {
// first pair element is prb, second is ref
matchVect[i].first = d_SDMPtrVect[i]->idx[1];
matchVect[i].second = d_SDMPtrVect[i]->idx[0];
weights[i] = (weightFunc ? weightFunc(d_SDMPtrVect[i]->idx[1],
d_SDMPtrVect[i]->idx[0], data)
: 1.0);
if (d_SDMPtrVect[i]->o3aConstraint) {
weights[i] += d_SDMPtrVect[i]->o3aConstraint->getWeight();
}
if ((!i) || (weights[i] < min)) {
min = weights[i];
}
}
for (unsigned int i = 0; i < weights.size(); ++i) {
weights[i] /= min;
}
}
double o3aMMFFScoringFunc(const unsigned int prbIdx, const unsigned int refIdx,
void *data) {
const RDGeom::POINT3D_VECT &refPos =
(static_cast<O3AFuncData *>(data))->refConf->getPositions();
const RDGeom::POINT3D_VECT &prbPos =
(static_cast<O3AFuncData *>(data))->prbConf->getPositions();
double sqDist = (refPos[refIdx] - prbPos[prbIdx]).lengthSq();
double chargeDiff = fabs((static_cast<MMFF::MMFFMolProperties *>(
(static_cast<O3AFuncData *>(data))->refProp))
->getMMFFPartialCharge(refIdx) -
(static_cast<MMFF::MMFFMolProperties *>(
(static_cast<O3AFuncData *>(data))->prbProp))
->getMMFFPartialCharge(prbIdx));
double chargeSum = fabs((static_cast<MMFF::MMFFMolProperties *>(
(static_cast<O3AFuncData *>(data))->refProp))
->getMMFFPartialCharge(refIdx) +
(static_cast<MMFF::MMFFMolProperties *>(
(static_cast<O3AFuncData *>(data))->prbProp))
->getMMFFPartialCharge(prbIdx));
return ((O3_SCORING_FUNCTION_ALPHA +
(1.0 + O3_CHARGE_COEFF * chargeSum) / (1.0 + chargeDiff)) *
exp(-O3_SCORING_FUNCTION_BETA * sqDist));
}
double o3aCrippenScoringFunc(const unsigned int prbIdx,
const unsigned int refIdx, void *data) {
const RDGeom::POINT3D_VECT &refPos =
(static_cast<O3AFuncData *>(data))->refConf->getPositions();
const RDGeom::POINT3D_VECT &prbPos =
(static_cast<O3AFuncData *>(data))->prbConf->getPositions();
double sqDist = (refPos[refIdx] - prbPos[prbIdx]).lengthSq();
double logpDiff =
fabs((*(static_cast<std::vector<double> *>(
(static_cast<O3AFuncData *>(data))->refProp)))[refIdx] -
(*(static_cast<std::vector<double> *>(
(static_cast<O3AFuncData *>(data))->prbProp)))[prbIdx]);
double logpSum =
fabs((*(static_cast<std::vector<double> *>(
(static_cast<O3AFuncData *>(data))->refProp)))[refIdx] +
(*(static_cast<std::vector<double> *>(
(static_cast<O3AFuncData *>(data))->prbProp)))[prbIdx]);
return ((O3_SCORING_FUNCTION_ALPHA +
(1.0 + O3_CRIPPEN_COEFF * logpSum) / (1.0 + logpDiff)) *
exp(-O3_SCORING_FUNCTION_BETA * sqDist));
}
double SDM::scoreAlignment(double (*scoringFunc)(const unsigned int,
const unsigned int, void *),
void *data) {
double score = 0.0;
for (auto &ptr : d_SDMPtrVect) {
score += scoringFunc(ptr->idx[1], ptr->idx[0], data);
}
return score;
}
O3A::O3A(int (*costFunc)(const unsigned int, const unsigned int, double,
void *),
double (*weightFunc)(const unsigned int, const unsigned int, void *),
double (*scoringFunc)(const unsigned int, const unsigned int, void *),
void *data, ROMol &prbMol, const ROMol &refMol, const int prbCid,
const int refCid, const boost::dynamic_bitset<> &prbHvyAtoms,
const boost::dynamic_bitset<> &refHvyAtoms, const bool reflect,
const unsigned int maxIters, unsigned int options,
O3AConstraintVect *o3aConstraintVect, ROMol *extWorkPrbMol,
LAP *extLAP, MolHistogram *extPrbHist, MolHistogram *extRefHist)
: d_prbMol(&prbMol),
d_refMol(&refMol),
d_prbCid(prbCid),
d_refCid(refCid),
d_reflect(reflect),
d_maxIters(maxIters) {
ROMol *workPrbMol = (extWorkPrbMol ? extWorkPrbMol : new ROMol(prbMol));
Conformer &prbConf = workPrbMol->getConformer(prbCid);
const Conformer &refConf = refMol.getConformer(refCid);
unsigned int accuracy = options & O3_ACCURACY_MASK;
bool local = options & O3_LOCAL_ONLY;
unsigned int refNHeavyAtoms = refMol.getNumHeavyAtoms();
unsigned int prbNHeavyAtoms = prbMol.getNumHeavyAtoms();
unsigned int largestNHeavyAtoms = std::max(refNHeavyAtoms, prbNHeavyAtoms);
unsigned int i;
std::vector<unsigned int> pairs(4, 0);
std::vector<double> score(3, 0.0);
std::vector<double> pairsRMSD(2, 0.0);
std::vector<SDM *> bestSDM((unsigned int)3, nullptr);
SDM startSDM(&prbConf, &refConf, o3aConstraintVect);
MolHistogram *refHist = nullptr;
MolHistogram *prbHist = nullptr;
LAP *lap = nullptr;
if (local) {
startSDM.fillFromDist(O3_SDM_THRESHOLD_START, refHvyAtoms, prbHvyAtoms);
} else {
refHist =
(extRefHist ? extRefHist
: new MolHistogram(refMol,
MolOps::get3DDistanceMat(
refMol, refCid, false, false, ""),
true));
prbHist =
(extPrbHist ? extPrbHist
: new MolHistogram(prbMol,
MolOps::get3DDistanceMat(
prbMol, prbCid, false, false, ""),
true));
lap = (extLAP ? extLAP : new LAP(largestNHeavyAtoms));
lap->computeCostMatrix(prbMol, *prbHist, refMol, *refHist,
o3aConstraintVect, costFunc, data);
lap->computeMinCostPath(largestNHeavyAtoms);
startSDM.fillFromLAP(*lap);
}
for (pairs[0] = 0, score[0] = 0.0, i = 3; i < startSDM.size(); ++i) {
RDKit::MatchVectType startMatchVect(i);
RDNumeric::DoubleVector startWeights(i);
startSDM.prepareMatchWeightsVect(startMatchVect, startWeights, weightFunc,
data);
alignMol(*workPrbMol, refMol, prbCid, refCid, &startMatchVect,
&startWeights, reflect, maxIters);
unsigned int sdmThresholdIt;
for (sdmThresholdIt = ((accuracy < 1) ? 0 : O3_MAX_SDM_THRESHOLD_ITER - 1),
pairs[1] = 0, score[1] = 0.0;
sdmThresholdIt < O3_MAX_SDM_THRESHOLD_ITER; ++sdmThresholdIt) {
pairs[2] = 0;
bool flag = true;
unsigned int iter = 0;
double sdmThresholdDist = O3_SDM_THRESHOLD_START +
(double)sdmThresholdIt * O3_SDM_THRESHOLD_STEP;
while (flag && (iter < O3_MAX_SDM_ITERATIONS)) {
SDM progressSDM(&prbConf, &refConf, o3aConstraintVect);
progressSDM.fillFromDist(sdmThresholdDist, refHvyAtoms, prbHvyAtoms);
pairs[3] = progressSDM.size();
if (pairs[3] < 3) {
break;
}
RDKit::MatchVectType progressMatchVect(pairs[3]);
RDNumeric::DoubleVector progressWeights(pairs[3]);
progressSDM.prepareMatchWeightsVect(progressMatchVect, progressWeights,
weightFunc, data);
RDGeom::Transform3D trans;
pairsRMSD[1] = getAlignmentTransform(
*workPrbMol, refMol, trans, prbCid, refCid, &progressMatchVect,
&progressWeights, reflect, maxIters);
flag = ((pairs[3] > pairs[2]) ||
((pairs[3] == pairs[2]) &&
((!iter) ||
((pairsRMSD[0] - pairsRMSD[1]) > O3_RMSD_THRESHOLD))));
if (flag) {
pairs[2] = pairs[3];
pairsRMSD[0] = pairsRMSD[1];
if (bestSDM[2]) {
delete bestSDM[2];
}
bestSDM[2] = new SDM();
*(bestSDM[2]) = progressSDM;
MolTransforms::transformConformer(prbConf, trans);
}
++iter;
}
score[2] =
((pairs[2] >= 3) ? bestSDM[2]->scoreAlignment(scoringFunc, data)
: 0.0);
if ((score[2] - score[1]) > O3_SCORE_THRESHOLD) {
pairs[1] = pairs[2];
score[1] = score[2];
if (bestSDM[1]) {
delete bestSDM[1];
}
bestSDM[1] = new SDM();
*(bestSDM[1]) = *(bestSDM[2]);
}
}
if ((score[1] - score[0]) > O3_SCORE_THRESHOLD) {
pairs[0] = pairs[1];
score[0] = score[1];
if (bestSDM[0]) {
delete bestSDM[0];
}
bestSDM[0] = new SDM();
*(bestSDM[0]) = *(bestSDM[1]);
}
}
if ((pairs[0] < 3) && (startSDM.size() >= 3)) {
pairs[0] = startSDM.size();
if (bestSDM[0]) {
delete bestSDM[0];
}
bestSDM[0] = new SDM();
*(bestSDM[0]) = startSDM;
score[0] = bestSDM[0]->scoreAlignment(scoringFunc, data);
}
RDKit::MatchVectType *o3aMatchVect = new RDKit::MatchVectType(pairs[0]);
auto *o3aWeights = new RDNumeric::DoubleVector(pairs[0]);
d_o3aMatchVect = o3aMatchVect;
d_o3aWeights = o3aWeights;
if (pairs[0] >= 3) {
bestSDM[0]->prepareMatchWeightsVect(*o3aMatchVect, *o3aWeights, weightFunc,
data);
d_o3aScore = score[0];
} else {
d_o3aScore = 0.0;
}
for (i = 0; i < 3; ++i) {
if (bestSDM[i]) {
delete bestSDM[i];
}
}
if (!extLAP) {
delete lap;
}
if (!extRefHist) {
delete refHist;
}
if (!extPrbHist) {
delete prbHist;
}
if (!extWorkPrbMol) {
delete workPrbMol;
}
}
O3A::O3A(ROMol &prbMol, const ROMol &refMol, void *prbProp, void *refProp,
AtomTypeScheme atomTypes, const int prbCid, const int refCid,
const bool reflect, const unsigned int maxIters, unsigned int options,
const MatchVectType *constraintMap,
const RDNumeric::DoubleVector *constraintWeights, LAP *extLAP,
MolHistogram *extPrbHist, MolHistogram *extRefHist)
: d_prbMol(&prbMol),
d_refMol(&refMol),
d_prbCid(prbCid),
d_refCid(refCid),
d_reflect(reflect),
d_maxIters(maxIters) {
int (*costFunc)(const unsigned int, const unsigned int, double, void *) =
o3aMMFFCostFunc;
double (*weightFunc)(const unsigned int, const unsigned int, void *) =
o3aMMFFWeightFunc;
double (*scoringFunc)(const unsigned int, const unsigned int, void *) =
o3aMMFFScoringFunc;
if (atomTypes == CRIPPEN) {
costFunc = o3aCrippenCostFunc;
weightFunc = o3aCrippenWeightFunc;
scoringFunc = o3aCrippenScoringFunc;
}
O3AFuncData data;
ROMol extWorkPrbMol(prbMol);
data.prbConf = &(extWorkPrbMol.getConformer(prbCid));
data.refConf = &(refMol.getConformer(refCid));
data.prbProp = prbProp;
data.refProp = refProp;
int refNAtoms = rdcast<int>(refMol.getNumAtoms());
int prbNAtoms = rdcast<int>(prbMol.getNumAtoms());
boost::dynamic_bitset<> refHvyAtoms(refNAtoms);
boost::dynamic_bitset<> prbHvyAtoms(prbNAtoms);
for (int i = 0; i < refNAtoms; ++i) {
if (refMol[i]->getAtomicNum() != 1) {
refHvyAtoms.set(i);
}
}
for (int i = 0; i < prbNAtoms; ++i) {
if (prbMol[i]->getAtomicNum() != 1) {
prbHvyAtoms.set(i);
}
}
unsigned int refNHeavyAtoms = refMol.getNumHeavyAtoms();
unsigned int prbNHeavyAtoms = prbMol.getNumHeavyAtoms();
unsigned int largestNHeavyAtoms = std::max(refNHeavyAtoms, prbNHeavyAtoms);
unsigned int accuracy = options & O3_ACCURACY_MASK;
bool local = options & O3_LOCAL_ONLY;
O3AConstraintVect o3aConstraintVect;
if (constraintMap) {
if (constraintWeights) {
if ((*constraintMap).size() != (*constraintWeights).size()) {
throw MolAlignException(
"The number of weights should match the number of constraints");
}
}
for (unsigned int i = 0; i < (*constraintMap).size(); ++i) {
if (((*constraintMap)[i].first < 0) ||
((*constraintMap)[i].first >= prbNAtoms) ||
((*constraintMap)[i].second < 0) ||
((*constraintMap)[i].second >= refNAtoms)) {
throw MolAlignException("Constrained atom idx out of range");
}
if ((!prbHvyAtoms[(*constraintMap)[i].first]) ||
(!refHvyAtoms[(*constraintMap)[i].second])) {
throw MolAlignException("Constrained atoms must be heavy atoms");
}
o3aConstraintVect.append(
(*constraintMap)[i].first, (*constraintMap)[i].second,
constraintWeights ? (*constraintWeights)[i]
: O3_DEFAULT_CONSTRAINT_WEIGHT);
}
}
int c;
int l;
std::vector<double> score(2, 0.0);
O3A *bestO3A = nullptr;
MolHistogram *refHist = nullptr;
MolHistogram *prbHist = nullptr;
LAP *lap = nullptr;
if (!local) {
refHist =
(extRefHist ? extRefHist
: new MolHistogram(refMol,
MolOps::get3DDistanceMat(
refMol, refCid, false, false, ""),
true));
prbHist =
(extPrbHist ? extPrbHist
: new MolHistogram(prbMol,
MolOps::get3DDistanceMat(
prbMol, prbCid, false, false, ""),
true));
lap = (extLAP ? extLAP : new LAP(largestNHeavyAtoms));
}
for (l = 0, score[0] = 0.0;
l <= (((accuracy < 2) && (atomTypes == MMFF94)) ? 1 : 0); ++l) {
data.useMMFFSim = (bool)l;
for (c = 0; c <= O3_MAX_WEIGHT_COEFF;
c += ((accuracy < 3) ? 1 : (O3_MAX_WEIGHT_COEFF + 1))) {
data.weight = (l ? c : 0);
data.coeff = c;
auto *o3a = new O3A(costFunc, weightFunc, scoringFunc, &data, prbMol,
refMol, prbCid, refCid, prbHvyAtoms, refHvyAtoms,
reflect, maxIters, options, &o3aConstraintVect,
&extWorkPrbMol, lap, prbHist, refHist);
score[1] = o3a->score();
if (((score[1] - score[0]) > O3_SCORE_THRESHOLD) ||
isDoubleZero(score[0])) {
if (bestO3A) {
delete bestO3A;
}
bestO3A = o3a;
score[0] = score[1];
} else {
delete o3a;
}
}
}
auto pairs = rdcast<unsigned int>(bestO3A->matches()->size());
RDKit::MatchVectType *bestO3AMatchVect = new RDKit::MatchVectType(pairs);
auto *bestO3AWeights = new RDNumeric::DoubleVector(pairs);
d_o3aMatchVect = bestO3AMatchVect;
d_o3aWeights = bestO3AWeights;
if (pairs >= 3) {
for (unsigned int i = 0; i < pairs; ++i) {
(*bestO3AMatchVect)[i].first = (*(bestO3A->matches()))[i].first;
(*bestO3AMatchVect)[i].second = (*(bestO3A->matches()))[i].second;
(*bestO3AWeights)[i] = (*(bestO3A->weights()))[i];
}
d_o3aScore = bestO3A->score();
} else {
d_o3aScore = 0.0;
}
delete bestO3A;
if (!extLAP) {
delete lap;
}
if (!extRefHist) {
delete refHist;
}
if (!extPrbHist) {
delete prbHist;
}
}
double _rmsdMatchVect(ROMol *d_prbMol, const ROMol *d_refMol,
const RDGeom::POINT3D_VECT &prbPos,
const RDGeom::POINT3D_VECT &refPos,
const RDKit::MatchVectType *matchVect) {
double rmsd = 0.0;
if (matchVect) {
for (const auto &i : (*matchVect)) {
// first pair element is prb, second is ref
rmsd += (prbPos[i.first] - refPos[i.second]).lengthSq();
}
rmsd = sqrt(rmsd / (*matchVect).size());
} else {
RDGeom::Point3D refCtd;
RDGeom::Point3D prbCtd;
unsigned int i;
unsigned int nHeavy;
for (i = 0, nHeavy = 0; i < refPos.size(); ++i) {
if (d_refMol->getAtomWithIdx(i)->getAtomicNum() == 1) {
continue;
}
refCtd[0] += refPos[i][0];
refCtd[1] += refPos[i][1];
refCtd[2] += refPos[i][2];
++nHeavy;
}
refCtd[0] /= (double)nHeavy;
refCtd[1] /= (double)nHeavy;
refCtd[2] /= (double)nHeavy;
for (i = 0, nHeavy = 0; i < prbPos.size(); ++i) {
if (d_prbMol->getAtomWithIdx(i)->getAtomicNum() == 1) {
continue;
}
prbCtd[0] += prbPos[i][0];
prbCtd[1] += prbPos[i][1];
prbCtd[2] += prbPos[i][2];
++nHeavy;
}
prbCtd[0] /= (double)nHeavy;
prbCtd[1] /= (double)nHeavy;
prbCtd[2] /= (double)nHeavy;
rmsd = (refCtd - prbCtd).length();
}
return rmsd;
};
double O3A::align() {
double rmsd = 0.0;
const RDKit::MatchVectType *matchVectPtr = nullptr;
if (d_o3aMatchVect && (d_o3aMatchVect->size() >= 3)) {
alignMol(*d_prbMol, *d_refMol, d_prbCid, d_refCid, d_o3aMatchVect,
d_o3aWeights, d_reflect, d_maxIters);
matchVectPtr = d_o3aMatchVect;
}
rmsd = _rmsdMatchVect(
d_prbMol, d_refMol, d_prbMol->getConformer(d_prbCid).getPositions(),
d_refMol->getConformer(d_refCid).getPositions(), matchVectPtr);
return rmsd;
}
double O3A::trans(RDGeom::Transform3D &trans) {
double rmsd = 0.0;
Conformer transConf(d_prbMol->getConformer(d_prbCid));
if (d_o3aMatchVect && (d_o3aMatchVect->size() >= 3)) {
getAlignmentTransform(*d_prbMol, *d_refMol, trans, d_prbCid, d_refCid,
d_o3aMatchVect, d_o3aWeights, d_reflect, d_maxIters);
MolTransforms::transformConformer(transConf, trans);
}
rmsd = _rmsdMatchVect(d_prbMol, d_refMol, transConf.getPositions(),
d_refMol->getConformer(d_refCid).getPositions(),
d_o3aMatchVect);
return rmsd;
};
void randomTransform(ROMol &mol, const int cid, const int seed) {
if (seed > 0) {
rng_type &generator = getRandomGenerator();
generator.seed(seed);
}
Conformer &conf = mol.getConformer(cid);
RDGeom::POINT3D_VECT &pos = conf.getPositions();
double maxCoord[3];
double minCoord[3];
RDGeom::Point3D rndTrans;
double rndRot[3];
for (unsigned int j = 0; j < 3; ++j) {
for (unsigned int i = 0; i < pos.size(); ++i) {
if ((!i) || (pos[i][j] > maxCoord[j])) {
maxCoord[j] = pos[i][j];
}
if ((!i) || (pos[i][j] < minCoord[j])) {
minCoord[j] = pos[i][j];
}
}
rndTrans[j] = (maxCoord[j] - minCoord[j]) * O3_RANDOM_TRANS_COEFF *
(getRandomVal() - 0.5);
rndRot[j] = getRandomVal() * 2.0 * M_PI;
}
RDGeom::Point3D ctd = MolTransforms::computeCentroid(conf, false);
RDGeom::Transform3D transToOrigin;
RDGeom::Transform3D xRot;
RDGeom::Transform3D yRot;
RDGeom::Transform3D zRotAndTrans;
transToOrigin.SetTranslation(-ctd);
xRot.SetRotation(rndRot[0], RDGeom::X_Axis);
yRot.SetRotation(rndRot[1], RDGeom::Y_Axis);
zRotAndTrans.SetRotation(rndRot[2], RDGeom::Z_Axis);
zRotAndTrans.SetTranslation(ctd + rndTrans);
MolTransforms::transformConformer(conf,
zRotAndTrans * yRot * xRot * transToOrigin);
}
#ifdef RDK_BUILD_THREADSAFE_SSS
namespace detail {
typedef struct {
O3A::AtomTypeScheme atomTypes;
int refCid;
bool reflect;
unsigned int maxIters;
unsigned int options;
MatchVectType const *constraintMap;
RDNumeric::DoubleVector const *constraintWeights;
} O3AHelperArgs_;
void O3AHelper_(ROMol *prbMol, const ROMol *refMol, void *prbProp,
void *refProp, std::vector<boost::shared_ptr<O3A>> *res,
unsigned int threadIdx, int numThreads,
const O3AHelperArgs_ *args) {
unsigned int i = 0;
for (ROMol::ConstConformerIterator cit = prbMol->beginConformers();
cit != prbMol->endConformers(); ++cit, ++i) {
if (i % numThreads != threadIdx) {
continue;
}
auto *lres =
new O3A(*prbMol, *refMol, prbProp, refProp, args->atomTypes,
(*cit)->getId(), args->refCid, args->reflect, args->maxIters,
args->options, args->constraintMap, args->constraintWeights);
(*res)[i].reset(lres);
}
}
} // namespace detail
#endif
void getO3AForProbeConfs(ROMol &prbMol, const ROMol &refMol, void *prbProp,
void *refProp,
std::vector<boost::shared_ptr<O3A>> &res,
int numThreads, O3A::AtomTypeScheme atomTypes,
const int refCid, const bool reflect,
const unsigned int maxIters, unsigned int options,
const MatchVectType *constraintMap,
const RDNumeric::DoubleVector *constraintWeights) {
numThreads = getNumThreadsToUse(numThreads);
res.resize(prbMol.getNumConformers());
if (numThreads == 1) {
unsigned int i = 0;
for (ROMol::ConstConformerIterator cit = prbMol.beginConformers();
cit != prbMol.endConformers(); ++cit, ++i) {
auto *lres = new O3A(prbMol, refMol, prbProp, refProp, atomTypes,
(*cit)->getId(), refCid, reflect, maxIters, options,
constraintMap, constraintWeights);
res[i].reset(lres);
}
}
#ifdef RDK_BUILD_THREADSAFE_SSS
else {
std::vector<std::future<void>> tg;
detail::O3AHelperArgs_ args = {atomTypes, refCid, reflect,
maxIters, options, constraintMap,
constraintWeights};
for (int ti = 0; ti < numThreads; ++ti) {
tg.emplace_back(std::async(std::launch::async, detail::O3AHelper_,
&prbMol, &refMol, prbProp, refProp, &res, ti,
numThreads, &args));
}
for (auto &fut : tg) {
fut.get();
}
}
#endif
}
} // end of namespace MolAlign
} // end of namespace RDKit