Branch data Line data Source code
1 : : // ***************************************************************************** 2 : : /*! 3 : : \file src/Statistics/UniPDF.hpp 4 : : \copyright 2012-2015 J. Bakosi, 5 : : 2016-2018 Los Alamos National Security, LLC., 6 : : 2019-2021 Triad National Security, LLC. 7 : : All rights reserved. See the LICENSE file for details. 8 : : \brief Univariate PDF estimator 9 : : \details Univariate PDF estimator. This class can be used to estimate a 10 : : probability density function of (PDF) a scalar variable from an ensemble. 11 : : The implementation uses the standard container std::unordered_map, which is 12 : : a hash-based associative container with linear algorithmic complexity for 13 : : insertion of a new sample. 14 : : */ 15 : : // ***************************************************************************** 16 : : #ifndef UniPDF_h 17 : : #define UniPDF_h 18 : : 19 : : #include <array> 20 : : #include <unordered_map> 21 : : #include <algorithm> 22 : : #include <cfenv> 23 : : 24 : : #include "Types.hpp" 25 : : #include "Exception.hpp" 26 : : #include "PUPUtil.hpp" 27 : : 28 : : namespace tk { 29 : : 30 : : //! Univariate PDF estimator 31 : : class UniPDF { 32 : : 33 : : public: 34 : : //! Number of sample space dimensions 35 : : static const std::size_t dim = 1; 36 : : 37 : : //! Key type 38 : : using key_type = long; 39 : : 40 : : //! Pair type 41 : : using pair_type = std::pair< const key_type, tk::real >; 42 : : 43 : : //! \brief Univariate PDF 44 : : //! \details The underlying container type is an unordered_map where the key 45 : : //! is one bin id corresponding to the single sample space dimension, and 46 : : //! the mapped value is the sample counter. The hasher functor used here 47 : : //! is the default for the key type provided by the standard library. 48 : : using map_type = std::unordered_map< key_type, tk::real >; 49 : : 50 : : //! Empty constructor for Charm++ 51 : 6807 : explicit UniPDF() : m_binsize( 0 ), m_nsample( 0 ), m_pdf() {} 52 : : 53 : : //! Constructor: Initialize univariate PDF container 54 : : //! \param[in] bs Sample space bin size 55 : 5829 : explicit UniPDF( tk::real bs ) : 56 : 5829 : m_binsize( bs ), m_nsample( 0 ), m_pdf() {} 57 : : 58 : : //! Accessor to number of samples 59 : : //! \return Number of samples collected 60 : 131708 : std::size_t nsample() const noexcept { return m_nsample; } 61 : : 62 : : //! Add sample to univariate PDF 63 : : //! \param[in] sample Sample to insert 64 : 2310986 : void add( tk::real sample ) { 65 [ - + ][ - - ]: 2310986 : Assert( m_binsize > 0, "Bin size must be positive" ); [ - - ][ - - ] 66 : 2310986 : ++m_nsample; 67 : : fenv_t fe; 68 : 2310986 : feholdexcept( &fe ); 69 [ + - ]: 2310986 : ++m_pdf[ std::lround( sample / m_binsize ) ]; 70 : 2310986 : feclearexcept( FE_UNDERFLOW ); 71 : 2310986 : feupdateenv( &fe ); 72 : 2310986 : } 73 : : 74 : : //! Add multiple samples from a PDF 75 : : //! \param[in] p PDF whose samples to add 76 : 5262 : void addPDF( const UniPDF& p ) { 77 : 5262 : m_binsize = p.binsize(); 78 : 5262 : m_nsample += p.nsample(); 79 [ + + ][ + - ]: 284629 : for (const auto& e : p.map()) m_pdf[ e.first ] += e.second; 80 : 5262 : } 81 : : 82 : : //! Zero bins 83 : 0 : void zero() noexcept { m_nsample = 0; m_pdf.clear(); } 84 : : 85 : : //! Constant accessor to underlying PDF map 86 : : //! \return Constant reference to underlying map 87 : 5829 : const map_type& map() const noexcept { return m_pdf; } 88 : : 89 : : //! Constant accessor to bin size 90 : : //! \return Sample space bin size 91 : 5829 : tk::real binsize() const noexcept { return m_binsize; } 92 : : 93 : : //! Return minimum and maximum bin ids of sample space 94 : : //! \return {min,max} Minimum and maximum of the bin ids 95 : 567 : std::array< long, 2*dim > extents() const { 96 [ - + ][ - - ]: 567 : Assert( !m_pdf.empty(), "PDF empty" ); [ - - ][ - - ] 97 : 1134 : auto x = std::minmax_element( begin(m_pdf), end(m_pdf), 98 : 188680 : []( const pair_type& a, const pair_type& b ) 99 [ + - ]: 189247 : { return a.first < b.first; } ); 100 : 567 : return {{ x.first->first, x.second->first }}; 101 : : } 102 : : 103 : : //! Compute integral of the distribution across the whole sample space 104 : : //! \return Integral of the distribution 105 : 567 : tk::real integral() const { 106 : 567 : return std::accumulate( m_pdf.cbegin(), m_pdf.cend(), 0.0, 107 : 126446 : [&]( tk::real i, const pair_type& p ){ 108 : 127013 : return i + p.second; } ) / static_cast< tk::real >( m_nsample ); 109 : : } 110 : : 111 : : /** @name Pack/Unpack: Serialize UniPDF object for Charm++ */ 112 : : ///@{ 113 : : //! Pack/Unpack serialize member function 114 : : //! \param[in,out] p Charm++'s PUP::er serializer object reference 115 : 20421 : void pup( PUP::er& p ) { 116 : 20421 : p | m_binsize; 117 : 20421 : p | m_nsample; 118 : 20421 : p | m_pdf; 119 : 20421 : } 120 : : //! \brief Pack/Unpack serialize operator| 121 : : //! \param[in,out] p Charm++'s PUP::er serializer object reference 122 : : //! \param[in,out] c UniPDF object reference 123 : 20421 : friend void operator|( PUP::er& p, UniPDF& c ) { c.pup(p); } 124 : : ///@} 125 : : 126 : : private: 127 : : tk::real m_binsize; //!< Sample space bin size 128 : : std::size_t m_nsample; //!< Number of samples collected 129 : : map_type m_pdf; //!< Probability density function 130 : : }; 131 : : 132 : : //! Output univariate PDF to output stream 133 : : //! \param[in,out] os Stream to output to 134 : : //! \param[in] p PDF to output 135 : : //! \return Updated stream 136 : : //! \note Used for debugging. 137 : : static inline 138 : : std::ostream& operator<< ( std::ostream& os, const tk::UniPDF& p ) { 139 : : os << p.binsize() << ", " << p.nsample() << ": "; 140 : : std::map< typename tk::UniPDF::key_type, tk::real > 141 : : sorted( p.map().begin(), p.map().end() ); 142 : : for (const auto& b : sorted) os << '(' << b.first << ',' << b.second << ") "; 143 : : return os; 144 : : } 145 : : 146 : : } // tk:: 147 : : 148 : : #endif // UniPDF_h