Skip to content

Add serialization for native random number generator of Pythia8 #247

Merged
jncots merged 8 commits intomainfrom
pythia8_rng_getset
Dec 16, 2025
Merged

Add serialization for native random number generator of Pythia8 #247
jncots merged 8 commits intomainfrom
pythia8_rng_getset

Conversation

@jncots
Copy link
Collaborator

@jncots jncots commented Nov 20, 2025

Add functions for setting/getting of 'RndmState' state in pybind11 pythia8 interface cpp/_pythia8.cpp and use them to implement Pythia8 class's random_state property

@jncots
Copy link
Collaborator Author

jncots commented Nov 25, 2025

There are couple of implementations here so far:

  • cli logging - this adds log file alongside generated output file in cli run. It logs general information about kinematics and the model, as well as information about internal state/settings of generators variables on fortran level. So far Pythia8 and Sibyll generators has this implementation. Other generators has dummy output for their state so far

@jncots
Copy link
Collaborator Author

jncots commented Nov 25, 2025

It turned out that that our save/restore functionality didn't work. The tests passed because random_state returned None, which for the Python rule "None==None" allowed to pass tests. I fixed that by using explicit API of Python bit generators instead of __getstate__() and __setstate__().

@jncots
Copy link
Collaborator Author

jncots commented Nov 25, 2025

The implementation, in principle, allowed to use any numpy's bit generator, by passing instead of integer seed an instance of bit generator like this

from numpy.random import PCG64, PCG64DXSM, MT19937, PHILOX, SFC64

gen = model(evt_kin, seed = MT19937(seed=42)

The only problem is EposLHC which needed internally save and restore generators state. To allow this case to work, I added explicit structs of each of 5 bit generators in random.c. To choose the correct struct the id for each generator is added that is passed/set from python code..

@jncots
Copy link
Collaborator Author

jncots commented Nov 25, 2025

It turned out that rng tests are not passed for EposLHCR, as it is relies of Urqmd internally. Urqmd has known issues with reproducibility of sequencies after resetting rng state, which means that there are some hidden state that is not reset by resetting rng state and influence rng state.

I tried to investigate urqmd issue againt. However, it didn't give results. My conclusions about it:
There are several places where it can occur:

  • Commong blocks: I tried to save and restore all common blocks with help of Copilot. It doesn't work, meaning that answer buried deep
  • Save statements: they are not reset and might influence rng
  • Pythia internal rng: it might be but, it seems pythia uses correct rng
    Noticed: there are kind of "warm-up" that calculates some variables that at later stages is not called. There are many different rng in the code it might be that connecting all of them to one rng merge different rng branches that must be independent: event and variable sequencies

@jncots
Copy link
Collaborator Author

jncots commented Nov 25, 2025

Fix the problem with Pythia6 and add log info for it.

@jncots
Copy link
Collaborator Author

jncots commented Nov 25, 2025

For easier readability, chromo -h help message is formatted, so that each model on its own line.

Copy link
Member

@afedynitch afedynitch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice implementation!

It could be a bit brittle if numpy makes many internal changes. We should add some sort of cron job to build chromo once a week with the latest version to check that but this doesn't need to happen here.

@afedynitch
Copy link
Member

@jncots I still get an error when testing on WSL
FAILED tests/test_rng_state.py::test_rng_state_bitgens[Philox-33333-EposLHC] - AssertionError: queue empty, process probably crashed

@jncots
Copy link
Collaborator Author

jncots commented Dec 16, 2025

@afedynitch, I tested in my WSL environment and could not reproduce the problem. For a reference:

Ubuntu 22.04.5 LTS (Jammy Jellyfish)
Kernel: 6.6.87.2-microsoft-standard-WSL2 (WSL2)

Python: 3.12.12
GCC: 11.4.0

numpy: 2.3.5
scipy: 1.16.3
particle: 0.26.0
rich: 14.2.0

meson: 1.10.0
meson-python: 0.18.0
ninja: 1.13.0

pytest: 9.0.2
pytest-benchmark: 5.2.3
pytest-xdist: 3.8.0

pyhepmc: 2.15.1
uproot: 5.6.8
awkward: 2.8.11
chromo Version: 0.10.1

@jncots
Copy link
Collaborator Author

jncots commented Dec 16, 2025

The PR #247 adds serialization and deserialization for the native RNG state of the Pythia8, that allows saving and restoring the state of the generator.

The PR introduces improvements and fixes related to RNGs across multiple generators and addresses related logging issues.


Key Features and Changes

  • Pythia8 RNG Serialization: Implements get/set methods for the native Pythia8 RNG state (RndmState) in the pybind11 C++ interface. These are exposed to Python via the Pythia8 class's random_state property.
  • Fix for NumPy Bit Generators: Fixes a critical bug where the previous save/restore functionality failed because random_state returned None. The fix involves switching to the explicit API of Python bit generators (e.g., PCG64, MT19937, PHILOX) instead of relying on the standard __getstate__() and __setstate__() methods.
  • Universal NumPy Generator Support: The implementation is enhanced to allow the use of any of the five native NumPy bit generators by passing an instance of the generator class as the seed during model initialization (e.g., model(..., seed=MT19937(seed=42))).
    • Explicit C-structs and IDs were added for each of the five NumPy bit generators in the underlying C code to manage the state saving/restoring process correctly.
  • Generator State Logging (CLI): Implements a logging feature for CLI runs, creating a log file alongside the generated output. This file records general kinematics, model settings, and information about the internal state/settings of the Fortran-level generator variables (initially for Pythia8 and Sibyll, later extended to Pythia6).

Known Issues and Resolved Dependencies

  • EposLHC/UrQMD Issue: The RNG tests failed for EposLHCR because it internally relies on the UrQMD generator, which is known to have issues with reproducibility after resetting the RNG state. The investigation concluded that this is due to "hidden state" (possibly in common blocks or SAVE statements) that is not being reset, and this issue is not solvable within the scope of this PR.
  • Pythia6 + MT19937 Incompatibility: A specific skip was added for the combination of the Pythia6 model and the MT19937 bit generator interaction issue.
  • Documentation Improvement: The output for the chromo -h help message was improved for better readability, formatting each model on its own line.

@jncots jncots merged commit b13c949 into main Dec 16, 2025
5 checks passed
@jncots jncots deleted the pythia8_rng_getset branch December 16, 2025 06:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants