1 /**
2  *
3  *   Copyright (c) 2005-2021 by Pierre-Henri WUILLEMIN(_at_LIP6) & Christophe GONZALES(_at_AMU)
4  *   info_at_agrum_dot_org
5  *
6  *  This library is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU Lesser General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public License
17  *  along with this library.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 
22 #include <gumtest/AgrumTestSuite.h>
23 #include <gumtest/testsuite_utils.h>
24 #include <iostream>
25 
26 #include <agrum/tools/database/DBTranslator4LabelizedVariable.h>
27 #include <agrum/tools/database/DBTranslatorSet.h>
28 #include <agrum/BN/learning/aprioris/aprioriSmoothing.h>
29 #include <agrum/BN/learning/scores_and_tests/scorefNML.h>
30 
31 namespace gum_tests {
32 
33   class ScorefNMLTestSuite: public CxxTest::TestSuite {
34     private:
_score_(const std::vector<double> & N_ijk,const std::vector<double> & N_ij)35     double _score_(const std::vector< double >& N_ijk, const std::vector< double >& N_ij) const {
36       double score   = 0;
37       double N       = 0;
38       double penalty = 0;
39       for (const auto n_ijk: N_ijk) {
40         if (n_ijk) {
41           score += n_ijk * std::log2(n_ijk);
42           N += n_ijk;
43         }
44       }
45 
46       gum::GammaLog2 gamma_log2;
47       if (!N_ij.empty()) {
48         const std::size_t r = N_ijk.size() / N_ij.size();
49         for (const auto n_ij: N_ij) {
50           if (n_ij) {
51             score -= n_ij * std::log2(n_ij);
52 
53             // compute the penalty:
54             double cn2 = 2;
55             for (int h = 1; h < n_ij; h++) {
56               double elt
57                  = (gamma_log2(n_ij + 1) - gamma_log2(h + 1) - gamma_log2((n_ij - h) + 1)) * M_LN2
58                  + h * std::log(h / n_ij) + (n_ij - h) * std::log((n_ij - h) / n_ij);
59               cn2 += std::exp(elt);
60             }
61             double cnr1 = cn2;   // equal C_n^2
62             double cnr2 = 1;     // equal C_n^1
63             double cnr  = cnr1;
64             for (std::size_t i = std::size_t(3); i <= r; ++i) {
65               cnr  = cnr1 + (n_ij / (i - 2.0)) * cnr2;
66               cnr2 = cnr1;
67               cnr1 = cnr;
68             }
69             penalty += std::log2(cnr);
70           }
71         }
72       } else {
73         score -= N * std::log2(N);
74         const std::size_t r = N_ijk.size();
75 
76         // compute the penalty:
77         long double cn2 = 2;
78         for (double h = 1; h < N; ++h) {
79           long double elt
80              = (gamma_log2(N + 1) - gamma_log2(h + 1) - gamma_log2((N - h) + 1)) * M_LN2
81              + h * std::log(h / N) + (N - h) * std::log((N - h) / N);
82           cn2 += std::exp(elt);
83         }
84         long double cnr1 = cn2;   // equal C_n^2
85         long double cnr2 = 1;     // equal C_n^1
86         long double cnr  = cnr1;
87         for (std::size_t i = std::size_t(3); i <= r; ++i) {
88           cnr  = cnr1 + (N / (i - 2.0)) * cnr2;
89           cnr2 = cnr1;
90           cnr1 = cnr;
91         }
92         penalty += std::log2(cnr);
93       }
94 
95       score -= penalty;
96       return score;
97     }
98 
99 
_equal_(const double x,const double y)100     bool _equal_(const double x, const double y) const {
101       double dev = x >= y ? (x - y) / x : (y - x) / y;
102       if (dev < 0) dev = -dev;
103       return dev <= TS_GUM_SMALL_ERROR;
104     }
105 
106 
107     public:
_test_no_range_no_nodeId2col()108     void _test_no_range_no_nodeId2col() {
109       // create the translator set
110       gum::LabelizedVariable var("X1", "", 0);
111       var.addLabel("0");
112       var.addLabel("1");
113       var.addLabel("2");
114 
115       gum::learning::DBTranslatorSet<> trans_set;
116       {
117         const std::vector< std::string >                miss;
118         gum::learning::DBTranslator4LabelizedVariable<> translator(var, miss);
119         std::vector< std::string >                      names{"A", "B", "C", "D", "E", "F"};
120 
121         for (std::size_t i = std::size_t(0); i < names.size(); ++i) {
122           translator.setVariableName(names[i]);
123           trans_set.insertTranslator(translator, i);
124         }
125       }
126 
127       // create the database
128       gum::learning::DatabaseTable<> database(trans_set);
129       std::vector< std::string >     row0{"0", "1", "0", "2", "1", "1"};
130       std::vector< std::string >     row1{"1", "2", "0", "1", "2", "2"};
131       std::vector< std::string >     row2{"2", "1", "0", "1", "1", "0"};
132       std::vector< std::string >     row3{"1", "0", "0", "0", "0", "0"};
133       std::vector< std::string >     row4{"0", "0", "0", "1", "1", "1"};
134       for (int i = 0; i < 1000; ++i)
135         database.insertRow(row0);
136       for (int i = 0; i < 50; ++i)
137         database.insertRow(row1);
138       for (int i = 0; i < 75; ++i)
139         database.insertRow(row2);
140       for (int i = 0; i < 75; ++i)
141         database.insertRow(row3);
142       for (int i = 0; i < 200; ++i)
143         database.insertRow(row4);
144 
145       // create the parser
146       gum::learning::DBRowGeneratorSet<>    genset;
147       gum::learning::DBRowGeneratorParser<> parser(database.handler(), genset);
148 
149       gum::learning::AprioriSmoothing<> apriori(database);
150       gum::learning::ScorefNML<>        score(parser, apriori);
151 
152       TS_GUM_ASSERT_THROWS_NOTHING(gum::learning::ScorefNML<>::isAprioriCompatible(
153          gum::learning::AprioriSmoothing<>::type::type));
154       TS_GUM_ASSERT_THROWS_NOTHING(gum::learning::ScorefNML<>::isAprioriCompatible(apriori));
155       TS_GUM_ASSERT_THROWS_NOTHING(
156          score.isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
157       TS_GUM_ASSERT_THROWS_NOTHING(score.isAprioriCompatible(apriori));
158 
159       gum::NodeId                node0 = 0;
160       gum::NodeId                node1 = 1;
161       gum::NodeId                node3 = 3;
162       std::vector< gum::NodeId > cond_empty;
163       std::vector< gum::NodeId > cond2{node1};
164       std::vector< gum::NodeId > cond3{node3};
165 
166       gum::learning::IdCondSet<> idset1(node0, cond_empty);    // #3,#0
167       gum::learning::IdCondSet<> idset2(node0, cond2, true);   // #9,#3
168       gum::learning::IdCondSet<> idset3(node0, cond3, true);   // #9,#3
169 
170       // idset1: node0 | emptyset
171       std::vector< double > N_ijk_1{1201.0, 126.0, 76.0};
172       std::vector< double > N_ij_1;
173       double                xscore_1 = _score_(N_ijk_1, N_ij_1);
174       TS_ASSERT(_equal_(xscore_1, score.score(node0)))
175 
176       // idset2: node0 | node1
177       std::vector< double > N_ijk_2{201, 76, 1, 1001, 1, 76, 1, 51, 1};
178       std::vector< double > N_ij_2{278, 1078, 53};
179       double                xscore_2 = _score_(N_ijk_2, N_ij_2);
180       TS_ASSERT(_equal_(xscore_2, score.score(node0, cond2)))
181 
182       // idset3: node0 | node3
183       std::vector< double > N_ijk_3{1, 76, 1, 201, 51, 76, 1001, 1, 1};
184       std::vector< double > N_ij_3{78, 328, 1003};
185       double                xscore_3 = _score_(N_ijk_3, N_ij_3);
186       TS_ASSERT(_equal_(xscore_3, score.score(node0, cond3)))
187 
188 
189       gum::learning::ScorefNML<> score2(score);
190       TS_GUM_ASSERT_THROWS_NOTHING(
191          score2.isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
192       TS_GUM_ASSERT_THROWS_NOTHING(score2.isAprioriCompatible(apriori));
193 
194       TS_ASSERT(_equal_(xscore_1, score2.score(node0)))
195       TS_ASSERT(_equal_(xscore_2, score2.score(node0, cond2)))
196       TS_ASSERT(_equal_(xscore_3, score2.score(node0, cond3)))
197 
198       gum::learning::ScorefNML<> score3(std::move(score2));
199       TS_GUM_ASSERT_THROWS_NOTHING(
200          score3.isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
201       TS_GUM_ASSERT_THROWS_NOTHING(score3.isAprioriCompatible(apriori));
202 
203       TS_ASSERT(_equal_(xscore_1, score3.score(node0)))
204       TS_ASSERT(_equal_(xscore_2, score3.score(node0, cond2)))
205       TS_ASSERT(_equal_(xscore_3, score3.score(node0, cond3)))
206 
207       gum::learning::ScorefNML<>* score4 = score3.clone();
208       TS_GUM_ASSERT_THROWS_NOTHING(
209          score4->isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
210       TS_GUM_ASSERT_THROWS_NOTHING(score4->isAprioriCompatible(apriori));
211 
212       TS_ASSERT(_equal_(xscore_1, score4->score(node0)))
213       TS_ASSERT(_equal_(xscore_2, score4->score(node0, cond2)))
214       TS_ASSERT(_equal_(xscore_3, score4->score(node0, cond3)))
215 
216       score4->operator=(score);
217       TS_GUM_ASSERT_THROWS_NOTHING(
218          score4->isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
219       TS_GUM_ASSERT_THROWS_NOTHING(score4->isAprioriCompatible(apriori));
220 
221       TS_ASSERT(_equal_(xscore_1, score4->score(node0)))
222       TS_ASSERT(_equal_(xscore_2, score4->score(node0, cond2)))
223       TS_ASSERT(_equal_(xscore_3, score4->score(node0, cond3)))
224 
225       score4->operator=(std::move(score));
226       TS_GUM_ASSERT_THROWS_NOTHING(
227          score4->isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
228       TS_GUM_ASSERT_THROWS_NOTHING(score4->isAprioriCompatible(apriori));
229 
230       TS_ASSERT(_equal_(xscore_1, score4->score(node0)))
231       TS_ASSERT(_equal_(xscore_2, score4->score(node0, cond2)))
232       TS_ASSERT(_equal_(xscore_3, score4->score(node0, cond3)))
233 
234       delete score4;
235     }
236 
237 
_test_no_range_has_nodeId2col()238     void _test_no_range_has_nodeId2col() {
239       // create the translator set
240       gum::LabelizedVariable var("X1", "", 0);
241       var.addLabel("0");
242       var.addLabel("1");
243       var.addLabel("2");
244 
245       gum::learning::DBTranslatorSet<> trans_set;
246       {
247         const std::vector< std::string >                miss;
248         gum::learning::DBTranslator4LabelizedVariable<> translator(var, miss);
249         std::vector< std::string >                      names{"A", "B", "C", "D", "E", "F"};
250 
251         for (std::size_t i = std::size_t(0); i < names.size(); ++i) {
252           translator.setVariableName(names[i]);
253           trans_set.insertTranslator(translator, i);
254         }
255       }
256 
257       // create the database
258       gum::learning::DatabaseTable<> database(trans_set);
259       std::vector< std::string >     row0{"0", "1", "0", "2", "1", "1"};
260       std::vector< std::string >     row1{"1", "2", "0", "1", "2", "2"};
261       std::vector< std::string >     row2{"2", "1", "0", "1", "1", "0"};
262       std::vector< std::string >     row3{"1", "0", "0", "0", "0", "0"};
263       std::vector< std::string >     row4{"0", "0", "0", "1", "1", "1"};
264       for (int i = 0; i < 1000; ++i)
265         database.insertRow(row0);
266       for (int i = 0; i < 50; ++i)
267         database.insertRow(row1);
268       for (int i = 0; i < 75; ++i)
269         database.insertRow(row2);
270       for (int i = 0; i < 75; ++i)
271         database.insertRow(row3);
272       for (int i = 0; i < 200; ++i)
273         database.insertRow(row4);
274 
275       // create the parser
276       gum::learning::DBRowGeneratorSet<>    genset;
277       gum::learning::DBRowGeneratorParser<> parser(database.handler(), genset);
278 
279       gum::Bijection< gum::NodeId, std::size_t > nodeId2columns;
280       gum::NodeId                                node0 = 0;
281       gum::NodeId                                node1 = 1;
282       gum::NodeId                                node2 = 2;
283       gum::NodeId                                node3 = 3;
284       gum::NodeId                                node4 = 4;
285       gum::NodeId                                node5 = 5;
286       nodeId2columns.insert(node0, std::size_t(4));
287       nodeId2columns.insert(node1, std::size_t(3));
288       nodeId2columns.insert(node2, std::size_t(0));
289       nodeId2columns.insert(node3, std::size_t(2));
290       nodeId2columns.insert(node4, std::size_t(5));
291       nodeId2columns.insert(node5, std::size_t(1));
292 
293       gum::learning::AprioriSmoothing<> apriori(database, nodeId2columns);
294       gum::learning::ScorefNML<>        score(parser, apriori, nodeId2columns);
295 
296       TS_GUM_ASSERT_THROWS_NOTHING(gum::learning::ScorefNML<>::isAprioriCompatible(
297          gum::learning::AprioriSmoothing<>::type::type));
298       TS_GUM_ASSERT_THROWS_NOTHING(gum::learning::ScorefNML<>::isAprioriCompatible(apriori));
299       TS_GUM_ASSERT_THROWS_NOTHING(
300          score.isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
301       TS_GUM_ASSERT_THROWS_NOTHING(score.isAprioriCompatible(apriori));
302 
303       std::vector< gum::NodeId > cond_empty;
304       std::vector< gum::NodeId > cond2{node5};
305       std::vector< gum::NodeId > cond3{node1};
306 
307       gum::learning::IdCondSet<> idset1(node2, cond_empty);    // #3,#0
308       gum::learning::IdCondSet<> idset2(node2, cond2, true);   // #9,#3
309       gum::learning::IdCondSet<> idset3(node2, cond3, true);   // #9,#3
310 
311       // idset1: node2 | emptyset
312       std::vector< double > N_ijk_1{1201.0, 126.0, 76.0};
313       std::vector< double > N_ij_1;
314       double                xscore_1 = _score_(N_ijk_1, N_ij_1);
315       TS_ASSERT(_equal_(xscore_1, score.score(node2)))
316 
317 
318       // idset2: node2 | node5
319       std::vector< double > N_ijk_2{201, 76, 1, 1001, 1, 76, 1, 51, 1};
320       std::vector< double > N_ij_2{278, 1078, 53};
321       double                xscore_2 = _score_(N_ijk_2, N_ij_2);
322       TS_ASSERT(_equal_(xscore_2, score.score(node2, cond2)))
323 
324       // idset3: node2 | node1
325       std::vector< double > N_ijk_3{1, 76, 1, 201, 51, 76, 1001, 1, 1};
326       std::vector< double > N_ij_3{78, 328, 1003};
327       double                xscore_3 = _score_(N_ijk_3, N_ij_3);
328       TS_ASSERT(_equal_(xscore_3, score.score(node2, cond3)))
329 
330 
331       gum::learning::ScorefNML<> score2(score);
332       TS_GUM_ASSERT_THROWS_NOTHING(
333          score2.isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
334       TS_GUM_ASSERT_THROWS_NOTHING(score2.isAprioriCompatible(apriori));
335 
336       TS_ASSERT(_equal_(xscore_1, score2.score(node2)))
337       TS_ASSERT(_equal_(xscore_2, score2.score(node2, cond2)))
338       TS_ASSERT(_equal_(xscore_3, score2.score(node2, cond3)))
339 
340       gum::learning::ScorefNML<> score3(std::move(score2));
341       TS_GUM_ASSERT_THROWS_NOTHING(
342          score3.isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
343       TS_GUM_ASSERT_THROWS_NOTHING(score3.isAprioriCompatible(apriori));
344 
345       TS_ASSERT(_equal_(xscore_1, score3.score(node2)))
346       TS_ASSERT(_equal_(xscore_2, score3.score(node2, cond2)))
347       TS_ASSERT(_equal_(xscore_3, score3.score(node2, cond3)))
348 
349       gum::learning::ScorefNML<>* score4 = score3.clone();
350       TS_GUM_ASSERT_THROWS_NOTHING(
351          score4->isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
352       TS_GUM_ASSERT_THROWS_NOTHING(score4->isAprioriCompatible(apriori));
353 
354       TS_ASSERT(_equal_(xscore_1, score4->score(node2)))
355       TS_ASSERT(_equal_(xscore_2, score4->score(node2, cond2)))
356       TS_ASSERT(_equal_(xscore_3, score4->score(node2, cond3)))
357 
358       score4->operator=(score);
359       TS_GUM_ASSERT_THROWS_NOTHING(
360          score4->isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
361       TS_GUM_ASSERT_THROWS_NOTHING(score4->isAprioriCompatible(apriori));
362 
363       TS_ASSERT(_equal_(xscore_1, score4->score(node2)))
364       TS_ASSERT(_equal_(xscore_2, score4->score(node2, cond2)))
365       TS_ASSERT(_equal_(xscore_3, score4->score(node2, cond3)))
366 
367       score4->operator=(std::move(score));
368       TS_GUM_ASSERT_THROWS_NOTHING(
369          score4->isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
370       TS_GUM_ASSERT_THROWS_NOTHING(score4->isAprioriCompatible(apriori));
371 
372       TS_ASSERT(_equal_(xscore_1, score4->score(node2)))
373       TS_ASSERT(_equal_(xscore_2, score4->score(node2, cond2)))
374       TS_ASSERT(_equal_(xscore_3, score4->score(node2, cond3)))
375 
376       delete score4;
377     }
378 
379 
_test_has_range_no_nodeId2col()380     void _test_has_range_no_nodeId2col() {
381       // create the translator set
382       gum::LabelizedVariable var("X1", "", 0);
383       var.addLabel("0");
384       var.addLabel("1");
385       var.addLabel("2");
386 
387       gum::learning::DBTranslatorSet<> trans_set;
388       {
389         const std::vector< std::string >                miss;
390         gum::learning::DBTranslator4LabelizedVariable<> translator(var, miss);
391         std::vector< std::string >                      names{"A", "B", "C", "D", "E", "F"};
392 
393         for (std::size_t i = std::size_t(0); i < names.size(); ++i) {
394           translator.setVariableName(names[i]);
395           trans_set.insertTranslator(translator, i);
396         }
397       }
398 
399       // create the database
400       gum::learning::DatabaseTable<> database(trans_set);
401       std::vector< std::string >     row0{"0", "1", "0", "2", "1", "1"};
402       std::vector< std::string >     row1{"1", "2", "0", "1", "2", "2"};
403       std::vector< std::string >     row2{"2", "1", "0", "1", "1", "0"};
404       std::vector< std::string >     row3{"1", "0", "0", "0", "0", "0"};
405       std::vector< std::string >     row4{"0", "0", "0", "1", "1", "1"};
406       for (int i = 0; i < 1000; ++i)
407         database.insertRow(row0);
408       for (int i = 0; i < 50; ++i)
409         database.insertRow(row1);
410       for (int i = 0; i < 75; ++i)
411         database.insertRow(row2);
412       for (int i = 0; i < 75; ++i)
413         database.insertRow(row3);
414       for (int i = 0; i < 200; ++i)
415         database.insertRow(row4);
416 
417       // create the parser
418       gum::learning::DBRowGeneratorSet<>    genset;
419       gum::learning::DBRowGeneratorParser<> parser(database.handler(), genset);
420 
421       std::vector< std::pair< std::size_t, std::size_t > > ranges{{800, 1000}, {1050, 1400}};
422 
423       gum::learning::AprioriSmoothing<> apriori(database);
424       gum::learning::ScorefNML<>        score(parser, apriori, ranges);
425 
426       TS_GUM_ASSERT_THROWS_NOTHING(gum::learning::ScorefNML<>::isAprioriCompatible(
427          gum::learning::AprioriSmoothing<>::type::type));
428       TS_GUM_ASSERT_THROWS_NOTHING(gum::learning::ScorefNML<>::isAprioriCompatible(apriori));
429       TS_GUM_ASSERT_THROWS_NOTHING(
430          score.isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
431       TS_GUM_ASSERT_THROWS_NOTHING(score.isAprioriCompatible(apriori));
432 
433       gum::NodeId                node0 = 0;
434       gum::NodeId                node1 = 1;
435       gum::NodeId                node3 = 3;
436       std::vector< gum::NodeId > cond_empty;
437       std::vector< gum::NodeId > cond2{node1};
438       std::vector< gum::NodeId > cond3{node3};
439 
440       gum::learning::IdCondSet<> idset1(node0, cond_empty);    // #3,#0
441       gum::learning::IdCondSet<> idset2(node0, cond2, true);   // #9,#3
442       gum::learning::IdCondSet<> idset3(node0, cond3, true);   // #9,#3
443 
444       // idset1: node0 | emptyset
445       std::vector< double > N_ijk_1{401.0, 76.0, 76.0};
446       std::vector< double > N_ij_1;
447       double                xscore_1 = _score_(N_ijk_1, N_ij_1);
448       TS_ASSERT(_equal_(xscore_1, score.score(node0)))
449 
450       // idset2: node0 | node1
451       std::vector< double > N_ijk_2{201, 76, 1, 201, 1, 76, 1, 1, 1};
452       std::vector< double > N_ij_2{278, 278, 3};
453       double                xscore_2 = _score_(N_ijk_2, N_ij_2);
454       TS_ASSERT(_equal_(xscore_2, score.score(node0, cond2)))
455 
456       // idset3: node0 | node3
457       std::vector< double > N_ijk_3{1, 76, 1, 201, 1, 76, 201, 1, 1};
458       std::vector< double > N_ij_3{78, 278, 203};
459       double                xscore_3 = _score_(N_ijk_3, N_ij_3);
460       TS_ASSERT(_equal_(xscore_3, score.score(node0, cond3)))
461 
462 
463       gum::learning::ScorefNML<> score2(score);
464       TS_GUM_ASSERT_THROWS_NOTHING(
465          score2.isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
466       TS_GUM_ASSERT_THROWS_NOTHING(score2.isAprioriCompatible(apriori));
467 
468       TS_ASSERT(_equal_(xscore_1, score2.score(node0)))
469       TS_ASSERT(_equal_(xscore_2, score2.score(node0, cond2)))
470       TS_ASSERT(_equal_(xscore_3, score2.score(node0, cond3)))
471 
472       gum::learning::ScorefNML<> score3(std::move(score2));
473       TS_GUM_ASSERT_THROWS_NOTHING(
474          score3.isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
475       TS_GUM_ASSERT_THROWS_NOTHING(score3.isAprioriCompatible(apriori));
476 
477       TS_ASSERT(_equal_(xscore_1, score3.score(node0)))
478       TS_ASSERT(_equal_(xscore_2, score3.score(node0, cond2)))
479       TS_ASSERT(_equal_(xscore_3, score3.score(node0, cond3)))
480 
481       gum::learning::ScorefNML<>* score4 = score3.clone();
482       TS_GUM_ASSERT_THROWS_NOTHING(
483          score4->isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
484       TS_GUM_ASSERT_THROWS_NOTHING(score4->isAprioriCompatible(apriori));
485 
486       TS_ASSERT(_equal_(xscore_1, score4->score(node0)))
487       TS_ASSERT(_equal_(xscore_2, score4->score(node0, cond2)))
488       TS_ASSERT(_equal_(xscore_3, score4->score(node0, cond3)))
489 
490       score4->operator=(score);
491       TS_GUM_ASSERT_THROWS_NOTHING(
492          score4->isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
493       TS_GUM_ASSERT_THROWS_NOTHING(score4->isAprioriCompatible(apriori));
494 
495       TS_ASSERT(_equal_(xscore_1, score4->score(node0)))
496       TS_ASSERT(_equal_(xscore_2, score4->score(node0, cond2)))
497       TS_ASSERT(_equal_(xscore_3, score4->score(node0, cond3)))
498 
499       score4->operator=(std::move(score));
500       TS_GUM_ASSERT_THROWS_NOTHING(
501          score4->isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
502       TS_GUM_ASSERT_THROWS_NOTHING(score4->isAprioriCompatible(apriori));
503 
504       TS_ASSERT(_equal_(xscore_1, score4->score(node0)))
505       TS_ASSERT(_equal_(xscore_2, score4->score(node0, cond2)))
506       TS_ASSERT(_equal_(xscore_3, score4->score(node0, cond3)))
507 
508       delete score4;
509     }
510 
511 
_test_has_range_has_nodeId2col()512     void _test_has_range_has_nodeId2col() {
513       // create the translator set
514       gum::LabelizedVariable var("X1", "", 0);
515       var.addLabel("0");
516       var.addLabel("1");
517       var.addLabel("2");
518 
519       gum::learning::DBTranslatorSet<> trans_set;
520       {
521         const std::vector< std::string >                miss;
522         gum::learning::DBTranslator4LabelizedVariable<> translator(var, miss);
523         std::vector< std::string >                      names{"A", "B", "C", "D", "E", "F"};
524 
525         for (std::size_t i = std::size_t(0); i < names.size(); ++i) {
526           translator.setVariableName(names[i]);
527           trans_set.insertTranslator(translator, i);
528         }
529       }
530 
531       // create the database
532       gum::learning::DatabaseTable<> database(trans_set);
533       std::vector< std::string >     row0{"0", "1", "0", "2", "1", "1"};
534       std::vector< std::string >     row1{"1", "2", "0", "1", "2", "2"};
535       std::vector< std::string >     row2{"2", "1", "0", "1", "1", "0"};
536       std::vector< std::string >     row3{"1", "0", "0", "0", "0", "0"};
537       std::vector< std::string >     row4{"0", "0", "0", "1", "1", "1"};
538       for (int i = 0; i < 1000; ++i)
539         database.insertRow(row0);
540       for (int i = 0; i < 50; ++i)
541         database.insertRow(row1);
542       for (int i = 0; i < 75; ++i)
543         database.insertRow(row2);
544       for (int i = 0; i < 75; ++i)
545         database.insertRow(row3);
546       for (int i = 0; i < 200; ++i)
547         database.insertRow(row4);
548 
549       // create the parser
550       gum::learning::DBRowGeneratorSet<>    genset;
551       gum::learning::DBRowGeneratorParser<> parser(database.handler(), genset);
552 
553       std::vector< std::pair< std::size_t, std::size_t > > ranges{{800, 1000}, {1050, 1400}};
554 
555       gum::Bijection< gum::NodeId, std::size_t > nodeId2columns;
556       gum::NodeId                                node0 = 0;
557       gum::NodeId                                node1 = 1;
558       gum::NodeId                                node2 = 2;
559       gum::NodeId                                node3 = 3;
560       gum::NodeId                                node4 = 4;
561       gum::NodeId                                node5 = 5;
562       nodeId2columns.insert(node0, std::size_t(4));
563       nodeId2columns.insert(node1, std::size_t(3));
564       nodeId2columns.insert(node2, std::size_t(0));
565       nodeId2columns.insert(node3, std::size_t(2));
566       nodeId2columns.insert(node4, std::size_t(5));
567       nodeId2columns.insert(node5, std::size_t(1));
568 
569       gum::learning::AprioriSmoothing<> apriori(database, nodeId2columns);
570       gum::learning::ScorefNML<>        score(parser, apriori, ranges, nodeId2columns);
571 
572       TS_GUM_ASSERT_THROWS_NOTHING(gum::learning::ScorefNML<>::isAprioriCompatible(
573          gum::learning::AprioriSmoothing<>::type::type));
574       TS_GUM_ASSERT_THROWS_NOTHING(gum::learning::ScorefNML<>::isAprioriCompatible(apriori));
575       TS_GUM_ASSERT_THROWS_NOTHING(
576          score.isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
577       TS_GUM_ASSERT_THROWS_NOTHING(score.isAprioriCompatible(apriori));
578 
579       std::vector< gum::NodeId > cond_empty;
580       std::vector< gum::NodeId > cond2{node5};
581       std::vector< gum::NodeId > cond3{node1};
582 
583       gum::learning::IdCondSet<> idset1(node2, cond_empty);    // #3,#0
584       gum::learning::IdCondSet<> idset2(node2, cond2, true);   // #9,#3
585       gum::learning::IdCondSet<> idset3(node2, cond3, true);   // #9,#3
586 
587       // idset1: node2 | emptyset
588       std::vector< double > N_ijk_1{401.0, 76.0, 76.0};
589       std::vector< double > N_ij_1;
590       double                xscore_1 = _score_(N_ijk_1, N_ij_1);
591       TS_ASSERT(_equal_(xscore_1, score.score(node2)))
592 
593 
594       // idset2: node2 | node5
595       std::vector< double > N_ijk_2{201, 76, 1, 201, 1, 76, 1, 1, 1};
596       std::vector< double > N_ij_2{278, 278, 3};
597       double                xscore_2 = _score_(N_ijk_2, N_ij_2);
598       TS_ASSERT(_equal_(xscore_2, score.score(node2, cond2)))
599 
600       // idset3: node2 | node1
601       std::vector< double > N_ijk_3{1, 76, 1, 201, 1, 76, 201, 1, 1};
602       std::vector< double > N_ij_3{78, 278, 203};
603       double                xscore_3 = _score_(N_ijk_3, N_ij_3);
604       TS_ASSERT(_equal_(xscore_3, score.score(node2, cond3)))
605 
606 
607       gum::learning::ScorefNML<> score2(score);
608       TS_GUM_ASSERT_THROWS_NOTHING(
609          score2.isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
610       TS_GUM_ASSERT_THROWS_NOTHING(score2.isAprioriCompatible(apriori));
611 
612       TS_ASSERT(_equal_(xscore_1, score2.score(node2)))
613       TS_ASSERT(_equal_(xscore_2, score2.score(node2, cond2)))
614       TS_ASSERT(_equal_(xscore_3, score2.score(node2, cond3)))
615 
616       gum::learning::ScorefNML<> score3(std::move(score2));
617       TS_GUM_ASSERT_THROWS_NOTHING(
618          score3.isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
619       TS_GUM_ASSERT_THROWS_NOTHING(score3.isAprioriCompatible(apriori));
620 
621       TS_ASSERT(_equal_(xscore_1, score3.score(node2)))
622       TS_ASSERT(_equal_(xscore_2, score3.score(node2, cond2)))
623       TS_ASSERT(_equal_(xscore_3, score3.score(node2, cond3)))
624 
625       gum::learning::ScorefNML<>* score4 = score3.clone();
626       TS_GUM_ASSERT_THROWS_NOTHING(
627          score4->isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
628       TS_GUM_ASSERT_THROWS_NOTHING(score4->isAprioriCompatible(apriori));
629 
630       TS_ASSERT(_equal_(xscore_1, score4->score(node2)))
631       TS_ASSERT(_equal_(xscore_2, score4->score(node2, cond2)))
632       TS_ASSERT(_equal_(xscore_3, score4->score(node2, cond3)))
633 
634       score4->operator=(score);
635       TS_GUM_ASSERT_THROWS_NOTHING(
636          score4->isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
637       TS_GUM_ASSERT_THROWS_NOTHING(score4->isAprioriCompatible(apriori));
638 
639       TS_ASSERT(_equal_(xscore_1, score4->score(node2)))
640       TS_ASSERT(_equal_(xscore_2, score4->score(node2, cond2)))
641       TS_ASSERT(_equal_(xscore_3, score4->score(node2, cond3)))
642 
643       score4->operator=(std::move(score));
644       TS_GUM_ASSERT_THROWS_NOTHING(
645          score4->isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
646       TS_GUM_ASSERT_THROWS_NOTHING(score4->isAprioriCompatible(apriori));
647 
648       TS_ASSERT(_equal_(xscore_1, score4->score(node2)))
649       TS_ASSERT(_equal_(xscore_2, score4->score(node2, cond2)))
650       TS_ASSERT(_equal_(xscore_3, score4->score(node2, cond3)))
651 
652       delete score4;
653     }
654 
655 
test_multicore()656     void test_multicore() {
657       // create the translator set
658       gum::LabelizedVariable var("X1", "", 0);
659       var.addLabel("0");
660       var.addLabel("1");
661       var.addLabel("2");
662 
663       gum::learning::DBTranslatorSet<> trans_set;
664       {
665         const std::vector< std::string >                miss;
666         gum::learning::DBTranslator4LabelizedVariable<> translator(var, miss);
667         std::vector< std::string >                      names{"A", "B", "C", "D", "E", "F"};
668 
669         for (std::size_t i = std::size_t(0); i < names.size(); ++i) {
670           translator.setVariableName(names[i]);
671           trans_set.insertTranslator(translator, i);
672         }
673       }
674 
675       // create the database
676       gum::learning::DatabaseTable<> database(trans_set);
677       std::vector< std::string >     row0{"0", "1", "0", "2", "1", "1"};
678       std::vector< std::string >     row1{"1", "2", "0", "1", "2", "2"};
679       std::vector< std::string >     row2{"2", "1", "0", "1", "1", "0"};
680       std::vector< std::string >     row3{"1", "0", "0", "0", "0", "0"};
681       std::vector< std::string >     row4{"0", "0", "0", "1", "1", "1"};
682       for (int i = 0; i < 1000; ++i)
683         database.insertRow(row0);
684       for (int i = 0; i < 50; ++i)
685         database.insertRow(row1);
686       for (int i = 0; i < 75; ++i)
687         database.insertRow(row2);
688       for (int i = 0; i < 75; ++i)
689         database.insertRow(row3);
690       for (int i = 0; i < 200; ++i)
691         database.insertRow(row4);
692 
693       // create the parser
694       gum::learning::DBRowGeneratorSet<>    genset;
695       gum::learning::DBRowGeneratorParser<> parser(database.handler(), genset);
696 
697       std::vector< std::pair< std::size_t, std::size_t > > ranges{{800, 1000}, {1050, 1400}};
698 
699       gum::learning::AprioriSmoothing<> apriori(database);
700 
701       for (std::size_t i = std::size_t(1); i < std::size_t(24); ++i) {
702         gum::learning::ScorefNML<> score(parser, apriori, ranges);
703         score.setMaxNbThreads(i);
704 
705         TS_GUM_ASSERT_THROWS_NOTHING(gum::learning::ScorefNML<>::isAprioriCompatible(
706            gum::learning::AprioriSmoothing<>::type::type));
707         TS_GUM_ASSERT_THROWS_NOTHING(gum::learning::ScorefNML<>::isAprioriCompatible(apriori));
708         TS_GUM_ASSERT_THROWS_NOTHING(
709            score.isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
710         TS_GUM_ASSERT_THROWS_NOTHING(score.isAprioriCompatible(apriori));
711 
712         gum::NodeId                node0 = 0;
713         gum::NodeId                node1 = 1;
714         gum::NodeId                node3 = 3;
715         std::vector< gum::NodeId > cond_empty;
716         std::vector< gum::NodeId > cond2{node1};
717         std::vector< gum::NodeId > cond3{node3};
718 
719         gum::learning::IdCondSet<> idset1(node0, cond_empty);    // #3,#0
720         gum::learning::IdCondSet<> idset2(node0, cond2, true);   // #9,#3
721         gum::learning::IdCondSet<> idset3(node0, cond3, true);   // #9,#3
722 
723         // idset1: node0 | emptyset
724         std::vector< double > N_ijk_1{401.0, 76.0, 76.0};
725         std::vector< double > N_ij_1;
726         double                xscore_1 = _score_(N_ijk_1, N_ij_1);
727         TS_ASSERT(_equal_(xscore_1, score.score(node0)))
728 
729         // idset2: node0 | node1
730         std::vector< double > N_ijk_2{201, 76, 1, 201, 1, 76, 1, 1, 1};
731         std::vector< double > N_ij_2{278, 278, 3};
732         double                xscore_2 = _score_(N_ijk_2, N_ij_2);
733         TS_ASSERT(_equal_(xscore_2, score.score(node0, cond2)))
734 
735         // idset3: node0 | node3
736         std::vector< double > N_ijk_3{1, 76, 1, 201, 1, 76, 201, 1, 1};
737         std::vector< double > N_ij_3{78, 278, 203};
738         double                xscore_3 = _score_(N_ijk_3, N_ij_3);
739         TS_ASSERT(_equal_(xscore_3, score.score(node0, cond3)))
740 
741 
742         gum::learning::ScorefNML<> score2(score);
743         TS_GUM_ASSERT_THROWS_NOTHING(
744            score2.isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
745         TS_GUM_ASSERT_THROWS_NOTHING(score2.isAprioriCompatible(apriori));
746 
747         TS_ASSERT(_equal_(xscore_1, score2.score(node0)))
748         TS_ASSERT(_equal_(xscore_2, score2.score(node0, cond2)))
749         TS_ASSERT(_equal_(xscore_3, score2.score(node0, cond3)))
750 
751         gum::learning::ScorefNML<> score3(std::move(score2));
752         TS_GUM_ASSERT_THROWS_NOTHING(
753            score3.isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
754         TS_GUM_ASSERT_THROWS_NOTHING(score3.isAprioriCompatible(apriori));
755 
756         TS_ASSERT(_equal_(xscore_1, score3.score(node0)))
757         TS_ASSERT(_equal_(xscore_2, score3.score(node0, cond2)))
758         TS_ASSERT(_equal_(xscore_3, score3.score(node0, cond3)))
759 
760         gum::learning::ScorefNML<>* score4 = score3.clone();
761         TS_GUM_ASSERT_THROWS_NOTHING(
762            score4->isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
763         TS_GUM_ASSERT_THROWS_NOTHING(score4->isAprioriCompatible(apriori));
764 
765         TS_ASSERT(_equal_(xscore_1, score4->score(node0)))
766         TS_ASSERT(_equal_(xscore_2, score4->score(node0, cond2)))
767         TS_ASSERT(_equal_(xscore_3, score4->score(node0, cond3)))
768 
769         score4->operator=(score);
770         TS_GUM_ASSERT_THROWS_NOTHING(
771            score4->isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
772         TS_GUM_ASSERT_THROWS_NOTHING(score4->isAprioriCompatible(apriori));
773 
774         TS_ASSERT(_equal_(xscore_1, score4->score(node0)))
775         TS_ASSERT(_equal_(xscore_2, score4->score(node0, cond2)))
776         TS_ASSERT(_equal_(xscore_3, score4->score(node0, cond3)))
777 
778         score4->operator=(std::move(score));
779         TS_GUM_ASSERT_THROWS_NOTHING(
780            score4->isAprioriCompatible(gum::learning::AprioriSmoothing<>::type::type));
781         TS_GUM_ASSERT_THROWS_NOTHING(score4->isAprioriCompatible(apriori));
782 
783         TS_ASSERT(_equal_(xscore_1, score4->score(node0)))
784         TS_ASSERT(_equal_(xscore_2, score4->score(node0, cond2)))
785         TS_ASSERT(_equal_(xscore_3, score4->score(node0, cond3)))
786 
787         delete score4;
788       }
789     }
790   };
791 
792 
793 } /* namespace gum_tests */
794