// graph-tool -- a general graph modification and manipulation thingy // // Copyright (C) 2006-2020 Tiago de Paula Peixoto // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU Lesser General Public License as published by the Free // Software Foundation; either version 3 of the License, or (at your option) any // later version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS // FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more // details. // // You should have received a copy of the GNU Lesser General Public License // along with this program. If not, see . #ifndef SAMPLER_HH #define SAMPLER_HH #include "random.hh" #include namespace graph_tool { using namespace std; using namespace boost; // Discrete sampling via vose's alias method. // See http://www.keithschwarz.com/darts-dice-coins/ for a very clear // explanation. template class Sampler { public: Sampler(const vector& items, const vector& probs) : _items(items), _probs(probs), _alias(items.size()), _S(0) { for (size_t i = 0; i < _probs.size(); ++i) _S += _probs[i]; vector small; vector large; for (size_t i = 0; i < _probs.size(); ++i) { _probs[i] *= _probs.size() / _S; if (_probs[i] < 1) small.push_back(i); else large.push_back(i); } while (!(small.empty() || large.empty())) { size_t l = small.back(); size_t g = large.back(); small.pop_back(); large.pop_back(); _alias[l] = g; _probs[g] = (_probs[l] + _probs[g]) - 1; if (_probs[g] < 1) small.push_back(g); else large.push_back(g); } // fix numerical instability for (size_t i = 0; i < large.size(); ++i) _probs[large[i]] = 1; for (size_t i = 0; i < small.size(); ++i) _probs[small[i]] = 1; _sample = uniform_int_distribution(0, _probs.size() - 1); } Sampler() {} template const Value& sample(RNG& rng) { size_t i = _sample(rng); bernoulli_distribution coin(_probs[i]); if (coin(rng)) return _items[i]; else return _items[_alias[i]]; } size_t size() const { return _items.size(); } bool empty() const { return _S == 0; } bool has_n(size_t n) const { return (n == 0 || !empty()); } double prob_sum() const { return _S; } const Value& operator[](size_t i) const { return _items[i]; } const auto& items() const { return _items; } auto begin() const { return _items.begin(); } auto end() const { return _items.end(); } private: typedef typename mpl::if_&, vector >::type items_t; items_t _items; vector _probs; vector _alias; uniform_int_distribution _sample; double _S; }; // uniform sampling from containers template auto uniform_sample_iter(Iter begin, const Iter& end, RNG& rng) { auto N = std::distance(begin, end); std::uniform_int_distribution i_rand(0, N - 1); std::advance(begin, i_rand(rng)); return begin; } template auto uniform_sample_iter(Container& v, RNG& rng) { return uniform_sample_iter(v.begin(), v.end(), rng); } template typename std::iterator_traits::reference uniform_sample(const Iter& begin, const Iter& end, RNG& rng) { auto iter = uniform_sample_iter(begin, end, rng); return *iter; } template auto&& uniform_sample(Container& v, RNG& rng) { return *uniform_sample_iter(v, rng); } template typename boost::graph_traits::vertex_descriptor random_out_neighbor(typename boost::graph_traits::vertex_descriptor v, const Graph& g, RNG& rng) { auto iter = out_edges(v, g); return target(*uniform_sample_iter(iter.first, iter.second, rng), g); } template typename boost::graph_traits::vertex_descriptor random_in_neighbor(typename boost::graph_traits::vertex_descriptor v, const Graph& g, RNG& rng) { auto iter = in_edge_iteratorS::get_edges(v, g); return source(*uniform_sample_iter(iter.first, iter.second, rng), g); } } // namespace graph_tool #endif // SAMPLER_HH