aGrUM 3.1.1
a C++ library for (probabilistic) graphical models
gum::VariableLog2ParamComplexity Class Reference

the class for computing the log2 of the parametric complexity of an r-ary multinomial variable More...

#include <variableLog2ParamComplexity.h>

Collaboration diagram for gum::VariableLog2ParamComplexity:

Public Member Functions

Constructors / Destructors
 VariableLog2ParamComplexity ()
 default constructor
 VariableLog2ParamComplexity (const VariableLog2ParamComplexity &from)
 copy constructor
 VariableLog2ParamComplexity (VariableLog2ParamComplexity &&from)
 move constructor
virtual VariableLog2ParamComplexityclone () const
 virtual copy constructor
virtual ~VariableLog2ParamComplexity ()
 destructor
Operators
VariableLog2ParamComplexityoperator= (const VariableLog2ParamComplexity &from)
 copy operator
VariableLog2ParamComplexityoperator= (VariableLog2ParamComplexity &&from)
 move operator
Accessors / Modifiers
double log2Cnr (const std::size_t r, const double n)
 returns the value of the log in base 2 of Cnr
void CnrToFile (std::string_view filename)
 the function used to write the cpp file with the values of log2(Cnr)
void useCache (const bool on_off)
 indicates whether we wish to use a cache for the Cnr
void clearCache ()
 clears the current cache

Private Attributes

const double _cst1_ = -0.5 + std::log2(std::sqrt(M_PI))
 the value of N above which we should use Szpankowski's approximation
const double _cst2_ = std::sqrt(2.0 / M_PI) / 3.0
const double _cst3_ = 3.0 / 36.0 - 4.0 / (9.0 * M_PI)
bool _use_cache_ {true}
HashTable< std::pair< std::size_t, double >, double_cache_

Detailed Description

the class for computing the log2 of the parametric complexity of an r-ary multinomial variable

This class enables to compute the log in base 2 of the parametric complexity of a single r-ary multinomial variable, i.e., the log in base 2 of the C_N^r term used by NML scores in Bayesian network structure learning algorithm (see, e.g., Silander, Roos, Kontkanen and Myllymaki (2007) "Factorized Normalized Maximum " Likelihood Criterion for Learning Bayesian network Structures)"

Definition at line 88 of file variableLog2ParamComplexity.h.

Constructor & Destructor Documentation

◆ VariableLog2ParamComplexity() [1/3]

gum::VariableLog2ParamComplexity::VariableLog2ParamComplexity ( )

default constructor

Referenced by VariableLog2ParamComplexity(), VariableLog2ParamComplexity(), clone(), operator=(), and operator=().

Here is the caller graph for this function:

◆ VariableLog2ParamComplexity() [2/3]

gum::VariableLog2ParamComplexity::VariableLog2ParamComplexity ( const VariableLog2ParamComplexity & from)

copy constructor

References VariableLog2ParamComplexity().

Here is the call graph for this function:

◆ VariableLog2ParamComplexity() [3/3]

gum::VariableLog2ParamComplexity::VariableLog2ParamComplexity ( VariableLog2ParamComplexity && from)

move constructor

References VariableLog2ParamComplexity().

Here is the call graph for this function:

◆ ~VariableLog2ParamComplexity()

virtual gum::VariableLog2ParamComplexity::~VariableLog2ParamComplexity ( )
virtual

destructor

Member Function Documentation

◆ clearCache()

void gum::VariableLog2ParamComplexity::clearCache ( )

clears the current cache

◆ clone()

virtual VariableLog2ParamComplexity * gum::VariableLog2ParamComplexity::clone ( ) const
nodiscardvirtual

virtual copy constructor

References VariableLog2ParamComplexity().

Here is the call graph for this function:

◆ CnrToFile()

void gum::VariableLog2ParamComplexity::CnrToFile ( std::string_view filename)

the function used to write the cpp file with the values of log2(Cnr)

Definition at line 154 of file variableLog2ParamComplexity.cpp.

