diff --git a/src/lib/random-draw.hpp b/src/lib/random-draw.hpp
index 6afd82188..115beb731 100644
--- a/src/lib/random-draw.hpp
+++ b/src/lib/random-draw.hpp
@@ -48,11 +48,11 @@
** transformations. A wide array of function signatures can be accepted, as long as it is possible
** somehow to _adapt_ those functions to conform to the overall scheme as defined by the Policy base.
** Such a mapping function can be given directly at construction, or it can be set up later through
- ** the configuration DSL. As a special twist, it is even possible to bind a function to _manipulate_
- ** the actual instance of RandomDraw dynamically. Such a function takes `RandomDraw&` as first
- ** argument, plus any sequence of further arguments which can be adapted from the overall input;
- ** it is invoked prior to evaluating each input value and can tweak the instance by side-effect.
- ** After that, the input value is passed to the adapted instance.
+ ** the configuration DSL. As a special twist, it is even possible to change parameters dynamically,
+ ** based on the current input value. This requires the mapping function to construct a pristine
+ ** instance of RandomDraw, apply configuration based on the input and then return this instance
+ ** by value — without ever »engaging« and invoking; this dynamically configured instance will
+ ** then be invoked once, passing the current input values to yield the result value.
**
** ## Policy template
** For practical use, the RandomDraw template must be instantiated with a custom provided
@@ -344,17 +344,6 @@ namespace lib {
private:
- /// metafunction: the given function wants to manipulate `*this` dynamically
- template
- struct is_Manipulator
- : std::false_type { };
-
- template
- struct is_Manipulator
- : std::true_type { };
-
-
-
/** @internal adapt a function and install it to control drawing and mapping */
template
void
@@ -383,10 +372,6 @@ namespace lib {
if constexpr (std::is_same_v)
// function accepts same arguments as this RandomDraw
return forward (fun); // pass-through directly
- else
- if constexpr (is_Manipulator())
- // function wants to manipulate *this dynamically...
- return adaptIn (applyFirst (forward (fun), *this));
else
{// attempt to find a custom adaptor via Policy template
using Adaptor = typename POL::template Adaptor;
@@ -398,9 +383,9 @@ namespace lib {
* - a function producing the overall result-type is installed as-is
* - a `size_t` result is assumed be a hash and passed into #drawLimited
* - likewise a `double` is assumed to be already a random val to be #limited
- * - special treatment is given to a function with `void` result, which is assumed
- * to perform some manipulation on this RandomDraw instance by side-effect;
- * this allows to changes parameters dynamically, based on the input data.
+ * - special treatment is given to a function returning a `RandomDraw` instance
+ * by value; such a function is assumed to set some parametrisation based
+ * on the input data, allowing to change parameters dynamically.
* @return adapted function which produces a result value of type #Tar
*/
template
@@ -426,36 +411,17 @@ namespace lib {
,[this](double rand){ return limited(rand); }
);
else
- if constexpr (std::is_same_v) // ◁────────────────────────┨ function manipulates parameters by side-effect
- return [functor=std::forward(fun)
- ,processDraw=getCurrMapping()]
+ if constexpr (std::is_same_v) // ◁───────────────────┨ RandomDraw with dynamically adjusted parameters
+ return [functor=std::forward(fun)]
(auto&& ...inArgs) -> _FunRet
- {
- functor(inArgs...); // invoke manipulator with copy
- return processDraw (forward (inArgs)...);
- }; // forward arguments to mapping-fun
+ { // invoke manipulator with copy
+ RandomDraw adaptedDraw = functor(inArgs...);
+ return adaptedDraw (forward (inArgs)...);
+ }; // forward arguments to mapping-fun
else
static_assert (not sizeof(Res), "unable to adapt / handle result type");
NOTREACHED("Handle based on return type");
}
-
-
- /** @internal capture the current mapping processing-chain as function.
- * RandomDraw is-a function to process and map the input argument into a
- * limited and quantised output value. The actual chain can be re-configured.
- * This function picks up a snapshot copy of the current configuration; it is
- * used to build a chain of functions, which incorporates this current function.
- * If no function was configured yet, the default processing chain is returned.
- */
- Fun
- getCurrMapping()
- {
- Fun& currentProcessingFunction = *this;
- if (currentProcessingFunction)
- return Fun{currentProcessingFunction};
- else
- return Fun{adaptOut(POL::defaultSrc)};
- }
};
diff --git a/src/lib/test/transiently.hpp b/src/lib/test/transiently.hpp
index 49f28ee40..79e7e8573 100644
--- a/src/lib/test/transiently.hpp
+++ b/src/lib/test/transiently.hpp
@@ -65,7 +65,7 @@ namespace test{
~Transiently()
{
- manipulated_ = originalVal_;
+ manipulated_ = std::move (originalVal_);
}
template
diff --git a/tests/library/random-draw-test.cpp b/tests/library/random-draw-test.cpp
index 8996de0e6..23e21f5c4 100644
--- a/tests/library/random-draw-test.cpp
+++ b/tests/library/random-draw-test.cpp
@@ -723,32 +723,24 @@ namespace test{
CHECK (d2( 8) == 0);
CHECK (d2( 9) == 0);
CHECK (d2(10) == 0);
+
+ // NOTE: once a custom mapping function has been installed,
+ // the object can no longer be moved, due to reference binding.
+ VERIFY_ERROR (LIFECYCLE, Draw dx{move(d2)} );
}
- /** @test change the generation profile dynamically
- * - a »manipulator function« gets the current RandomDraw instance,
- * and any arguments that can generally be adapted for mapping functions;
- * it uses these arguments to manipulate the state before each new invocation;
+ /** @test change the generation profile dynamically, based on current input;
* in the example here, the probability is manipulated in each cycle.
- * - a »manipulator function« can be installed on top of any existing configuration,
- * including another custom mapping function; in the example here, we first install
- * a custom mapping for the hash values, to change the cycle to 4 steps only. Then,
- * in a second step, a »manipulator« is installed on top, this time accepting the
- * raw hash value and manipulating the minValue. After the manipulator was invoked,
- * the RandomDraw instance will be evaluated through the mapping-chain present
- * prior to installation of the »manipulator« — in this case, still the mapping
- * to change the cycle to 4 steps length; so in the result, the minValue is
- * increased in each cycle.
*/
void
verify_dynamicChange()
{
- auto d1 = Draw([](Draw& draw, uint cycle, uint)
- { // manipulate the probability
- draw.probability((cycle+1)*0.25);
+ auto d1 = Draw([](uint cycle, uint)
+ { // dynamically control probability
+ return Draw().probability((cycle+1)*0.25);
});
CHECK (d1( 0) == 0);
@@ -788,50 +780,6 @@ namespace test{
CHECK (d1(128+64+56) == -1);
CHECK (d1(128+64+63) == -1);
CHECK (d1(128+64+64) == 1);
-
- // NOTE: once a custom mapping function has been installed,
- // the object can no longer be moved, due to reference binding.
- VERIFY_ERROR (LIFECYCLE, Draw dx{move(d1)} );
-
-
- auto d2 = Draw([](size_t hash)
- { // change cycle 4 steps only
- return fmod (hash/4.0, 1.0);
- });
-
- CHECK (d2( 0) == +1); // 1st cycle
- CHECK (d2( 1) == +2);
- CHECK (d2( 2) == -2);
- CHECK (d2( 3) == -1);
- CHECK (d2( 4) == +1); // 2nd cycle
- CHECK (d2( 5) == +2);
- CHECK (d2( 6) == -2);
- CHECK (d2( 7) == -1);
- CHECK (d2( 8) == +1); // 3rd cycle
- CHECK (d2( 9) == +2);
- CHECK (d2(10) == -2);
- CHECK (d2(11) == -1);
- CHECK (d2(12) == +1);
-
- d2.mapping([](Draw& draw, size_t hash)
- { // manipulate the minVal per cycle
- int cycle = hash / 4;
- draw.minVal(-2+cycle);
- });
-
- CHECK (d2( 0) == +1); // 1st cycle -> minVal ≡ -2
- CHECK (d2( 1) == +2);
- CHECK (d2( 2) == -2);
- CHECK (d2( 3) == -1);
- CHECK (d2( 4) == +1); // 2nd cycle -> minVal ≡ -1
- CHECK (d2( 5) == +1);
- CHECK (d2( 6) == +2);
- CHECK (d2( 7) == -1);
- CHECK (d2( 8) == +1); // 3rd cycle -> minVal ≡ 0
- CHECK (d2( 9) == +1);
- CHECK (d2(10) == +2);
- CHECK (d2(11) == +2);
- CHECK (d2(12) == +1);
}
};
diff --git a/tests/vault/gear/test-chain-load-test.cpp b/tests/vault/gear/test-chain-load-test.cpp
index ae4d93ea0..4dc84b60e 100644
--- a/tests/vault/gear/test-chain-load-test.cpp
+++ b/tests/vault/gear/test-chain-load-test.cpp
@@ -298,9 +298,6 @@ namespace test {
CHECK (stat.indicators[STAT_JOIN].pL == "0.78378378"_expect); // but also almost one join per level to deal with the limitation
CHECK (stat.indicators[STAT_FORK].frac == "0.24609375"_expect); // 25% forks (there is just not enough room for more forks)
CHECK (stat.indicators[STAT_JOIN].frac == "0.11328125"_expect); // and 11% joins
-//SHOW_EXPR(graph.getHash())
-//SHOW_EXPR(stat.indicators[STAT_NODE].pL)
-//SHOW_EXPR(stat.indicators[STAT_JOIN].cL)
}
@@ -312,8 +309,27 @@ namespace test {
void
verify_Reduction()
{
+ ChainLoad32 graph;
+ // moderate symmetrical expansion with 40% probability and maximal +2 links
+ graph.expansionRule(graph.rule().mapping([&](Node* n)
+ {
+ if (isStart(n))
+ return graph.rule().probability(1.0).maxVal(8).mapping([](size_t){ return 1.0; });
+ else
+ return graph.rule();
+ }))
+ .reductionRule(graph.rule().probability(0.2).maxVal(3).shuffle(555))
+ .buildToplolgy()
+ .printTopologyDOT()
+ .printTopologyStatistics()
+ ;
+// CHECK (graph.getHash() == 0xAE332109116C5100);
+SHOW_EXPR(graph.getHash())
}
+//SHOW_EXPR(graph.getHash())
+//SHOW_EXPR(stat.indicators[STAT_NODE].pL)
+//SHOW_EXPR(stat.indicators[STAT_JOIN].cL)
diff --git a/tests/vault/gear/test-chain-load.hpp b/tests/vault/gear/test-chain-load.hpp
index 2fb402230..4a18ad27e 100644
--- a/tests/vault/gear/test-chain-load.hpp
+++ b/tests/vault/gear/test-chain-load.hpp
@@ -47,12 +47,13 @@
** configurable _control functions_ driven by each node's (hash)value. This way, each node
** can optionally fork out to several successor nodes, but can also reduce and combine its
** predecessor nodes; additionally, new chains can be spawned (to simulate the effect of
- ** data loading Jobs without predecessor). The computation always begins with the _root
- ** node_, proceeds over the node links and finally leads to the _top node,_ which connects
- ** all chains of computation, leaving no dead end. The probabilistic rules controlling the
- ** topology can be configured using the lib::RandomDraw component, allowing either just
- ** to set a fixed probability or to define elaborate dynamic configurations based on the
- ** graph height or node connectivity properties.
+ ** data loading Jobs without predecessor) and chains can be deliberately pruned, possibly
+ ** splitting the computation into several disjoint sub-graphs. Anyway, the computation always
+ ** begins with the _root node_, proceeds over the node links and finally connects any open
+ ** chains of computation to the _top node,_ leaving no dead end. The probabilistic rules
+ ** controlling the topology can be configured using the lib::RandomDraw component, allowing
+ ** either just to set a fixed probability or to define elaborate dynamic configurations
+ ** based on the graph height or node connectivity properties.
**
** ## Usage
** A TestChainLoad instance is created with predetermined maximum fan factor and a fixed
@@ -80,7 +81,7 @@
#include "vault/common.hpp"
-#include "lib/test/test-helper.hpp"
+#include "lib/test/transiently.hpp"
//#include "vault/gear/job.h"
//#include "vault/gear/activity.hpp"
@@ -127,6 +128,7 @@ namespace test {
using util::toString;
using util::showHashLSB;
using lib::meta::_FunRet;
+ using lib::test::Transiently;
// using std::forward;
// using std::string;
@@ -348,6 +350,9 @@ namespace test {
/**
* Use current configuration and seed to (re)build Node connectivity.
+ * While working in-place, the wiring and thus the resulting hash values
+ * are completely rewritten, progressing from start and controlled by
+ * evaluating the _drawing rules_ on the current node, computing its hash.
*/
TestChainLoad&&
buildToplolgy()
@@ -358,11 +363,11 @@ namespace test {
Node* node = &nodes_->front();
size_t level{0};
- // local copy of all rules (non-copyable, once engaged)
- Rule expansionRule = expansionRule_;
- Rule reductionRule = reductionRule_;
- Rule seedingRule = seedingRule_;
- Rule pruningRule = pruningRule_;
+ // transient snapshot of rules (non-copyable, once engaged)
+ Transiently originalExpansionRule{expansionRule_};
+ Transiently originalReductionRule{reductionRule_};
+ Transiently originalseedingRule {seedingRule_};
+ Transiently originalPruningRule {pruningRule_};
// prepare building blocks for the topology generation...
auto moreNext = [&]{ return next->size() < maxFan; };
@@ -392,10 +397,10 @@ namespace test {
for (Node* o : *curr)
{ // follow-up on all Nodes in current level...
o->calculate();
- if (apply (pruningRule,o))
+ if (apply (pruningRule_,o))
continue; // discontinue
- size_t toSeed = apply (seedingRule, o);
- size_t toExpand = apply (expansionRule,o);
+ size_t toSeed = apply (seedingRule_, o);
+ size_t toExpand = apply (expansionRule_,o);
while (0 < toSeed and spaceLeft())
{ // start a new chain from seed
addNode(this->getSeed());
@@ -410,7 +415,7 @@ namespace test {
if (not toReduce)
{ // carry-on chain from o
r = spaceLeft()? addNode():nullptr;
- toReduce = apply (reductionRule, o);
+ toReduce = apply (reductionRule_, o);
}
else
--toReduce;
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm
index 0d433e522..1346f0699 100644
--- a/wiki/thinkPad.ichthyo.mm
+++ b/wiki/thinkPad.ichthyo.mm
@@ -96233,6 +96233,40 @@ Date: Thu Apr 20 18:53:17 2023 +0200
+
+
+
+
+
+
+
+
+
+
+ das bedingt eine dynamische Regel
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mit Abstrichen: jetzt läufts...
+
+
+
+
+
+
+
+
+
@@ -96795,7 +96829,7 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
+
@@ -98164,6 +98198,57 @@ Date: Thu Apr 20 18:53:17 2023 +0200
+
+
+
+
+
+
+
+
+
+
+
+ es ist zu »funktional«
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ konkret hilft mir:
+
+
+ die »Manipulation« kann per Definitionem
+
+
+ auf einem leeren Objekt statfinden...
+
+
+
+
+
+
+
+ vom konkreten use-Case her gedacht, ist es typischerweise gar nicht hilfreich, wenn das zu manipulierende Objekt schon »Zustand« hat; man schafft viel klarere Verhältnisse, wenn es per Definition ein default-konstruiertes Objekt ist
+
+
+
+
+
+
@@ -98195,8 +98280,9 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
+
+
@@ -98221,6 +98307,12 @@ Date: Thu Apr 20 18:53:17 2023 +0200
+
+
+
+
+
+
@@ -98262,8 +98354,9 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
+
+
@@ -98380,6 +98473,78 @@ Date: Thu Apr 20 18:53:17 2023 +0200
+
+
+
+
+
+
+
+
+
+
+ dynamische Manipulation versaut this
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -98516,7 +98681,7 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
+
@@ -98538,7 +98703,7 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
+
@@ -98579,9 +98744,134 @@ Date: Thu Apr 20 18:53:17 2023 +0200
+
+
+
+
+ ...da RandomDraw jetzt so wunderbar elegant kopierbar ist...
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+ ...während der Berechnung der Topologie (und das ist die einzige tatsächliche Verwendung der Regeln) arbeiten wir auf lokalen Kopien; eine dynamische Änderung aus der Berechnung heraus hinterläßt Spuren in der »schwebenden« globalen Regelkonfiguration — dringt aber grade nicht in die aktivierte Regel-Konfiguration durch.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ das macht fast genau das hier Benötigte
+
+
+
+
+
+
+
+
+
+
+
+
+ ...zum einen ist TestChainLoad selbst ein Test-Tool
+
+
+ ...und außerdem ist der lib::test - Namespace weder verboten noch gefährlich
+
+
+ ...und schließlich steht der Makro-Name TRANSIENTLY nicht wirklich in Konkurrenz zu irgendwas
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ...sondern nur eine Verbesserung in der Code-Struktur (deshalb belasse ich es jetzt auch dabei)....
+
+
+ Das eigentliche Problem zeigte sich erst einen Schritt weiter: es ist eben nicht möglich, eine bereits gebundene Mapping-Funktion nochmal zu „entbinden“ und separat aufzurufen..
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mit Abstrichen: jetzt läufts...
+
+
+
+
+
+