Library: some first thoughts regarding random number generation

Relying on random numbers for verification and measurements is known to be problematic.
At some point we are bound to control the seed values -- and in the actual
application usage we want to record sequence seeding in the event log.

Some initial thoughts regarding this intricate topic.
 * a low-ceremony drop-in replacement for rand() is required
 * we want the ability to pick-up and control each and every usage eventually
 * however, some usages explicitly require true randomness
 * the ability to use separate streams of random-number generation is desirable
This commit is contained in:
Fischlurch 2024-03-11 22:47:29 +01:00
parent 6e8c07ccd6
commit 7a3e4098c8
16 changed files with 397 additions and 14 deletions

View file

@ -46,6 +46,7 @@
#ifndef LUMIERA_OPTION_H
#define LUMIERA_OPTION_H
#include "lib/nocopy.hpp"
#include "lib/cmdline.hpp"
#include "lib/format-cout.hpp"

View file

@ -45,6 +45,7 @@
#include "lib/error.hpp"
#include "lib/symbol.hpp"
#include "lib/nocopy.hpp"
#include <algorithm>

View file

@ -33,6 +33,7 @@
#include "lib/error.hpp"
#include "include/lifecycle.h"
#include "lib/meta/util.hpp"
#include "lib/util.hpp"

View file

@ -42,7 +42,7 @@
#define _STDBOOL_H // prevent <atomic> from including stdbool.h
#include "include/logging.h"
#include "include/lifecycle.h"
#include "lib/hash-standard.hpp"
#include "lib/error.h"
#include <exception>

View file

@ -108,6 +108,7 @@
#define UTIL_FORMAT_STRING_H
#include "lib/error.hpp"
#include "lib/symbol.hpp"
#include "lib/nocopy.hpp"
#include "lib/meta/util.hpp"
#include "lib/meta/size-trait.hpp"

43
src/lib/integral.hpp Normal file
View file

@ -0,0 +1,43 @@
/*
INTEGRAL.hpp - there is nothing like one
Copyright (C) Lumiera.org
2024, Hermann Vosseler <Ichthyostega@web.de>
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 integral.h
** Inclusion for common place integral types and constants.
*/
#ifndef LIB_INTEGRAL_H
#define LIB_INTEGRAL_H
#include <stdlib.h>
#include <cstddef>
#include <cstdint>
/* === minimal common place === */
using uchar = unsigned char;
using uint = unsigned int;
#endif /*LIB_INTEGRAL_H*/

38
src/lib/random.cpp Normal file
View file

@ -0,0 +1,38 @@
/*
random.cpp - storage and implementation for random number framework
Copyright (C) Lumiera.org
2024, Hermann Vosseler <Ichthyostega@web.de>
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.cpp
** Implementation details and common storage for random number generation.
*/
//#include "error.hpp"
#include "lib/random.hpp"
//#include "lib/util.hpp"
//#include <functional>
namespace lib {
} // namespace lib

96
src/lib/random.hpp Normal file
View file

@ -0,0 +1,96 @@
/*
RANDOM.hpp - support for random number generation with controlled seed
Copyright (C) Lumiera.org
2024, Hermann Vosseler <Ichthyostega@web.de>
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.hpp
** Generating (pseudo) random numbers with controlled seed.
** As an extension on top of the [C++ random number framework] to integral arithmetics, rational numbers can be defined
** [C++ random number framework]: https://en.cppreference.com/w/cpp/numeric/random
** @see Random_test
*/
#ifndef LIB_RANDOM_H
#define LIB_RANDOM_H
#include "lib/integral.hpp"
#include "lib/nocopy.hpp"
#include <random>
namespace lib {
/** Establishes a seed point for any instance or performance. */
class SeedNucleus;
/**
* Access point to a selection of random number sources.
* For each kind of performance or usage, a common execution scheme
* is established to initiate generated number sequences, allowing
* for seemingly random yet reproducible behaviour or for actually
* contingent behaviour when necessary. Client code should rely on
* [Dependency-Injection](\ref lib::Depend) or the static accessors.
*/
template<class GEN>
class RandomSequencer
: util::NonCopyable
{
std::uniform_int_distribution<int> uniformI_;
std::uniform_int_distribution<uint64_t> uniformU_;
std::uniform_real_distribution<double> uniformD_;
GEN generator_;
/** Random instances are created as part of an execution scheme */
RandomSequencer(SeedNucleus&);
friend class SeedNucleus;
public:
int i32() { return uniformI_(generator_); }
uint64_t u64() { return uniformU_(generator_); }
double uni() { return uniformD_(generator_); }
};
/**
* PRNG engine to use by default: 64bit mersenne twister.
*/
using Random = RandomSequencer<std::mt19937_64>;
/** a global default RandomSequencer for mundane purposes */
extern Random defaultGen;
/** a global RandomSequencer seeded with real entropy */
extern Random entropyGen;
/* ===== convenience accessors ===== */
inline int rani() { return defaultGen.i32(); }
inline uint64_t ranu() { return defaultGen.u64(); }
inline double rado() { return defaultGen.uni(); }
} // namespace lib
#endif /*LIB_RANDOM_H*/

