IMP logo
IMP Reference Guide  2.15.0
The Integrative Modeling Platform
piecewise_linear_distribution.h
Go to the documentation of this file.
1 /**
2  * \file IMP/piecewise_linear_distribution.h
3  * \brief boost piecewise linear.
4  *
5  * Copyright 2007-2021 IMP Inventors. All rights reserved.
6  *
7  */
8 
9 #ifndef IMPKERNEL_PIECEWISE_LINEAR_DISTRIBUTION_H
10 #define IMPKERNEL_PIECEWISE_LINEAR_DISTRIBUTION_H
11 
12 #include <IMP/kernel_config.h>
13 #include "Vector.h"
14 #include <boost/version.hpp>
15 #include <boost/random/uniform_real.hpp>
16 #include <numeric>
17 #include <algorithm>
18 #include <cmath>
19 #include <iostream>
20 #include <iterator>
21 #include <stdexcept>
22 
23 IMPKERNEL_BEGIN_NAMESPACE
24 
25 //! Draw random nums from a distribution defined as a piecewise linear function.
26 /** It models the boost random number generators and is made redundant by the
27  boost::piecewise_linear_distribution in boost 1.47. Currently, it won't
28  use that class if available, but that is just due to lack of
29  boost 1.47 to test on.
30  */
31 template <class T = double>
33  Vector<T> dividers_;
34  Vector<T> accum_;
35  Vector<T> weights_;
36  mutable boost::uniform_real<> un_;
37  double get_divider(int divider) const { return dividers_[divider]; }
38  double get_accum(int divider) const { return accum_[divider]; }
39  double get_slope(int divider) const {
40  return (weights_[divider + 1] - weights_[divider]) /
41  (dividers_[divider + 1] - dividers_[divider]);
42  }
43  double get_weight(int divider) const { return weights_[divider]; }
44 
45  public:
46  //! construct a uniform 01
48  dividers_.push_back(0);
49  dividers_.push_back(1);
50  accum_.push_back(0);
51  accum_.push_back(1);
52  weights_.push_back(1);
53  weights_.push_back(1);
54  }
55  /** Construct the distribution by interpolating between samples
56  at locations[i] with corresponding weight weights[i]. The
57  weight outside of the first and last location is assume to
58  be immediately 0.
59  */
60  template <class LIt, class WIt>
61  piecewise_linear_distribution(LIt locations_begin, LIt locations_end,
62  WIt weights_begin)
63  : dividers_(locations_begin, locations_end),
64  accum_(std::distance(locations_begin, locations_end)),
65  weights_(std::distance(locations_begin, locations_end)),
66  un_(0, 1) {
67  for (unsigned int i = 0; i < weights_.size(); ++i) {
68  weights_[i] = *weights_begin;
69  ++weights_begin;
70  }
71  accum_[0] = 0;
72  for (unsigned int i = 1; i < accum_.size(); ++i) {
73  double wid = (dividers_[i] - dividers_[i - 1]);
74  accum_[i] = accum_[i - 1] + wid * .5 * (weights_[i - 1] + weights_[i]);
75  }
76  double total = accum_.back();
77 #if IMP_HAS_CHECKS >= IMP_INTERNAL
78  for (unsigned int i = 1; i < dividers_.size(); ++i) {
79  if (dividers_[i] <= dividers_[i - 1]) {
80  std::cerr << "Found non-monotonic locations: ";
81  std::copy(locations_begin, locations_end,
82  std::ostream_iterator<double>(std::cerr, ", "));
83  std::cerr << std::endl;
84  throw std::logic_error("Locations are not strictly increasing");
85  }
86  }
87  // check that the total weight is ~1
88  if (std::abs(total - 1.0) > .05) {
89  std::cerr << "Found weight " << total << std::endl;
90  std::cerr << "Locations: [";
91  std::copy(locations_begin, locations_end,
92  std::ostream_iterator<double>(std::cerr, ", "));
93  std::cerr << "]" << std::endl;
94  std::cerr << "Weights: [";
95  std::copy(weights_.begin(), weights_.end(),
96  std::ostream_iterator<double>(std::cerr, ", "));
97  std::cerr << "]" << std::endl;
98  throw std::logic_error(
99  "The total weight of the distribution is not close to 1");
100  }
101 #endif
102  // correct for discretization errors
103  for (unsigned int i = 0; i < accum_.size(); ++i) {
104  accum_[i] /= total;
105  }
106  /*std::cout << "dividers: ";
107  std::copy(dividers_.begin(), dividers_.end(),
108  std::ostream_iterator<double>(std::cout, ", "));
109  std::cout << std::endl << "sums: ";
110  std::copy(accum_.begin(), accum_.end(),
111  std::ostream_iterator<double>(std::cout, ", "));
112  std::cout << std::endl;
113  std::cout << "weights: ";
114  std::copy(weights_.begin(), weights_.end(),
115  std::ostream_iterator<double>(std::cout, ", "));
116  std::cout << std::endl;*/
117  }
118  template <class RNG>
119  double operator()(RNG& rng) const {
120  double rn = un_(rng);
121  typename Vector<T>::const_iterator it =
122  std::upper_bound(accum_.begin() + 1, accum_.end(), rn);
123  if (it == accum_.end()) {
124  std::cerr << "Hit end " << rn << " " << accum_.back() << std::endl;
125  return dividers_.back();
126  } else {
127  int offset = it - accum_.begin() - 1;
128  double pd = get_weight(offset);
129  double ad = get_accum(offset);
130  double sd = get_slope(offset);
131  double leftd = rn - ad;
132  double radix = std::sqrt(pd * pd + 2 * leftd * sd);
133  double xmd;
134  if (sd < 0) {
135  xmd = (-pd + radix) / sd;
136  } else {
137  xmd = (-pd + radix) / (sd);
138  }
139 #ifndef NDEBUG
140  if (xmd < 0) {
141  std::cerr << "negative " << std::endl;
142  }
143 #endif
144  /*std::cout << rn << " " << pd << " " << ad << " " << sd << " "
145  << leftd << " " << xmd << std::endl;*/
146  return xmd + get_divider(offset);
147  }
148  }
149 };
150 
151 IMPKERNEL_END_NAMESPACE
152 
153 #endif /* IMPKERNEL_PIECEWISE_LINEAR_DISTRIBUTION_H */
piecewise_linear_distribution(LIt locations_begin, LIt locations_end, WIt weights_begin)
piecewise_linear_distribution()
construct a uniform 01
A more IMP-like version of the std::vector.
Definition: Vector.h:40
A class for storing lists of IMP items.
Draw random nums from a distribution defined as a piecewise linear function.