9 #ifndef IMPKERNEL_PIECEWISE_LINEAR_DISTRIBUTION_H
10 #define IMPKERNEL_PIECEWISE_LINEAR_DISTRIBUTION_H
12 #include <IMP/kernel_config.h>
14 #include <boost/version.hpp>
15 #include <boost/random/uniform_real.hpp>
23 IMPKERNEL_BEGIN_NAMESPACE
31 template <
class T =
double>
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]);
43 double get_weight(
int divider)
const {
return weights_[divider]; }
48 dividers_.push_back(0);
49 dividers_.push_back(1);
52 weights_.push_back(1);
53 weights_.push_back(1);
60 template <
class LIt,
class WIt>
63 : dividers_(locations_begin, locations_end),
64 accum_(std::distance(locations_begin, locations_end)),
65 weights_(std::distance(locations_begin, locations_end)),
67 for (
unsigned int i = 0; i < weights_.size(); ++i) {
68 weights_[i] = *weights_begin;
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]);
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");
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");
103 for (
unsigned int i = 0; i < accum_.size(); ++i) {
119 double operator()(RNG& rng)
const {
120 double rn = un_(rng);
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();
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);
135 xmd = (-pd + radix) / sd;
137 xmd = (-pd + radix) / (sd);
141 std::cerr <<
"negative " << std::endl;
146 return xmd + get_divider(offset);
151 IMPKERNEL_END_NAMESPACE
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.
A class for storing lists of IMP items.
Draw random nums from a distribution defined as a piecewise linear function.