sample.hpp
Go to the documentation of this file.
1 // Copyright 2024 Ekumen, Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef BELUGA_VIEWS_SAMPLE_HPP
16 #define BELUGA_VIEWS_SAMPLE_HPP
17 
18 #include <random>
19 
20 #include <range/v3/utility/random.hpp>
21 #include <range/v3/view/common.hpp>
22 #include <range/v3/view/generate.hpp>
23 
26 
32 namespace beluga::views {
33 
34 namespace detail {
35 
37 
46 template <class Range, class Distribution, class URNG = typename ranges::detail::default_random_engine>
47 struct sample_view : public ranges::view_facade<sample_view<Range, Distribution, URNG>, ranges::infinite> {
48  public:
50  sample_view() = default;
51 
53 
58  constexpr sample_view(Range range, Distribution distribution, URNG& engine = ranges::detail::get_random_engine())
59  : range_{std::move(range)}, distribution_{std::move(distribution)}, engine_{std::addressof(engine)} {
60  assert(ranges::size(range) > 0);
61  assert(distribution_.min() == 0);
62  assert(distribution_.max() == static_cast<typename Distribution::result_type>(ranges::size(range_)) - 1);
63  }
64 
65  private:
66  // `ranges::range_access` needs access to the cursor members.
67  friend ranges::range_access;
68 
69  static_assert(ranges::sized_range<Range>);
70  static_assert(ranges::random_access_range<Range>);
71  static_assert(std::is_same_v<typename Distribution::result_type, ranges::range_difference_t<Range>>);
72 
74  struct cursor {
75  public:
77  cursor() = default;
78 
80  constexpr explicit cursor(sample_view* view)
81  : view_(view), first_{ranges::begin(view_->range_)}, it_{first_ + view_->compute_offset()} {}
82 
84  [[nodiscard]] constexpr decltype(auto) read() const noexcept(noexcept(*this->it_)) { return *it_; }
85 
87  constexpr void next() { it_ = first_ + view_->compute_offset(); }
88 
89  private:
91  ranges::iterator_t<Range> first_;
92  ranges::iterator_t<Range> it_;
93  };
94 
96  [[nodiscard]] constexpr auto begin_cursor() { return cursor{this}; }
97 
99  [[nodiscard]] constexpr auto end_cursor() const noexcept { return ranges::unreachable_sentinel_t{}; }
100 
102  [[nodiscard]] constexpr auto compute_offset() { return distribution_(*engine_); }
103 
104  Range range_;
105  Distribution distribution_;
106  URNG* engine_;
107 };
108 
110 
111 template <class T, class Enable = void>
112 struct is_random_distribution : public std::false_type {};
113 
114 template <class T>
115 struct is_random_distribution<T, std::void_t<decltype(std::declval<T&>()(std::declval<std::mt19937&>()))>>
116  : std::true_type {};
117 
118 template <class T>
119 inline constexpr bool is_random_distribution_v = is_random_distribution<T>::value;
120 
122 
125  protected:
127  template <class Range, class Weights, class URNG>
128  constexpr auto sample_from_range(Range&& range, Weights&& weights, URNG& engine) const {
129  static_assert(ranges::sized_range<Range>);
130  static_assert(ranges::random_access_range<Range>);
131  static_assert(ranges::input_range<Weights>);
132  using result_type = ranges::range_difference_t<Range>;
133  auto w = ranges::views::common(weights);
134  auto distribution = std::discrete_distribution<result_type>{ranges::begin(w), ranges::end(w)};
135  return sample_view{ranges::views::all(std::forward<Range>(range)), std::move(distribution), engine};
136  }
137 
139 
146  template <class Range, class URNG>
147  constexpr auto sample_from_range(Range&& range, URNG& engine) const {
148  static_assert(ranges::sized_range<Range>);
149  static_assert(ranges::random_access_range<Range>);
150  if constexpr (beluga::is_particle_range_v<Range>) {
151  return sample_from_range(beluga::views::states(range), beluga::views::weights(range), engine) |
152  ranges::views::transform(beluga::make_from_state<ranges::range_value_t<Range>>);
153  } else {
154  using result_type = ranges::range_difference_t<Range>;
155  auto distribution =
156  std::uniform_int_distribution<result_type>{0, static_cast<result_type>(ranges::size(range) - 1)};
157  return sample_view{ranges::views::all(std::forward<Range>(range)), std::move(distribution), engine};
158  }
159  }
160 
162  template <class Distribution, class URNG>
163  constexpr auto sample_from_distribution(Distribution distribution, URNG& engine) const {
164  return ranges::views::generate(
165  [distribution = std::move(distribution), &engine]() mutable { return distribution(engine); });
166  }
167 };
168 
170 struct sample_fn : public sample_base_fn {
172  template <class T, class U, class V>
173  constexpr auto operator()(T&& t, U&& u, V& v) const {
174  static_assert(ranges::range<T>);
175  static_assert(ranges::range<U>);
176  return sample_from_range(std::forward<T>(t), std::forward<U>(u), v); // Assume V is a URNG
177  }
178 
180  template <class T, class U>
181  constexpr auto operator()(T&& t, U&& u) const {
182  if constexpr (ranges::range<T> && ranges::range<U>) {
183  auto& engine = ranges::detail::get_random_engine();
184  return sample_from_range(std::forward<T>(t), std::forward<U>(u), engine);
185  } else if constexpr (is_random_distribution_v<T>) {
186  static_assert(std::is_lvalue_reference_v<U&&>); // Assume U is a URNG
187  return sample_from_distribution(std::forward<T>(t), u);
188  } else {
189  static_assert(ranges::range<T>);
190  static_assert(std::is_lvalue_reference_v<U&&>); // Assume U is a URNG
191  return sample_from_range(std::forward<T>(t), u);
192  }
193  }
194 
196  template <class T>
197  constexpr auto operator()(T&& t) const {
198  if constexpr (ranges::range<T>) {
199  auto& engine = ranges::detail::get_random_engine();
200  return sample_from_range(std::forward<T>(t), engine);
201  } else if constexpr (is_random_distribution_v<T>) {
202  auto& engine = ranges::detail::get_random_engine();
203  return sample_from_distribution(std::forward<T>(t), engine);
204  } else {
205  static_assert(std::is_lvalue_reference_v<T&&>); // Assume T is a URNG
206  return ranges::make_view_closure(ranges::bind_back(sample_fn{}, std::ref(t)));
207  }
208  }
209 
211  template <class Range, class URNG>
212  constexpr auto operator()(Range&& range, std::reference_wrapper<URNG> engine) const {
213  static_assert(ranges::range<Range>);
214  return sample_from_range(std::forward<Range>(range), engine.get());
215  }
216 };
217 
218 } // namespace detail
219 
222 
240 inline constexpr ranges::views::view_closure<detail::sample_fn> sample;
241 
242 } // namespace beluga::views
243 
244 #endif
beluga::views
Definition: elements.hpp:27
beluga::views::detail::sample_view::compute_offset
constexpr auto compute_offset()
Return a new offset value to advance the current iterator.
Definition: sample.hpp:102
beluga::views::detail::sample_fn
Implementation detail for a sample range adaptor object.
Definition: sample.hpp:170
beluga::views::detail::sample_view::cursor::cursor
constexpr cursor(sample_view *view)
Construct a cursor from the parent view elements.
Definition: sample.hpp:80
beluga::views::detail::sample_view::range_
Range range_
Definition: sample.hpp:104
beluga::views::detail::sample_base_fn::sample_from_distribution
constexpr auto sample_from_distribution(Distribution distribution, URNG &engine) const
Sample from random distributions.
Definition: sample.hpp:163
particles.hpp
Implementation of views related to particle ranges.
beluga::views::detail::sample_view::cursor::first_
ranges::iterator_t< Range > first_
Definition: sample.hpp:91
beluga::views::detail::sample_view::cursor::read
constexpr decltype(auto) read() const noexcept(noexcept(*this->it_))
Access the current iterator.
Definition: sample.hpp:84
beluga::views::detail::sample_view::cursor::view_
sample_view * view_
Definition: sample.hpp:90
beluga::views::detail::sample_view::cursor::cursor
cursor()=default
Default constructor.
beluga::views::detail::sample_fn::operator()
constexpr auto operator()(Range &&range, std::reference_wrapper< URNG > engine) const
Overload that unwraps the engine reference from a view closure.
Definition: sample.hpp:212
beluga::make_from_state
constexpr detail::make_from_state_fn< Particle > make_from_state
A function object to create a particle from a given state.
Definition: particle_traits.hpp:120
beluga::views::detail::sample_view::cursor::next
constexpr void next()
Position the current iterator.
Definition: sample.hpp:87
particle_traits.hpp
Implementation of traits for particle types, see the Particle named requirements.
beluga::views::detail::sample_base_fn::sample_from_range
constexpr auto sample_from_range(Range &&range, Weights &&weights, URNG &engine) const
Sample from weighted ranges.
Definition: sample.hpp:128
beluga::views::detail::sample_view::cursor
Cursor class that handles the iteration logic.
Definition: sample.hpp:74
beluga::views::detail::sample_view::sample_view
sample_view()=default
Default constructor.
beluga::views::detail::sample_view::engine_
URNG * engine_
Definition: sample.hpp:106
beluga::views::detail::sample_base_fn
Implementation detail for a sample algorithm.
Definition: sample.hpp:124
beluga::views::detail::sample_view::distribution_
Distribution distribution_
Definition: sample.hpp:105
beluga::views::detail::sample_base_fn::sample_from_range
constexpr auto sample_from_range(Range &&range, URNG &engine) const
Sample from any range.
Definition: sample.hpp:147
beluga::views::detail::sample_fn::operator()
constexpr auto operator()(T &&t) const
Overload that takes one argument.
Definition: sample.hpp:197
beluga::views::detail::sample_view
Implementation of the sample view.
Definition: sample.hpp:47
std
Definition: circular_array.hpp:529
beluga::views::detail::sample_view::begin_cursor
constexpr auto begin_cursor()
Return the cursor for the begin iterator.
Definition: sample.hpp:96
beluga::views::weights
constexpr auto weights
Definition: particles.hpp:34
beluga::views::detail::sample_view::sample_view
constexpr sample_view(Range range, Distribution distribution, URNG &engine=ranges::detail::get_random_engine())
Construct the view from an existing range.
Definition: sample.hpp:58
beluga::views::detail::sample_fn::operator()
constexpr auto operator()(T &&t, U &&u) const
Overload that takes two arguments.
Definition: sample.hpp:181
beluga::views::detail::sample_view::cursor::it_
ranges::iterator_t< Range > it_
Definition: sample.hpp:92
beluga::views::detail::sample_fn::operator()
constexpr auto operator()(T &&t, U &&u, V &v) const
Overload that takes three arguments.
Definition: sample.hpp:173
beluga::views::detail::sample_view::end_cursor
constexpr auto end_cursor() const noexcept
Return an unreachable sentinel since this is an infinite range.
Definition: sample.hpp:99
beluga::views::states
constexpr auto states
Definition: particles.hpp:30
beluga::views::sample
constexpr ranges::views::view_closure< detail::sample_fn > sample
Definition: sample.hpp:240


beluga
Author(s):
autogenerated on Tue Jul 16 2024 02:59:53