467 lines
17 KiB
C++
467 lines
17 KiB
C++
/* boost random/piecewise_constant_distribution.hpp header file
|
|
*
|
|
* Copyright Steven Watanabe 2011
|
|
* Distributed under the Boost Software License, Version 1.0. (See
|
|
* accompanying file LICENSE_1_0.txt or copy at
|
|
* http://www.boost.org/LICENSE_1_0.txt)
|
|
*
|
|
* See http://www.boost.org for most recent version including documentation.
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
#ifndef BOOST_RANDOM_PIECEWISE_CONSTANT_DISTRIBUTION_HPP_INCLUDED
|
|
#define BOOST_RANDOM_PIECEWISE_CONSTANT_DISTRIBUTION_HPP_INCLUDED
|
|
|
|
#include <vector>
|
|
#include <numeric>
|
|
#include <boost/assert.hpp>
|
|
#include <boost/random/uniform_real.hpp>
|
|
#include <boost/random/discrete_distribution.hpp>
|
|
#include <boost/random/detail/config.hpp>
|
|
#include <boost/random/detail/operators.hpp>
|
|
#include <boost/random/detail/vector_io.hpp>
|
|
|
|
#ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
|
|
#include <initializer_list>
|
|
#endif
|
|
|
|
#include <boost/range/begin.hpp>
|
|
#include <boost/range/end.hpp>
|
|
|
|
namespace boost {
|
|
namespace random {
|
|
|
|
/**
|
|
* The class @c piecewise_constant_distribution models a \random_distribution.
|
|
*/
|
|
template<class RealType = double, class WeightType = double>
|
|
class piecewise_constant_distribution {
|
|
public:
|
|
typedef std::size_t input_type;
|
|
typedef RealType result_type;
|
|
|
|
class param_type {
|
|
public:
|
|
|
|
typedef piecewise_constant_distribution distribution_type;
|
|
|
|
/**
|
|
* Constructs a @c param_type object, representing a distribution
|
|
* that produces values uniformly distributed in the range [0, 1).
|
|
*/
|
|
param_type()
|
|
{
|
|
_weights.push_back(WeightType(1));
|
|
_intervals.push_back(RealType(0));
|
|
_intervals.push_back(RealType(1));
|
|
}
|
|
/**
|
|
* Constructs a @c param_type object from two iterator ranges
|
|
* containing the interval boundaries and the interval weights.
|
|
* If there are less than two boundaries, then this is equivalent to
|
|
* the default constructor and creates a single interval, [0, 1).
|
|
*
|
|
* The values of the interval boundaries must be strictly
|
|
* increasing, and the number of weights must be one less than
|
|
* the number of interval boundaries. If there are extra
|
|
* weights, they are ignored.
|
|
*/
|
|
template<class IntervalIter, class WeightIter>
|
|
param_type(IntervalIter intervals_first, IntervalIter intervals_last,
|
|
WeightIter weight_first)
|
|
: _intervals(intervals_first, intervals_last)
|
|
{
|
|
if(_intervals.size() < 2) {
|
|
_intervals.clear();
|
|
_intervals.push_back(RealType(0));
|
|
_intervals.push_back(RealType(1));
|
|
_weights.push_back(WeightType(1));
|
|
} else {
|
|
_weights.reserve(_intervals.size() - 1);
|
|
for(std::size_t i = 0; i < _intervals.size() - 1; ++i) {
|
|
_weights.push_back(*weight_first++);
|
|
}
|
|
}
|
|
}
|
|
#ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
|
|
/**
|
|
* Constructs a @c param_type object from an
|
|
* initializer_list containing the interval boundaries
|
|
* and a unary function specifying the weights. Each
|
|
* weight is determined by calling the function at the
|
|
* midpoint of the corresponding interval.
|
|
*
|
|
* If the initializer_list contains less than two elements,
|
|
* this is equivalent to the default constructor and the
|
|
* distribution will produce values uniformly distributed
|
|
* in the range [0, 1).
|
|
*/
|
|
template<class T, class F>
|
|
param_type(const std::initializer_list<T>& il, F f)
|
|
: _intervals(il.begin(), il.end())
|
|
{
|
|
if(_intervals.size() < 2) {
|
|
_intervals.clear();
|
|
_intervals.push_back(RealType(0));
|
|
_intervals.push_back(RealType(1));
|
|
_weights.push_back(WeightType(1));
|
|
} else {
|
|
_weights.reserve(_intervals.size() - 1);
|
|
for(std::size_t i = 0; i < _intervals.size() - 1; ++i) {
|
|
RealType midpoint = (_intervals[i] + _intervals[i + 1]) / 2;
|
|
_weights.push_back(f(midpoint));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
/**
|
|
* Constructs a @c param_type object from Boost.Range
|
|
* ranges holding the interval boundaries and the weights. If
|
|
* there are less than two interval boundaries, this is equivalent
|
|
* to the default constructor and the distribution will produce
|
|
* values uniformly distributed in the range [0, 1). The
|
|
* number of weights must be one less than the number of
|
|
* interval boundaries.
|
|
*/
|
|
template<class IntervalRange, class WeightRange>
|
|
param_type(const IntervalRange& intervals_arg,
|
|
const WeightRange& weights_arg)
|
|
: _intervals(boost::begin(intervals_arg), boost::end(intervals_arg)),
|
|
_weights(boost::begin(weights_arg), boost::end(weights_arg))
|
|
{
|
|
if(_intervals.size() < 2) {
|
|
_intervals.clear();
|
|
_intervals.push_back(RealType(0));
|
|
_intervals.push_back(RealType(1));
|
|
_weights.push_back(WeightType(1));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Constructs the parameters for a distribution that approximates a
|
|
* function. The range of the distribution is [xmin, xmax). This
|
|
* range is divided into nw equally sized intervals and the weights
|
|
* are found by calling the unary function f on the midpoints of the
|
|
* intervals.
|
|
*/
|
|
template<class F>
|
|
param_type(std::size_t nw, RealType xmin, RealType xmax, F f)
|
|
{
|
|
std::size_t n = (nw == 0) ? 1 : nw;
|
|
double delta = (xmax - xmin) / n;
|
|
BOOST_ASSERT(delta > 0);
|
|
for(std::size_t k = 0; k < n; ++k) {
|
|
_weights.push_back(f(xmin + k*delta + delta/2));
|
|
_intervals.push_back(xmin + k*delta);
|
|
}
|
|
_intervals.push_back(xmax);
|
|
}
|
|
|
|
/** Returns a vector containing the interval boundaries. */
|
|
std::vector<RealType> intervals() const { return _intervals; }
|
|
|
|
/**
|
|
* Returns a vector containing the probability densities
|
|
* over all the intervals of the distribution.
|
|
*/
|
|
std::vector<RealType> densities() const
|
|
{
|
|
RealType sum = std::accumulate(_weights.begin(), _weights.end(),
|
|
static_cast<RealType>(0));
|
|
std::vector<RealType> result;
|
|
result.reserve(_weights.size());
|
|
for(std::size_t i = 0; i < _weights.size(); ++i) {
|
|
RealType width = _intervals[i + 1] - _intervals[i];
|
|
result.push_back(_weights[i] / (sum * width));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/** Writes the parameters to a @c std::ostream. */
|
|
BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, param_type, parm)
|
|
{
|
|
detail::print_vector(os, parm._intervals);
|
|
detail::print_vector(os, parm._weights);
|
|
return os;
|
|
}
|
|
|
|
/** Reads the parameters from a @c std::istream. */
|
|
BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, param_type, parm)
|
|
{
|
|
std::vector<RealType> new_intervals;
|
|
std::vector<WeightType> new_weights;
|
|
detail::read_vector(is, new_intervals);
|
|
detail::read_vector(is, new_weights);
|
|
if(is) {
|
|
parm._intervals.swap(new_intervals);
|
|
parm._weights.swap(new_weights);
|
|
}
|
|
return is;
|
|
}
|
|
|
|
/** Returns true if the two sets of parameters are the same. */
|
|
BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(param_type, lhs, rhs)
|
|
{
|
|
return lhs._intervals == rhs._intervals
|
|
&& lhs._weights == rhs._weights;
|
|
}
|
|
/** Returns true if the two sets of parameters are different. */
|
|
BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(param_type)
|
|
|
|
private:
|
|
|
|
friend class piecewise_constant_distribution;
|
|
|
|
std::vector<RealType> _intervals;
|
|
std::vector<WeightType> _weights;
|
|
};
|
|
|
|
/**
|
|
* Creates a new @c piecewise_constant_distribution with
|
|
* a single interval, [0, 1).
|
|
*/
|
|
piecewise_constant_distribution()
|
|
{
|
|
_intervals.push_back(RealType(0));
|
|
_intervals.push_back(RealType(1));
|
|
}
|
|
/**
|
|
* Constructs a piecewise_constant_distribution from two iterator ranges
|
|
* containing the interval boundaries and the interval weights.
|
|
* If there are less than two boundaries, then this is equivalent to
|
|
* the default constructor and creates a single interval, [0, 1).
|
|
*
|
|
* The values of the interval boundaries must be strictly
|
|
* increasing, and the number of weights must be one less than
|
|
* the number of interval boundaries. If there are extra
|
|
* weights, they are ignored.
|
|
*
|
|
* For example,
|
|
*
|
|
* @code
|
|
* double intervals[] = { 0.0, 1.0, 4.0 };
|
|
* double weights[] = { 1.0, 1.0 };
|
|
* piecewise_constant_distribution<> dist(
|
|
* &intervals[0], &intervals[0] + 3, &weights[0]);
|
|
* @endcode
|
|
*
|
|
* The distribution has a 50% chance of producing a
|
|
* value between 0 and 1 and a 50% chance of producing
|
|
* a value between 1 and 4.
|
|
*/
|
|
template<class IntervalIter, class WeightIter>
|
|
piecewise_constant_distribution(IntervalIter first_interval,
|
|
IntervalIter last_interval,
|
|
WeightIter first_weight)
|
|
: _intervals(first_interval, last_interval)
|
|
{
|
|
if(_intervals.size() < 2) {
|
|
_intervals.clear();
|
|
_intervals.push_back(RealType(0));
|
|
_intervals.push_back(RealType(1));
|
|
} else {
|
|
std::vector<WeightType> actual_weights;
|
|
actual_weights.reserve(_intervals.size() - 1);
|
|
for(std::size_t i = 0; i < _intervals.size() - 1; ++i) {
|
|
actual_weights.push_back(*first_weight++);
|
|
}
|
|
typedef discrete_distribution<std::size_t, WeightType> bins_type;
|
|
typename bins_type::param_type bins_param(actual_weights);
|
|
_bins.param(bins_param);
|
|
}
|
|
}
|
|
#ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
|
|
/**
|
|
* Constructs a piecewise_constant_distribution from an
|
|
* initializer_list containing the interval boundaries
|
|
* and a unary function specifying the weights. Each
|
|
* weight is determined by calling the function at the
|
|
* midpoint of the corresponding interval.
|
|
*
|
|
* If the initializer_list contains less than two elements,
|
|
* this is equivalent to the default constructor and the
|
|
* distribution will produce values uniformly distributed
|
|
* in the range [0, 1).
|
|
*/
|
|
template<class T, class F>
|
|
piecewise_constant_distribution(std::initializer_list<T> il, F f)
|
|
: _intervals(il.begin(), il.end())
|
|
{
|
|
if(_intervals.size() < 2) {
|
|
_intervals.clear();
|
|
_intervals.push_back(RealType(0));
|
|
_intervals.push_back(RealType(1));
|
|
} else {
|
|
std::vector<WeightType> actual_weights;
|
|
actual_weights.reserve(_intervals.size() - 1);
|
|
for(std::size_t i = 0; i < _intervals.size() - 1; ++i) {
|
|
RealType midpoint = (_intervals[i] + _intervals[i + 1]) / 2;
|
|
actual_weights.push_back(f(midpoint));
|
|
}
|
|
typedef discrete_distribution<std::size_t, WeightType> bins_type;
|
|
typename bins_type::param_type bins_param(actual_weights);
|
|
_bins.param(bins_param);
|
|
}
|
|
}
|
|
#endif
|
|
/**
|
|
* Constructs a piecewise_constant_distribution from Boost.Range
|
|
* ranges holding the interval boundaries and the weights. If
|
|
* there are less than two interval boundaries, this is equivalent
|
|
* to the default constructor and the distribution will produce
|
|
* values uniformly distributed in the range [0, 1). The
|
|
* number of weights must be one less than the number of
|
|
* interval boundaries.
|
|
*/
|
|
template<class IntervalsRange, class WeightsRange>
|
|
piecewise_constant_distribution(const IntervalsRange& intervals_arg,
|
|
const WeightsRange& weights_arg)
|
|
: _bins(weights_arg),
|
|
_intervals(boost::begin(intervals_arg), boost::end(intervals_arg))
|
|
{
|
|
if(_intervals.size() < 2) {
|
|
_intervals.clear();
|
|
_intervals.push_back(RealType(0));
|
|
_intervals.push_back(RealType(1));
|
|
}
|
|
}
|
|
/**
|
|
* Constructs a piecewise_constant_distribution that approximates a
|
|
* function. The range of the distribution is [xmin, xmax). This
|
|
* range is divided into nw equally sized intervals and the weights
|
|
* are found by calling the unary function f on the midpoints of the
|
|
* intervals.
|
|
*/
|
|
template<class F>
|
|
piecewise_constant_distribution(std::size_t nw,
|
|
RealType xmin,
|
|
RealType xmax,
|
|
F f)
|
|
: _bins(nw, xmin, xmax, f)
|
|
{
|
|
if(nw == 0) { nw = 1; }
|
|
RealType delta = (xmax - xmin) / nw;
|
|
_intervals.reserve(nw + 1);
|
|
for(std::size_t i = 0; i < nw; ++i) {
|
|
_intervals.push_back(xmin + i * delta);
|
|
}
|
|
_intervals.push_back(xmax);
|
|
}
|
|
/**
|
|
* Constructs a piecewise_constant_distribution from its parameters.
|
|
*/
|
|
explicit piecewise_constant_distribution(const param_type& parm)
|
|
: _bins(parm._weights),
|
|
_intervals(parm._intervals)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Returns a value distributed according to the parameters of the
|
|
* piecewist_constant_distribution.
|
|
*/
|
|
template<class URNG>
|
|
RealType operator()(URNG& urng) const
|
|
{
|
|
std::size_t i = _bins(urng);
|
|
return uniform_real<RealType>(_intervals[i], _intervals[i+1])(urng);
|
|
}
|
|
|
|
/**
|
|
* Returns a value distributed according to the parameters
|
|
* specified by param.
|
|
*/
|
|
template<class URNG>
|
|
RealType operator()(URNG& urng, const param_type& parm) const
|
|
{
|
|
return piecewise_constant_distribution(parm)(urng);
|
|
}
|
|
|
|
/** Returns the smallest value that the distribution can produce. */
|
|
result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const
|
|
{ return _intervals.front(); }
|
|
/** Returns the largest value that the distribution can produce. */
|
|
result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const
|
|
{ return _intervals.back(); }
|
|
|
|
/**
|
|
* Returns a vector containing the probability density
|
|
* over each interval.
|
|
*/
|
|
std::vector<RealType> densities() const
|
|
{
|
|
std::vector<RealType> result(_bins.probabilities());
|
|
for(std::size_t i = 0; i < result.size(); ++i) {
|
|
result[i] /= (_intervals[i+1] - _intervals[i]);
|
|
}
|
|
return(result);
|
|
}
|
|
/** Returns a vector containing the interval boundaries. */
|
|
std::vector<RealType> intervals() const { return _intervals; }
|
|
|
|
/** Returns the parameters of the distribution. */
|
|
param_type param() const
|
|
{
|
|
return param_type(_intervals, _bins.probabilities());
|
|
}
|
|
/** Sets the parameters of the distribution. */
|
|
void param(const param_type& parm)
|
|
{
|
|
std::vector<RealType> new_intervals(parm._intervals);
|
|
typedef discrete_distribution<std::size_t, WeightType> bins_type;
|
|
typename bins_type::param_type bins_param(parm._weights);
|
|
_bins.param(bins_param);
|
|
_intervals.swap(new_intervals);
|
|
}
|
|
|
|
/**
|
|
* Effects: Subsequent uses of the distribution do not depend
|
|
* on values produced by any engine prior to invoking reset.
|
|
*/
|
|
void reset() { _bins.reset(); }
|
|
|
|
/** Writes a distribution to a @c std::ostream. */
|
|
BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(
|
|
os, piecewise_constant_distribution, pcd)
|
|
{
|
|
os << pcd.param();
|
|
return os;
|
|
}
|
|
|
|
/** Reads a distribution from a @c std::istream */
|
|
BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(
|
|
is, piecewise_constant_distribution, pcd)
|
|
{
|
|
param_type parm;
|
|
if(is >> parm) {
|
|
pcd.param(parm);
|
|
}
|
|
return is;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the two distributions will return the
|
|
* same sequence of values, when passed equal generators.
|
|
*/
|
|
BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(
|
|
piecewise_constant_distribution, lhs, rhs)
|
|
{
|
|
return lhs._bins == rhs._bins && lhs._intervals == rhs._intervals;
|
|
}
|
|
/**
|
|
* Returns true if the two distributions may return different
|
|
* sequences of values, when passed equal generators.
|
|
*/
|
|
BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(piecewise_constant_distribution)
|
|
|
|
private:
|
|
discrete_distribution<std::size_t, WeightType> _bins;
|
|
std::vector<RealType> _intervals;
|
|
};
|
|
|
|
}
|
|
}
|
|
|
|
#endif
|