diff --git a/research/try.cpp b/research/try.cpp index 95b323296..cea4acedb 100644 --- a/research/try.cpp +++ b/research/try.cpp @@ -69,6 +69,7 @@ * real implementation this will have to be supplied at usage site through a traits template, * otherwise it would not be possible to integrate seamlessly with custom data sources (as * happens in the intended use case, where actually a Node is the data source) + * @note transformed into a generic library component for usage by vault::gear::TestChainLoad */ typedef unsigned int uint; diff --git a/src/lib/random-draw.hpp b/src/lib/random-draw.hpp new file mode 100644 index 000000000..517267cc7 --- /dev/null +++ b/src/lib/random-draw.hpp @@ -0,0 +1,304 @@ +/* + RANDOM-DRAW.hpp - randomly pick limited values + + Copyright (C) Lumiera.org + 2023, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +// 8/07 - how to control NOBUG?? +// execute with NOBUG_LOG='ttt:TRACE' bin/try +// 1/08 - working out a static initialisation problem for Visitor (Tag creation) +// 1/08 - check 64bit longs +// 4/08 - comparison operators on shared_ptr +// 4/08 - conversions on the value_type used for boost::any +// 5/08 - how to guard a downcasting access, so it is compiled in only if the involved types are convertible +// 7/08 - combining partial specialisation and subclasses +// 10/8 - abusing the STL containers to hold noncopyable values +// 6/09 - investigating how to build a mixin template providing an operator bool() +// 12/9 - tracking down a strange "warning: type qualifiers ignored on function return type" +// 1/10 - can we determine at compile time the presence of a certain function (for duck-typing)? +// 4/10 - pretty printing STL containers with python enabled GDB? +// 1/11 - exploring numeric limits +// 1/11 - integer floor and wrap operation(s) +// 1/11 - how to fetch the path of the own executable -- at least under Linux? +// 10/11 - simple demo using a pointer and a struct +// 11/11 - using the boost random number generator(s) +// 12/11 - how to detect if string conversion is possible? +// 1/12 - is partial application of member functions possible? +// 5/14 - c++11 transition: detect empty function object +// 7/14 - c++11 transition: std hash function vs. boost hash +// 9/14 - variadic templates and perfect forwarding +// 11/14 - pointer to member functions and name mangling +// 8/15 - Segfault when loading into GDB (on Debian/Jessie 64bit +// 8/15 - generalising the Variant::Visitor +// 1/16 - generic to-string conversion for ostream +// 1/16 - build tuple from runtime-typed variant container +// 3/17 - generic function signature traits, including support for Lambdas +// 9/17 - manipulate variadic templates to treat varargs in several chunks +// 11/17 - metaprogramming to detect the presence of extension points +// 11/17 - detect generic lambda +// 12/17 - investigate SFINAE failure. Reason was indirect use while in template instantiation +// 03/18 - Dependency Injection / Singleton initialisation / double checked locking +// 04/18 - investigate construction of static template members +// 08/18 - Segfault when compiling some regular expressions for EventLog search +// 10/18 - investigate insidious reinterpret cast +// 12/18 - investigate the trinomial random number algorithm from the C standard lib +// 04/19 - forwarding tuple element(s) to function invocation +// 06/19 - use a statefull counting filter in a treeExplorer pipeline +// 03/20 - investigate type deduction bug with PtrDerefIter +// 01/21 - look for ways to detect the presence of an (possibly inherited) getID() function +// 08/22 - techniques to supply additional feature selectors to a constructor call +// 10/23 - search for ways to detect signatures of member functions and functors uniformly +// 11/23 - prototype for a builder-DSL to configure a functor to draw and map random values + + +/** @file random-draw.hpp + ** Build a component to select limited values randomly. + ** Prototyping to find a suitable DSL to configure drawing of random numbers and mapping results. + ** The underlying implementation shall be extracted from (and later used by) TestChainLoad; the + ** random numbers will be derived from node hash values and must be mapped to yield parameters + ** limited to a very small value range. While numerically simple, this turns out to be rather + ** error-prone, hence the desire to put a DSL in front. The challenge however arises from + ** the additional requirement to support various usage patters, all with minimal specs. + ** + ** The following code lays out the ground structure, while treating Spec as a distinct + ** type, which is then mixed into Draw. This logical separation basically was led me to the + ** final solution: Draw both _is_ a function and _embodies_ the implementation of this function. + ** This somewhat surprising layout is what enables use as a DSL builder, because it allows both + ** to have the _builder use_ and the _converter use_ in the same class, even allowing to _define_ + ** a Draw by giving a function which _produces_ a (dynamically parametrised) Draw. + ** + ** In this prototype, all of the functor adaptation is also part of the Draw template; for the + ** real implementation this will have to be supplied at usage site through a traits template, + ** otherwise it would not be possible to integrate seamlessly with custom data sources (as + ** happens in the intended use case, where actually a Node is the data source) + ** + ** @see RandomDraw_test + ** @see TestChainLoad_test + ** @see SchedulerStress_test + */ + + +#ifndef LIB_RANDOM_DRAW_H +#define LIB_RANDOM_DRAW_H + + +#include "lib/meta/function.hpp" +#include "lib/util.hpp" + +#include +#include + + +namespace lib { + +using lib::meta::_Fun; +using std::function; +using std::forward; +using std::move; + + +template +struct Limited + { + static constexpr T minVal() { return T(0); } + static constexpr T maxVal() { return max; } + + T val; + + template + Limited (X raw) + : val(util::limited (X(minVal()), raw, X(maxVal()))) + { } + }; + +template +struct Spec + { + using Lim = Limited; + static constexpr double CAP_EPSILON = 0.0001; + + double probability{0}; + T maxResult{Lim::maxVal()}; + + Spec() = default; + + explicit + Spec (double p) : probability{p}{ } + + Lim + limited (double val) + { + if (probability == 0.0 or val == 0.0) + return Lim{0}; + double q = (1.0 - probability); + val -= q; + val /= probability; + val *= maxResult; + val += 1 + CAP_EPSILON; + return Lim{val}; + } + + }; + +template +struct Draw + : Spec + , function(size_t)> + { + using Spc = Spec; + using Lim = typename Spc::Lim; + using Fun = function; + + Draw() + : Spc{} + , Fun{[this](size_t hash){ return Spc::limited (asRand (hash)); }} + { } + + template + Draw(FUN&& fun) + : Spc{1.0} + , Fun{adaptOut(adaptIn(std::forward (fun)))} + { } + + + Draw&& + probability (double p) + { + Spc::probability = p; + return move (*this); + } + + Draw&& + maxVal (uint m) + { + Spc::maxResult = m; + return move (*this); + } + + template + Draw&& + mapping (FUN&& fun) + { + Fun(*this) = adaptOut(adaptIn(std::forward (fun))); + return move (*this); + } + + + + double + asRand (size_t hash) + { + return double(hash % 256)/256; + } + + /** + * @internal helper to expose the signature `size_t(size_t)` + * by wrapping a given lambda or functor. + */ + template + struct Adaptor + { + static_assert (not sizeof(SIG), "Unable to adapt given functor."); + }; + + template + struct Adaptor + { + template + static decltype(auto) + build (FUN&& fun) + { + return std::forward(fun); + } + }; + + template + struct Adaptor + { + template + static auto + build (FUN&& fun) + { + return [functor=std::forward(fun)] + (size_t) + { + return functor(); + }; + } + }; + + + template + decltype(auto) + adaptIn (FUN&& fun) + { + static_assert (lib::meta::_Fun(), "Need something function-like."); + static_assert (lib::meta::_Fun::ARITY <= 1, "Function with zero or one argument expected."); + + using Sig = typename lib::meta::_Fun::Sig; + + return Adaptor::build (forward (fun)); + } + + template + decltype(auto) + adaptOut (FUN&& fun) + { + static_assert (lib::meta::_Fun(), "Need something function-like."); + static_assert (lib::meta::_Fun::ARITY ==1, "Function with exactly one argument required."); + + using Res = typename lib::meta::_Fun::Ret; + + if constexpr (std::is_same_v) + return std::forward(fun); + else + if constexpr (std::is_same_v) + return [functor=std::forward(fun), this] + (size_t rawHash) + { + size_t hash = functor(rawHash); + double randomNum = asRand (hash); + return Spc::limited (randomNum); + }; + else + if constexpr (std::is_same_v) + return [functor=std::forward(fun), this] + (size_t rawHash) + { + double randomNum = functor(rawHash); + return Spc::limited (randomNum); + }; + else + if constexpr (std::is_same_v) + return [functor=std::forward(fun), this] + (size_t rawHash) + { + Draw parametricDraw = functor(rawHash); + return parametricDraw (rawHash); + }; + else + static_assert (not sizeof(Res), "unable to adapt / handle result type"); + NOTREACHED("Handle based on return type"); + } + + }; + + +} // namespace lib +#endif /*LIB_RANDOM_DRAW_H*/ diff --git a/tests/15library.tests b/tests/15library.tests index 992eac0c4..e19501ae3 100644 --- a/tests/15library.tests +++ b/tests/15library.tests @@ -508,6 +508,11 @@ return: 0 END +PLANNED "pick limited numbers randomly" RandomDraw_test < + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + +/** @file random-draw-test.cpp + ** unit test \ref RandomDraw_test + */ + + + + +#include "lib/test/run.hpp" +//#include "lib/test/test-helper.hpp" +#include "lib/random-draw.hpp" +//#include "lib/util.hpp" + +//#include + + + +namespace lib { +namespace test{ + +// using util::isSameObject; +// using std::rand; + +// namespace error = lumiera::error; +// using error::LUMIERA_ERROR_FATAL; +// using error::LUMIERA_ERROR_STATE; + + + namespace { +// const Literal THE_END = "all dead and hero got the girl"; + + } + + + + + + /***********************************************************************************//** + * @test Verify a flexible builder for random-value generators; using a config template, + * these can be outfitted to use a suitable source of randomness and to produce + * values from a desired target type and limited range. + * - TODO + * @see result.hpp + * @see lib::ThreadJoinable usage example + */ + class RandomDraw_test + : public Test + { + + void + run (Arg) + { + simpleUse(); + + verify_numerics(); + verify_dynamicChange(); + } + + + + /** @test TODO demonstrate a basic usage scenario + * @todo WIP 11/23 🔁 define ⟶ implement + */ + void + simpleUse() + { + UNIMPLEMENTED ("simple usage example"); + } + + + + /** @test TODO verify random number transformations + * @todo WIP 11/23 🔁 define ⟶ implement + */ + void + verify_numerics() + { + UNIMPLEMENTED ("verify random number transformations"); + } + + + + /** @test TODO change the generation profile dynamically + * @todo WIP 11/23 🔁 define ⟶ implement + */ + void + verify_dynamicChange() + { + UNIMPLEMENTED ("change the generation profile dynamically"); + } + }; + + + /** Register this test class... */ + LAUNCHER (RandomDraw_test, "unit common"); + + +}} // namespace lib::test diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index ed2d4164c..8e75dc0e7 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -96100,27 +96100,39 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- +

