Initial import

This commit is contained in:
Guilhem Lavaux 2023-05-29 10:41:03 +02:00
commit 56a50eead3
820 changed files with 192077 additions and 0 deletions

View file

@ -0,0 +1,124 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/samplers/rgen/gsl_miser.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __GSL_RANDOM_NUMBER_MISER_HPP
#define __GSL_RANDOM_NUMBER_MISER_HPP
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_monte.h>
#include <gsl/gsl_monte_miser.h>
#include <cstring>
#include "libLSS/tools/errors.hpp"
#include "libLSS/samplers/core/random_number.hpp"
#include "libLSS/samplers/rgen/gsl_random_number.hpp"
namespace LibLSS {
/**
* This is an adaptor class for the MISER integrator in GSL.
* It handles the life cycle of the MISER object, and support for a generic
* functor for the integrand.
*/
class GSL_Miser {
protected:
gsl_monte_miser_state *state;
size_t Nd;
template<typename Functor>
struct MiserCall {
Functor f;
MiserCall(Functor g) : f(g) {}
};
template<typename Functor>
static double adaptor_functor(double *x, size_t, void *params)
{
MiserCall<Functor> *c = (MiserCall<Functor> *) params;
return c->f(x);
}
public:
/**
* Constructor.
* @param dim number of dimensions over which the integration will occur.
*/
GSL_Miser(size_t dim)
: state(0), Nd(dim) {
state = gsl_monte_miser_alloc(dim);
}
/**
* Destructor.
*/
~GSL_Miser() {
gsl_monte_miser_free(state);
}
/**
* Integrate the provided integrand over some range, with a maximum number of calls. A bound
* on the maximum error is returned.
* Here is a use example:
*
* @code
* // ...
* size_t calls = 10;
* double xl[2] = {0, 0};
* double xu[2] = {1, 2};
* double value;
*
* GSL_Miser miser(2); // 2-dimensions
* value = miser.integrate(rng, [](double *x) {
* // do something with x[0], x[1]
* return x[0]*x[0] + x[1]*x[1]; // for example sum(x^2)
* }, xl, xu, calls, abserr);
* //...
* @endcode
*
* @param rng Class adapting the GSL random number generator
* @param f Functor representing the integrand. It must have one pointer to double and return a double.
* @param xl lower bound for integration (N-dimension contiguous C-array)
* @param xu upper bound for integration
* @param calls maximum number of calls
* @param abserr return medium for estimated maximum absolute error
*
*/
// Only valid for GSL
template<typename Functor,typename A>
double integrate(GSL_RandomNumber& rng, Functor f, A& xl, A& xu, size_t calls, double &abserr) {
gsl_monte_function mf;
MiserCall<Functor> call(f);
double result;
int err;
mf.f = &adaptor_functor<Functor>;
mf.dim = Nd;
mf.params = &call;
if ((err = gsl_monte_miser_integrate(&mf, &xl[0], &xu[0], Nd, calls, rng.rng, state, &result, &abserr)) != GSL_SUCCESS)
error_helper<ErrorGSL>(boost::format("Error while doing monte carlo integration: error code = %d ") % err);
return result;
}
/**
* Use a multi-threaded random number generator deriving from a base "Rng".
* This is a helper class to unwrap the GSL base class for the random number generation.
* @see integrate(GSL_RandomNumber& rng, Functor f, A& xl, A& xu, size_t calls, double &abserr)
*/
template<typename Rng, typename Functor, typename A>
double integrate(RandomNumberThreaded<Rng>& rng, Functor f, A& xl, A& xu, size_t calls, double &abserr) {
return integrate(rng.base(), f, xl, xu, calls, abserr);
}
};
}
#endif

View file