View file

@ -82,9 +82,6 @@ namespace util {
using boost::rational_cast;
using std::abs;
// these are neither standard C++ nor POSIX, yet pretty much common place...
using uchar = unsigned char;
using uint = unsigned int;
inline bool

View file

@ -141,12 +141,12 @@ namespace stat {
explicit operator bool() const
{
return isValid ();
return isValid();
}
string operator*() const
{
if (not isValid ()) fail();
if (not isValid()) fail();
auto& mat = *curr_;
return mat[2].matched? mat[2]
: mat[1];
@ -196,12 +196,12 @@ namespace stat {
throw error::Invalid{_Fmt{"Garbage after last field. Line:%s|↯|%s"}
% line_.substr(0,pos_) % line_.substr(pos_)};
else
if (pos_ != curr_->position())
if (pos_ != size_t(curr_->position()))
throw error::Invalid{_Fmt{"Garbage before field(%d):%s|↯|%s"}
% (field_+1)
% line_.substr(0,pos_) % line_.substr(pos_)};
else
throw error::Invalid{"CSV parse floundered. Line:"+toString(line_)};
throw error::Invalid{_Fmt{"CSV parse floundered. Line:%s"} % line_};
}
};

View file

@ -439,11 +439,13 @@ namespace stat{
,[&](auto& col)
{
if (not csv)
if (csv.isParseFail())
csv.fail();
else
throw error::Invalid{_Fmt{"Insufficient data; only %d fields, %d expected. Line:%s"}
% csv.getParsedFieldCnt() % columnCnt % line};
{
if (csv.isParseFail())
csv.fail();
else
throw error::Invalid{_Fmt{"Insufficient data; only %d fields, %d expected. Line:%s"}
% csv.getParsedFieldCnt() % columnCnt % line};
}
using Value = typename std::remove_reference<decltype(col)>::type::ValueType;
col.get() = parseAs<Value>(*csv);

View file

@ -193,6 +193,7 @@ typedef lumiera_jobDescriptor* LumieraJobDescriptor;
#ifdef __cplusplus /* ============== C++ Interface ================= */
#include "lib/nocopy.hpp"
#include <string>

View file

@ -10,3 +10,8 @@ END
PLANNED "Statistic and Regression" Statistic_test << END
return: 0
END
PLANNED "Random numbers and Seed" Random_test << END
return: 0
END

View file

@ -0,0 +1,71 @@
/*
Random(Test) - verify framework for controlled random number generation
Copyright (C) Lumiera.org
2024, Hermann Vosseler <Ichthyostega@web.de>
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-test.cpp
** unit test \ref Random_test
*/
#include "lib/test/run.hpp"
#include "lib/rational.hpp"
#include "lib/test/diagnostic-output.hpp"/////////////////////////TODO
#include <chrono>
#include <array>
using std::array;
namespace util {
namespace test {
/***************************************************************************//**
* @test demonstrate simple access to random number generation, as well as
* the setup of controlled random number sequences.
* @see random.hpp
*/
class Random_test : public Test
{
virtual void
run (Arg)
{
simpleUsage();
}
/**
* @test demonstrate usage of default random number generators
*/
void
simpleUsage()
{
}
};
LAUNCHER (Random_test, "unit common");
}} // namespace util::test

View file

@ -28,6 +28,7 @@
#include "lib/error.hpp"
#include "lib/test/run.hpp"
#include "lib/integral.hpp"
#include "lib/format-cout.hpp"
#include "lib/rational.hpp"

View file

@ -57242,6 +57242,68 @@
<arrowlink COLOR="#ad7a88" DESTINATION="ID_992572632" ENDARROW="Default" ENDINCLINATION="-1582;-265;" ID="Arrow_ID_891079113" STARTARROW="None" STARTINCLINATION="1511;114;"/>
<icon BUILTIN="flag-yellow"/>
</node>
<node CREATED="1710185830600" ID="ID_1750124828" MODIFIED="1710185837239" TEXT="Spezifikation">
<node CREATED="1710185838462" ID="ID_1494325621" MODIFIED="1710185847781" TEXT="modelliert als RAII"/>
<node CREATED="1710185850649" ID="ID_507946689" MODIFIED="1710185923149" TEXT="erzeugt einen zuf&#xe4;lligen Namen"/>
</node>
<node CREATED="1710185930891" ID="ID_406086033" MODIFIED="1710185934026" TEXT="Implementierung">
<node CREATED="1710185934990" ID="ID_1561994557" LINK="https://stackoverflow.com/a/58454949" MODIFIED="1710186020053" TEXT="Ausgangspunkt: L&#xf6;sung von Stackoverflow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1710186086663" ID="ID_206197040" MODIFIED="1710186142632" TEXT="Zufallserzeugung extrahiert">
<arrowlink COLOR="#752d90" DESTINATION="ID_588494337" ENDARROW="Default" ENDINCLINATION="33;-54;" ID="Arrow_ID_353980881" STARTARROW="None" STARTINCLINATION="-193;18;"/>
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1710188183049" ID="ID_91824274" MODIFIED="1710188192430" TEXT="Allokation">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1710188193120" ID="ID_988204568" MODIFIED="1710188200824" TEXT="Destruktion">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1710186076931" ID="ID_1533745732" MODIFIED="1710186080916" TEXT="Zufallszahlen">
<icon BUILTIN="hourglass"/>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1710186100840" ID="ID_588494337" MODIFIED="1710186269542" TEXT="erst mal Stanard-L&#xf6;sungen sammeln">
<arrowlink COLOR="#5c6fbb" DESTINATION="ID_1793624210" ENDARROW="Default" ENDINCLINATION="-1371;142;" ID="Arrow_ID_573303638" STARTARROW="None" STARTINCLINATION="-2119;146;"/>
<linktarget COLOR="#752d90" DESTINATION="ID_588494337" ENDARROW="Default" ENDINCLINATION="33;-54;" ID="Arrow_ID_353980881" SOURCE="ID_206197040" STARTARROW="None" STARTINCLINATION="-193;18;"/>
<icon BUILTIN="pencil"/>
<node CREATED="1710187145204" ID="ID_233861357" MODIFIED="1710187149447" TEXT="lib/random.hpp"/>
<node CREATED="1710187680111" ID="ID_878266882" MODIFIED="1710187724802" TEXT="lib::Random">
<node CREATED="1710187754747" ID="ID_99802932" MODIFIED="1710190647554" TEXT="i32()"/>
<node CREATED="1710187814309" ID="ID_1788616687" MODIFIED="1710190647554" TEXT="u64()"/>
<node CREATED="1710188143198" ID="ID_1163605838" MODIFIED="1710190647554" TEXT="unit()"/>
<node CREATED="1710187826841" ID="ID_1405337337" MODIFIED="1710187828904" TEXT="...."/>
<node CREATED="1710187924680" ID="ID_1505424962" MODIFIED="1710187938719" TEXT="gen() &#x27f6; liefert einen Singleton-Generator"/>
</node>
<node CREATED="1710187950470" ID="ID_344436676" MODIFIED="1710188052886" TEXT="lib::rani() &#xd83e;&#xdc46; lib::Random::gen().i32()"/>
<node CREATED="1710188156019" ID="ID_958269270" MODIFIED="1710188169095" TEXT="lib:ranu() &#xd83e;&#xdc46; lib::Random::gen().u64()"/>
<node CREATED="1710187996810" ID="ID_1375983004" MODIFIED="1710190639188" TEXT="lib:rado() &#xd83e;&#xdc46; lib::Random::gen().unit()"/>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1710187630035" ID="ID_552644754" MODIFIED="1710187676838" TEXT="Idee: reproduzierbares Schma per DI">
<icon BUILTIN="idea"/>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1710193419628" ID="ID_1554210735" MODIFIED="1710193431797" TEXT="Problem: brauche eine Art von Abstraktion">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1710193434291" ID="ID_425843093" MODIFIED="1710193454407" TEXT="aaaber will m&#xf6;glichst wenig Indirektionen">
<icon BUILTIN="ksmiletris"/>
</node>
<node COLOR="#6d2260" CREATED="1710193467726" ID="ID_1508129981" MODIFIED="1710193490034" TEXT="einmal von allem mit Extra f&#xfc;r umsonst">
<icon BUILTIN="smiley-oh"/>
</node>
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1710195949544" ID="ID_1945350847" MODIFIED="1710195960956" TEXT="die Belange trennnen">
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
<icon BUILTIN="yes"/>
<node CREATED="1710195969254" ID="ID_1184037791" MODIFIED="1710195994256" TEXT="einfacher Zugang zu konkreten Number-Sources"/>
<node CREATED="1710195997745" ID="ID_1412161113" MODIFIED="1710196023402" TEXT="Instanzen und damit die Sequenz-Stabilit&#xe4;t"/>
<node CREATED="1710196025149" ID="ID_785759201" MODIFIED="1710196038816" TEXT="der Ausf&#xfc;hrungs-Rahmen"/>
</node>
</node>
<node CREATED="1710193536285" ID="ID_564127531" MODIFIED="1710193541417" TEXT="Use-Cases">
<icon BUILTIN="idea"/>
<node CREATED="1710193562872" ID="ID_654637403" MODIFIED="1710193567117" TEXT="zuf&#xe4;llige Tests"/>
<node CREATED="1710193567888" ID="ID_157101351" MODIFIED="1710193572444" TEXT="reproduzierbare Tests"/>
<node CREATED="1710193821918" ID="ID_366634549" MODIFIED="1710193851769" TEXT="Entkopplung der Ziehungen "/>
<node CREATED="1710193614234" ID="ID_1747236990" MODIFIED="1710193785467" TEXT="per Event-Log fixierbare Seeds"/>
</node>
</node>
</node>
</node>
</node>
@ -118998,6 +119060,69 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</html></richcontent>
</node>
</node>
<node CREATED="1710186151268" ID="ID_1793624210" MODIFIED="1710186269543" TEXT="Zufallszahlen/Distributionen">
<linktarget COLOR="#5c6fbb" DESTINATION="ID_1793624210" ENDARROW="Default" ENDINCLINATION="-1371;142;" ID="Arrow_ID_573303638" SOURCE="ID_588494337" STARTARROW="None" STARTINCLINATION="-2119;146;"/>
<node CREATED="1710186359368" ID="ID_765629285" MODIFIED="1710186365813" TEXT="Info">
<icon BUILTIN="info"/>
<node CREATED="1710186277136" ID="ID_336649935" LINK="https://en.cppreference.com/w/cpp/numeric/random" MODIFIED="1710186690629" TEXT="Standard-Lib: (pseudo)-random number generation"/>
</node>
<node CREATED="1710186369715" ID="ID_1826063920" MODIFIED="1710186378766" TEXT="Begriffe/Typen">
<node CREATED="1710186389857" ID="ID_1473096769" MODIFIED="1710186401416" TEXT="Generator (uniform random bit generator)">
<node CREATED="1710186743266" ID="ID_1755434423" MODIFIED="1710186788972" TEXT="Entropy Source : std::random_device"/>
<node CREATED="1710186733554" ID="ID_790943723" MODIFIED="1710186738750" TEXT="Concept: uniform_random_bit_generator"/>
<node CREATED="1710186828486" ID="ID_1530005376" MODIFIED="1710186856629" TEXT="&#x26a0; Achtung: jede Engine ist auch ein Generator">
<icon BUILTIN="idea"/>
</node>
</node>
<node CREATED="1710186408118" ID="ID_1416571014" MODIFIED="1710186419825" TEXT="Engine (pseudo random number generator)">
<node CREATED="1710186863330" ID="ID_700178442" MODIFIED="1710186877015" TEXT="Librarie bietet eine Reihe bekannter Alogrithmen"/>
<node CREATED="1710186877792" ID="ID_1911727077" MODIFIED="1710186889017" TEXT="zudem predefinierte Prametrisierungen derselben"/>
</node>
<node CREATED="1710186433875" ID="ID_1561079532" MODIFIED="1710186459299" TEXT="Distribution (random-source &#x27fc; number value)">
<node CREATED="1710186915124" ID="ID_1491128032" MODIFIED="1710186919230" TEXT="uniform-int"/>
<node CREATED="1710186919796" ID="ID_1760472152" MODIFIED="1710186923069" TEXT="uniform-real"/>
<node CREATED="1710186937704" ID="ID_1756009248" MODIFIED="1710186939723" TEXT="bernoulli"/>
<node CREATED="1710186940175" ID="ID_11267247" MODIFIED="1710186941859" TEXT="binomial"/>
<node CREATED="1710186944222" ID="ID_679867522" MODIFIED="1710186947018" TEXT="geometrisch"/>
<node CREATED="1710186958477" ID="ID_686984771" MODIFIED="1710186960144" TEXT="poisson"/>
<node CREATED="1710186980666" ID="ID_1928919437" MODIFIED="1710186982397" TEXT="normal"/>
<node CREATED="1710186982889" ID="ID_693414135" MODIFIED="1710186985566" TEXT="value-sampler"/>
</node>
</node>
<node CREATED="1710197590684" ID="ID_1817093923" MODIFIED="1710197595831" TEXT="Eigenschaften">
<node CREATED="1710197597553" ID="ID_1607458050" MODIFIED="1710197647648" TEXT="alle Engines im Standard sind erwiesenerma&#xdf;en 100% deterministisch">
<node CREATED="1710197649675" ID="ID_599623310" MODIFIED="1710197677899" TEXT="der Standard definiert die 10000-ste Zufallszahl">
<icon BUILTIN="idea"/>
</node>
<node CREATED="1710197680765" ID="ID_235148142" MODIFIED="1710197697107" TEXT="damit sind Portabilit&#xe4;ts- und Implementierungsfehler minimiert"/>
</node>
<node CREATED="1710197778929" ID="ID_34841952" MODIFIED="1710197787371" TEXT="Engine &#xbb;linear-congruential&#xab;">
<node CREATED="1710197788537" ID="ID_1612784281" MODIFIED="1710197799179" TEXT=" nur m&#xe4;&#xdf;ig schnell"/>
<node CREATED="1710197799755" ID="ID_518732759" MODIFIED="1710197814433" TEXT="relativ kurze Zyklen (limitiert durch Storage)"/>
<node CREATED="1710197814933" ID="ID_505159534" MODIFIED="1710197829551" TEXT="extrem kleine State-Storage (nur 1 Maschinenwort)"/>
</node>
<node CREATED="1710197831635" ID="ID_432094420" MODIFIED="1710197977031" TEXT="Engine &#xbb;mersenne twister&#xab;">
<icon BUILTIN="back"/>
<node CREATED="1710197860721" ID="ID_321118731" MODIFIED="1710197872509" TEXT="gilt als schnell"/>
<node CREATED="1710197873141" ID="ID_65291816" MODIFIED="1710197879496" TEXT="liefert sehr gute lange Zyklen"/>
<node CREATED="1710197879941" ID="ID_124918104" MODIFIED="1710197909724" TEXT="Nachteil: state-storage ist gro&#xdf; (624 Maschinenworte)"/>
</node>
</node>
<node CREATED="1710186999663" ID="ID_1246129278" MODIFIED="1710187005258" TEXT="typisches Schema">
<node CREATED="1710187011981" ID="ID_1202665110" MODIFIED="1710187084907" TEXT="random_device &#x27f6; seed">
<icon BUILTIN="full-1"/>
</node>
<node CREATED="1710187025940" ID="ID_46198054" MODIFIED="1710187087292" TEXT="seed &#x27f6; Engine">
<icon BUILTIN="full-2"/>
</node>
<node CREATED="1710187053064" ID="ID_532108648" MODIFIED="1710187090063" TEXT="Distribution instantiieren / verwenden">
<icon BUILTIN="full-3"/>
</node>
<node CREATED="1710187061143" ID="ID_264395876" MODIFIED="1710187092938" TEXT="Engine + Distribution &#x27fc; value">
<icon BUILTIN="full-4"/>
</node>
</node>
</node>
<node CREATED="1703175606961" ID="ID_977524589" MODIFIED="1703176863414" TEXT="Number Conversions">
<node CREATED="1703175642069" ID="ID_1436256174" LINK="https://en.cppreference.com/w/cpp/language/implicit_conversion#Integral_promotion" MODIFIED="1703175661949" TEXT="Implicit Conversions in expression evaluation">
<node CREATED="1703175682088" ID="ID_1645952636" MODIFIED="1703175704352" TEXT="narrow values will be widened to int/uint"/>