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) Lumiera.org
5  2023, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 */
22 
94 #ifndef LIB_RANDOM_DRAW_H
95 #define LIB_RANDOM_DRAW_H
96 
97 
98 #include "lib/error.h"
99 #include "lib/lazy-init.hpp"
100 #include "lib/meta/function.hpp"
102 #include "lib/util-quant.hpp"
103 #include "lib/util.hpp"
104 
105 #include <functional>
106 #include <utility>
107 
108 
109 namespace lib {
110  namespace err = lumiera::error;
111 
113  using lib::meta::_Fun;
114  using std::function;
115  using std::forward;
116  using std::move;
117 
118 
119 
127  template<typename T, T max, T min =T(0), T zero =min>
128  struct Limited
129  {
130  static_assert (min < max);
131  static_assert (min <= zero and zero < max);
132 
133  static constexpr T maxVal() { return max; }
134  static constexpr T minVal() { return min; }
135  static constexpr T zeroVal(){ return zero;}
136 
137  T val;
138 
139  template<typename X>
140  Limited (X raw)
141  : val(util::limited (X(minVal()), raw, X(maxVal())))
142  { }
143 
144  operator T&()
145  {
146  return val;
147  }
148  operator T const&() const
149  {
150  return val;
151  }
152  };
153 
154 
155 
156  namespace random_draw { // Policy definitions
157 
162  template<uint max>
164  : function<Limited<uint, max>(void)>
165  {
166  static double defaultSrc() { return rand()/double(RAND_MAX); }
167  };
168 
169  }//(End)Policy definitions
170 
171 
172 
173 
174  /**********************************************************/
181  template<class POL>
183  : public LazyInit<POL>
184  {
185  using Lazy = LazyInit<POL>;
186  using Disabled = typename Lazy::MarkDisabled;
187 
188  using Sig = typename _Fun<POL>::Sig;
189  using Fun = function<Sig>;
190  using Tar = typename _Fun<POL>::Ret;
191 
192  Tar maxResult_{Tar::maxVal()};
193  Tar minResult_{Tar::minVal()};
194  double probability_{0};
195  size_t shuffle_{0};
196 
197 
199  Tar
200  limited (double val)
201  {
202  if (probability_ == 0.0)
203  return Tar::zeroVal();
204  //
205  REQUIRE (Tar::minVal() <= minResult_);
206  REQUIRE (Tar::maxVal() >= maxResult_);
207  REQUIRE (minResult_ < maxResult_);
208  REQUIRE (0.0 <= probability_);
209  REQUIRE (probability_ <= 1.0);
210  double q = (1.0 - probability_);
211  if (val < q) // control probability of values ≠ neutral
212  return Tar::zeroVal();
213  if (val > 1.0)
214  val = 1.0;
215  val -= q; // [0 .. [q .. 1[
216  val /= probability_; // [0 .. 1[
217  auto org = Tar::zeroVal();
218  if (org == minResult_)
219  { // simple standard case
220  val *= maxResult_ - org; // [0 .. m[
221  val += org+1; // [1 .. m]
222  val += CAP_EPSILON; // round down yet absorb dust
223  return Tar{floor (val)};
224  }
225  else
226  if (org < minResult_ or org > maxResult_)
227  { // disjoint form origin, but compact
228  org = minResult_; // ensure all values covered
229  val *= maxResult_ - org + 1; // [o .. m]
230  val += org;
231  val += CAP_EPSILON;
232  return Tar{floor (val)};
233  }
234  else// Origin is somewhere within value range
235  {// ==> wrap "negative" part above max
236  // to map 0.0 ⟼ org (≙neutral)
237  val *= maxResult_ - minResult_;
238  val += org+1; // max inclusive but <0 ⟼ org
239  if (val >= maxResult_+1) // wrap the "negatives"
240  val -= maxResult_+1 - minResult_;
241  val += CAP_EPSILON;
242  return Tar{floor (val)};
243  }
244  } //----headroom to accommodate low probabilities
245  static size_t constexpr QUANTISER = 1 << 4 + util::ilog2 (Tar::maxVal()-Tar::minVal());
246  static double constexpr CAP_EPSILON = 1/(2.0 * QUANTISER);
247 
248 
250  double
251  asRand (size_t hash)
252  {
253  if (shuffle_)
254  hash *= shuffle_++;
255  return double(hash % QUANTISER) / QUANTISER;
256  }
257 
259  Tar
260  drawLimited (size_t hash)
261  {
262  return limited (asRand (hash));
263  }
264 
265 
266  public:
271  : Lazy{Disabled()}
272  {
273  mapping (POL::defaultSrc);
274  }
275 
285  template<class FUN, typename =disable_if_self<RandomDraw, FUN>>
286  RandomDraw(FUN&& fun)
287  : Lazy{Disabled()}
288  , probability_{1.0}
289  {
290  mapping (forward<FUN> (fun));
291  }
292 
293 
294 
295  /* ===== Builder API ===== */
296 
297  RandomDraw&&
298  probability (double p)
299  {
300  probability_ = util::limited (0.0, p ,1.0);
301  return move (*this);
302  }
303 
304  RandomDraw&&
305  maxVal (Tar m)
306  {
307  maxResult_ = util::min (m, Tar::maxVal());
308  if (minResult_>=maxResult_)
309  minVal (--m);
310  return move (*this);
311  }
312 
313  RandomDraw&&
314  minVal (Tar m)
315  {
316  minResult_ = util::max (m, Tar::minVal());
317  if (maxResult_<=minResult_)
318  maxVal (++m);
319  return move (*this);
320  }
321 
322  RandomDraw&&
323  shuffle (size_t seed =55)
324  {
325  shuffle_ = seed;
326  return move (*this);
327  }
328 
329  RandomDraw&&
330  fixedVal (Tar v)
331  {
332  mapping ([v](size_t){ return v; });
333  return move (*this);
334  }
335 
336  template<class FUN>
337  RandomDraw&&
338  mapping (FUN&& fun)
339  {
340  Fun& thisMapping = static_cast<Fun&> (*this);
341  Lazy::installInitialiser (thisMapping
342  ,[theFun = forward<FUN> (fun)]
343  (RandomDraw* self)
344  { // when lazy init is performed....
345  self->installAdapted (theFun);
346  });
347  return move (*this);
348  }
349 
350 
351 
352 
353  private:
355  template<class FUN>
356  void
357  installAdapted (FUN&& fun)
358  {
359  Fun& thisMapping = static_cast<Fun&> (*this);
360  thisMapping = adaptOut(adaptIn(std::forward<FUN> (fun)));
361  }
362 
363 
367  template<class FUN>
368  auto
369  adaptIn (FUN&& fun)
370  {
371  using lib::meta::func::applyFirst;
372  using _Fun = lib::meta::_Fun<FUN>;
373  static_assert (_Fun(), "Need something function-like.");
374 
375  using Sig = typename _Fun::Sig;
376  using Args = typename _Fun::Args;
377  using BaseIn = typename lib::meta::_Fun<POL>::Args;
378 
379  if constexpr (std::is_same_v<Args, BaseIn>)
380  // function accepts same arguments as this RandomDraw
381  return forward<FUN> (fun); // pass-through directly
382  else
383  {// attempt to find a custom adaptor via Policy template
384  using Adaptor = typename POL::template Adaptor<Sig>;
385  return Adaptor::build (forward<FUN> (fun));
386  }
387  }
388 
398  template<class FUN>
399  auto
400  adaptOut (FUN&& fun)
401  {
402  static_assert (lib::meta::_Fun<FUN>(), "Need something function-like.");
403 
404  using Res = typename lib::meta::_Fun<FUN>::Ret;
405  using lib::meta::func::chained;
406  using lib::meta::_FunRet;
407 
408  if constexpr (std::is_same_v<Res, Tar>)// ◁──────────────────────────┨ function produces result directly
409  return std::forward<FUN>(fun);
410  else
411  if constexpr (std::is_same_v<Res, size_t>)// ◁───────────────────────┨ function yields random source to draw value
412  return chained (std::forward<FUN>(fun)
413  ,[this](size_t hash){ return drawLimited(hash); }
414  );
415  else
416  if constexpr (std::is_same_v<Res, double>)// ◁───────────────────────┨ function yields mapping value to be quantised
417  return chained (std::forward<FUN>(fun)
418  ,[this](double rand){ return limited(rand); }
419  );
420  else
421  if constexpr (std::is_same_v<Res, RandomDraw>) // ◁───────────────────┨ RandomDraw with dynamically adjusted parameters
422  return [functor=std::forward<FUN>(fun)]
423  (auto&& ...inArgs) -> _FunRet<RandomDraw>
424  { // invoke manipulator with copy
425  RandomDraw adaptedDraw = functor(inArgs...);
426  return adaptedDraw (forward<decltype(inArgs)> (inArgs)...);
427  }; // forward arguments to mapping-fun
428  else
429  static_assert (not sizeof(Res), "unable to adapt / handle result type");
430  NOTREACHED("Handle based on return type");
431  }
432  };
433 
434 
435 } // namespace lib
436 #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:215
double asRand(size_t hash)
Tar drawLimited(size_t hash)
Helper for uniform access to function signature types.
Definition: function.hpp:108
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)
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:160
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:196
void installAdapted(FUN &&fun)
auto adaptOut(FUN &&fun)