// @(#)root/unuran:$Id$ // Authors: L. Moneta, J. Leydold Tue Sep 26 16:25:09 2006 /********************************************************************** * * * Copyright (c) 2006 LCG ROOT Math Team, CERN/PH-SFT * * * * * **********************************************************************/ // Implementation file for class TUnuran #include "TUnuran.h" #include "TUnuranContDist.h" #include "TUnuranMultiContDist.h" #include "TUnuranDiscrDist.h" #include "TUnuranEmpDist.h" #include "UnuranRng.h" #include "UnuranDistrAdapter.h" #include "TRandom.h" #include "TSystem.h" #include "TH1.h" #include #include #include "TError.h" TUnuran::TUnuran(TRandom * r, unsigned int debugLevel) : fGen(0), fUdistr(0), fUrng(0), fRng(r) { // constructor implementation with a ROOT random generator // if no generator is given the ROOT default is used if (fRng == 0) fRng = gRandom; // set debug level at global level // (should be in a static initialization function of the library ? ) if ( debugLevel > 1) unur_set_default_debug(UNUR_DEBUG_ALL); else if (debugLevel == 1) unur_set_default_debug(UNUR_DEBUG_INIT); else unur_set_default_debug(UNUR_DEBUG_OFF); } TUnuran::~TUnuran() { // Destructor implementation if (fGen != 0) unur_free(fGen); if (fUrng != 0) unur_urng_free(fUrng); // we can delete now the distribution object if (fUdistr != 0) unur_distr_free(fUdistr); } //private (no impl.) TUnuran::TUnuran(const TUnuran &) { // Implementation of copy constructor. } TUnuran & TUnuran::operator = (const TUnuran &rhs) { // Implementation of assignment operator. if (this == &rhs) return *this; // time saving self-test return *this; } bool TUnuran::Init(const std::string & dist, const std::string & method) { // initialize with a string std::string s = dist + " & " + method; fGen = unur_str2gen(s.c_str() ); if (fGen == 0) { Error("Init","Cannot create generator object"); return false; } if (! SetRandomGenerator() ) return false; return true; } bool TUnuran::Init(const TUnuranContDist & distr, const std::string & method) { // initialization with a distribution and and generator // the distribution object is copied in and managed by this class // use auto_ptr to manage previously existing distribution objects TUnuranContDist * distNew = distr.Clone(); fDist = std::auto_ptr< TUnuranBaseDist>(distNew); fMethod = method; if (! SetContDistribution(*distNew) ) return false; if (! SetMethodAndInit() ) return false; if (! SetRandomGenerator() ) return false; return true; } bool TUnuran::Init(const TUnuranMultiContDist & distr, const std::string & method) { // initialization with a distribution and method // the distribution object is copied in and managed by this class // use auto_ptr to manage previously existing distribution objects TUnuranMultiContDist * distNew = distr.Clone(); fDist = std::auto_ptr< TUnuranBaseDist>(distNew); fMethod = method; if (! SetMultiDistribution(*distNew) ) return false; if (! SetMethodAndInit() ) return false; if (! SetRandomGenerator() ) return false; return true; } bool TUnuran::Init(const TUnuranDiscrDist & distr, const std::string & method ) { // initialization with a distribution and and generator // the distribution object is copied in and managed by this class // use auto_ptr to manage previously existing distribution objects TUnuranDiscrDist * distNew = distr.Clone(); fDist = std::auto_ptr< TUnuranBaseDist>(distNew); fMethod = method; if (! SetDiscreteDistribution(*distNew) ) return false; if (! SetMethodAndInit() ) return false; if (! SetRandomGenerator() ) return false; return true; } bool TUnuran::Init(const TUnuranEmpDist & distr, const std::string & method ) { // initialization with a distribution and and generator // the distribution object is copied in and managed by this class // use auto_ptr to manage previously existing distribution objects TUnuranEmpDist * distNew = distr.Clone(); fDist = std::auto_ptr< TUnuranBaseDist>(distNew); fMethod = method; if (distr.IsBinned()) fMethod = "hist"; else if (distr.NDim() > 1) fMethod = "vempk"; if (! SetEmpiricalDistribution(*distNew) ) return false; if (! SetMethodAndInit() ) return false; if (! SetRandomGenerator() ) return false; return true; } bool TUnuran::SetRandomGenerator() { // set an external random generator if (fRng == 0) return false; if (fGen == 0) return false; fUrng = unur_urng_new(&UnuranRng::Rndm, fRng ); if (fUrng == 0) return false; unsigned int ret = 0; ret |= unur_urng_set_delete(fUrng, &UnuranRng::Delete); ret |= unur_urng_set_seed(fUrng, &UnuranRng::Seed); if (ret != 0) return false; unur_chg_urng( fGen, fUrng); return true; } bool TUnuran::SetContDistribution(const TUnuranContDist & dist ) { // internal method to set in unuran the function pointer for a continuous univariate distribution if (fUdistr != 0) unur_distr_free(fUdistr); fUdistr = unur_distr_cont_new(); if (fUdistr == 0) return false; unsigned int ret = 0; ret = unur_distr_set_extobj(fUdistr, &dist); if ( ! dist.IsLogPdf() ) { ret |= unur_distr_cont_set_pdf(fUdistr, &ContDist::Pdf); ret |= unur_distr_cont_set_dpdf(fUdistr, &ContDist::Dpdf); if (dist.HasCdf() ) ret |= unur_distr_cont_set_cdf(fUdistr, &ContDist::Cdf); } else { // case user provides log of pdf ret |= unur_distr_cont_set_logpdf(fUdistr, &ContDist::Pdf); ret |= unur_distr_cont_set_dlogpdf(fUdistr, &ContDist::Dpdf); } double xmin, xmax = 0; if (dist.GetDomain(xmin,xmax) ) { ret = unur_distr_cont_set_domain(fUdistr,xmin,xmax); if (ret != 0) { Error("SetContDistribution","invalid domain xmin = %g xmax = %g ",xmin,xmax); return false; } } if (dist.HasMode() ) { ret = unur_distr_cont_set_mode(fUdistr, dist.Mode()); if (ret != 0) { Error("SetContDistribution","invalid mode given, mode = %g ",dist.Mode()); return false; } } if (dist.HasPdfArea() ) { ret = unur_distr_cont_set_pdfarea(fUdistr, dist.PdfArea()); if (ret != 0) { Error("SetContDistribution","invalid area given, area = %g ",dist.PdfArea()); return false; } } return (ret ==0) ? true : false; } bool TUnuran::SetMultiDistribution(const TUnuranMultiContDist & dist ) { // internal method to set in unuran the function pointer for a multivariate distribution if (fUdistr != 0) unur_distr_free(fUdistr); fUdistr = unur_distr_cvec_new(dist.NDim() ); if (fUdistr == 0) return false; unsigned int ret = 0; ret |= unur_distr_set_extobj(fUdistr, &dist ); if ( ! dist.IsLogPdf() ) { ret |= unur_distr_cvec_set_pdf(fUdistr, &MultiDist::Pdf); ret |= unur_distr_cvec_set_dpdf(fUdistr, &MultiDist::Dpdf); ret |= unur_distr_cvec_set_pdpdf(fUdistr, &MultiDist::Pdpdf); } else { ret |= unur_distr_cvec_set_logpdf(fUdistr, &MultiDist::Pdf); ret |= unur_distr_cvec_set_dlogpdf(fUdistr, &MultiDist::Dpdf); ret |= unur_distr_cvec_set_pdlogpdf(fUdistr, &MultiDist::Pdpdf); } const double * xmin = dist.GetLowerDomain(); const double * xmax = dist.GetUpperDomain(); if ( xmin != 0 || xmax != 0 ) { ret = unur_distr_cvec_set_domain_rect(fUdistr,xmin,xmax); if (ret != 0) { Error("SetMultiDistribution","invalid domain"); return false; } #ifdef OLDVERS Error("SetMultiDistribution","domain setting not available in UNURAN 0.8.1"); #endif } const double * xmode = dist.GetMode(); if (xmode != 0) { ret = unur_distr_cvec_set_mode(fUdistr, xmode); if (ret != 0) { Error("SetMultiDistribution","invalid mode"); return false; } } return (ret ==0) ? true : false; } bool TUnuran::SetEmpiricalDistribution(const TUnuranEmpDist & dist) { // internal method to set in unuran the function pointer for am empiral distribution (from histogram) if (fUdistr != 0) unur_distr_free(fUdistr); if (dist.NDim() == 1) fUdistr = unur_distr_cemp_new(); else fUdistr = unur_distr_cvemp_new(dist.NDim() ); if (fUdistr == 0) return false; unsigned int ret = 0; // get info from histogram if (dist.IsBinned() ) { int nbins = dist.Data().size(); double min = dist.LowerBin(); double max = dist.UpperBin(); const double * pv = &(dist.Data().front()); ret |= unur_distr_cemp_set_hist(fUdistr, pv, nbins, min, max); #ifdef OLDVERS Error("SetEmpiricalDistribution","hist method not available in UNURAN 0.8.1"); #endif } else { const double * pv = &dist.Data().front(); // n is number of points (size/ndim) int n = dist.Data().size()/dist.NDim(); if (dist.NDim() == 1) ret |= unur_distr_cemp_set_data(fUdistr, pv, n); else ret |= unur_distr_cvemp_set_data(fUdistr, pv, n); } if (ret != 0) { Error("SetEmpiricalDistribution","invalid distribution object"); return false; } return true; } bool TUnuran::SetDiscreteDistribution(const TUnuranDiscrDist & dist) { // internal method to set in unuran the function pointer for a discrete univariate distribution if (fUdistr != 0) unur_distr_free(fUdistr); fUdistr = unur_distr_discr_new(); if (fUdistr == 0) return false; unsigned int ret = 0; // if a probability mesh function is provided if (dist.ProbVec().size() == 0) { ret = unur_distr_set_extobj(fUdistr, &dist ); ret |= unur_distr_discr_set_pmf(fUdistr, &DiscrDist::Pmf); if (dist.HasCdf() ) ret |= unur_distr_discr_set_cdf(fUdistr, &DiscrDist::Cdf); } else { // case user provides vector of probabilities ret |= unur_distr_discr_set_pv(fUdistr, &dist.ProbVec().front(), dist.ProbVec().size() ); } int xmin, xmax = 0; if (dist.GetDomain(xmin,xmax) ) { ret = unur_distr_discr_set_domain(fUdistr,xmin,xmax); if (ret != 0) { Error("SetDiscrDistribution","invalid domain xmin = %d xmax = %d ",xmin,xmax); return false; } } if (dist.HasMode() ) { ret = unur_distr_discr_set_mode(fUdistr, dist.Mode()); if (ret != 0) { Error("SetContDistribution","invalid mode given, mode = %d ",dist.Mode()); return false; } } if (dist.HasProbSum() ) { ret = unur_distr_discr_set_pmfsum(fUdistr, dist.ProbSum()); if (ret != 0) { Error("SetContDistribution","invalid sum given, mode = %g ",dist.ProbSum()); return false; } } return (ret ==0) ? true : false; } //bool TUnuran::SetMethodAndInit(const std::string & s) { bool TUnuran::SetMethodAndInit() { // internal function to set a method from a distribution and // initialize unuran with the given distribution. if (fUdistr == 0) return false; struct unur_slist *mlist = NULL; UNUR_PAR * par = _unur_str2par(fUdistr, fMethod.c_str(), &mlist); if (par == 0) { Error("SetMethod","missing distribution information or syntax error"); if (mlist != 0) _unur_slist_free(mlist); return false; } // set unuran to not use a private copy of the distribution object unur_set_use_distr_privatecopy (par, false); // need to free fGen if already existing ? if (fGen != 0 ) unur_free(fGen); fGen = unur_init(par); _unur_slist_free(mlist); if (fGen == 0) { Error("SetMethod","initializing Unuran: condition for method violated"); return false; } return true; } int TUnuran::SampleDiscr() { // sample one-dimensional distribution assert(fGen != 0); return unur_sample_discr(fGen); } double TUnuran::Sample() { // sample one-dimensional distribution assert(fGen != 0); return unur_sample_cont(fGen); } bool TUnuran::SampleMulti(double * x) { // sample multidimensional distribution if (fGen == 0) return false; unur_sample_vec(fGen,x); return true; } void TUnuran::SetSeed(unsigned int seed) { return fRng->SetSeed(seed); } bool TUnuran::SetLogLevel(unsigned int debugLevel) { if (fGen == 0) return false; int ret = 0; if ( debugLevel > 1) ret |= unur_chg_debug(fGen, UNUR_DEBUG_ALL); else if (debugLevel == 1) ret |= unur_chg_debug(fGen, UNUR_DEBUG_ALL); else ret |= unur_chg_debug(fGen, UNUR_DEBUG_OFF); return (ret ==0) ? true : false; } bool TUnuran::InitPoisson(double mu, const std::string & method) { // initializaton for a Poisson double p[1]; p[0] = mu; fUdistr = unur_distr_poisson(p,1); fMethod = method; if (fUdistr == 0) return false; if (! SetMethodAndInit() ) return false; if (! SetRandomGenerator() ) return false; return true; } bool TUnuran::InitBinomial(unsigned int ntot, double prob, const std::string & method ) { // initializaton for a Binomial double par[2]; par[0] = ntot; par[1] = prob; fUdistr = unur_distr_binomial(par,2); fMethod = method; if (fUdistr == 0) return false; if (! SetMethodAndInit() ) return false; if (! SetRandomGenerator() ) return false; return true; } bool TUnuran::ReInitDiscrDist(unsigned int npar, double * par) { // re-initialization of UNURAN without freeing and creating a new fGen object // works only for pre-defined distribution by changing their parameters if (!fGen ) return false; if (!fUdistr) return false; unur_distr_discr_set_pmfparams(fUdistr,par,npar); int iret = unur_reinit(fGen); if (iret) Warning("ReInitDiscrDist","re-init failed - a full initizialization must be performed"); return (!iret); }