piecewise_linear_distribution.hpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. /* boost random/piecewise_linear_distribution.hpp header file
  2. *
  3. * Copyright Steven Watanabe 2011
  4. * Distributed under the Boost Software License, Version 1.0. (See
  5. * accompanying file LICENSE_1_0.txt or copy at
  6. * http://www.boost.org/LICENSE_1_0.txt)
  7. *
  8. * See http://www.boost.org for most recent version including documentation.
  9. *
  10. * $Id$
  11. */
  12. #ifndef BOOST_RANDOM_PIECEWISE_LINEAR_DISTRIBUTION_HPP_INCLUDED
  13. #define BOOST_RANDOM_PIECEWISE_LINEAR_DISTRIBUTION_HPP_INCLUDED
  14. #include <vector>
  15. #include <algorithm>
  16. #include <cmath>
  17. #include <cstdlib>
  18. #include <boost/assert.hpp>
  19. #include <boost/random/uniform_real.hpp>
  20. #include <boost/random/discrete_distribution.hpp>
  21. #include <boost/random/detail/config.hpp>
  22. #include <boost/random/detail/operators.hpp>
  23. #include <boost/random/detail/vector_io.hpp>
  24. #ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
  25. #include <initializer_list>
  26. #endif
  27. namespace boost {
  28. namespace random {
  29. /**
  30. * The class @c piecewise_linear_distribution models a \random_distribution.
  31. */
  32. template<class RealType = double>
  33. class piecewise_linear_distribution {
  34. public:
  35. typedef std::size_t input_type;
  36. typedef RealType result_type;
  37. class param_type {
  38. public:
  39. typedef piecewise_linear_distribution distribution_type;
  40. /**
  41. * Constructs a @c param_type object, representing a distribution
  42. * that produces values uniformly distributed in the range [0, 1).
  43. */
  44. param_type()
  45. {
  46. _weights.push_back(RealType(1));
  47. _weights.push_back(RealType(1));
  48. _intervals.push_back(RealType(0));
  49. _intervals.push_back(RealType(1));
  50. }
  51. /**
  52. * Constructs a @c param_type object from two iterator ranges
  53. * containing the interval boundaries and weights at the boundaries.
  54. * If there are fewer than two boundaries, then this is equivalent to
  55. * the default constructor and the distribution will produce values
  56. * uniformly distributed in the range [0, 1).
  57. *
  58. * The values of the interval boundaries must be strictly
  59. * increasing, and the number of weights must be the same as
  60. * the number of interval boundaries. If there are extra
  61. * weights, they are ignored.
  62. */
  63. template<class IntervalIter, class WeightIter>
  64. param_type(IntervalIter intervals_first, IntervalIter intervals_last,
  65. WeightIter weight_first)
  66. : _intervals(intervals_first, intervals_last)
  67. {
  68. if(_intervals.size() < 2) {
  69. _intervals.clear();
  70. _weights.push_back(RealType(1));
  71. _weights.push_back(RealType(1));
  72. _intervals.push_back(RealType(0));
  73. _intervals.push_back(RealType(1));
  74. } else {
  75. _weights.reserve(_intervals.size());
  76. for(std::size_t i = 0; i < _intervals.size(); ++i) {
  77. _weights.push_back(*weight_first++);
  78. }
  79. }
  80. }
  81. #ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
  82. /**
  83. * Constructs a @c param_type object from an initializer_list
  84. * containing the interval boundaries and a unary function
  85. * specifying the weights at the boundaries. Each weight is
  86. * determined by calling the function at the corresponding point.
  87. *
  88. * If the initializer_list contains fewer than two elements,
  89. * this is equivalent to the default constructor and the
  90. * distribution will produce values uniformly distributed
  91. * in the range [0, 1).
  92. */
  93. template<class T, class F>
  94. param_type(const std::initializer_list<T>& il, F f)
  95. : _intervals(il.begin(), il.end())
  96. {
  97. if(_intervals.size() < 2) {
  98. _intervals.clear();
  99. _weights.push_back(RealType(1));
  100. _weights.push_back(RealType(1));
  101. _intervals.push_back(RealType(0));
  102. _intervals.push_back(RealType(1));
  103. } else {
  104. _weights.reserve(_intervals.size());
  105. for(typename std::vector<RealType>::const_iterator
  106. iter = _intervals.begin(), end = _intervals.end();
  107. iter != end; ++iter)
  108. {
  109. _weights.push_back(f(*iter));
  110. }
  111. }
  112. }
  113. #endif
  114. /**
  115. * Constructs a @c param_type object from Boost.Range ranges holding
  116. * the interval boundaries and the weights at the boundaries. If
  117. * there are fewer than two interval boundaries, this is equivalent
  118. * to the default constructor and the distribution will produce
  119. * values uniformly distributed in the range [0, 1). The
  120. * number of weights must be equal to the number of
  121. * interval boundaries.
  122. */
  123. template<class IntervalRange, class WeightRange>
  124. param_type(const IntervalRange& intervals_arg,
  125. const WeightRange& weights_arg)
  126. : _intervals(std::begin(intervals_arg), std::end(intervals_arg)),
  127. _weights(std::begin(weights_arg), std::end(weights_arg))
  128. {
  129. if(_intervals.size() < 2) {
  130. _weights.clear();
  131. _weights.push_back(RealType(1));
  132. _weights.push_back(RealType(1));
  133. _intervals.clear();
  134. _intervals.push_back(RealType(0));
  135. _intervals.push_back(RealType(1));
  136. }
  137. }
  138. /**
  139. * Constructs the parameters for a distribution that approximates a
  140. * function. The range of the distribution is [xmin, xmax). This
  141. * range is divided into nw equally sized intervals and the weights
  142. * are found by calling the unary function f on the boundaries of the
  143. * intervals.
  144. */
  145. template<class F>
  146. param_type(std::size_t nw, RealType xmin, RealType xmax, F f)
  147. {
  148. std::size_t n = (nw == 0) ? 1 : nw;
  149. double delta = (xmax - xmin) / n;
  150. BOOST_ASSERT(delta > 0);
  151. for(std::size_t k = 0; k < n; ++k) {
  152. _weights.push_back(f(xmin + k*delta));
  153. _intervals.push_back(xmin + k*delta);
  154. }
  155. _weights.push_back(f(xmax));
  156. _intervals.push_back(xmax);
  157. }
  158. /** Returns a vector containing the interval boundaries. */
  159. std::vector<RealType> intervals() const { return _intervals; }
  160. /**
  161. * Returns a vector containing the probability densities
  162. * at all the interval boundaries.
  163. */
  164. std::vector<RealType> densities() const
  165. {
  166. RealType sum = static_cast<RealType>(0);
  167. for(std::size_t i = 0; i < _intervals.size() - 1; ++i) {
  168. RealType width = _intervals[i + 1] - _intervals[i];
  169. sum += (_weights[i] + _weights[i + 1]) * width / 2;
  170. }
  171. std::vector<RealType> result;
  172. result.reserve(_weights.size());
  173. for(typename std::vector<RealType>::const_iterator
  174. iter = _weights.begin(), end = _weights.end();
  175. iter != end; ++iter)
  176. {
  177. result.push_back(*iter / sum);
  178. }
  179. return result;
  180. }
  181. /** Writes the parameters to a @c std::ostream. */
  182. BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, param_type, parm)
  183. {
  184. detail::print_vector(os, parm._intervals);
  185. detail::print_vector(os, parm._weights);
  186. return os;
  187. }
  188. /** Reads the parameters from a @c std::istream. */
  189. BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, param_type, parm)
  190. {
  191. std::vector<RealType> new_intervals;
  192. std::vector<RealType> new_weights;
  193. detail::read_vector(is, new_intervals);
  194. detail::read_vector(is, new_weights);
  195. if(is) {
  196. parm._intervals.swap(new_intervals);
  197. parm._weights.swap(new_weights);
  198. }
  199. return is;
  200. }
  201. /** Returns true if the two sets of parameters are the same. */
  202. BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(param_type, lhs, rhs)
  203. {
  204. return lhs._intervals == rhs._intervals
  205. && lhs._weights == rhs._weights;
  206. }
  207. /** Returns true if the two sets of parameters are different. */
  208. BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(param_type)
  209. private:
  210. friend class piecewise_linear_distribution;
  211. std::vector<RealType> _intervals;
  212. std::vector<RealType> _weights;
  213. };
  214. /**
  215. * Creates a new @c piecewise_linear_distribution that
  216. * produces values uniformly distributed in the range [0, 1).
  217. */
  218. piecewise_linear_distribution()
  219. {
  220. default_init();
  221. }
  222. /**
  223. * Constructs a piecewise_linear_distribution from two iterator ranges
  224. * containing the interval boundaries and the weights at the boundaries.
  225. * If there are fewer than two boundaries, then this is equivalent to
  226. * the default constructor and creates a distribution that
  227. * produces values uniformly distributed in the range [0, 1).
  228. *
  229. * The values of the interval boundaries must be strictly
  230. * increasing, and the number of weights must be equal to
  231. * the number of interval boundaries. If there are extra
  232. * weights, they are ignored.
  233. *
  234. * For example,
  235. *
  236. * @code
  237. * double intervals[] = { 0.0, 1.0, 2.0 };
  238. * double weights[] = { 0.0, 1.0, 0.0 };
  239. * piecewise_constant_distribution<> dist(
  240. * &intervals[0], &intervals[0] + 3, &weights[0]);
  241. * @endcode
  242. *
  243. * produces a triangle distribution.
  244. */
  245. template<class IntervalIter, class WeightIter>
  246. piecewise_linear_distribution(IntervalIter first_interval,
  247. IntervalIter last_interval,
  248. WeightIter first_weight)
  249. : _intervals(first_interval, last_interval)
  250. {
  251. if(_intervals.size() < 2) {
  252. default_init();
  253. } else {
  254. _weights.reserve(_intervals.size());
  255. for(std::size_t i = 0; i < _intervals.size(); ++i) {
  256. _weights.push_back(*first_weight++);
  257. }
  258. init();
  259. }
  260. }
  261. #ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
  262. /**
  263. * Constructs a piecewise_linear_distribution from an
  264. * initializer_list containing the interval boundaries
  265. * and a unary function specifying the weights. Each
  266. * weight is determined by calling the function at the
  267. * corresponding interval boundary.
  268. *
  269. * If the initializer_list contains fewer than two elements,
  270. * this is equivalent to the default constructor and the
  271. * distribution will produce values uniformly distributed
  272. * in the range [0, 1).
  273. */
  274. template<class T, class F>
  275. piecewise_linear_distribution(std::initializer_list<T> il, F f)
  276. : _intervals(il.begin(), il.end())
  277. {
  278. if(_intervals.size() < 2) {
  279. default_init();
  280. } else {
  281. _weights.reserve(_intervals.size());
  282. for(typename std::vector<RealType>::const_iterator
  283. iter = _intervals.begin(), end = _intervals.end();
  284. iter != end; ++iter)
  285. {
  286. _weights.push_back(f(*iter));
  287. }
  288. init();
  289. }
  290. }
  291. #endif
  292. /**
  293. * Constructs a piecewise_linear_distribution from Boost.Range
  294. * ranges holding the interval boundaries and the weights. If
  295. * there are fewer than two interval boundaries, this is equivalent
  296. * to the default constructor and the distribution will produce
  297. * values uniformly distributed in the range [0, 1). The
  298. * number of weights must be equal to the number of
  299. * interval boundaries.
  300. */
  301. template<class IntervalsRange, class WeightsRange>
  302. piecewise_linear_distribution(const IntervalsRange& intervals_arg,
  303. const WeightsRange& weights_arg)
  304. : _intervals(std::begin(intervals_arg), std::end(intervals_arg)),
  305. _weights(std::begin(weights_arg), std::end(weights_arg))
  306. {
  307. if(_intervals.size() < 2) {
  308. default_init();
  309. } else {
  310. init();
  311. }
  312. }
  313. /**
  314. * Constructs a piecewise_linear_distribution that approximates a
  315. * function. The range of the distribution is [xmin, xmax). This
  316. * range is divided into nw equally sized intervals and the weights
  317. * are found by calling the unary function f on the interval boundaries.
  318. */
  319. template<class F>
  320. piecewise_linear_distribution(std::size_t nw,
  321. RealType xmin,
  322. RealType xmax,
  323. F f)
  324. {
  325. if(nw == 0) { nw = 1; }
  326. RealType delta = (xmax - xmin) / nw;
  327. _intervals.reserve(nw + 1);
  328. for(std::size_t i = 0; i < nw; ++i) {
  329. RealType x = xmin + i * delta;
  330. _intervals.push_back(x);
  331. _weights.push_back(f(x));
  332. }
  333. _intervals.push_back(xmax);
  334. _weights.push_back(f(xmax));
  335. init();
  336. }
  337. /**
  338. * Constructs a piecewise_linear_distribution from its parameters.
  339. */
  340. explicit piecewise_linear_distribution(const param_type& parm)
  341. : _intervals(parm._intervals),
  342. _weights(parm._weights)
  343. {
  344. init();
  345. }
  346. /**
  347. * Returns a value distributed according to the parameters of the
  348. * piecewise_linear_distribution.
  349. */
  350. template<class URNG>
  351. RealType operator()(URNG& urng) const
  352. {
  353. std::size_t i = _bins(urng);
  354. bool is_in_rectangle = (i % 2 == 0);
  355. i = i / 2;
  356. uniform_real<RealType> dist(_intervals[i], _intervals[i+1]);
  357. if(is_in_rectangle) {
  358. return dist(urng);
  359. } else if(_weights[i] < _weights[i+1]) {
  360. return (std::max)(dist(urng), dist(urng));
  361. } else {
  362. return (std::min)(dist(urng), dist(urng));
  363. }
  364. }
  365. /**
  366. * Returns a value distributed according to the parameters
  367. * specified by param.
  368. */
  369. template<class URNG>
  370. RealType operator()(URNG& urng, const param_type& parm) const
  371. {
  372. return piecewise_linear_distribution(parm)(urng);
  373. }
  374. /** Returns the smallest value that the distribution can produce. */
  375. result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const
  376. { return _intervals.front(); }
  377. /** Returns the largest value that the distribution can produce. */
  378. result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const
  379. { return _intervals.back(); }
  380. /**
  381. * Returns a vector containing the probability densities
  382. * at the interval boundaries.
  383. */
  384. std::vector<RealType> densities() const
  385. {
  386. RealType sum = static_cast<RealType>(0);
  387. for(std::size_t i = 0; i < _intervals.size() - 1; ++i) {
  388. RealType width = _intervals[i + 1] - _intervals[i];
  389. sum += (_weights[i] + _weights[i + 1]) * width / 2;
  390. }
  391. std::vector<RealType> result;
  392. result.reserve(_weights.size());
  393. for(typename std::vector<RealType>::const_iterator
  394. iter = _weights.begin(), end = _weights.end();
  395. iter != end; ++iter)
  396. {
  397. result.push_back(*iter / sum);
  398. }
  399. return result;
  400. }
  401. /** Returns a vector containing the interval boundaries. */
  402. std::vector<RealType> intervals() const { return _intervals; }
  403. /** Returns the parameters of the distribution. */
  404. param_type param() const
  405. {
  406. return param_type(_intervals, _weights);
  407. }
  408. /** Sets the parameters of the distribution. */
  409. void param(const param_type& parm)
  410. {
  411. std::vector<RealType> new_intervals(parm._intervals);
  412. std::vector<RealType> new_weights(parm._weights);
  413. init(new_intervals, new_weights);
  414. _intervals.swap(new_intervals);
  415. _weights.swap(new_weights);
  416. }
  417. /**
  418. * Effects: Subsequent uses of the distribution do not depend
  419. * on values produced by any engine prior to invoking reset.
  420. */
  421. void reset() { _bins.reset(); }
  422. /** Writes a distribution to a @c std::ostream. */
  423. BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(
  424. os, piecewise_linear_distribution, pld)
  425. {
  426. os << pld.param();
  427. return os;
  428. }
  429. /** Reads a distribution from a @c std::istream */
  430. BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(
  431. is, piecewise_linear_distribution, pld)
  432. {
  433. param_type parm;
  434. if(is >> parm) {
  435. pld.param(parm);
  436. }
  437. return is;
  438. }
  439. /**
  440. * Returns true if the two distributions will return the
  441. * same sequence of values, when passed equal generators.
  442. */
  443. BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(
  444. piecewise_linear_distribution, lhs, rhs)
  445. {
  446. return lhs._intervals == rhs._intervals && lhs._weights == rhs._weights;
  447. }
  448. /**
  449. * Returns true if the two distributions may return different
  450. * sequences of values, when passed equal generators.
  451. */
  452. BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(piecewise_linear_distribution)
  453. private:
  454. /// @cond \show_private
  455. void init(const std::vector<RealType>& intervals_arg,
  456. const std::vector<RealType>& weights_arg)
  457. {
  458. using std::abs;
  459. std::vector<RealType> bin_weights;
  460. bin_weights.reserve((intervals_arg.size() - 1) * 2);
  461. for(std::size_t i = 0; i < intervals_arg.size() - 1; ++i) {
  462. RealType width = intervals_arg[i + 1] - intervals_arg[i];
  463. RealType w1 = weights_arg[i];
  464. RealType w2 = weights_arg[i + 1];
  465. bin_weights.push_back((std::min)(w1, w2) * width);
  466. bin_weights.push_back(abs(w1 - w2) * width / 2);
  467. }
  468. typedef discrete_distribution<std::size_t, RealType> bins_type;
  469. typename bins_type::param_type bins_param(bin_weights);
  470. _bins.param(bins_param);
  471. }
  472. void init()
  473. {
  474. init(_intervals, _weights);
  475. }
  476. void default_init()
  477. {
  478. _intervals.clear();
  479. _intervals.push_back(RealType(0));
  480. _intervals.push_back(RealType(1));
  481. _weights.clear();
  482. _weights.push_back(RealType(1));
  483. _weights.push_back(RealType(1));
  484. init();
  485. }
  486. discrete_distribution<std::size_t, RealType> _bins;
  487. std::vector<RealType> _intervals;
  488. std::vector<RealType> _weights;
  489. /// @endcond
  490. };
  491. }
  492. }
  493. #endif