@ -0,0 +1,91 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/samplers/rgen/gsl_random_number.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __GSL_RANDOM_NUMBER_HPP
#define __GSL_RANDOM_NUMBER_HPP
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <cstring>
#include "libLSS/tools/errors.hpp"
#include "libLSS/samplers/core/random_number.hpp"
namespace LibLSS {
class GSL_RandomNumber: public RandomNumber
{
public:
gsl_rng *rng;
GSL_RandomNumber() :
rng(gsl_rng_alloc(gsl_rng_mt19937)) {
}
~GSL_RandomNumber() {
gsl_rng_free(rng);
}
virtual double uniform() {
return gsl_rng_uniform(rng);
}
virtual double unitexp() {
return gsl_ran_exponential(rng, 1.);
}
virtual void seed(unsigned long i) {
Console::instance().print<LOG_DEBUG>(boost::format("GSL: Changing random number generation seed with %ld") % i);
gsl_rng_set(rng, i);
}
virtual unsigned long get() {
return gsl_rng_get(rng);
}
using RandomNumber::poisson;
using RandomNumber::gaussian;
using RandomNumber::gamma;
using RandomNumber::negative_binomial;
virtual unsigned int poisson(double mean) {
return gsl_ran_poisson(rng, mean);
}
virtual unsigned int negative_binomial(double p, double n) {
return gsl_ran_negative_binomial(rng, p, n);
}
virtual double gamma(double a, double b) {
return gsl_ran_gamma(rng, a, b);
}
virtual void save(H5_CommonFileGroup& g) {
boost::multi_array<char, 1> out(boost::extents[gsl_rng_size(rng)]);
::memcpy(out.origin(), gsl_rng_state(rng), gsl_rng_size(rng));
CosmoTool::hdf5_write_array(g, "state", out);
}
virtual void restore(H5_CommonFileGroup& g, bool flexible) {
size_t sz = gsl_rng_size(rng);
boost::multi_array<char, 1> in;
CosmoTool::hdf5_read_array(g, "state", in);
if (in.shape()[0] != sz) {
error_helper<ErrorIO>("Could not read state in GSL_RandomNumber");
}
memcpy(gsl_rng_state(rng), in.origin(), sz);
}
};
};
#endif

View file

