Skip to content

Commit

Permalink
Merge pull request #157 from JeffersonLab/new_bethe_heitler_internal_…
Browse files Browse the repository at this point in the history
…generator_rtj

New bethe heitler internal generator rtj
  • Loading branch information
rjones30 authored May 27, 2020
2 parents 8ca54e0 + dd14ef2 commit 053db78
Show file tree
Hide file tree
Showing 9 changed files with 1,617 additions and 34 deletions.
5 changes: 4 additions & 1 deletion GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,13 @@ $(G4LIBDIR)/../../../g4py/G4fixes/libG4fixes.so: $(G4LIBDIR)/libG4fixes.so
@rm -f $@
@cd g4py/G4fixes && ln -s ../../tmp/*/hdgeant4/libG4fixes.so .

utils: $(G4BINDIR)/beamtree $(G4BINDIR)/genBH
utils: $(G4BINDIR)/beamtree $(G4BINDIR)/genBH $(G4BINDIR)/adapt

$(G4BINDIR)/beamtree: src/utils/beamtree.cc
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -o $@ $^ -L$(G4LIBDIR) -lhdgeant4 $(ROOTLIBS) -Wl,-rpath=$(G4LIBDIR)

$(G4BINDIR)/genBH: src/utils/genBH.cc
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -o $@ $^ -L$(G4LIBDIR) -lhdgeant4 $(DANALIBS) $(ROOTLIBS) -Wl,-rpath=$(G4LIBDIR)

$(G4BINDIR)/adapt: src/utils/adapt.cc
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -o $@ $^ -L$(G4LIBDIR) -lhdgeant4 $(DANALIBS) $(ROOTLIBS) -Wl,-rpath=$(G4LIBDIR)
730 changes: 730 additions & 0 deletions src/AdaptiveSampler.cc

Large diffs are not rendered by default.

352 changes: 352 additions & 0 deletions src/AdaptiveSampler.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,352 @@
//
// AdaptiveSampler - provide an adaptive algorithm for improved
// importance sampling in Monte Carlo integration
//
// file: AdaptiveSampler.hh (see AdaptiveSampler.cc for implementation)
// author: richard.t.jones at uconn.edu
// version: february 23, 2016
//
// usage:
// 1) Express your Monte Carlo integral in the form
// result = Int[0,1]^N { integrand(u[N]) d^N u }
// where you have included the Jacobian that relates the measure
// of your problem's integration coordinates to these rescaled
// coordinates u[i], i=1..N that vary from 0 to 1. In this form,
// a simple MC estimator for result is given by
// result_MC = (1 / nMC) sum[i=1,nMC] { integrand(u_i) }
// where the vectors u_i in the above sum are uniformly sampled
// within the unit hypercube [0,1]^N. The idea of importance
// sampling stems from the fact that the statistical noise on this
// sum can be extremely large, even for very large nMC, if the
// integrand is sharply peaked around specific points in the unit
// hypercube. Convergence can be greatly improved by replacing the
// result_MC estimator with the importance-sampled estimator:
// result_IS = (1 / nMC) sum[i=1,nMC] { integrand(u_i) w_i }
// where the u_i are non-uniformly sampled over the unit hypercube
// and the weights w_i compensate for the non-uniformity by
// multiplying the integrand by 1 / the_oversampling_factor.
// By preferentially visiting the regions in the hypercube where
// the integrand is large, evaluation of the integral to a given
// statistical precision can be accomplished with a smaller nMC.
// In the large nMC limit, it can be proved that the statistical
// variance on this estimator is
// var_IS = (1 / nMC^2) sum[i=1,nMC] { integrand(u_i) w_i }^2
//
// 2) The efficiency of the MC estimation procedure is measured by the
// number of events that would be needed to obtain an equivalent
// statistical precision over any small subset of the full domain
// of integration for a perfect sampler, divided by the actual
// number required by this sampler. A perfect sampler for a given
// integrand is one for which the product { integrand(u_i) w_i } is
// the same for all i. For a perfect sampler, var_IS is exactly 0
// because every sample returns the same value: the true result.
// Actually achieving a perfect sampler is not a practical goal
// in a problem solving because it requires one to already know the
// answer in order to compute it. However, getting reasonably close
// to unity is all that is needed to ensure rapid convergence.
// In the large N limit, the efficiency can be measured as
// efficiency = (S_1 S_1) / (S_0 S_2)
// where
// S_P = sum[i=1,nMC] { integrand(u_i) w_i }^P
// Thus the above formula for var_IS should thus be augmented by the
// factor sqrt(1 - efficiency), although this makes very little
// difference in most practical situations where efficiencies close
// to unity are very difficult to achieve.
//
// 2) The AdaptiveSampler class generates a sequence of (u,w) pairs
// which a user application uses to compute the integrand(u_i) whose
// sum gives result_IS. If the user feeds back the integrand(u_i) to
// AdaptiveSampler, the AdaptiveSampler can use this information to
// internally adjust its weighting scheme for the following (u,w)
// pairs that it generates, thus improving the rate at which var_IS
// decreases with increasing nMC. In the following example, D is the
// dimension of the Monte Carlo integral, ie. the number of independent
// u variables ~Unif(0,1) are needed to completely specify a point in
// the space of integration.
//
// #include <TRandom.h>
// #include <AdaptiveSampler.h>
//
// // This is just an example,
// // implement my_randoms as you wish.
// TRandom randoms(0);
// void my_randoms(int n, double *u) { randoms.RndmArray(n,u); }
//
// AdaptiveSampler sampler(D, my_randoms);
// double sum[3] = {0,0,0};
// for (int i=0; i < nMC; i++) {
// double u[D];
// double w = sampler.sample(u);
// double I = ... compute your integrand here ...
// double wI = w*I;
// sum[0] += 1;
// sum[1] += wI;
// sum[2] += wI*wI;
// sampler.feedback(u,wI); // if you want AdaptiveSampler to adapt
// if (i % 1000 == 0)
// sampler.adapt(); // adapt based on feedback received
// }
// double mu = sum[1] / sum[0];
// double effic = sum[1] * sum[1] / (sum[0] * sum[2]);
// double sigma = sqrt((1 - effic) * sum[2]) / sum[0];
// std::cout << "result_IS is " << mu << " +/- " << sigma << std::endl;
//

#include <vector>
#include <string>
#include <map>
#include <math.h>
#include <fstream>
#include <iomanip>
#include <sstream>

using Uniform01 = void (*)(int n, double *randoms);

class AdaptiveSampler {
public:
AdaptiveSampler(int dim, Uniform01 user_generator);
AdaptiveSampler(const AdaptiveSampler &src);
~AdaptiveSampler();
AdaptiveSampler operator=(const AdaptiveSampler &src);

public:
// action methods
double sample(double *u) const;
void feedback(const double *u, double wI);
void rebalance_tree();
int adapt();
void reset_stats();

// getter methods
int getNcells() const;
long int getNsample() const;
double getWItotal() const;
double getWI2total() const;
double getEfficiency() const;
double getResult(double *error=0) const;
double getAdaptation_sampling_threshold() const;
double getAdaptation_efficiency_target() const;
int getAdaptation_maximum_depth() const;
int getAdaptation_maximum_cells() const;
static int getVerbosity();

// setter methods
void setAdaptation_sampling_threshold(double threshold);
void setAdaptation_efficiency_target(double target);
void setAdaptation_maximum_depth(int depth);
void setAdaptation_maximum_cells(int ncells);
static void setVerbosity(int verbose);

// persistency methods
int saveState(const std::string filename) const;
int mergeState(const std::string filename);
int restoreState(const std::string filename);

// diagnostic methods
void display_tree();

protected:
int fNdim;
Uniform01 fRandom;
unsigned int fMaximum_depth;
unsigned int fMaximum_cells;
double fSampling_threshold;
double fEfficiency_target;
double fMinimum_sum_wI2_delta;
static int verbosity;

class Cell;
Cell *fTopCell;

Cell *findCell(double ucell, int &depth,
double *u0, double *u1) const;
Cell *findCell(const double *u, int &depth,
double *u0, double *u1) const;
int recursively_update(std::vector<int> index);
double display_tree(Cell *cell, double subset, int level, double *u0,
double *u1);

// internal weighting tables

class Cell {
public:
int ndim;
int divAxis;
long int nhit;
double sum_wI;
double sum_wI2;
double *sum_wI2u;
double subset;
Cell *subcell[3];

Cell(int dim) : ndim(dim), divAxis(-1), nhit(0),
sum_wI(0), sum_wI2(0), subset(0)
{
int dim3 = 1;
for (int n=0; n < dim; ++n)
dim3 *= 3;
sum_wI2u = new double[dim3];
std::fill(sum_wI2u, sum_wI2u + dim3, 0);
subcell[0] = 0;
subcell[1] = 0;
subcell[2] = 0;
}
Cell(const Cell &src) {
int dim3 = 1;
for (int n=0; n < src.ndim; ++n)
dim3 *= 3;
ndim = src.ndim;
divAxis = src.divAxis;
nhit = src.nhit;
sum_wI = src.sum_wI;
sum_wI2 = src.sum_wI2;
sum_wI2u = new double[dim3];
for (int i=0; i < dim3; ++i) {
sum_wI2u[i] = src.sum_wI2u[i];
}
subset = src.subset;
if (src.subcell[0] != 0) {
subcell[0] = new Cell(*src.subcell[0]);
subcell[1] = new Cell(*src.subcell[1]);
subcell[2] = new Cell(*src.subcell[2]);
}
else {
subcell[0] = 0;
subcell[1] = 0;
subcell[2] = 0;
}
}
~Cell() {
if (subcell[0] != 0) {
delete subcell[0];
delete subcell[1];
delete subcell[2];
}
delete [] sum_wI2u;
}
Cell operator=(const Cell &src) {
return Cell(src);
}
void reset_stats() {
nhit = 0;
sum_wI = 0;
sum_wI2 = 0;
int dim3 = 1;
for (int i=0; i < ndim; ++i)
dim3 *= 3;
std::fill(sum_wI2u, sum_wI2u + dim3, 0);
if (divAxis > -1) {
subcell[0]->reset_stats();
subcell[1]->reset_stats();
subcell[2]->reset_stats();
}
}
int sum_stats(long int &net_nhit, double &net_wI,
double &net_wI2,
double &net_wI2s) const
{
net_nhit += nhit;
net_wI += sum_wI;
if (sum_wI2 > 0) {
net_wI2 += sum_wI2;
net_wI2s += sqrt(sum_wI2);
}
int count = 1;
if (divAxis > -1) {
count += subcell[0]->sum_stats(net_nhit, net_wI, net_wI2, net_wI2s);
count += subcell[1]->sum_stats(net_nhit, net_wI, net_wI2, net_wI2s);
count += subcell[2]->sum_stats(net_nhit, net_wI, net_wI2, net_wI2s);
}
return count;
}
void repartition(double wI2s, double total_wI2s) {
subset = wI2s / total_wI2s;
if (divAxis > -1) {
double sub_wI2s[3] = {};
for (int n=0; n < 3; ++n) {
long int nhitsum = 0;
double wIsum = 0;
double wI2sum = 0;
subcell[n]->sum_stats(nhitsum, wIsum, wI2sum, sub_wI2s[n]);
}
double subtotal = sub_wI2s[0] + sub_wI2s[1] + sub_wI2s[2];
double part[3];
double psum = 0;
for (int n=0; n < 3; ++n) {
if (subtotal == 0)
part[n] = subcell[n]->subset / subset;
else
part[n] = sub_wI2s[n] / subtotal;
part[n] = (part[n] > 1e-3)? part[n] : 1e-3;
psum += part[n];
}
subcell[0]->repartition(wI2s * part[0]/psum, total_wI2s);
subcell[1]->repartition(wI2s * part[1]/psum, total_wI2s);
subcell[2]->repartition(wI2s * part[2]/psum, total_wI2s);
}
}
int serialize(std::ofstream &ofs) {
ofs << "ndim=" << ndim << std::endl;
ofs << "divAxis=" << divAxis << std::endl;
if (nhit != 0)
ofs << "nhit=" << nhit << std::endl;
if (sum_wI != 0)
ofs << "sum_wI=" << sum_wI << std::endl;
if (sum_wI2 != 0)
ofs << "sum_wI2=" << sum_wI2 << std::endl;
int dim3 = 1;
for (int i=0; i < ndim; ++i)
dim3 *= 3;
for (int i=0; i < dim3; ++i) {
if (sum_wI2u[i] != 0)
ofs << "sum_wI2u[" << i << "]=" << sum_wI2u[i] << std::endl;
}
ofs << "subset=" << std::setprecision(20) << subset << std::endl;
ofs << "=" << std::endl;
int count = 1;
if (divAxis > -1) {
count += subcell[0]->serialize(ofs);
count += subcell[1]->serialize(ofs);
count += subcell[2]->serialize(ofs);
}
return count;
}
int deserialize(std::ifstream &ifs) {
std::map<std::string,double> keyval;
while (true) {
std::string key;
std::getline(ifs, key, '=');
if (key.size() == 0) {
std::getline(ifs, key);
break;
}
ifs >> keyval[key];
std::getline(ifs, key);
}
ndim = keyval.at("ndim");
divAxis = keyval.at("divAxis");
nhit += keyval["nhit"];
sum_wI += keyval["sum_wI"];
sum_wI2 += keyval["sum_wI2"];
int dim3 = 1;
for (int i=0; i < ndim; ++i)
dim3 *= 3;
for (int i=0; i < dim3; ++i) {
std::stringstream key("");
key << "sum_wI2u[" << i << "]";
sum_wI2u[i] += keyval[key.str()];
}
subset = keyval.at("subset");
int count = 1;
if (divAxis > -1) {
for (int i=0; i < 3; ++i) {
if (subcell[i] == 0) {
subcell[i] = new Cell(ndim);
}
count += subcell[i]->deserialize(ifs);
}
}
return count;
}
};
};
Loading

0 comments on commit 053db78

Please sign in to comment.