154 {
155 // save all the value of cn2
156 std::vector< long double > cn2_table(VariableLog2ParamComplexityCTableNSize);
157 cn2_table[0] = 1;
158 cn2_table[1] = 2;
159
160 // for every value of n less than Szpankowski_threshold, we compute the
161 // value of C_n^2 and write it into cn2_table
162 GammaLog2 gamma_log2;
163 for (double n = 2; n < VariableLog2ParamComplexityCTableNSize; ++n) {
164 // here, note that, in Silander, Roos, Kontkanen and Myllymaki (2007)
165 // "Factorized Normalized Maximum Likelihood Criterion for Learning
166 // Bayesian network Structures" paper, there is an uppercase N in the
167 // formula, but this should be a lowercase n. In addition, we will loop
168 // only on h=1 to n-1 and add to 2.0 the value computed to take into
169 // account of h=0 and h=n.
170 long double cn2 = 2;
171 for (double h = 1; h < n; ++h) {
172 long double elt = (gamma_log2(n + 1) - gamma_log2(h + 1) - gamma_log2((n - h) + 1)) * M_LN2
173 + h * std::log(h / n) + (n - h) * std::log((n - h) / n);
174 cn2 += std::exp(elt);
175 }
176
177 // const double logCn2 = (double) std::log2 ( cn2 );
178
179 cn2_table[int(n)] = cn2;
180 }
181
182 // write the header of the output file
183 std::ofstream outfile{std::string(filename)};
184 if (!outfile.is_open()) { GUM_ERROR(IOError, "It is impossible to open file " << filename) }
185 outfile.precision(20);
186 outfile << "namespace gum {\n\n";
187 /*
188 outfile << " // the size in r of VariableLog2ParamComplexityCTable\n";
189 outfile << " const std::size_t VariableLog2ParamComplexityCTableRSize = "
190 << "4;\n\n";
191 outfile << " // the size in n of VariableLog2ParamComplexityCTable\n";
192 outfile << " const std::size_t VariableLog2ParamComplexityCTableNSize = "
193 << VariableLog2ParamComplexityCTableNSize << ";\n\n";
194 */
195 outfile << " // the CTable cache for log2(Cnr), n < " << VariableLog2ParamComplexityCTableNSize
196 << " and r in {2,3,4,5}\n";
197 outfile << " const double VariableLog2ParamComplexityCTable[4]["
199
200 // write the values of Cn2:
201 outfile << " { ";
202 bool first = true;
203 for (const auto cn2: cn2_table) {
204 if (first) first = false;
205 else outfile << ",\n ";
206 const double logCn2 = (double)std::log2(cn2);
207 outfile << logCn2;
208 }
209 outfile << " },\n";
210
211 // write the values of cn3, which are equal to cn2 + n
212 outfile << " { ";
213 for (std::size_t i = std::size_t(0); i < VariableLog2ParamComplexityCTableNSize; ++i) {
214 if (i > std::size_t(0)) outfile << ",\n ";
215 const double logCn3 = (double)std::log2(cn2_table[i] + i);
216 outfile << logCn3;
217 }
218 outfile << " },\n";
219
220 // write the values of cn4, which are equal to cn2 * (1 + n/2) + n
221 outfile << " { ";
222 for (std::size_t i = std::size_t(0); i < VariableLog2ParamComplexityCTableNSize; ++i) {
223 if (i > std::size_t(0)) outfile << ",\n ";
224 const double logCn4 = (double)std::log2(cn2_table[i] * (1.0 + i / 2.0) + i);
225 outfile << logCn4;
226 }
227 outfile << " },\n";
228
229 // write the values of cn5, which are equal to cn2 * (1 + 5n/6) + n + n^2/3
230 outfile << " { ";
231 for (std::size_t i = std::size_t(0); i < VariableLog2ParamComplexityCTableNSize; ++i) {
232 if (i > std::size_t(0)) outfile << ",\n ";
233 const double logCn5
234 = (double)std::log2(cn2_table[i] * (1.0 + 5.0 * i / 6.0) + i + i * i / 3.0);
235 outfile << logCn5;
236 }
237 outfile << " }\n";
238
239 // write the footer and close the file
240 outfile << " };\n\n";
241 outfile << "} /* namespace gum */\n";
242 outfile.close();
243 }
#define GUM_ERROR(type, msg)
Definition exceptions.h:76
#define M_LN2
Definition math_utils.h:63
constexpr std::size_t VariableLog2ParamComplexityCTableNSize

