| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531 | /* boost random/piecewise_linear_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_LINEAR_DISTRIBUTION_HPP_INCLUDED#define BOOST_RANDOM_PIECEWISE_LINEAR_DISTRIBUTION_HPP_INCLUDED#include <vector>#include <algorithm>#include <cmath>#include <cstdlib>#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_linear_distribution models a \random_distribution. */template<class RealType = double>class piecewise_linear_distribution {public:    typedef std::size_t input_type;    typedef RealType result_type;    class param_type {    public:        typedef piecewise_linear_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(RealType(1));            _weights.push_back(RealType(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 weights at the boundaries.         * If there are fewer than two boundaries, then this is equivalent to         * the default constructor and the distribution will produce values         * uniformly distributed in the range [0, 1).         *         * The values of the interval boundaries must be strictly         * increasing, and the number of weights must be the same as         * 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();                _weights.push_back(RealType(1));                _weights.push_back(RealType(1));                _intervals.push_back(RealType(0));                _intervals.push_back(RealType(1));            } else {                _weights.reserve(_intervals.size());                for(std::size_t i = 0; i < _intervals.size(); ++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 at the boundaries.  Each weight is         * determined by calling the function at the corresponding point.         *         * If the initializer_list contains fewer 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();                _weights.push_back(RealType(1));                _weights.push_back(RealType(1));                _intervals.push_back(RealType(0));                _intervals.push_back(RealType(1));            } else {                _weights.reserve(_intervals.size());                for(typename std::vector<RealType>::const_iterator                    iter = _intervals.begin(), end = _intervals.end();                    iter != end; ++iter)                {                    _weights.push_back(f(*iter));                }            }        }#endif        /**         * Constructs a @c param_type object from Boost.Range ranges holding         * the interval boundaries and the weights at the boundaries.  If         * there are fewer 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 equal to 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) {                _weights.clear();                _weights.push_back(RealType(1));                _weights.push_back(RealType(1));                _intervals.clear();                _intervals.push_back(RealType(0));                _intervals.push_back(RealType(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 boundaries 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));                _intervals.push_back(xmin + k*delta);            }            _weights.push_back(f(xmax));            _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         * at all the interval boundaries.         */        std::vector<RealType> densities() const        {            RealType sum = static_cast<RealType>(0);            for(std::size_t i = 0; i < _intervals.size() - 1; ++i) {                RealType width = _intervals[i + 1] - _intervals[i];                sum += (_weights[i] + _weights[i + 1]) * width / 2;            }            std::vector<RealType> result;            result.reserve(_weights.size());            for(typename std::vector<RealType>::const_iterator                iter = _weights.begin(), end = _weights.end();                iter != end; ++iter)            {                result.push_back(*iter / sum);            }            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<RealType> 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_linear_distribution;        std::vector<RealType> _intervals;        std::vector<RealType> _weights;    };    /**     * Creates a new @c piecewise_linear_distribution that     * produces values uniformly distributed in the range [0, 1).     */    piecewise_linear_distribution()    {        default_init();    }    /**     * Constructs a piecewise_linear_distribution from two iterator ranges     * containing the interval boundaries and the weights at the boundaries.     * If there are fewer than two boundaries, then this is equivalent to     * the default constructor and creates a distribution that     * produces values uniformly distributed in the range [0, 1).     *     * The values of the interval boundaries must be strictly     * increasing, and the number of weights must be equal to     * the number of interval boundaries.  If there are extra     * weights, they are ignored.     *     * For example,     *     * @code     * double intervals[] = { 0.0, 1.0, 2.0 };     * double weights[] = { 0.0, 1.0, 0.0 };     * piecewise_constant_distribution<> dist(     *     &intervals[0], &intervals[0] + 3, &weights[0]);     * @endcode     *     * produces a triangle distribution.     */    template<class IntervalIter, class WeightIter>    piecewise_linear_distribution(IntervalIter first_interval,                                  IntervalIter last_interval,                                  WeightIter first_weight)      : _intervals(first_interval, last_interval)    {        if(_intervals.size() < 2) {            default_init();        } else {            _weights.reserve(_intervals.size());            for(std::size_t i = 0; i < _intervals.size(); ++i) {                _weights.push_back(*first_weight++);            }            init();        }    }#ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST    /**     * Constructs a piecewise_linear_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     * corresponding interval boundary.     *     * If the initializer_list contains fewer 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_linear_distribution(std::initializer_list<T> il, F f)      : _intervals(il.begin(), il.end())    {        if(_intervals.size() < 2) {            default_init();        } else {            _weights.reserve(_intervals.size());            for(typename std::vector<RealType>::const_iterator                iter = _intervals.begin(), end = _intervals.end();                iter != end; ++iter)            {                _weights.push_back(f(*iter));            }            init();        }    }#endif    /**     * Constructs a piecewise_linear_distribution from Boost.Range     * ranges holding the interval boundaries and the weights.  If     * there are fewer 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 equal to the number of     * interval boundaries.     */    template<class IntervalsRange, class WeightsRange>    piecewise_linear_distribution(const IntervalsRange& intervals_arg,                                  const WeightsRange& weights_arg)      : _intervals(boost::begin(intervals_arg), boost::end(intervals_arg)),        _weights(boost::begin(weights_arg), boost::end(weights_arg))    {        if(_intervals.size() < 2) {            default_init();        } else {            init();        }    }    /**     * Constructs a piecewise_linear_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 interval boundaries.     */    template<class F>    piecewise_linear_distribution(std::size_t nw,                                  RealType xmin,                                  RealType xmax,                                  F f)    {        if(nw == 0) { nw = 1; }        RealType delta = (xmax - xmin) / nw;        _intervals.reserve(nw + 1);        for(std::size_t i = 0; i < nw; ++i) {            RealType x = xmin + i * delta;            _intervals.push_back(x);            _weights.push_back(f(x));        }        _intervals.push_back(xmax);        _weights.push_back(f(xmax));        init();    }    /**     * Constructs a piecewise_linear_distribution from its parameters.     */    explicit piecewise_linear_distribution(const param_type& parm)      : _intervals(parm._intervals),        _weights(parm._weights)    {        init();    }    /**     * Returns a value distributed according to the parameters of the     * piecewise_linear_distribution.     */    template<class URNG>    RealType operator()(URNG& urng) const    {        std::size_t i = _bins(urng);        bool is_in_rectangle = (i % 2 == 0);        i = i / 2;        uniform_real<RealType> dist(_intervals[i], _intervals[i+1]);        if(is_in_rectangle) {            return dist(urng);        } else if(_weights[i] < _weights[i+1]) {            return (std::max)(dist(urng), dist(urng));        } else {            return (std::min)(dist(urng), dist(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_linear_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 densities     * at the interval boundaries.     */    std::vector<RealType> densities() const    {        RealType sum = static_cast<RealType>(0);        for(std::size_t i = 0; i < _intervals.size() - 1; ++i) {            RealType width = _intervals[i + 1] - _intervals[i];            sum += (_weights[i] + _weights[i + 1]) * width / 2;        }        std::vector<RealType> result;        result.reserve(_weights.size());        for(typename std::vector<RealType>::const_iterator            iter = _weights.begin(), end = _weights.end();            iter != end; ++iter)        {            result.push_back(*iter / sum);        }        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, _weights);    }    /** Sets the parameters of the distribution. */    void param(const param_type& parm)    {        std::vector<RealType> new_intervals(parm._intervals);        std::vector<RealType> new_weights(parm._weights);        init(new_intervals, new_weights);        _intervals.swap(new_intervals);        _weights.swap(new_weights);    }        /**     * 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_linear_distribution, pld)    {        os << pld.param();        return os;    }    /** Reads a distribution from a @c std::istream */    BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(        is, piecewise_linear_distribution, pld)    {        param_type parm;        if(is >> parm) {            pld.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_linear_distribution, lhs,  rhs)    {        return lhs._intervals == rhs._intervals && lhs._weights == rhs._weights;    }    /**     * Returns true if the two distributions may return different     * sequences of values, when passed equal generators.     */    BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(piecewise_linear_distribution)private:    /// @cond \show_private    void init(const std::vector<RealType>& intervals_arg,              const std::vector<RealType>& weights_arg)    {        using std::abs;        std::vector<RealType> bin_weights;        bin_weights.reserve((intervals_arg.size() - 1) * 2);        for(std::size_t i = 0; i < intervals_arg.size() - 1; ++i) {            RealType width = intervals_arg[i + 1] - intervals_arg[i];            RealType w1 = weights_arg[i];            RealType w2 = weights_arg[i + 1];            bin_weights.push_back((std::min)(w1, w2) * width);            bin_weights.push_back(abs(w1 - w2) * width / 2);        }        typedef discrete_distribution<std::size_t, RealType> bins_type;        typename bins_type::param_type bins_param(bin_weights);        _bins.param(bins_param);    }    void init()    {        init(_intervals, _weights);    }    void default_init()    {        _intervals.clear();        _intervals.push_back(RealType(0));        _intervals.push_back(RealType(1));        _weights.clear();        _weights.push_back(RealType(1));        _weights.push_back(RealType(1));        init();    }    discrete_distribution<std::size_t, RealType> _bins;    std::vector<RealType> _intervals;    std::vector<RealType> _weights;    /// @endcond};}}#endif
 |