- Cap ⟶ Ergebnis-Typ Draw + Ausgangspunkt: Cap ⟶ Ergebnis-Typ Draw +

+ + +
+ + + + + +

+ ...dabei ist Cap die bereits geschaffene Hilfs-Komponente, die einen Zahlenwert mit einer Klammer von Iber/Untergrenze speichert und ihn in in diesen Rahmen hinein konditionieren kann. Das war ein relativ leichtgewichtiger erster Schritt, reicht aber nicht aus, um die techniche Komplexität aus dem Nutzkontext zu entfernen

- + - + - - + + + @@ -96130,15 +96142,15 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - + + + - + @@ -96296,7 +96308,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -96364,13 +96376,11 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - - - - + + @@ -96398,8 +96408,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + + @@ -96439,8 +96450,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + @@ -96461,9 +96473,15 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + - + + + + @@ -96535,20 +96553,78 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + + + + + + + - + + + + + + + + +

+ Fazit Design-Analyse +

+ + +
+ + + + + + + + + + + +

+ warum? eine zweckgebundene Lösung mit diesem Komplexitätsgrad wäre nicht zu rechtfertigen; man würde dann nach einer einfacheren Alternative suchen. Konkret komme ich allerdings von dieser einfacheren Alternative her, weil sie immer noch nicht einfach und klar genug war. Insofern bin ich froh, überhaupt eine Lösung zu haben, die im Rückblick sinnvoll darstellbar ist +

+ + +
+
+ + + + + + + + + + + + + + + + + + + +