References GUM_ERROR, M_LN2, and gum::VariableLog2ParamComplexityCTableNSize.

◆ log2Cnr()

double gum::VariableLog2ParamComplexity::log2Cnr ( const std::size_t r,
const double n )

returns the value of the log in base 2 of Cnr

Definition at line 59 of file variableLog2ParamComplexity.cpp.

59 {
60 // we know that c_n^1 = 1 for all values of n
61 // in addition, c_0^r = 1 for all values of r
62 // finally, it is easy to see that c_1^r = r for all r
63 if (r == std::size_t(1)) return 0.0; // log2(1)
64 if (n == 0.0) return 0.0; // log2(1)
65 if (n == 1.0) return std::log2((double)r); // log2(r)
66
67 if (n < 0.0) {
68 GUM_ERROR(OutOfBounds,
69 "In the penalty of the fNML score, n must be greater "
70 << "than or equal to 0. But, here, n = " << n);
71 }
72
74 // check if we can find the value we look for in precomputed table
75 // ScorefNMLVariableLog2ParamComplexity
76 std::size_t xn = (std::size_t)n;
78 return VariableLog2ParamComplexityCTable[r - 2][xn];
79 } else {
80 // try to find the value in the cache
81 if (_use_cache_) {
82 const auto key = std::pair< std::size_t, double >{r, n};
83 if (auto p = _cache_.tryGet(key)) return *p;
84 }
85
86 // use Equation (13) of the paper to compute the value of cnr:
87 // C_n^r = C_n^{r-1} + (n / (r-2)) C_n^{r-2}
88 // as we handle only log2's of C_n^r, we have the following:
89 // let k_r be such that C_n^{r-2} = k_r * C_n^{r-1}
90 // log2 ( C_n^r ) = log2 ( C_n^{r-1} + k_r * (n/(r-2)) * C_n^{r-1} )
91 // = log2 ( C_n^{r-1} ) + log2 ( 1 + k_r * (n/(r-2)) )
92 // as k_r = C_n^{r-2} / C_n^{r-1}, we have that
93 // log2(k_r) = log2 ( C_n^{r-2} ) - log2 ( C_n^{r-1} )
94 // so, k_r = exp ( (log2(cn_^{r-2}) - log2(C_n^{r-1})) * log(2) )
95 // now, let q_r = 1 + k_r * (n/(r-2)), then
96 // C_n^r = C_n^{r-1} * q_r, or, equivalently,
97 // log2(C_n^r) = log2(C_n^{r-1}) + log2(q_r)
98 // Now, we can use the same method to compute C_n^{r+1}:
99 // k_{r+1} = C_n^{r-1} / C_n^r = 1 / q_r
100 // q_{r+1} = 1 + k_{r+1} * (n/(r-1))
101 // C_n^{r+1} = C_n^r * q_{r+1}
102 double log2Cnr1 = VariableLog2ParamComplexityCTable[3][xn]; // log(C_n^5)
103 double log2Cnr2 = VariableLog2ParamComplexityCTable[2][xn]; // log(C_n^4)
104 double log2Cnr = 0.0;
105 double k_r = std::exp((log2Cnr2 - log2Cnr1) * M_LN2);
106 double q_r = 1.0 + k_r * n / (6.0 - 2.0); // we first compute C_n^6
107 for (std::size_t i = std::size_t(6); i <= r; ++i) {
108 log2Cnr = log2Cnr1 + std::log2(q_r);
109 log2Cnr1 = log2Cnr;
110 k_r = 1.0 / q_r;
111 q_r = 1.0 + k_r * (n / (i - 1.0));
112 }
113
114 // if we use a cache, update it
115 if (_use_cache_) { _cache_.insert(std::pair< std::size_t, double >{r, n}, log2Cnr); }
116
117 return log2Cnr;
118 }
119 } else {
120 // try to find the value in the cache
121 if (_use_cache_) {
122 const auto key = std::pair< std::size_t, double >{r, n};
123 if (auto p = _cache_.tryGet(key)) return *p;
124 }
125
126 // compute the corrected Szpankowski approximation of cn2 (see the
127 // documentation of constants cst1, cst2, cst3 in the ScorefNML header)
128 double log2Cnr1 = 0.5 * std::log2(n) + _cst1_ + _cst2_ / std::sqrt(n) + _cst3_ / n;
129 if (r == std::size_t(2)) return log2Cnr1;
130
131 // the value of log2(cn1), which is always equal to 0
132 double log2Cnr2 = 0.0;
133
134 // use Equation (13) of the paper to compute the value of cnr
135 // (see the detail of the formulas in the above if statement)
136 double k_r = std::exp((log2Cnr2 - log2Cnr1) * M_LN2);
137 double q_r = 1.0 + k_r * n / (3.0 - 2.0); // we first compute C_n^3
138 double log2Cnr = 0.0;
139 for (std::size_t i = std::size_t(3); i <= r; ++i) {
140 log2Cnr = log2Cnr1 + std::log2(q_r);
141 log2Cnr1 = log2Cnr;
142 k_r = 1.0 / q_r;
143 q_r = 1.0 + k_r * (n / (i - 1.0));
144 }
145
146 // if we use a cache, update it
147 if (_use_cache_) { _cache_.insert(std::pair< std::size_t, double >{r, n}, log2Cnr); }
148
149 return log2Cnr;
150 }
151 }
const double _cst1_
the value of N above which we should use Szpankowski's approximation
double log2Cnr(const std::size_t r, const double n)
returns the value of the log in base 2 of Cnr
HashTable< std::pair< std::size_t, double >, double > _cache_
const double VariableLog2ParamComplexityCTable[4][1000]
constexpr std::size_t VariableLog2ParamComplexityCTableRSize

