CMS 3D CMS Logo

binnor.h
Go to the documentation of this file.
1 #ifndef RecoTracker_MkFitCore_interface_binnor_h
2 #define RecoTracker_MkFitCore_interface_binnor_h
3 
4 #include "radix_sort.h"
5 
6 #include <algorithm>
7 #include <numeric>
8 #include <vector>
9 #include <cassert>
10 #include <cmath>
11 
12 namespace mkfit {
13 
14  // For all axis types:
15  //--------------------
16  // R - real type
17  // I - bin index type
18  // M and N - number of bits for fine and normal binning
19 
20  // axis_base
21  //----------
22  template <typename R, typename I, unsigned M, unsigned N>
23  struct axis_base {
24  static_assert(M >= N);
25 
26  typedef R real_t;
27  typedef I index_t;
28 
29  static constexpr unsigned int c_M = M;
30  static constexpr unsigned int c_N = N;
31  static constexpr unsigned int c_M2N_shift = M - N;
32 
33  const R m_R_min, m_R_max;
34  const R m_M_fac, m_N_fac;
35  const R m_M_lbhp, m_N_lbhp; // last bin half-posts
37 
38  struct I_pair {
40  I end;
41 
42  I_pair() : begin(0), end(0) {}
43  I_pair(I b, I e) : begin(b), end(e) {}
44  };
45 
46  axis_base(R min, R max, unsigned int M_size, unsigned int N_size)
47  : m_R_min(min),
48  m_R_max(max),
49  m_M_fac(M_size / (max - min)),
50  m_N_fac(N_size / (max - min)),
51  m_M_lbhp(max - 0.5 / m_M_fac),
52  m_N_lbhp(max - 0.5 / m_N_fac),
53  m_last_M_bin(M_size - 1),
54  m_last_N_bin(N_size - 1) {
55  // Requested number of bins must fit within the intended bit-field (declared by binnor, later).
56  assert(M_size <= (1 << M));
57  assert(N_size <= (1 << N));
58  assert(max > min);
59  }
60 
61  I from_R_to_M_bin(R r) const { return std::floor((r - m_R_min) * m_M_fac); }
62  I from_R_to_N_bin(R r) const { return std::floor((r - m_R_min) * m_N_fac); }
63 
64  I from_R_to_M_bin_safe(R r) const { return r <= m_R_min ? 0 : (r >= m_M_lbhp ? m_last_M_bin : from_R_to_M_bin(r)); }
65  I from_R_to_N_bin_safe(R r) const { return r <= m_R_min ? 0 : (r >= m_N_lbhp ? m_last_N_bin : from_R_to_N_bin(r)); }
66 
67  I from_M_bin_to_N_bin(I m) const { return m >> c_M2N_shift; }
68 
69  I_pair from_R_minmax_to_N_bins(R rmin, R rmax) const {
70  return I_pair(from_R_to_N_bin_safe(rmin), from_R_to_N_bin_safe(rmax) + I{1});
71  }
72 
73  I_pair from_R_rdr_to_N_bins(R r, R dr) const { return from_R_minmax_to_N_bins(r - dr, r + dr); }
74  I next_N_bin(I bin) const { return bin + 1; }
75  };
76 
77  // axis_pow2_base
78  //---------------
79  // Assumes the numbers of fine/normal bins are powers of 2 that are inferred directly from the number of bits.
80  template <typename R, typename I, unsigned M, unsigned N>
81  struct axis_pow2_base : public axis_base<R, I, M, N> {
82  static constexpr unsigned int c_M_end = 1 << M;
83  static constexpr unsigned int c_N_end = 1 << N;
84 
86 
87  unsigned int size_of_M() const { return c_M_end; }
88  unsigned int size_of_N() const { return c_N_end; }
89  };
90 
91  // axis_pow2_u1
92  //-------------
93  // Specialization of axis_pow2 for the "U(1)" case where the coordinate is periodic with period (Rmax - Rmin).
94  // In the "safe" methods below, bit masking serves as the modulo operator for out-of-range bin numbers.
95  template <typename R, typename I, unsigned int M, unsigned int N>
96  struct axis_pow2_u1 : public axis_pow2_base<R, I, M, N> {
97  static constexpr I c_M_mask = (1 << M) - 1;
98  static constexpr I c_N_mask = (1 << N) - 1;
99 
101 
102  I from_R_to_M_bin_safe(R r) const { return this->from_R_to_M_bin(r) & c_M_mask; }
103  I from_R_to_N_bin_safe(R r) const { return this->from_R_to_N_bin(r) & c_N_mask; }
104 
107  (this->from_R_to_N_bin(rmax) + I{1}) & c_N_mask);
108  }
109 
111  return from_R_minmax_to_N_bins(r - dr, r + dr);
112  }
113  I next_N_bin(I bin) const { return (bin + 1) & c_N_mask; }
114  };
115 
116  // axis_pow2
117  //----------
118  template <typename R, typename I, unsigned int M, unsigned int N>
119  struct axis_pow2 : public axis_pow2_base<R, I, M, N> {
121  };
122 
123  // axis
124  //-----
125  template <typename R, typename I, unsigned M = 8 * sizeof(I), unsigned N = 8 * sizeof(I)>
126  struct axis : public axis_base<R, I, M, N> {
127  const unsigned int m_num_M_bins, m_num_N_bins;
128 
129  axis(R min, R max, unsigned n_bins)
130  : axis_base<R, I, M, N>(min, max, n_bins << this->c_M2N_shift, n_bins),
131  m_num_M_bins(n_bins << this->c_M2N_shift),
132  m_num_N_bins(n_bins) {}
133 
134  axis(R min, R max, R bin_width) {
135  R extent = max - min;
136  unsigned int n_bins = std::ceil(extent / bin_width);
137  R extra = (n_bins * bin_width - extent) / 2;
138 
139  axis(min - extra, max + extra, n_bins);
140  }
141 
142  unsigned int size_of_M() const { return m_num_M_bins; }
143  unsigned int size_of_N() const { return m_num_N_bins; }
144  };
145 
146  // binnor
147  //---------------
148  // To build and populate bins, do the following:
149  // 1. Construct two axis objects, giving numbers of bits and bins, and extents.
150  // 2. Construct a binnor from the axis objects, and begin_registration on it.
151  // 3. Loop register_entry (optional: _safe) over pairs of coordinates, to fill
152  // m_cons with the corresponding pairs of bin indices (B_pairs).
153  // 4. Call finalize_registration, which sorts m_ranks based on m_cons, making
154  // m_ranks into an in-order map into m_cons (as well as the inputs that were
155  // used to fill it). Final counts for all the bins, as well as starting
156  // indices for the bins (within m_ranks), are computed and stored in packed
157  // form (i.e., bit-fields) in m_bins.
158  // 5. m_cons can be kept to do preselection when determining search ranges.
159  // Note that additional precision on Axis2 is screened out during sorting.
160 
161  //
162  // C - bin content type, to hold "bin population coordinates" in packed form (bit-fields)
163  // A1, A2 - axis types
164  // NB_first, NB_count - number of bits for storage of { first, count } pairs
165 
166  template <typename C,
167  typename A1,
168  typename A2,
169  unsigned int NB_first = 8 * sizeof(C),
170  unsigned int NB_count = 8 * sizeof(C)>
171  struct binnor {
172  static_assert(std::is_same<C, unsigned short>() || std::is_same<C, unsigned int>());
173  static_assert(std::is_same<typename A1::real_t, typename A2::real_t>());
174  static_assert(A1::c_M + A2::c_M <= 32);
175 
176  static constexpr unsigned int c_A1_mask = (1 << A1::c_M) - 1;
177  static constexpr unsigned int c_A2_Mout_mask = ~(((1 << A2::c_M2N_shift) - 1) << A1::c_M);
178 
179  // Pair of axis bin indices packed into unsigned.
180  struct B_pair {
181  unsigned int packed_value; // bin1 in A1::c_M lower bits, bin2 above
182 
184  B_pair(unsigned int pv) : packed_value(pv) {}
185  B_pair(typename A1::index_t i1, typename A2::index_t i2) : packed_value(i2 << A1::c_M | i1) {}
186 
187  typename A1::index_t bin1() const { return packed_value & c_A1_mask; }
188  typename A2::index_t bin2() const { return packed_value >> A1::c_M; }
189 
190  unsigned int mask_A2_M_bins() const { return packed_value & c_A2_Mout_mask; }
191  };
192 
193  // Bin content pair (bit-fields).
194  struct C_pair {
195  C first : NB_first;
196  C count : NB_count;
197 
198  C_pair() : first(0), count(0) {}
199  C_pair(C f, C c) : first(f), count(c) {}
200 
201  bool empty() const { return count == 0; }
202  C begin() const { return first; }
203  C end() const { return first + count; }
204  };
205 
206  const A1 &m_a1;
207  const A2 &m_a2;
208  std::vector<B_pair> m_cons;
209  std::vector<unsigned int> m_cons_masked;
210  std::vector<C_pair> m_bins;
211  std::vector<C> m_ranks;
212  const bool m_radix_sort;
213  const bool m_keep_cons;
214  const bool m_do_masked;
215 
216  binnor(const A1 &a1, const A2 &a2, bool radix = true, bool keep_cons = false)
217  : m_a1(a1),
218  m_a2(a2),
219  m_bins(m_a1.size_of_N() * m_a2.size_of_N()),
220  m_radix_sort(radix),
221  m_keep_cons(keep_cons),
222  m_do_masked(radix || !keep_cons) {}
223 
224  // Access -- bin indices
225 
226  B_pair m_bin_to_n_bin(B_pair m_bin) {
227  return {m_a1.from_M_bin_to_N_bin(m_bin.bin1()), m_a2.from_M_bin_to_N_bin(m_bin.bin2())};
228  }
229 
230  B_pair get_n_bin(typename A1::index_t n1, typename A2::index_t n2) const { return {n1, n2}; }
231 
232  B_pair get_n_bin(typename A1::real_t r1, typename A2::real_t r2) const {
233  return {m_a1.from_R_to_N_bin(r1), m_a2.from_R_to_N_bin(r2)};
234  }
235 
236  // Access -- content of bins
237 
238  C_pair &ref_content(B_pair n_bin) { return m_bins[n_bin.bin2() * m_a1.size_of_N() + n_bin.bin1()]; }
239 
240  C_pair get_content(B_pair n_bin) const { return m_bins[n_bin.bin2() * m_a1.size_of_N() + n_bin.bin1()]; }
241 
242  C_pair get_content(typename A1::index_t n1, typename A2::index_t n2) const {
243  return m_bins[n2 * m_a1.size_of_N() + n1];
244  }
245 
246  C_pair get_content(typename A1::real_t r1, typename A2::real_t r2) const {
247  return get_content(m_a1.from_R_to_N_bin(r1), m_a2.from_R_to_N_bin(r2));
248  }
249 
250  // Filling
251 
252  void reset_contents(bool shrink_vectors = true) {
253  if (m_keep_cons) {
254  m_cons.clear();
255  if (shrink_vectors)
256  m_cons.shrink_to_fit();
257  }
258  m_bins.assign(m_bins.size(), C_pair());
259  m_ranks.clear();
260  if (shrink_vectors)
261  m_ranks.shrink_to_fit();
262  }
263 
264  void begin_registration(C n_items) {
265  if (m_keep_cons)
266  m_cons.reserve(n_items);
267  if (m_do_masked)
268  m_cons_masked.reserve(n_items);
269  }
270 
271  void register_entry(B_pair bp) {
272  if (m_keep_cons)
273  m_cons.push_back(bp);
274  if (m_do_masked)
275  m_cons_masked.push_back(bp.mask_A2_M_bins());
276  }
277 
278  void register_entry(typename A1::real_t r1, typename A2::real_t r2) {
279  register_entry({m_a1.from_R_to_M_bin(r1), m_a2.from_R_to_M_bin(r2)});
280  }
281 
282  void register_entry_safe(typename A1::real_t r1, typename A2::real_t r2) {
283  register_entry({m_a1.from_R_to_M_bin_safe(r1), m_a2.from_R_to_M_bin_safe(r2)});
284  }
285 
286  // Do M-binning outside, potentially using R_to_M_bin_safe().
287  void register_m_bins(typename A1::index_t m1, typename A2::index_t m2) { register_entry({m1, m2}); }
288 
290  unsigned int n_entries = m_do_masked ? m_cons_masked.size() : m_cons.size();
291  if (m_radix_sort && n_entries >= (m_keep_cons ? 128 : 256)) {
293  radix.sort(m_cons_masked, m_ranks);
294  } else {
295  m_ranks.resize(n_entries);
296  std::iota(m_ranks.begin(), m_ranks.end(), 0);
297  if (m_do_masked)
298  std::sort(
299  m_ranks.begin(), m_ranks.end(), [&](auto &a, auto &b) { return m_cons_masked[a] < m_cons_masked[b]; });
300  else
301  std::sort(m_ranks.begin(), m_ranks.end(), [&](auto &a, auto &b) {
302  return m_cons[a].mask_A2_M_bins() < m_cons[b].mask_A2_M_bins();
303  });
304  }
305 
306  for (C i = 0; i < m_ranks.size(); ++i) {
307  C j = m_ranks[i];
308  C_pair &c_bin = ref_content(m_bin_to_n_bin(m_keep_cons ? m_cons[j] : B_pair(m_cons_masked[j])));
309  if (c_bin.count == 0)
310  c_bin.first = i;
311  ++c_bin.count;
312 
313 #ifdef DEBUG
314  B_pair n_pair = m_bin_to_n_bin(m_cons[j]);
315  printf("i=%4u j=%4u %u %u %u %u\n", i, j, n_pair.bin1, n_pair.bin2, c_bin.first, c_bin.count);
316 #endif
317  }
318 
319  if (m_keep_cons)
320  m_cons.shrink_to_fit();
321  if (m_do_masked) {
322  m_cons_masked.clear();
323  m_cons_masked.shrink_to_fit();
324  }
325  }
326  };
327 
328 } // namespace mkfit
329 
330 #endif
constexpr int32_t ceil(float num)
I from_R_to_M_bin_safe(R r) const
Definition: binnor.h:64
axis(R min, R max, R bin_width)
Definition: binnor.h:134
static constexpr unsigned int c_M
Definition: binnor.h:29
I from_R_to_M_bin(R r) const
Definition: binnor.h:61
const A2 & m_a2
Definition: binnor.h:207
B_pair get_n_bin(typename A1::index_t n1, typename A2::index_t n2) const
Definition: binnor.h:230
void register_entry(B_pair bp)
Definition: binnor.h:271
I_pair from_R_rdr_to_N_bins(R r, R dr) const
Definition: binnor.h:73
A1::index_t bin1() const
Definition: binnor.h:187
static constexpr unsigned int c_N_end
Definition: binnor.h:83
std::vector< unsigned int > m_cons_masked
Definition: binnor.h:209
C_pair get_content(B_pair n_bin) const
Definition: binnor.h:240
I from_R_to_M_bin_safe(R r) const
Definition: binnor.h:102
C_pair(C f, C c)
Definition: binnor.h:199
I next_N_bin(I bin) const
Definition: binnor.h:113
axis_pow2(R min, R max)
Definition: binnor.h:120
axis_base(R min, R max, unsigned int M_size, unsigned int N_size)
Definition: binnor.h:46
axis_pow2_base(R min, R max)
Definition: binnor.h:85
std::vector< B_pair > m_cons
Definition: binnor.h:208
std::vector< C_pair > m_bins
Definition: binnor.h:210
assert(be >=bs)
const unsigned int m_num_N_bins
Definition: binnor.h:127
const R m_N_lbhp
Definition: binnor.h:35
void finalize_registration()
Definition: binnor.h:289
void register_entry(typename A1::real_t r1, typename A2::real_t r2)
Definition: binnor.h:278
axis_base< R, I, M, N >::I_pair from_R_rdr_to_N_bins(R r, R dr) const
Definition: binnor.h:110
axis_pow2_u1(R min, R max)
Definition: binnor.h:100
const bool m_radix_sort
Definition: binnor.h:212
std::vector< C > m_ranks
Definition: binnor.h:211
unsigned int size_of_N() const
Definition: binnor.h:143
I from_R_to_N_bin_safe(R r) const
Definition: binnor.h:103
const R m_R_max
Definition: binnor.h:33
const I m_last_M_bin
Definition: binnor.h:36
void register_entry_safe(typename A1::real_t r1, typename A2::real_t r2)
Definition: binnor.h:282
axis_base< R, I, M, N >::I_pair from_R_minmax_to_N_bins(R rmin, R rmax) const
Definition: binnor.h:105
static constexpr unsigned int c_N
Definition: binnor.h:30
const std::complex< double > I
Definition: I.h:8
double f[11][100]
const bool m_do_masked
Definition: binnor.h:214
I from_M_bin_to_N_bin(I m) const
Definition: binnor.h:67
I from_R_to_N_bin(R r) const
Definition: binnor.h:62
void register_m_bins(typename A1::index_t m1, typename A2::index_t m2)
Definition: binnor.h:287
binnor(const A1 &a1, const A2 &a2, bool radix=true, bool keep_cons=false)
Definition: binnor.h:216
void begin_registration(C n_items)
Definition: binnor.h:264
C_pair get_content(typename A1::real_t r1, typename A2::real_t r2) const
Definition: binnor.h:246
const A1 & m_a1
Definition: binnor.h:206
B_pair(unsigned int pv)
Definition: binnor.h:184
unsigned int size_of_M() const
Definition: binnor.h:87
axis(R min, R max, unsigned n_bins)
Definition: binnor.h:129
static constexpr unsigned int c_M2N_shift
Definition: binnor.h:31
static constexpr unsigned int c_A2_Mout_mask
Definition: binnor.h:177
B_pair(typename A1::index_t i1, typename A2::index_t i2)
Definition: binnor.h:185
static constexpr unsigned int c_A1_mask
Definition: binnor.h:176
#define N
Definition: blowfish.cc:9
static constexpr I c_M_mask
Definition: binnor.h:97
unsigned int mask_A2_M_bins() const
Definition: binnor.h:190
unsigned int packed_value
Definition: binnor.h:181
const R m_M_fac
Definition: binnor.h:34
const unsigned int m_num_M_bins
Definition: binnor.h:127
double b
Definition: hdecay.h:120
unsigned int size_of_N() const
Definition: binnor.h:88
C begin() const
Definition: binnor.h:202
const I m_last_N_bin
Definition: binnor.h:36
void sort(const std::vector< V > &values, std::vector< R > &ranks)
Definition: radix_sort.cc:11
I_pair from_R_minmax_to_N_bins(R rmin, R rmax) const
Definition: binnor.h:69
static constexpr I c_N_mask
Definition: binnor.h:98
const bool m_keep_cons
Definition: binnor.h:213
double a
Definition: hdecay.h:121
unsigned int size_of_M() const
Definition: binnor.h:142
bool empty() const
Definition: binnor.h:201
B_pair get_n_bin(typename A1::real_t r1, typename A2::real_t r2) const
Definition: binnor.h:232
static constexpr unsigned int c_M_end
Definition: binnor.h:82
const R m_M_lbhp
Definition: binnor.h:35
I next_N_bin(I bin) const
Definition: binnor.h:74
C_pair get_content(typename A1::index_t n1, typename A2::index_t n2) const
Definition: binnor.h:242
B_pair m_bin_to_n_bin(B_pair m_bin)
Definition: binnor.h:226
void reset_contents(bool shrink_vectors=true)
Definition: binnor.h:252
C_pair & ref_content(B_pair n_bin)
Definition: binnor.h:238
A2::index_t bin2() const
Definition: binnor.h:188
I from_R_to_N_bin_safe(R r) const
Definition: binnor.h:65
const R m_N_fac
Definition: binnor.h:34
const R m_R_min
Definition: binnor.h:33