Lumiera  0.pre.03
»edit your freedom«
random-draw.hpp
Go to the documentation of this file.
1 /*
2  RANDOM-DRAW.hpp - randomly pick limited values
3 
4  Copyright (C)
5  2023, Hermann Vosseler <Ichthyostega@web.de>
6 
7   **Lumiera** is free software; you can redistribute it and/or modify it
8   under the terms of the GNU General Public License as published by the
9   Free Software Foundation; either version 2 of the License, or (at your
10   option) any later version. See the file COPYING for further details.
11 
12 */
13 
85 #ifndef LIB_RANDOM_DRAW_H
86 #define LIB_RANDOM_DRAW_H
87 
88 
89 #include "lib/error.h"
90 #include "lib/random.hpp"
91 #include "lib/lazy-init.hpp"
92 #include "lib/meta/function.hpp"
94 #include "lib/util-quant.hpp"
95 #include "lib/util.hpp"
96 
97 #include <functional>
98 #include <utility>
99 
100 
101 namespace lib {
102  namespace err = lumiera::error;
103 
105  using lib::meta::_Fun;
106  using std::function;
107  using std::forward;
108  using std::move;
109 
110 
111 
119  template<typename T, T max, T min =T(0), T zero =min>
120  struct Limited
121  {
122  static_assert (min < max);
123  static_assert (min <= zero and zero < max);
124 
125  static constexpr T maxVal() { return max; }
126  static constexpr T minVal() { return min; }
127  static constexpr T zeroVal(){ return zero;}
128 
129  T val;
130 
131  template<typename X>
132  Limited (X raw)
133  : val(util::limited (X(minVal()), raw, X(maxVal())))
134  { }
135 
136  operator T&()
137  {
138  return val;
139  }
140  operator T const&() const
141  {
142  return val;
143  }
144  };
145 
146 
147 
148  namespace random_draw { // Policy definitions
149 
154  template<uint max>
156  : function<Limited<uint, max>(void)>
157  {
158  static double defaultSrc() { return lib::defaultGen.uni(); }
159  };
160 
161  }//(End)Policy definitions
162 
163 
164 
165 
166  /**********************************************************/
173  template<class POL>
175  : public LazyInit<POL>
176  {
177  using Lazy = LazyInit<POL>;
178  using Disabled = typename Lazy::MarkDisabled;
179 
180  using Sig = typename _Fun<POL>::Sig;
181  using Fun = function<Sig>;
182  using Tar = typename _Fun<POL>::Ret;
183 
184  Tar maxResult_{Tar::maxVal()};
185  Tar minResult_{Tar::minVal()};
186  double probability_{0};
187  size_t shuffle_{0};
188 
189 
191  Tar
192  limited (double val)
193  {
194  if (probability_ == 0.0)
195  return Tar::zeroVal();
196  //
197  REQUIRE (Tar::minVal() <= minResult_);
198  REQUIRE (Tar::maxVal() >= maxResult_);
199  REQUIRE (minResult_ < maxResult_);
200  REQUIRE (0.0 <= probability_);
201  REQUIRE (probability_ <= 1.0);
202  double q = (1.0 - probability_);
203  if (val < q) // control probability of values ≠ neutral
204  return Tar::zeroVal();
205  if (val > 1.0)
206  val = 1.0;
207  val -= q; // [0 .. [q .. 1[
208  val /= probability_; // [0 .. 1[
209  auto org = Tar::zeroVal();
210  if (org == minResult_)
211  { // simple standard case
212  val *= maxResult_ - org; // [0 .. m[
213  val += org+1; // [1 .. m]
214  val += CAP_EPSILON; // round down yet absorb dust
215  return Tar{floor (val)};
216  }
217  else
218  if (org < minResult_ or org > maxResult_)
219  { // disjoint form origin, but compact
220  org = minResult_; // ensure all values covered
221  val *= maxResult_ - org + 1; // [o .. m]
222  val += org;
223  val += CAP_EPSILON;
224  return Tar{floor (val)};
225  }
226  else// Origin is somewhere within value range
227  {// ==> wrap "negative" part above max
228  // to map 0.0 ⟼ org (≙neutral)
229  val *= maxResult_ - minResult_;
230  val += org+1; // max inclusive but <0 ⟼ org
231  if (val >= maxResult_+1) // wrap the "negatives"
232  val -= maxResult_+1 - minResult_;
233  val += CAP_EPSILON;
234  return Tar{floor (val)};
235  }
236  } //----headroom to accommodate low probabilities
237  static size_t constexpr QUANTISER = 1 << 4 + util::ilog2 (Tar::maxVal()-Tar::minVal());
238  static double constexpr CAP_EPSILON = 1/(2.0 * QUANTISER);
239 
240 
242  double
243  asRand (size_t hash)
244  {
245  if (shuffle_)
246  hash *= shuffle_++;
247  return double(hash % QUANTISER) / QUANTISER;
248  }
249 
251  Tar
252  drawLimited (size_t hash)
253  {
254  return limited (asRand (hash));
255  }
256 
257 
258  public:
263  : Lazy{Disabled()}
264  {
265  mapping (POL::defaultSrc);
266  }
267 
277  template<class FUN, typename =disable_if_self<RandomDraw, FUN>>
278  RandomDraw(FUN&& fun)
279  : Lazy{Disabled()}
280  , probability_{1.0}
281  {
282  mapping (forward<FUN> (fun));
283  }
284 
285 
286 
287  /* ===== Builder API ===== */
288 
289  RandomDraw&&
290  probability (double p)
291  {
292  probability_ = util::limited (0.0, p ,1.0);
293  return move (*this);
294  }
295 
296  RandomDraw&&
297  maxVal (Tar m)
298  {
299  maxResult_ = util::min (m, Tar::maxVal());
300  if (minResult_>=maxResult_)
301  minVal (--m);
302  return move (*this);
303  }
304 
305  RandomDraw&&
306  minVal (Tar m)
307  {
308  minResult_ = util::max (m, Tar::minVal());
309  if (maxResult_<=minResult_)
310  maxVal (++m);
311  return move (*this);
312  }
313 
314  RandomDraw&&
315  shuffle (size_t seed =55)
316  {
317  shuffle_ = seed;
318  return move (*this);
319  }
320 
321  RandomDraw&&
322  fixedVal (Tar v)
323  {
324  mapping ([v](size_t){ return v; });
325  return move (*this);
326  }
327 
328  template<class FUN>
329  RandomDraw&&
330  mapping (FUN&& fun)
331  {
332  Fun& thisMapping = static_cast<Fun&> (*this);
333  Lazy::installInitialiser (thisMapping
334  ,[theFun = forward<FUN> (fun)]
335  (RandomDraw* self)
336  { // when lazy init is performed....
337  self->installAdapted (theFun);
338  });
339  return move (*this);
340  }
341 
342 
343 
344 
345  private:
347  template<class FUN>
348  void
349  installAdapted (FUN&& fun)
350  {
351  Fun& thisMapping = static_cast<Fun&> (*this);
352  thisMapping = adaptOut(adaptIn(std::forward<FUN> (fun)));
353  }
354 
355 
359  template<class FUN>
360  auto
361  adaptIn (FUN&& fun)
362  {
363  using lib::meta::func::applyFirst;
364  using _Fun = lib::meta::_Fun<FUN>;
365  static_assert (_Fun(), "Need something function-like.");
366 
367  using Sig = typename _Fun::Sig;
368  using Args = typename _Fun::Args;
369  using BaseIn = typename lib::meta::_Fun<POL>::Args;
370 
371  if constexpr (std::is_same_v<Args, BaseIn>)
372  // function accepts same arguments as this RandomDraw
373  return forward<FUN> (fun); // pass-through directly
374  else
375  {// attempt to find a custom adaptor via Policy template
376  using Adaptor = typename POL::template Adaptor<Sig>;
377  return Adaptor::build (forward<FUN> (fun));
378  }
379  }
380 
390  template<class FUN>
391  auto
392  adaptOut (FUN&& fun)
393  {
394  static_assert (lib::meta::_Fun<FUN>(), "Need something function-like.");
395 
396  using Res = typename lib::meta::_Fun<FUN>::Ret;
397  using lib::meta::func::chained;
398  using lib::meta::_FunRet;
399 
400  if constexpr (std::is_same_v<Res, Tar>)// ◁──────────────────────────┨ function produces result directly
401  return std::forward<FUN>(fun);
402  else
403  if constexpr (std::is_same_v<Res, size_t>)// ◁───────────────────────┨ function yields random source to draw value
404  return chained (std::forward<FUN>(fun)
405  ,[this](size_t hash){ return drawLimited(hash); }
406  );
407  else
408  if constexpr (std::is_same_v<Res, double>)// ◁───────────────────────┨ function yields mapping value to be quantised
409  return chained (std::forward<FUN>(fun)
410  ,[this](double rand){ return limited(rand); }
411  );
412  else
413  if constexpr (std::is_same_v<Res, RandomDraw>) // ◁───────────────────┨ RandomDraw with dynamically adjusted parameters
414  return [functor=std::forward<FUN>(fun)]
415  (auto&& ...inArgs) -> _FunRet<RandomDraw>
416  { // invoke manipulator with copy
417  RandomDraw adaptedDraw = functor(inArgs...);
418  return adaptedDraw (forward<decltype(inArgs)> (inArgs)...);
419  }; // forward arguments to mapping-fun
420  else
421  static_assert (not sizeof(Res), "unable to adapt / handle result type");
422  NOTREACHED("Handle based on return type");
423  }
424  };
425 
426 
427 } // namespace lib
428 #endif /*LIB_RANDOM_DRAW_H*/
RandomDraw(FUN &&fun)
Build a RandomDraw by attaching a value-processing function, which is adapted to accept the nominal i...
Mix-in for lazy/delayed initialisation of an embedded functor.
Definition: lazy-init.hpp:206
double asRand(size_t hash)
Tar drawLimited(size_t hash)
Helper for uniform access to function signature types.
Definition: function.hpp:99
Partial function application and building a complete function closure.
A Result Value confined into fixed bounds.
Lumiera error handling (C interface).
Implementation namespace for support and library code.
Building block to allow delayed initialisation of infrastructure tied to a functor.
Default policy for RandomDraw: generate limted-range random numbers.
Metaprogramming tools for transforming functor types.
A component and builder to draw limited parameter values based on some source of randomness (or hash ...
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
auto adaptIn(FUN &&fun)
Utilities for quantisation (grid alignment) and comparisons.
Tar limited(double val)
Generating (pseudo) random numbers with controlled seed.
double uni()
random double drawn from interval [0.0 ... 1.0[
Definition: random.hpp:232
Random defaultGen
a global default RandomSequencer for mundane purposes
Definition: random.cpp:70
disable_if< std::is_same< std::remove_cv_t< std::remove_reference_t< extractFirst_t< ARGS... > >>, SELF > > disable_if_self
helper to prevent a template constructor from shadowing inherited copy ctors
Definition: meta/util.hpp:151
RandomDraw()
Drawing is disabled by default, always yielding "zero".
typename _Fun< FUN >::Ret _FunRet
abbreviation for referring to a function&#39;s return type
Definition: function.hpp:187
void installAdapted(FUN &&fun)
auto adaptOut(FUN &&fun)