References _cache_, _cst1_, _cst2_, _cst3_, _use_cache_, GUM_ERROR, log2Cnr(), M_LN2, gum::VariableLog2ParamComplexityCTable, gum::VariableLog2ParamComplexityCTableNSize, and gum::VariableLog2ParamComplexityCTableRSize.

Referenced by log2Cnr().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ operator=() [1/2]

VariableLog2ParamComplexity & gum::VariableLog2ParamComplexity::operator= ( const VariableLog2ParamComplexity & from)

copy operator

References VariableLog2ParamComplexity().

Here is the call graph for this function:

◆ operator=() [2/2]

VariableLog2ParamComplexity & gum::VariableLog2ParamComplexity::operator= ( VariableLog2ParamComplexity && from)

move operator

References VariableLog2ParamComplexity().

Here is the call graph for this function:

◆ useCache()

void gum::VariableLog2ParamComplexity::useCache ( const bool on_off)

indicates whether we wish to use a cache for the Cnr

Member Data Documentation

◆ _cache_

HashTable< std::pair< std::size_t, double >, double > gum::VariableLog2ParamComplexity::_cache_
private

Definition at line 171 of file variableLog2ParamComplexity.h.

Referenced by log2Cnr().

◆ _cst1_

const double gum::VariableLog2ParamComplexity::_cst1_ = -0.5 + std::log2(std::sqrt(M_PI))
private

the value of N above which we should use Szpankowski's approximation

Definition at line 163 of file variableLog2ParamComplexity.h.

Referenced by log2Cnr().

◆ _cst2_

const double gum::VariableLog2ParamComplexity::_cst2_ = std::sqrt(2.0 / M_PI) / 3.0
private

Definition at line 164 of file variableLog2ParamComplexity.h.

Referenced by log2Cnr().

◆ _cst3_

const double gum::VariableLog2ParamComplexity::_cst3_ = 3.0 / 36.0 - 4.0 / (9.0 * M_PI)
private

Definition at line 165 of file variableLog2ParamComplexity.h.

Referenced by log2Cnr().

◆ _use_cache_

bool gum::VariableLog2ParamComplexity::_use_cache_ {true}
private

Definition at line 168 of file variableLog2ParamComplexity.h.

168{true};

Referenced by log2Cnr().


The documentation for this class was generated from the following files: