Skip to content

[math] Make TRandom compatible with UniformRandomBitGenerator - Issue # 7979#22508

Open
medeirosdev wants to merge 3 commits into
root-project:masterfrom
medeirosdev:trandom-std-interface
Open

[math] Make TRandom compatible with UniformRandomBitGenerator - Issue # 7979#22508
medeirosdev wants to merge 3 commits into
root-project:masterfrom
medeirosdev:trandom-std-interface

Conversation

@medeirosdev

Copy link
Copy Markdown

TRandom and its subclasses can now be used directly with standard library algorithms like std::shuffle and std::uniform_int_distribution.

This is done by adding the three required members to TRandom: result_type, min(), max(), and operator(). The operator() delegates to the virtual Rndm(), so polymorphism is preserved across all TRandom subclasses.

Fixes #7979

This Pull request:

Changes or fixes:

Checklist:

  • tested changes locally
  • updated the docs (if necessary)

This PR fixes #

TRandom and its subclasses can now be used directly with standard
library algorithms like std::shuffle and std::uniform_int_distribution.

This is done by adding the three required members to TRandom:
result_type, min(), max(), and operator(). The operator() delegates
to the virtual Rndm(), so polymorphism is preserved across all
TRandom subclasses.

Fixes root-project#7979
Comment thread math/mathcore/inc/TRandom.h Outdated
// NOTE: Rndm() returns a double in ]0,1[, so converting back to UInt_t
// has a small precision loss. Subclasses with access to raw integer output
// could override this for better accuracy.
result_type operator()() { return static_cast<result_type>(Rndm() * (static_cast<double>(max()) + 1.0)); }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
result_type operator()() { return static_cast<result_type>(Rndm() * (static_cast<double>(max()) + 1.0)); }
virtual result_type operator()() { return static_cast<result_type>(Rndm() * (static_cast<double>(max()) + 1.0)); }

since you mention that we allow overriding

I'd also suggest doing the overriding also here in this PR in TRandom1/2/3 classes to avoid division and then remultiplication by fScale

Comment thread math/mathcore/inc/TRandom.h Outdated
using result_type = UInt_t;
static constexpr result_type min() { return 0; }
static constexpr result_type max() { return std::numeric_limits<UInt_t>::max(); }
// NOTE: Rndm() returns a double in ]0,1[, so converting back to UInt_t

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

thanks for this!
Please convert to doxygen format

@ferdymercury

Copy link
Copy Markdown
Collaborator

also to discuss whether this can be unified with tmva/tmva/inc/TMVA/Tools.h to avoid code duplicities

- Convert // NOTE: comment to Doxygen /// \note format in TRandom.h
- Make TRandom::operator()() virtual so subclasses can override it
- Add TRandom2::operator()() returning raw Tausworthe XOR output,
  avoiding the double round-trip through Rndm() / kScale
- Add TRandom3::operator()() returning raw Mersenne Twister output,
  same motivation as above
- Extend testMathRandom test5() to exercise TRandom2 as a URBG

TRandom1 (RANLUX) has a float-based internal state with no meaningful
raw integer path, so it keeps the base-class fallback.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@medeirosdev

Copy link
Copy Markdown
Author

Thanks for the review!

Addressed all three points:

  • Converted the // NOTE: comment to Doxygen /// \note format
  • Made operator()() virtual in TRandom
  • Added operator()() overrides in TRandom2 and TRandom3 that return the raw integer output directly, skipping the double round-trip

For TRandom1 (RANLUX), the internal state is float-based (fFloatSeedTable), so there's no raw integer to expose — the base class fallback is the best we can do there.

@medeirosdev

medeirosdev commented Jun 7, 2026

Copy link
Copy Markdown
Author

also to discuss whether this can be unified with tmva/tmva/inc/TMVA/Tools.h to avoid code duplicities

The main difference is that our approach embeds the interface directly in TRandom, so any existing TRandom* pointer works as a URBG without wrapping. It also lets subclasses override operator()() to return raw integers directly (as done here for TRandom2 and TRandom3), which TMVA::RandomGenerator doesn't do, it goes through Integer() which still uses a double internally.

i'm open to explore that in a follow-up PR if that's the preferred direction.

@ferdymercury ferdymercury Jun 7, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could we call here the overridden operator () to avoid duplicities and save a few lines code? Same for Trandom3

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes we could!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done!

Avoids duplicating the Tausworthe/Mersenne Twister state-advance logic.
Rndm() now calls operator()() to get the raw integer and then scales it
to a double, keeping a single source of truth for the generator core.
The TAUSWORTHE macro is moved to file scope so RndmArray can still use it.
Comment on lines 59 to 60
const double kScale = 2.3283064365386963e-10; // range in 32 bit ( 1/(2**32)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

kScale could become constexpr

@ferdymercury ferdymercury left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

thanks!

@medeirosdev

medeirosdev commented Jun 9, 2026

Copy link
Copy Markdown
Author

@guitargeek @hageboeck hey guys! could you check this pr? please

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make TRandom compatible with the std UniformRandomBitGenerator interface

4 participants