Commit 27491f43 authored by thomas.forbriger's avatar thomas.forbriger

libtfxx [FEATURE]: support selection of random number generator type

Provide new constructor parameter and new query functions.
Provide a test program for RNGgaussian.

GSL provides a set of different random number generators. The type to be
used can be selected through environment variables. With the new
parameters provided by RNGgaussian, the calling program may select the
type.
parent bdb5ad13
...@@ -96,7 +96,7 @@ SRC=$(wildcard $(addsuffix /*.cc,$(DIRSTOINCLUDE))) ...@@ -96,7 +96,7 @@ SRC=$(wildcard $(addsuffix /*.cc,$(DIRSTOINCLUDE)))
TESTS=tests/commandlinetest.cc tests/fortraniotest.cc \ TESTS=tests/commandlinetest.cc tests/fortraniotest.cc \
tests/stringtest.cc tests/handletest.cc tests/regextest.cc \ tests/stringtest.cc tests/handletest.cc tests/regextest.cc \
tests/filetest.cc tests/rangetest.cc tests/fstest.cc \ tests/filetest.cc tests/rangetest.cc tests/fstest.cc \
tests/exceptiontest.cc tests/misctest.cc tests/exceptiontest.cc tests/misctest.cc tests/rngtest.cc
#tests/blitztest.cc #tests/blitztest.cc
FTESTS=$(wildcard tests/*.f) FTESTS=$(wildcard tests/*.f)
DOXYTXT=$(wildcard doxygen*.txt) DOXYTXT=$(wildcard doxygen*.txt)
......
...@@ -28,52 +28,125 @@ ...@@ -28,52 +28,125 @@
* *
* REVISIONS and CHANGES * REVISIONS and CHANGES
* - 19/12/2007 V1.0 Thomas Forbriger * - 19/12/2007 V1.0 Thomas Forbriger
* - 01/11/2019 V1.1 support selection of random number generator type
* *
* ============================================================================ * ============================================================================
*/ */
#define TF_RNG_CC_VERSION \ #define TF_RNG_CC_VERSION \
"TF_RNG_CC V1.0 " "TF_RNG_CC V1.1"
#include <iostream>
#include <tfxx/rng.h> #include <tfxx/rng.h>
#include <time.h> #include <time.h>
#include <gsl/gsl_rng.h>
#include <string>
namespace tfxx { namespace tfxx {
namespace numeric { namespace numeric {
/* ---------------------------------------------------------------------- */
// class RNGgaussian
// constructor // constructor
RNGgaussian::RNGgaussian(const double& std, RNGgaussian::RNGgaussian(const double& std,
const double& mean): const double& mean,
const char* type):
Mstd(std), Mmean(mean) Mstd(std), Mmean(mean)
{ {
gsl_rng_env_setup(); if (std::string(type) == "default")
const gsl_rng_type* T=gsl_rng_default; {
MR=gsl_rng_alloc(T); gsl_rng_env_setup();
this->set(); const gsl_rng_type* T=gsl_rng_default;
MR=gsl_rng_alloc(T);
TFXX_assert(MR!=NULL, "gsl_rng_alloc(T) failed to initialize RNG");
}
else
{
const gsl_rng_type* T=NULL;
const gsl_rng_type **t, **t0;
t0 = gsl_rng_types_setup ();
for (t = t0; *t != 0; t++)
{
if (std::string((*t)->name)==std::string(type)) { T=(*t); }
}
TFXX_Xassert(T!=NULL, type, UTException);
MR=gsl_rng_alloc(T);
TFXX_assert(MR!=NULL, "gsl_rng_alloc(T) failed to initialize RNG");
}
this->set(gsl_rng_default_seed);
} }
/* ---------------------------------------------------------------------- */
// destructor // destructor
RNGgaussian::~RNGgaussian() RNGgaussian::~RNGgaussian()
{ {
gsl_rng_free(MR); gsl_rng_free(MR);
} }
/* ---------------------------------------------------------------------- */
// return value // return value
double RNGgaussian::value() const double RNGgaussian::value() const
{ return(Mmean+Mstd*gsl_ran_ugaussian(MR)); } { return(Mmean+Mstd*gsl_ran_ugaussian(MR)); }
/* ---------------------------------------------------------------------- */
// return value // return value
double RNGgaussian::operator()() const double RNGgaussian::operator()() const
{ return(this->value()); } { return(this->value()); }
/* ---------------------------------------------------------------------- */
// set seed // set seed
void RNGgaussian::set(const unsigned long int& seed) const void RNGgaussian::set(const unsigned long int& seed)
{ gsl_rng_set(MR, seed); } {
gsl_rng_set(MR, seed);
Mseed=seed;
}
/* ---------------------------------------------------------------------- */
// set seed using time // set seed using time
void RNGgaussian::set() const void RNGgaussian::set()
{ this->set(time(0)); } { this->set(time(0)); }
/* ---------------------------------------------------------------------- */
/*! \brief Print list of random number generators to stream.
*
* \sa https://www.gnu.org/software/gsl/doc/html/rng.html#random-number-generator-algorithms
*/
void RNGgaussian::rng_list_types(std::ostream& os)
{
const gsl_rng_type **t, **t0;
t0 = gsl_rng_types_setup ();
for (t = t0; *t != 0; t++)
{
os << std::string((*t)->name) << std::endl;
}
} // RNGgaussian::rng_list_types(std::ostream* os)
/* ====================================================================== */
RNGgaussian::UTException::UTException(const char* type,
const char* file,
const int& line,
const char* condition):
TBase("unkown RNG type requested", file, line, condition),
Mtype(type)
{
if (this->report_on_construct_is_true())
{
std::cerr << " Requested RNG type: " << Mtype << std::endl;
}
} // RNGgaussian::UTException::UTException
} // namespace numeric } // namespace numeric
} // namespace tfxx } // namespace tfxx
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
* *
* REVISIONS and CHANGES * REVISIONS and CHANGES
* - 19/12/2007 V1.0 Thomas Forbriger * - 19/12/2007 V1.0 Thomas Forbriger
* - 01/11/2019 V1.1 support selection of random number generator type
* *
* ============================================================================ * ============================================================================
*/ */
...@@ -36,8 +37,11 @@ ...@@ -36,8 +37,11 @@
#ifndef TF_RNG_H_VERSION #ifndef TF_RNG_H_VERSION
#define TF_RNG_H_VERSION \ #define TF_RNG_H_VERSION \
"TF_RNG_H V1.0 " "TF_RNG_H V1.1"
#include <tfxx/error.h>
#include <ostream>
#include <string>
#include <gsl/gsl_rng.h> #include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h> #include <gsl/gsl_randist.h>
...@@ -56,28 +60,75 @@ namespace tfxx { ...@@ -56,28 +60,75 @@ namespace tfxx {
*/ */
namespace numeric { namespace numeric {
/* ---------------------------------------------------------------------- */
/*! \brief Provide random numbers with Gaussian distribution. /*! \brief Provide random numbers with Gaussian distribution.
* \ingroup group_numeric * \ingroup group_numeric
* *
* This class is an interface to the GSL random number generator. * This class is an interface to the GSL random number generator.
* \sa https://www.gnu.org/software/gsl/doc/html/rng.html
*
* \note
* This class specifically returns values based on a normal distribution.
* If for some reason a second class shall be provided to return values
* based on a uniform distribution, only the value() member function will
* be different. It then would be appropriate to define an abstract base
* class from which RNGgaussion and RNGuniform are derived.
*/ */
class RNGgaussian { class RNGgaussian {
public: public:
//! initialize random number generator //! initialize random number generator
explicit RNGgaussian(const double& std=1., explicit RNGgaussian(const double& std=1.,
const double& mean=0.); const double& mean=0.,
const char* type="default");
~RNGgaussian(); ~RNGgaussian();
//! returns a random number //! returns a random number
double operator()() const; double operator()() const;
//! returns a random number //! returns a random number
double value() const; double value() const;
//! feed with seed value //! feed with seed value
void set(const unsigned long int& seed) const; void set(const unsigned long int& seed);
//! use time as seed value //! use time as seed value
void set() const; void set();
//! return type of random number generator
std::string type() const {return std::string(MR->type->name); }
//! return last seed value
unsigned long int seed() const { return Mseed; }
//! return standard deviation
double std() const { return Mstd; }
//! return mean
double mean() const { return Mmean; }
//! print list of random number generators to stream.
static void rng_list_types(std::ostream& os);
private: private:
//! store standard deviation and mean value
double Mstd, Mmean; double Mstd, Mmean;
//! pointer to RNG
gsl_rng* MR; gsl_rng* MR;
//! memorize last seed value
unsigned long int Mseed;
public:
/*! \brief exception class for RNG indicating request for unkown type
*
* \sa TFXX_Xassert
* \sa tfxx::error::Exception
*/
class UTException: public tfxx::error::Exception
{
public:
//! base class
typedef tfxx::error::Exception TBase;
//! Create with message, failed assertion, and code position
UTException(const char* type,
const char* file,
const int& line,
const char* condition);
//! provide explicit virtual destructor
virtual ~UTException() { }
private:
//! pointer name string of requested RNG type
const char* Mtype;
}; // class UTException
}; // class RNGgaussian }; // class RNGgaussian
} // namespace numeric } // namespace numeric
......
...@@ -72,7 +72,9 @@ STANDARDTEST= ...@@ -72,7 +72,9 @@ STANDARDTEST=
# all that need the binary library # all that need the binary library
BINARYTEST=commandlinetest fortraniotest blitztest stringtest \ BINARYTEST=commandlinetest fortraniotest blitztest stringtest \
handletest regextest filetest rangetest fstest exceptiontest \ handletest regextest filetest rangetest fstest exceptiontest \
misctest misctest
# all that need the binary library and libgsl
GSLTEST=rngtest
# F77 part of the I/O test # F77 part of the I/O test
FORTRANTEST=fortranF77 FORTRANTEST=fortranF77
...@@ -92,6 +94,7 @@ $(addsuffix .o,$(CXXTESTS)): %.o: %.cc ...@@ -92,6 +94,7 @@ $(addsuffix .o,$(CXXTESTS)): %.o: %.cc
$(STANDARDTEST): %: %.o; $(CXX) -o $@ $< $(LDFLAGS) $(STANDARDTEST): %: %.o; $(CXX) -o $@ $< $(LDFLAGS)
$(BINARYTEST): %: %.o; $(CXX) -o $@ $< -ltfxx $(LDFLAGS) $(BINARYTEST): %: %.o; $(CXX) -o $@ $< -ltfxx $(LDFLAGS)
$(GSLTEST): %: %.o; $(CXX) -o $@ $< -ltfxx -lgsl -lgslcblas $(LDFLAGS)
$(FORTRANTEST): %: %.o; $(CC) -o $@ $< -ltf $(LDFLAGS) -lf2c -lm $(FORTRANTEST): %: %.o; $(CC) -o $@ $< -ltf $(LDFLAGS) -lf2c -lm
$(addsuffix .g77,$(FORTRANTEST)): %.g77: %.o77 $(addsuffix .g77,$(FORTRANTEST)): %.g77: %.o77
$(FC) -o $@ $< $(LDFLAGS) -ltf77 $(FC) -o $@ $< $(LDFLAGS) -ltf77
......
/*! \file rngtest.cc
* \brief test random number generator module
*
* ----------------------------------------------------------------------------
*
* \author Thomas Forbriger
* \date 31/10/2019
*
* test random number generator module
*
* Copyright (c) 2019 by Thomas Forbriger (BFO Schiltach)
*
* REVISIONS and CHANGES
* - 31/10/2019 V1.0 Thomas Forbriger
*
* ============================================================================
*/
#define RNGTEST_VERSION \
"RNGTEST V1.0 test random number generator module"
#include <iostream>
#include <tfxx/commandline.h>
#include <tfxx/rng.h>
using std::cout;
using std::cerr;
using std::endl;
/* ---------------------------------------------------------------------- */
struct Options {
double mean,std;
unsigned long int seed;
bool seedset,verbose,listtypes;
std::string type;
int nsamples;
}; // struct Options
/* ---------------------------------------------------------------------- */
void report(const tfxx::numeric::RNGgaussian& rng)
{
cout << "RNG parameters:\n"
<< " type: " << rng.type() << "\n"
<< " seed: " << rng.seed() << "\n"
<< " mean: " << rng.mean() << "\n"
<< " std: " << rng.std() << endl;
} // void report(const tfxx::numeric::RNGgaussian& rng)
/* ---------------------------------------------------------------------- */
int main(int iargc, char* argv[])
{
// define usage information
char usage_text[]=
{
RNGTEST_VERSION "\n"
"usage: rngtest [-l] [-v] [-t type] [-seed v] [-mean v]\n"
" [-std v] [-n n] [run]\n"
" or: rngtest --help|-h" "\n"
};
// define full help text
char help_text[]=
{
"run run the program\n"
"-l print list of random number generators and exit\n"
"-v be verbose\n"
"-t type select random number generator of type \"type\"\n"
" use option -l for a list of available types\n"
" type \"default\" will make selection based on\n"
" environment variables\n"
"-seed v initialize RNG with seed value v\n"
"-mean v set mean value\n"
"-std v set standard deviation\n"
"-n n print n random numbers\n"
"\n"
"In case of no type selected or type set to \"default\" type\n"
"and seed value can be specified through environment variables.\n"
"Simply try\n"
"\n"
"GSL_RNG_TYPE=ranlux GSL_RNG_SEED=125 rngtest -v run\n"
};
// define commandline options
using namespace tfxx::cmdline;
static Declare options[]=
{
// 0: print help
{"help",arg_no,"-"},
// 1: verbose mode
{"v",arg_no,"-"},
// 2: print list of random number generators
{"l",arg_no,"-"},
// 3: type of RNG
{"t",arg_yes,"default"},
// 4: seed value
{"seed",arg_yes,"0."},
// 5: mean value
{"mean",arg_yes,"0."},
// 6: standard deviation
{"std",arg_yes,"1."},
// 7: number of samples to be printed
{"n",arg_yes,"10"},
{NULL}
};
// no arguments? print usage...
if (iargc<2)
{
cerr << usage_text << endl;
exit(0);
}
// collect options from commandline
Commandline cmdline(iargc, argv, options);
// help requested? print full help text...
if (cmdline.optset(0))
{
cerr << usage_text << endl;
cerr << help_text << endl;
exit(0);
}
Options opt;
opt.verbose=cmdline.optset(1);
opt.listtypes=cmdline.optset(2);
opt.type=cmdline.string_arg(3);
opt.seedset=cmdline.optset(4);
opt.seed=cmdline.long_arg(4);
opt.mean=cmdline.double_arg(5);
opt.std=cmdline.double_arg(6);
opt.nsamples=cmdline.int_arg(7);
if (opt.listtypes)
{
tfxx::numeric::RNGgaussian::rng_list_types(cout);
}
else
{
cout << RNGTEST_VERSION << endl;
if (opt.verbose)
{
cout << "Parameters specified on command line (maybe default):\n"
<< " type: " << opt.type << "\n"
<< " mean: " << opt.mean << "\n"
<< " std: " << opt.std << "\n"
<< " n: " << opt.nsamples << endl;
if (opt.seedset)
{
cout << " seed: " << opt.seed << endl;
}
else
{
cout << " no seed value specified" << endl;
}
}
// create RNG
tfxx::numeric::RNGgaussian rng(opt.std, opt.mean, opt.type.c_str());
if (opt.seedset) { rng.set(opt.seed); }
// report
if (opt.verbose) { report(rng); }
cout << "\nRandom numbers:" << endl;
for (int i=0; i<opt.nsamples; ++i)
{
cout << i << " " << rng.value() << endl;
}
cout << endl << "set new seed" << endl;
rng.set();
if (opt.verbose) { report(rng); }
cout << "\nRandom numbers:" << endl;
for (int i=0; i<opt.nsamples; ++i)
{
cout << i << " " << rng.value() << endl;
}
}
}
/* ----- END OF rngtest.cc ----- */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment