IMP logo
IMP Reference Guide  develop.63b38c487d,2024/12/21
The Integrative Modeling Platform
MetricClosePairsFinder.h
Go to the documentation of this file.
1 /**
2  * \file IMP/misc/MetricClosePairsFinder.h
3  * \brief Decorator for a sphere-like particle.
4  *
5  * Copyright 2007-2022 IMP Inventors. All rights reserved.
6  *
7  */
8 
9 #ifndef IMPMISC_METRIC_CLOSE_PAIRS_FINDER_H
10 #define IMPMISC_METRIC_CLOSE_PAIRS_FINDER_H
11 
12 #include <IMP/misc/misc_config.h>
14 #include <IMP/Pointer.h>
15 #include <IMP/random.h>
16 #include <IMP/particle_index.h>
17 #include <boost/version.hpp>
18 #include <boost/unordered_map.hpp>
19 #include <algorithm>
20 #include <limits>
21 
22 IMPMISC_BEGIN_NAMESPACE
23 
24 /** A close pairs finder that only depends on metrics on the underlying
25  particles. As a result, it should be usable with weird metrics (eg ones
26  that include symmetry).
27 
28  The LowerBound and UpperBound templates should be functors that bound the
29  distance between two particles. For example the LowerBound
30  distance between two balls is the center distance minus the radii,
31  and the UpperBound distance is the center distance plus the
32  radii. The signature should be `double operator()(Model *m, const
33  ParticleIndexPair &pip) const`
34 
35  The algorithm works by building an index using `sqrt(n)` of the `n` input
36  particles, assigning each particle to a bucket based on the closest index
37  particle, and then checking for close pairs in buckets such that they can
38  be close enough.
39 
40  If we need something more involved, we can try
41  [this paper](https://pubs.acs.org/doi/abs/10.1021/ci034150f)
42  */
43 template <class LowerBound, class UpperBound>
45  LowerBound lb_;
46  UpperBound ub_;
47 
48  IMP_NAMED_TUPLE_2(Data, Datas, ParticleIndexes, indexes, double,
49  width, );
50 
51  typedef boost::unordered_map<ParticleIndex, Data> Index;
52  Index get_index(Model *m, ParticleIndexes inputs) const {
53  unsigned int index_size = std::min<unsigned int>(
54  1U, std::sqrt(static_cast<double>(inputs.size())));
55 #if IMP_COMPILER_HAS_RANDOM_SHUFFLE && BOOST_VERSION < 107500
56  // Older Boost RNG has non-constexpr min(), max() which won't compile
57  // with std::shuffle, so use the older random_shuffle instead
58  std::random_shuffle(inputs.begin(), inputs.end());
59 #else
60  std::shuffle(inputs.begin(), inputs.end(), random_number_generator);
61 #endif
62  Index ret;
63  ParticleIndexes indexes(inputs.begin(),
64  inputs.begin() + index_size);
65  for (unsigned int i = 0; i < index_size; ++i) {
66  ret[inputs[i]] = Data(ParticleIndexes(), 0.0);
67  }
68  IMP_LOG_VERBOSE("Index points are " << indexes << std::endl);
69  for (unsigned int i = 0; i < inputs.size(); ++i) {
70  double min_dist = std::numeric_limits<double>::max();
71  ParticleIndex min_index;
72  for (unsigned int j = 0; j < indexes.size(); ++j) {
74  lb_(m, ParticleIndexPair(inputs[i], indexes[j])) <=
75  ub_(m, ParticleIndexPair(inputs[i], indexes[j])),
76  "The bounds are not ordered.");
77  double cur_dist =
78  ub_(m, ParticleIndexPair(inputs[i], indexes[j]));
79  if (cur_dist < min_dist) {
80  min_index = indexes[j];
81  min_dist = cur_dist;
82  }
83  }
84  ret[min_index].access_indexes().push_back(inputs[i]);
85  ret[min_index].set_width(std::min(min_dist, ret[min_index].get_width()));
86  }
87  for (typename Index::const_iterator it = ret.begin(); it != ret.end();
88  ++it) {
89  IMP_LOG_VERBOSE(it->first << ": " << it->second << std::endl);
90  }
91  return ret;
92  }
93  ParticleIndexPairs get_close_pairs_internal(
94  Model *m, const ParticleIndexes &p) const {
96  for (unsigned int i = 0; i < p.size(); ++i) {
97  for (unsigned int j = 0; j < i; ++j) {
98  IMP_USAGE_CHECK(lb_(m, ParticleIndexPair(p[i], p[j])) <=
99  ub_(m, ParticleIndexPair(p[i], p[j])),
100  "The bounds are not ordered.");
101  if (lb_(m, ParticleIndexPair(p[i], p[j])) < get_distance()) {
102  ret.push_back(ParticleIndexPair(p[i], p[j]));
103  }
104  }
105  }
106  return ret;
107  }
108  ParticleIndexPairs get_close_pairs_internal(
109  Model *m, const ParticleIndexes &pa,
110  const ParticleIndexes &pb) const {
111  ParticleIndexPairs ret;
112  for (unsigned int i = 0; i < pa.size(); ++i) {
113  for (unsigned int j = 0; j < pb.size(); ++j) {
114  IMP_USAGE_CHECK(lb_(m, ParticleIndexPair(pa[i], pb[j])) <=
115  ub_(m, ParticleIndexPair(pa[i], pb[j])),
116  "The bounds are not ordered.");
117  if (lb_(m, ParticleIndexPair(pa[i], pb[j])) < get_distance()) {
118  ret.push_back(ParticleIndexPair(pa[i], pb[j]));
119  }
120  }
121  }
122  return ret;
123  }
124  ParticleIndexPairs get_close_pairs_internal(Model *m,
125  const Index &ia,
126  const Index &ib,
127  bool is_same) const {
128  ParticleIndexPairs ret;
129  for (typename Index::const_iterator ita = ia.begin(); ita != ia.end();
130  ++ita) {
131  for (typename Index::const_iterator itb = ib.begin(); itb != ib.end();
132  ++itb) {
133  if (is_same) {
134  if (ita->first < itb->first) continue;
135  if (ita->first == itb->first) {
136  ret += get_close_pairs_internal(m, ita->second.get_indexes());
137  continue;
138  }
139  }
141  lb_(m, ParticleIndexPair(ita->first, itb->first)) <=
142  ub_(m, ParticleIndexPair(ita->first, itb->first)),
143  "The bounds are not ordered.");
144  if (lb_(m, ParticleIndexPair(ita->first, itb->first)) -
145  ita->second.get_width() - itb->second.get_width()) {
146  ret += get_close_pairs_internal(m, ita->second.get_indexes(),
147  itb->second.get_indexes());
148  }
149  }
150  }
151  return ret;
152  }
153 
154  public:
155  MetricClosePairsFinder(LowerBound lb, UpperBound ub,
156  std::string name = "MetricClosePairsFinder%1%")
157  : core::ClosePairsFinder(name), lb_(lb), ub_(ub) {}
158  /** Not supported.*/
160  override {
161  IMP_FAILURE("Bounding boxes are not supported.");
162  }
163  /** Not supported.*/
165  const algebra::BoundingBox3Ds &) const
166  override {
167  IMP_FAILURE("Bounding boxes are not supported.");
168  }
169 
171  Model *m, const ParticleIndexes &pc) const override {
173  if (pc.empty()) return ParticleIndexPairs();
174  Index index = get_index(m, pc);
175  return get_close_pairs_internal(m, index, index, true);
176  }
178  Model *m, const ParticleIndexes &pca,
179  const ParticleIndexes &pcb) const override {
181  if (pca.empty() || pcb.empty()) return ParticleIndexPairs();
182  Index indexa = get_index(m, pca);
183  Index indexb = get_index(m, pcb);
184  return get_close_pairs_internal(m, indexa, indexb, false);
185  }
187  Model *m, const ParticleIndexes &pis) const override {
188  // for now assume we just read the particles
189  return IMP::get_particles(m, pis);
190  }
191 
193 };
194 
195 /** Create a new MetricClosePairsFinder, handling types. */
196 template <class LowerBound, class UpperBound>
198  UpperBound ub) {
200 }
201 
202 IMPMISC_END_NAMESPACE
203 
204 #endif /* IMPMISC_METRIC_CLOSE_PAIRS_FINDER_H */
IMP::Vector< ParticleIndexPair, std::allocator< ParticleIndexPair > > ParticleIndexPairs
Definition: base_types.h:186
virtual ModelObjectsTemp do_get_inputs(Model *m, const ParticleIndexes &pis) const override
Overload this method to specify the inputs.
A base class for algorithms to find spatial proximities.
Functions and adaptors for dealing with particle indexes.
virtual IntPairs get_close_pairs(const algebra::BoundingBox3Ds &) const override
#define IMP_FAILURE(message)
A runtime failure for IMP.
Definition: check_macros.h:72
#define IMP_OBJECT_METHODS(Name)
Define the basic things needed by any Object.
Definition: object_macros.h:25
#define IMP_OBJECT_LOG
Set the log level to the object's log level.
Definition: log_macros.h:284
ParticlesTemp get_particles(Model *m, const ParticleIndexes &ps)
Get the particles from a list of indexes.
#define IMP_LOG_VERBOSE(expr)
Definition: log_macros.h:83
Class for storing model, its restraints, constraints, and particles.
Definition: Model.h:86
Ints get_index(const ParticlesTemp &particles, const Subset &subset, const Subsets &excluded)
virtual IntPairs get_close_pairs(const algebra::BoundingBox3Ds &, const algebra::BoundingBox3Ds &) const override
virtual ParticleIndexPairs get_close_pairs(Model *m, const ParticleIndexes &pc) const override
return all close pairs among pc in model m
core::ClosePairsFinder * create_metric_close_pairs_finder(LowerBound lb, UpperBound ub)
virtual ParticleIndexPairs get_close_pairs(Model *m, const ParticleIndexes &pca, const ParticleIndexes &pcb) const override
return all close pairs among pc in model m
A nullptr-initialized pointer to an IMP Object.
A base class for algorithms to detect proximities.
#define IMP_USAGE_CHECK(expr, message)
A runtime test for incorrect usage of a class or method.
Definition: check_macros.h:168
Random number generators used by IMP.
double get_distance(const Line3D &s, const Vector3D &p)
Get closest distance between a line and a point.
RandomNumberGenerator random_number_generator
A shared non-GPU random number generator.