DLR-SR/Noise

Add an example (or test case) with multiple instances of GlobalSeed

Closed this issue · 15 comments

tbeu commented

Since I do not fully understand the concept of the RNG states I need to ask if the following scenario is useful and considered.

It is said in the documentation that the GlobalSeed can be instanciated multiple times in a model, e.g. by dragging Modelica_Noise.Blocks.Examples.NoiseExamples.GenericNoise twice in a new model. Now what happens to the external state variables (ModelicaRandom_s, ModelicaRandom_p and ModelicaRandom_id of ModelicaRandom.c) if there are multiple seeds and function calls from different RNGs?

I have included a hint that this is not possible. The other option would be to change for ExternalObjects. But that would introduce another big overhead, for which I'm not sure how many tools support this...

@tbeu, could you tell me where you found the info that multiple instances could be introduced? I could not find it, but would like to correct it...

tbeu commented

It is implicitly said here: The GlobalSeed block provides global options for all Noise blocks of the same or a lower hierarchical level.

I'd rather like to see it fixed since I consider this as a major design flaw otherwise. And I guess it should be easy to fix. We already have uthash in the MSL SVN trunk and can build up a hash table to hold the state variables of all seeds/RNGs. In MSL this is already done for the opened tables in the Modelica Standard Tables and the opened files in Modelica Internal.

@sjoelund FYI.

tbeu commented

Also, if you add one more RNG function call to an existing model with RNG calls and you have a fixed seed it is expected that this first RNG returns the same noise as before. Right?

Thanks for pointing me to the info.

Adding more blocks of GenericNoise will not alter the noise of other blocks, because the state is maintained locally. The function globalSeed.random() can be seen like a rand() function in C. So, the results will change, if you add more calls to the function.

That's the current state. However, looking at the uthash Website, this should not be too hard to improve... Would you mind suggesting a pull request? I'm not sure about the correct usage and you seem to know your way around.

tbeu commented

Well, you expect that I know what goes on there on the Modelica side. But I do not fully understand. I could help to update the C file only (if I find the time).

That would be a great help! I think, most changes will be on the C side actually. I understand that you have time constraints, though. I will also only be able to have a deeper look into this somewhen next week...

On the Modelica side, there is two things:

  1. id=Modelica_Noise.Math.Random.Utilities.initializeImpureRandom() generates a good initial state and writes it to the C memory. The id is only used for sorting equations, but could be extended to serve as index into the hash.
  2. r=Modelica_Noise.Math.Random.Utilities.impureRandom(id) is used to generate the next random number.

Probably, the cleanest would be to:
1a. Generate the initial state in id=Modelica_Noise.Math.Random.Utilities.initializeImpureRandom() as is.
1b. Pass the state to the external C.
1c. Create, mutex, store, hash a new structure with a unique id.
1d. Return the id to Modelica.
2a. Instead of simply checking the id, also use it to retrieve the structure from the hash in Modelica_Noise.Math.Random.Utilities.impureRandom(id). Taking care of correct mutex.

My problems will be in 1c and 2a mainly: How to correctly combine mutex and hash, and how to store variable numbers of structures. If you don't find the time, I will see what I can do next week and ask you for your advice on the result...

tbeu commented

Nice comprehension. Thanks.

Well, 1c and 2a are not difficult. Let me see what I can do.

Hm. I am not sure whether I understand you.
Modelica_Noise has two types of random generators:

  • Modelica_Noise.Blocks.Noise.GenericNoise has a random number generator (RNG) where every instance of GenericNoise has a different RNG (the seed of every RNG is defined explicitley or implicitely in the declaration of the instance). This RNG has a state of 4 Integers and this state is explicit in the model (it seems complete overkill to introduce an external object just to handle 4 Integers in C-code, instead of in Modelica code).
  • Modelica_Noise.Blocks.Noise.GlobalSeed has one random number generator (RNG) with a static state of 33 Integers (handled in C) and the seed of this state is defined in the instance of GlobalSeed. It is not allowed to have more as one instance of GlobalSeed. If this would be the case, during intialiization an error message of one the C-functions occurs, that this is not possible. This RNG is accessed as "globalSeed.random()" and every call returns a new random number. This RNG is used to (a) implicitely initialize the GenericNoise RNGs (by providing a random seed, if no explicit seed is provided by the user), (b) for particular applications (such as stochastic state machines where the transition depends on a random number).
    For our current use cases we only need one such RNG, and we do not have use cases where several instances of this RNG would be needed (if several instances of an RNG are needed, use GenericNoise).

To summarize, our current design is fine for the use cases that we have, and I do not see a reason to change it.

tbeu commented

It is not allowed to have more as one instance of GlobalSeed.

But why? This is an artificial limitation.

Sorry, I just saw the remarks from Andreas Klöckner. I am strictly against making GenericNoise.random more general, because there is no use case for it. Please, keep the design as it is (no change to the code).

But why. This is an artificial limitation.

There is no use case for it. If you need an RNG with different states, use GenericNoise. It does not make sense to introduce two of them.

To make it more clear: Assume you would have two instances of GlobalSeed and everyone of them would have a different RNG. Both can be initialized with the same seed (if user-seed is selected). Then this means that suddenly calls to globalseed1.random() and globalseed2.random() will return exactly the same random number. When you use these random numbers, then there is suddenly a strong correlation in the target model (e.g. initialize two GenericNoise blocks, one with globalseed1.random() and one with globalseed2.random(); and suddenly these two noise blocks would be correlated.

tbeu commented

But this is exactly as expected if same fixedseed is used twice.

Sorry, do not have more time to discuss this now.

Last try today: The current design is sub-optimal. What is desired is one RNG that is global to a model and has two options: (a) starts for every simulation run from the same seed. (b) starts for every simulation run from a different seed. Only a tool vendor can provide such a random number generator. However, this would not be portable between tools. The current design with GlobalSeed.random() is the best what can be done in Modelica in a portable way: A user has to drag it, set the desired seed-option and an error message occurs, if more as one instance of GlobalSeed is declared. Allowing more as one instance of GlobalSeed would be extremly dangerous and the risk is too high that a user makes a mistake without noticing it.