Reproducability of "Randomness" by Seed

Hi Guys,

I work with Processing since 2002 or so. I love it and it is very rare that I have a problem. But now I have:

It´s with the reproducability of my Algo.

To get a reproducable Sequence of random-numbers, I call randomSeed() with a fixed hard-coded integer. (here, it is 1)

The Code then produces a Structure by random-decisions like shown in this movie:

What I don´t get: Why is the Algo not working identical in two runs, if the number of traces is “high”, like 5 or 6, but it always is reproducable with only 1 or 2 traces. I have already checked and verified that the randomSeed(n) - works as expected.

There must be a reason for being not reproducable in my software. I am not using parallel threads. So there should be no racing conditions. What am I missing? Is there something hidden in Processing that influences the order of processing steps? I really need a hint here…

Unfortunately the code is a little too big to upload it all. But with the right hint, I can make a crystal and short version that shows the problem. I really want to understand the reason behind it.

Thx,
“QUERTZ” - Jens

P.S: Current Version is 4.0b2

It will be really hard to help you without seeing an MCVE or at least your full code.

Are you using the elapsed time (millis()) somewhere in your code to take decisions? That could explain why it stays the same with little numbers of steps and start to drift apart after some times.

No. I try to get a code that really reproduces. But Thx…

P.S: What is MCVE?

Note that noise() has its own noiseSeed() function and random() uses a separate randomSeed().

For a given, fixed, randomSeed(), all the following calls to random() should follow the same consistent order of values. Make sure that you don’t have any other external inputs such as time or mouse motion. The change in wall clock time between two frames will rarely be the same. And if you use any mouse motion, or the timing of a user pressing a key, that also won’t be consistent.

Either strip your code down to a simpler case or print out the first (or hundredth) random() value that you get after setting the seed to test if they are the same.

1 Like

An MCVE is a Minimal Complete and Verifiable Example: How to create a Minimal, Reproducible Example - Help Center - Stack Overflow

Basically it’s the smallest code that you can write that produces the same issue that you are trying to solve.

On your video it feels like you are clicking somewhere on the screen with your mouse. If that’s the case, your issue can come from this.

Also, you seem to be checking only visually that the results are the same but to be sure you would need to log some values (coordinates of each new points for exemple) so you can compare actual numerical values. Maybe the 2 images seem identical but the values are a tiny bit off?

Otherwise, what I would do is to check when it starts to break. Start with the lowest number of trace you can do and log the debug values. Repeat several times (not just 2) and compare all the values to see if they are always the same. If that’s the case, increase the number of traces and do the same check. This way you can pinpoint the exact number of traces at which it starts to differ and that can give you clues of what is happening.

Another point to consider is the sources of all of your constants, especially dealing with GUIs and floating-point numbers.

Imagine you had a slider going from 0 to 5 and you found a neat picture using a value of 2.4, so you hard code that as your initial value when the program starts up. Later you discover that values around 15 also make cool images, so you increase your slider range up to 20. Now the slider stops that used to be every 0.1 values only step by 0.25, so when you initial the slider to 2.4, it snaps to a value of 2.5. The first picture draws using 2.4, the second reads from the GUI and make a picture using the same random value series, but with a constant of 2.5 instead. Even further, your GUI might be lying to you about what actual values it has if it’s only printing floats to a fixed number of digits. It might say the value is 2.5 when it’s really using 2.499997.

Even floating-point rounding in general can lead to values that look really similar but are just enough off to make a difference. If you initialize a value to 1./17 * width but the computer later derives what should be the same value as width/17.0, you could well end up with 2 different values.

println() all of your constants before generating each of your images and make sure they really are starting from the same set of values.

Thx. Good Point with the Gui. But I wrote a Robot that reproduces the values by bypassing the gui. And: I intentionally Restart the whole Program. What can cause a non deterministic behaviour?

I think of making a min8malistic version. And a Version that Autochecks without the need of visual control.

Thx, Yes I am aware about the difference between noise and random. I do not use time and the code ignores the mouse as long as the reproducer is running. That was my first thought, too. I was afraid that the mouse could lead to additional random-calls, that take out values of the sequence. But this doesn‘t seem to be the case. Tomorrow I‘ll reinstall 3.5.4 to see if it shows the same behaviour.

I have let run the code with Processing 3.5.4.

