Skip to content

Commit 74e07f8

Browse files
committed
Added GroupedRngManager: a flexible RNG manager that groups components by key
Implements cIRngManager with configurable grouping modes: - 'module': each module gets its own independent RNG (default) - 'node': all submodules of a network node share one RNG - 'network': all modules share a single RNG (matches default cRngManager with num-rngs=1) The grouping mode is selected via the 'rng-grouping' per-run config option. A custom key function can also be installed programmatically via setKeyFunction() to define arbitrary groupings. getRNG() is O(1) using a flat vector indexed by component ID. Guarded with #if OMNETPP_VERSION >= 0x0604 for backward compatibility.
1 parent 2fbc64c commit 74e07f8

2 files changed

Lines changed: 251 additions & 0 deletions

File tree

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
//
2+
// Copyright (C) 2024 OpenSim Ltd.
3+
//
4+
// SPDX-License-Identifier: LGPL-3.0-or-later
5+
//
6+
7+
8+
#include "inet/common/GroupedRngManager.h"
9+
10+
#if OMNETPP_VERSION >= 0x0604
11+
12+
namespace omnetpp {
13+
extern cConfigOption *CFGID_NUM_RNGS; // registered in crngmanager.cc
14+
extern cConfigOption *CFGID_RNG_CLASS; // registered in crngmanager.cc
15+
extern cConfigOption *CFGID_SEED_SET; // registered in crngmanager.cc
16+
}
17+
18+
namespace inet {
19+
20+
Register_Class(GroupedRngManager);
21+
Register_PerRunConfigOption(CFGID_RNG_GROUPING, "rng-grouping", CFG_STRING, "module",
22+
"Determines how modules are grouped for RNG assignment when using GroupedRngManager. "
23+
"'module' = each module gets its own RNG, "
24+
"'node' = all submodules of a network node share one RNG, "
25+
"'network' = all modules share a single RNG. "
26+
"Ignored if a custom key function is installed via setKeyFunction().");
27+
28+
GroupedRngManager::~GroupedRngManager()
29+
{
30+
for (auto *rng : allRngs)
31+
delete rng;
32+
}
33+
34+
void GroupedRngManager::configure(cConfiguration *config)
35+
{
36+
cfg = dynamic_cast<cConfigurationEx *>(config);
37+
if (!cfg)
38+
throw cRuntimeError("GroupedRngManager: Configuration object is not a cConfigurationEx");
39+
40+
// clean up state from previous run
41+
for (auto *rng : allRngs)
42+
delete rng;
43+
allRngs.clear();
44+
keyToBaseIndex.clear();
45+
componentBaseIndex.clear();
46+
47+
rngClass = cfg->getAsString(CFGID_RNG_CLASS);
48+
if (rngClass.empty())
49+
rngClass = "omnetpp::cMersenneTwister";
50+
51+
seedSet = cfg->getAsInt(CFGID_SEED_SET);
52+
numRngsPerKey = cfg->getAsInt(CFGID_NUM_RNGS);
53+
54+
grouping = cfg->getAsString(CFGID_RNG_GROUPING);
55+
if (grouping != "module" && grouping != "node" && grouping != "network")
56+
throw cRuntimeError("GroupedRngManager: Invalid rng-grouping='%s', must be 'module', 'node', or 'network'", grouping.c_str());
57+
58+
// run RNG self-test
59+
cRNG *testRng = createByClassName<cRNG>(rngClass.c_str(), "random number generator");
60+
testRng->selfTest();
61+
delete testRng;
62+
}
63+
64+
void GroupedRngManager::configureRngs(cComponent *component)
65+
{
66+
std::string key = getKeyForComponent(component);
67+
68+
auto it = keyToBaseIndex.find(key);
69+
if (it == keyToBaseIndex.end()) {
70+
// first component with this key — allocate numRngsPerKey RNGs
71+
int baseIndex = (int)allRngs.size();
72+
for (int k = 0; k < numRngsPerKey; k++)
73+
allRngs.push_back(createRng(baseIndex + k));
74+
it = keyToBaseIndex.emplace(key, baseIndex).first;
75+
}
76+
77+
// store base index for this component (O(1) lookup by component ID)
78+
int baseIndex = it->second;
79+
int id = component->getId();
80+
if (id >= (int)componentBaseIndex.size())
81+
componentBaseIndex.resize(id + 1, -1);
82+
componentBaseIndex[id] = baseIndex;
83+
}
84+
85+
int GroupedRngManager::getNumRngs(const cComponent *component) const
86+
{
87+
return numRngsPerKey;
88+
}
89+
90+
static cModule *getContainingNode(cComponent *component)
91+
{
92+
cModule *mod = dynamic_cast<cModule *>(component);
93+
if (!mod)
94+
mod = component->getParentModule();
95+
while (mod && mod->getParentModule() && mod->getParentModule()->getParentModule())
96+
mod = mod->getParentModule();
97+
return mod;
98+
}
99+
100+
std::string GroupedRngManager::getKeyForComponent(cComponent *component)
101+
{
102+
if (keyFunction)
103+
return keyFunction(component);
104+
if (grouping == "node") {
105+
cModule *node = getContainingNode(component);
106+
return node ? node->getFullPath() : component->getFullPath();
107+
}
108+
if (grouping == "network")
109+
return getSimulation()->getSystemModule()->getFullPath();
110+
// default: "module" — each component gets its own RNG set
111+
return component->getFullPath();
112+
}
113+
114+
cRNG *GroupedRngManager::createRng(int rngId)
115+
{
116+
cRNG *rng = createByClassName<cRNG>(rngClass.c_str(), "random number generator");
117+
rng->initialize(seedSet, rngId, numRngsPerKey,
118+
getEnvir()->getParsimProcId(),
119+
getEnvir()->getParsimNumPartitions(),
120+
cfg);
121+
return rng;
122+
}
123+
124+
cRNG *GroupedRngManager::getRng(const cComponent *component, int k)
125+
{
126+
// fast path: flat vector lookup by component ID
127+
int id = component->getId();
128+
if (id < 0 || id >= (int)componentBaseIndex.size() || componentBaseIndex[id] < 0)
129+
throw cRuntimeError("GroupedRngManager: No RNG configured for component '%s'", component->getFullPath().c_str());
130+
int rngId = componentBaseIndex[id] + k;
131+
if (k < 0 || k >= numRngsPerKey || rngId >= (int)allRngs.size())
132+
throw cRuntimeError("GroupedRngManager: RNG index %d out of range (num-rngs=%d)", k, numRngsPerKey);
133+
return allRngs[rngId];
134+
}
135+
136+
uint32_t GroupedRngManager::getHash() const
137+
{
138+
cHasher hasher;
139+
for (auto *rng : allRngs)
140+
hasher << rng->getNumbersDrawn();
141+
return hasher.getHash();
142+
}
143+
144+
int GroupedRngManager::getTotalNumRngs() const
145+
{
146+
return (int)allRngs.size();
147+
}
148+
149+
cRNG *GroupedRngManager::getGlobalRng(int rngId)
150+
{
151+
if (rngId < 0 || rngId >= (int)allRngs.size())
152+
throw cRuntimeError("GroupedRngManager: Global RNG index %d out of range (total=%d)", rngId, (int)allRngs.size());
153+
return allRngs[rngId];
154+
}
155+
156+
} // namespace inet
157+
158+
#endif // OMNETPP_VERSION >= 0x0604
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//
2+
// Copyright (C) 2024 OpenSim Ltd.
3+
//
4+
// SPDX-License-Identifier: LGPL-3.0-or-later
5+
//
6+
7+
8+
#ifndef __INET_GROUPEDRNGMANAGER_H
9+
#define __INET_GROUPEDRNGMANAGER_H
10+
11+
#include <map>
12+
#include <vector>
13+
#include <functional>
14+
#include "inet/common/INETDefs.h"
15+
16+
#if OMNETPP_VERSION >= 0x0604
17+
18+
namespace inet {
19+
20+
/**
21+
* An RNG manager that assigns RNGs to components based on a string key.
22+
* A user-supplied key function maps each component to a string key.
23+
* Components that map to the same key share the same set of RNGs,
24+
* giving deterministic, isolated random number streams per module,
25+
* per network node, or any other grouping the key function defines.
26+
*
27+
* Usage in omnetpp.ini:
28+
* rngmanager-class = "inet::GroupedRngManager"
29+
*
30+
* The grouping mode is selected via the "rng-grouping" per-run config option:
31+
* - "module" — each module gets its own RNG (default)
32+
* - "node" — all submodules of a network node share one RNG
33+
* - "network" — all modules share a single RNG
34+
*
35+
* Alternatively, a custom key function can be installed programmatically
36+
* via setKeyFunction(), which overrides the rng-grouping option.
37+
*/
38+
class INET_API GroupedRngManager : public cIRngManager
39+
{
40+
public:
41+
using KeyFunction = std::function<std::string(cComponent *)>;
42+
43+
private:
44+
cConfigurationEx *cfg = nullptr;
45+
std::string rngClass;
46+
int seedSet = 0;
47+
int numRngsPerKey = 1;
48+
49+
KeyFunction keyFunction;
50+
std::string grouping = "module";
51+
52+
// key -> base index into allRngs (RNGs for key are at baseIndex..baseIndex+numRngsPerKey-1)
53+
std::map<std::string, int> keyToBaseIndex;
54+
// component ID -> base index into allRngs (O(1) lookup for getRNG)
55+
std::vector<int> componentBaseIndex;
56+
std::vector<cRNG *> allRngs;
57+
58+
protected:
59+
virtual cRNG *createRng(int rngId);
60+
virtual std::string getKeyForComponent(cComponent *component);
61+
62+
public:
63+
GroupedRngManager() {}
64+
virtual ~GroupedRngManager();
65+
66+
/**
67+
* Sets the key function that maps a component to a string key.
68+
* Components with the same key share the same RNG(s).
69+
*/
70+
virtual void setKeyFunction(KeyFunction f) { keyFunction = f; }
71+
72+
/**
73+
* Returns the currently installed key function.
74+
*/
75+
virtual KeyFunction getKeyFunction() const { return keyFunction; }
76+
77+
/** @name cIRngManager interface. */
78+
//@{
79+
virtual void configure(cConfiguration *cfg) override;
80+
virtual void configureRngs(cComponent *component) override;
81+
virtual int getNumRngs(const cComponent *component) const override;
82+
virtual cRNG *getRng(const cComponent *component, int k) override;
83+
virtual int getTotalNumRngs() const override;
84+
virtual cRNG *getGlobalRng(int rngId) override;
85+
virtual uint32_t getHash() const override;
86+
//@}
87+
};
88+
89+
} // namespace inet
90+
91+
#endif // OMNETPP_VERSION >= 0x0604
92+
93+
#endif

0 commit comments

Comments
 (0)