// // Class LatinHyperCube // This class does Latin hypercube sampling. // // Copyright (c) 2018, Matthias Frey, Paul Scherrer Institut, Villigen PSI, Switzerland // All rights reserved // // Implemented as part of the PhD thesis // "Precise Simulations of Multibunches in High Intensity Cyclotrons" // // This file is part of OPAL. // // OPAL is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // You should have received a copy of the GNU General Public License // along with OPAL. If not, see . // #ifndef OPAL_LATIN_HYPERCUBE_H #define OPAL_LATIN_HYPERCUBE_H #include "Sample/SamplingMethod.h" #include "Sample/RNGStream.h" #include #include class LatinHyperCube : public SamplingMethod { public: typedef typename std::uniform_real_distribution dist_t; LatinHyperCube(double lower, double upper) : binsize_m(0.0) , upper_m(upper) , lower_m(lower) , dist_m(0.0, 1.0) , RNGInstance_m(RNGStream::getInstance()) , seed_m(RNGStream::getGlobalSeed()) {} LatinHyperCube(double lower, double upper, int seed) : binsize_m(0.0) , upper_m(upper) , lower_m(lower) , dist_m(0.0, 1.0) , RNGInstance_m(nullptr) , seed_m(seed) {} ~LatinHyperCube() { if ( RNGInstance_m ) RNGStream::deleteInstance(RNGInstance_m); } void create(boost::shared_ptr& ind, std::size_t i) { /* values are created within [0, 1], thus, they need to be mapped * the domain [lower, upper] */ ind->genes[i] = map2domain_m(RNGInstance_m->getNext(dist_m)); } void allocate(const CmdArguments_t& args, const Comm::Bundle_t& comm) { int id = comm.island_id; if ( !RNGInstance_m ) RNGInstance_m = RNGStream::getInstance(seed_m + id); int nSamples = args->getArg("nsamples", true); int nMasters = args->getArg("num-masters", true); int nLocSamples = nSamples / nMasters; int rest = nSamples - nMasters * nLocSamples; if ( id < rest ) nLocSamples++; int startBin = 0; if ( rest == 0 ) startBin = nLocSamples * id; else { if ( id < rest ) { startBin = nLocSamples * id; } else { startBin = (nLocSamples + 1) * rest + (id - rest) * nLocSamples; } } binsize_m = ( upper_m - lower_m ) / double(nSamples); this->fillBins_m(nSamples, nLocSamples, startBin, seed_m); } private: double map2domain_m(double val) { /* y = mx + q * * [0, 1] --> [a, b] * * y = (b - a) * x + a * * where a and b are the lower, respectively, upper * bound of the current bin. */ std::size_t bin = bin_m.back(); bin_m.pop_back(); return binsize_m * (val + bin) + lower_m; } void fillBins_m(std::size_t nTotal, std::size_t nLocal, int startBin, std::size_t seed) { std::deque tmp; tmp.resize(nTotal); std::iota(tmp.begin(), tmp.end(), 0); // all masters need to shuffle the same way std::mt19937_64 eng(seed); std::shuffle(tmp.begin(), tmp.end(), eng); // each master takes its bins std::copy(tmp.begin()+startBin, tmp.begin()+startBin+nLocal, std::back_inserter(bin_m)); } private: std::deque bin_m; double binsize_m; double upper_m; double lower_m; dist_t dist_m; RNGStream *RNGInstance_m; std::size_t seed_m; }; #endif