It -as well- shows different results that should be the same - but with less abbreviations.

Could it be that there is a violation of execution-order inside processing?

I also started to write a minimal version. But unfortunately it exactly reproduces.
Whatever it is that lets the program show unreproducable results: it is not the random-generator.
Can it have to do anything with garbage-collection?

Do a binary search on your code. Comment out chunks and test to see where it introduces the variability. Or keep adding chunks of your original code to your simplified version until it loses the consistency.

Make sure you’re not initializing some global or class static variable with random() somewhere before you set the randomSeed.

Are you using any external libraries that might have played with the random seed on their own? Even simple algorithms like quicksort can use random() in picking their pivot point and a poorly written library might mess with more global state than it should.

Thx scudly, all good points. I am not using random() before I set the seed. (And I seed DRY, be sure.)
I do not use any external lib. (for reasons like you mentioned)

But I found something:

I start the Seeding and and all parameters with the seeding with a class called “Autopilot”, to get things reproducable and click-independent.

If I start that Autopilot inside the setup() at the end of all the initializations, I always get the same reproducable run.

If I start that Autopilot by a key:

void keyReleased()
{

if (key==‘a’)
{
gautoPilot=new Autopilot();
}

Then I get a different picture, every time I do it.
But the randomSeed(1) is called in that Autopilot after being created.

Can the time of setting the seed make any difference?
If so, I set the RandomSeed at program-start.

I have an Idea now: I take out the random-numbers from a class that encapsulates the calls to the processing-method random().So I can count how many random-numbers are taken away, when the first Trace starts. Maybe I find something here. (I think I am getting closer: The Keyboard event: When exactly is it processed? In my backhead I thought it would be processed INBETWEEN the draw()-calls of the framework. But maybe that´s not true. If it is processed in a separate thread, I could think of such problems.)

It’s safer to do very little work in the event handlers, like keyRelease(). Assuming you have a draw() loop running, I would instead just set a boolean flag to true when ‘a’ is released and then in draw() check for that flag and new Autopilot() from there (and then set the flag back to false). That way you can very precisely control the timing of any Processing calls that happen independent of any weird timing with event interrupts.

1 Like

My guess is that not everything is being reinitialised then – that a value set during the first run is affecting the second run.

Yes, I thought the same. Good Point. I found the causing code now, but did not really understand why it is causing that problem. Since I still try to reoroduce the same problem on a minimal codebase, I might come up with that code, soon.

OK, Problem solved.
It had nothing to to with the keyboard-interrupt. And nothing with racing-conditions with multithread or with the use of time() or any callback whatever.

It was much simpler. My own Algo simply was producing different results with every start. And that was, because I forgot to initialize a class with the restart. I completely forgot about this class. Then I thought about it but had the opinion that it does not matter. But it does.

Since the code is a little long, I explain here in a symbolic, condensed way:

I had a class “accellerator” that internally increased a counter with every draw().

accellerator is controlling a loop inside the maker of the geometry. (This is why you see the image accelerating in it´s construction).

So good so far.
But I oversaw a simple detail in the draw()-function:

draw()
{

if (resetFlag)
reset(); // Reset is not resetting acellerator, I oversaw that

accellerator.tic(); // increase acellerator
geometrybuilder.tic(); // build geometry, controlled by accellerator() <<-- takes .random()

secondaryAlgorithm.tic(); // algo, not using acellerator <<-- takes .random()
}

Now it is easy to see: After every reset (or with every new run of the code) the geometryBuilder sees a different accellerator. That would be no problem, if the “secondary Algorithm” would see the same, but
this secondary algo always sees no accellerator( or one that is aways set to “neutral”).

Both algos, “geometry” and “secondary” suck random-numbers.

My resulting algorithm now is different, depending on the exact time of keypress.
That explain explains everything.

I reset the accellerator now with the restart of the Autopilot and: “Problem gone”

What I do next: I want to guarantee that this problem does not come back.
So I write a little test that proofs that for me - before I go on.

I think that I really did a “Beginner´s mistake”. So - sorry for taking your time, guys. And: Thank you very much! You all helped!

Jens

P.S: I think that my “observation” of this problem being smaller with 3.5.4 does not hold. (I just saw a small difference by occasion. I don´t follow that now, because I want to go on with my code.)