@ -0,0 +1,231 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/samplers/rgen/slice_sweep.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef _LIBLSS_SLICE_SWEEP_HPP
#define _LIBLSS_SLICE_SWEEP_HPP
#include "libLSS/mpi/generic_mpi.hpp"
#include <cmath>
// These algorithms are described in https://www.aquila-consortium.org/wiki/index.php/File:Slice_sampling_Neal_97.pdf
namespace LibLSS {
namespace slice_details {
template<typename LogLikelihood>
double request(MPI_Communication *comm, LogLikelihood lh, double a, int ROOT) {
int job = 1;
comm->broadcast_t(&job, 1, ROOT);
comm->broadcast_t(&a, 1, ROOT);
return lh(a);
}
inline void shutdown(MPI_Communication *comm, double a, int ROOT) {
int job = 0;
comm->broadcast_t(&job, 1, ROOT);
comm->broadcast_t(&a, 1, ROOT);
}
inline int grab_job(MPI_Communication *comm, double& a, int ROOT) {
int job;
comm->broadcast_t(&job, 1, ROOT);
comm->broadcast_t(&a, 1, ROOT);
return job;
}
}
template<typename Random, typename LogLikelihood>
double slice_sweep(MPI_Communication *comm, Random& rng, LogLikelihood lh, double a0, double step, int ROOT = 0)
{
Console::instance().print<LOG_DEBUG>("Doing slicesweep EARLY init");
if (comm->rank() != ROOT) {
double v;
while (slice_details::grab_job(comm, v, ROOT)) {
lh(v);
}
return v;
}
Console::instance().print<LOG_DEBUG>("Doing slicesweep init");
double logp0 = slice_details::request(comm, lh, a0, ROOT);
double logu = logp0 + std::log(1-rng.uniform());//draw from (0,1], to avoid log(0)
Console::instance().c_assert(!std::isnan(logu), "logu must not be a NaN");
double rr = rng.uniform();
double al = a0 - rr*step;
double ar = a0 + (1-rr)*step;
Console::instance().print<LOG_DEBUG>(boost::format("First loop (logu = %lg)") % logu);
while (true) {
double logpl = slice_details::request(comm, lh, al, ROOT);
if (logpl < logu)
break;
al -= step;
}
Console::instance().print<LOG_DEBUG>("Second loop");
while (true) {
double logpr = slice_details::request(comm, lh, ar, ROOT);
if (logpr < logu)
break;
ar += step;
}
Console::instance().print<LOG_DEBUG>("Last loop");
while (true) {
double a1 = rng.uniform() * (ar - al) + al;
double logp1 = slice_details::request(comm, lh, a1, ROOT);
if (logp1 > logu) {
slice_details::shutdown(comm, a1, ROOT);
return a1;
} else {
// Shrink bracket
if (a1 > a0)
ar = a1;
else
al = a1;
}
}
}
template<typename Random, typename LogLikelihood>
double slice_sweep(Random& rng, LogLikelihood lh, double a0, double step)
{
double logp0 = lh(a0);
double logu = logp0 + std::log(1-rng.uniform());//draw from (0,1], to avoid log(0)
Console::instance().c_assert(!std::isnan(logu), "logu must not be a NaN");
double rr = rng.uniform();
double al = a0 - rr*step;
double ar = a0 + (1-rr)*step;
while (true) {
double logpl = lh(al);
if (logpl < logu)
break;
al -= step;
}
while (true) {
double logpr = lh(ar);
if (logpr < logu)
break;
ar += step;
}
while (true) {
double a1 = rng.uniform() * (ar - al) + al;
double logp1 = lh(a1);
if (logp1 > logu) {
return a1;
} else {
// Shrink bracket
if (a1 > a0)
ar = a1;
else
al = a1;
}
}
}
template<typename Random, typename LogLikelihood>
double slice_sweep_double(MPI_Communication *comm, Random& rng, LogLikelihood lh, double a0, double step, int ROOT = 0)
{
ConsoleContext<LOG_DEBUG> ctx("slicesweep_double");
if (comm->rank() != ROOT) {
double v;
while (slice_details::grab_job(comm, v, ROOT)) {
lh(v);
}
return v;
}
ctx.print("INIT");
// Find the initial likelihood and the slice level
double logp0 = slice_details::request(comm, lh, a0, ROOT);
double logu = logp0 + std::log(1-rng.uniform());//draw from (0,1], to avoid log(0)
Console::instance().c_assert(!std::isnan(logu), "logu must not be a NaN");
double rr = rng.uniform();
double al = a0 - rr*step;
double ar = a0 + (1-rr)*step;
ctx.print(boost::format("Step defining loop (logu = %lg)") % logu);
double logpl = slice_details::request(comm, lh, al, ROOT);
double logpr = slice_details::request(comm, lh, ar, ROOT);
while (logpl >= logu || logpr >= logu) {
double v= rng.uniform();
if (v < 0.5) {
al -= (ar - al);
logpl = slice_details::request(comm, lh, al, ROOT);
ctx.print(boost::format("new al=%g, logpl = %g") % al % logpl);
} else {
ar += (ar - al);
logpr = slice_details::request(comm, lh, ar, ROOT);
ctx.print(boost::format("new ar=%g, logpr = %g") % ar % logpr);
}
}
ctx.print("Sampling loop");
while (true) {
double a1 = rng.uniform() * (ar - al) + al;
double logp1 = slice_details::request(comm, lh, a1, ROOT);
if (logp1 > logu) {
double ar_hat = ar;
double al_hat = al;
double logpl_hat = slice_details::request(comm, lh, al_hat, ROOT);
double logpr_hat = slice_details::request(comm, lh, ar_hat, ROOT);
bool not_accepted = false;
ctx.print(boost::format("Got a candidate at a1=%g") % a1);
while ((ar_hat - al_hat) > (1.1*step) && !not_accepted) {
double am = 0.5 * (ar_hat+al_hat);
bool D = ((a0 < am && a1 >= am) || (a0 >= am && a1 < am));
if (a1 < am) {
ar_hat = am;
logpr_hat = slice_details::request(comm, lh, ar_hat, ROOT);
} else {
al_hat = am;
logpl_hat = slice_details::request(comm, lh, al_hat, ROOT);
}
ctx.print(boost::format("ar_hat=%lg, al_hat=%lg, logpl_hat=%lg, logpr_hat=%lg, D=%d") % ar_hat % al_hat % logpl_hat % logpr_hat % D);
if (D && logu >= logpl_hat && logu >= logpr_hat) {
// Not acceptable. Try again.
ctx.print("Not good");
not_accepted = true;
}
}
// Go back outside
if (not_accepted)
continue;
slice_details::shutdown(comm, a1, ROOT);
return a1;
} else {
// Shrink bracket
if (a1 > a0)
ar = a1;
else
al = a1;
}
}
}
}
#endif