Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[math] Add vector filling overload for scalar distributions of TRandom #17077

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions bindings/pyroot/pythonizations/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ ROOT_ADD_PYUNITTEST(pyroot_pyz_tvectort_getitem tvectort_getitem.py)
ROOT_ADD_PYUNITTEST(pyroot_pyz_tvector3_len tvector3_len.py)
ROOT_ADD_PYUNITTEST(pyroot_pyz_tvector3_getitem tvector3_getitem.py)

# TRandom pythonizations
ROOT_ADD_PYUNITTEST(pyroot_pyz_trandom_numpy trandom_array.py)

# TString pythonisations
ROOT_ADD_PYUNITTEST(pyroot_pyz_tstring_len tstring_len.py)
ROOT_ADD_PYUNITTEST(pyroot_pyz_tstring_str_repr tstring_str_repr.py)
Expand Down
73 changes: 73 additions & 0 deletions bindings/pyroot/pythonizations/test/trandom_array.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import unittest

import ROOT
import numpy as np

class TRandomNumpyArrays(unittest.TestCase):
"""
Test for pythonizations thst allow filling numpy arrays with TRandom scalar
RNGs.
We only cover the technical aspect, ensuring the array if filled. With the seed
set, all executions should be repeatable.
"""

# Tests
def test_binomial(self):
r = ROOT.TRandom2(123)
vi = np.zeros(100, dtype=np.int32)
r.BinomialN(100, vi, 123, 0.12345)
self.assertEqual(np.count_nonzero(vi), len(vi))

def test_breit_wigner(self):
r = ROOT.TRandom2(123)
vd = np.zeros(100, dtype=np.float64)
r.BreitWignerN(100, vd, 123, 0.12345)
self.assertEqual(np.count_nonzero(vd), len(vd))

def test_exp(self):
r = ROOT.TRandom2(123)
vd = np.zeros(100, dtype=np.float64)
r.ExpN(100, vd, 1.5)
self.assertEqual(np.count_nonzero(vd), len(vd))

def test_gaus(self):
r = ROOT.TRandom2(123)
vd = np.zeros(100, dtype=np.float64)
r.GausN(100, vd, 5, 1.5)
self.assertEqual(np.count_nonzero(vd), len(vd))

def test_integer(self):
r = ROOT.TRandom2(123)
vui = np.zeros(100, dtype=np.uint32)
r.IntegerN(100, vui, 15)
self.assertGreater(np.count_nonzero(vui), 0.9 * len(vui))

def test_landau(self):
r = ROOT.TRandom2(123)
vd = np.zeros(100, dtype=np.float64)
r.LandauN(100, vd, 5, 1.5)
self.assertEqual(np.count_nonzero(vd), len(vd))

def test_poisson(self):
r = ROOT.TRandom2(123)
vull = np.zeros(100, dtype=np.ulonglong)
r.PoissonN(100, vull, 15)
self.assertGreater(np.count_nonzero(vull), 0.9 * len(vull))

def test_poissond_d(self):
r = ROOT.TRandom2(123)
vd = np.zeros(100, dtype=np.float64)
r.PoissonDN(100, vd, 15)
self.assertGreater(np.count_nonzero(vd), 0.9 * len(vd))

def test_uniform(self):
r = ROOT.TRandom2(123)
vd = np.zeros(100, dtype=np.float64)
r.UniformN(100, vd, 5.0)
self.assertEqual(np.count_nonzero(vd), len(vd))

def test_uniform_2_params(self):
r = ROOT.TRandom2(123)
vd = np.zeros(100, dtype=np.float64)
r.UniformN(100, vd, 5, 1.5)
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks good! We can use something like:

 def _TRandom_Uniform(self, ...):

     import ROOT
     import numpy
 
    #  if user expects a vector:
         return self.UniformN(...)
    # if matches original Uniform implementation
        return self._Uniform(...)
    
   @pythonization('TRandom')
 def pythonize_trandom(klass):
     klass._Uniform = klass.Uniform
     klass.Uniform = _TRandom_Uniform

To automatically forward to the loop wrappers when the user expects an array of random numbers, or to the original generation function when the input signatures follow the traditional format

self.assertEqual(np.count_nonzero(vd), len(vd))
33 changes: 33 additions & 0 deletions math/mathcore/inc/TRandom.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@
#include "TNamed.h"

class TRandom : public TNamed, public ROOT::Math::TRandomEngine {
private:
template <typename DType, class... Args_t>
void loopWrapper(DType (TRandom::*func)(Args_t...), std::size_t n, DType *out, Args_t... args)
{
for (std::size_t i = 0; i < n; ++i) {
out[i] = (this->*func)(args...);
}
}

protected:
UInt_t fSeed; //Random number generator seed
Expand All @@ -33,15 +41,35 @@ class TRandom : public TNamed, public ROOT::Math::TRandomEngine {
TRandom(UInt_t seed=65539);
~TRandom() override;
virtual Int_t Binomial(Int_t ntot, Double_t prob);
virtual void BinomialN(Int_t n, Int_t *x, Int_t ntot, Double_t prob)
{
loopWrapper(&TRandom::Binomial, n, x, ntot, prob);
}
virtual Double_t BreitWigner(Double_t mean=0, Double_t gamma=1);
virtual void BreitWignerN(Int_t n, Double_t *x, Double_t mean = 0, Double_t gamma = 1)
{
loopWrapper(&TRandom::BreitWigner, n, x, mean, gamma);
}
virtual void Circle(Double_t &x, Double_t &y, Double_t r);
virtual Double_t Exp(Double_t tau);
virtual void ExpN(Int_t n, Double_t *x, Double_t tau) { loopWrapper(&TRandom::Exp, n, x, tau); }
virtual Double_t Gaus(Double_t mean=0, Double_t sigma=1);
virtual void GausN(Int_t n, Double_t *x, Double_t mean = 0, Double_t sigma = 1)
{
loopWrapper(&TRandom::Gaus, n, x, mean, sigma);
}
virtual UInt_t GetSeed() const;
virtual UInt_t Integer(UInt_t imax);
virtual void IntegerN(Int_t n, UInt_t *x, UInt_t imax) { loopWrapper(&TRandom::Integer, n, x, imax); }
virtual Double_t Landau(Double_t mean=0, Double_t sigma=1);
virtual void LandauN(Int_t n, Double_t *x, Double_t mean = 0, Double_t sigma = 1)
{
loopWrapper(&TRandom::Landau, n, x, mean, sigma);
}
virtual ULong64_t Poisson(Double_t mean);
virtual void PoissonN(Int_t n, ULong64_t *x, Double_t mean) { loopWrapper(&TRandom::Poisson, n, x, mean); }
virtual Double_t PoissonD(Double_t mean);
virtual void PoissonDN(Int_t n, Double_t *x, Double_t mean) { loopWrapper(&TRandom::PoissonD, n, x, mean); }
virtual void Rannor(Float_t &a, Float_t &b);
virtual void Rannor(Double_t &a, Double_t &b);
virtual void ReadRandom(const char *filename);
Expand All @@ -53,7 +81,12 @@ class TRandom : public TNamed, public ROOT::Math::TRandomEngine {
virtual void RndmArray(Int_t n, Double_t *array);
virtual void Sphere(Double_t &x, Double_t &y, Double_t &z, Double_t r);
virtual Double_t Uniform(Double_t x1=1);
virtual void UniformN(Int_t n, Double_t *x, Double_t x1 = 1) { loopWrapper(&TRandom::Uniform, n, x, x1); }
virtual Double_t Uniform(Double_t x1, Double_t x2);
virtual void UniformN(Int_t n, Double_t *x, Double_t x1, Double_t x2)
{
loopWrapper(&TRandom::Uniform, n, x, x1, x2);
}
virtual void WriteRandom(const char *filename) const;

ClassDefOverride(TRandom,3) //Simple Random number generator (periodicity = 10**9)
Expand Down
Loading