aGrUM
0.20.2
a C++ library for (probabilistic) graphical models
DBRowGeneratorSet_tpl.h
Go to the documentation of this file.
1
/**
2
*
3
* Copyright 2005-2020 Pierre-Henri WUILLEMIN(@LIP6) & Christophe GONZALES(@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
/** @file
23
* @brief Template implementation of DBRowGeneratorSet
24
*
25
* @author Christophe GONZALES(@AMU) and Pierre-Henri WUILLEMIN(@LIP6)
26
*/
27
28
#
ifndef
DOXYGEN_SHOULD_SKIP_THIS
29
30
namespace
gum
{
31
32
namespace
learning
{
33
34
35
/// default constructor
36
template
<
template
<
typename
>
class
ALLOC >
37
INLINE DBRowGeneratorSet<
ALLOC
>::
DBRowGeneratorSet
(
38
const
typename
DBRowGeneratorSet
<
ALLOC
>::
allocator_type
&
alloc
) :
39
generators__
(
alloc
),
40
setInputRow_performed__
(
alloc
) {
41
GUM_CONSTRUCTOR
(
DBRowGeneratorSet
);
42
}
43
44
45
/// copy constructor with a given allocator
46
template
<
template
<
typename
>
class
ALLOC
>
47
DBRowGeneratorSet
<
ALLOC
>::
DBRowGeneratorSet
(
48
const
DBRowGeneratorSet
<
ALLOC
>&
from
,
49
const
typename
DBRowGeneratorSet
<
ALLOC
>::
allocator_type
&
alloc
) :
50
generators__
(
from
.
nb_generators__
,
nullptr
,
alloc
),
51
nb_generators__
(
from
.
nb_generators__
),
52
setInputRow_performed__
(
from
.
nb_generators__
, 0,
alloc
) {
53
// create the generators
54
for
(
std
::
size_t
i
=
std
::
size_t
(0);
i
<
nb_generators__
; ++
i
)
55
generators__
[
i
] =
from
.
generators__
[
i
]->
clone
(
alloc
);
56
57
GUM_CONS_CPY
(
DBRowGeneratorSet
);
58
}
59
60
61
/// copy constructor
62
template
<
template
<
typename
>
class
ALLOC
>
63
INLINE
DBRowGeneratorSet
<
ALLOC
>::
DBRowGeneratorSet
(
64
const
DBRowGeneratorSet
<
ALLOC
>&
from
) :
65
DBRowGeneratorSet
<
ALLOC
>(
from
,
from
.
getAllocator
()) {}
66
67
68
/// move constructor with a given allocator
69
template
<
template
<
typename
>
class
ALLOC
>
70
INLINE
DBRowGeneratorSet
<
ALLOC
>::
DBRowGeneratorSet
(
71
DBRowGeneratorSet
<
ALLOC
>&&
from
,
72
const
typename
DBRowGeneratorSet
<
ALLOC
>::
allocator_type
&
alloc
) :
73
generators__
(
std
::
move
(
from
.
generators__
),
alloc
),
74
nb_generators__
(
from
.
nb_generators__
),
output_row__
(
from
.
output_row__
),
75
setInputRow_performed__
(
std
::
move
(
from
.
setInputRow_performed__
),
alloc
) {
76
GUM_CONS_MOV
(
DBRowGeneratorSet
);
77
}
78
79
80
/// move constructor
81
template
<
template
<
typename
>
class
ALLOC
>
82
INLINE
DBRowGeneratorSet
<
ALLOC
>::
DBRowGeneratorSet
(
83
DBRowGeneratorSet
<
ALLOC
>&&
from
) :
84
DBRowGeneratorSet
<
ALLOC
>(
std
::
move
(
from
),
from
.
getAllocator
()) {}
85
86
/// virtual copy constructor with a given allocator
87
template
<
template
<
typename
>
class
ALLOC
>
88
DBRowGeneratorSet
<
ALLOC
>*
DBRowGeneratorSet
<
ALLOC
>::
clone
(
89
const
typename
DBRowGeneratorSet
<
ALLOC
>::
allocator_type
&
alloc
)
const
{
90
ALLOC
<
DBRowGeneratorSet
<
ALLOC
> >
allocator
(
alloc
);
91
DBRowGeneratorSet
<
ALLOC
>*
generators
=
allocator
.
allocate
(1);
92
try
{
93
allocator
.
construct
(
generators
, *
this
,
alloc
);
94
}
catch
(...) {
95
allocator
.
deallocate
(
generators
, 1);
96
throw
;
97
}
98
return
generators
;
99
}
100
101
102
/// virtual copy constructor
103
template
<
template
<
typename
>
class
ALLOC
>
104
INLINE
DBRowGeneratorSet
<
ALLOC
>*
DBRowGeneratorSet
<
ALLOC
>::
clone
()
const
{
105
return
clone
(
this
->
getAllocator
());
106
}
107
108
109
/// removes all the generators
110
template
<
template
<
typename
>
class
ALLOC
>
111
void
DBRowGeneratorSet
<
ALLOC
>::
clear
() {
112
// delete all the generators
113
ALLOC
<
DBRowGenerator
<
ALLOC
> >
allocator
(
this
->
getAllocator
());
114
for
(
auto
gen
:
generators__
) {
115
allocator
.
destroy
(
gen
);
116
allocator
.
deallocate
(
gen
, 1);
117
}
118
119
// clear all the internal fields
120
generators__
.
clear
();
121
nb_generators__
=
std
::
size_t
(0);
122
output_row__
=
nullptr
;
123
setInputRow_performed__
.
clear
();
124
}
125
126
127
/// destructor
128
template
<
template
<
typename
>
class
ALLOC
>
129
DBRowGeneratorSet
<
ALLOC
>::~
DBRowGeneratorSet
() {
130
GUM_DESTRUCTOR
(
DBRowGeneratorSet
);
131
clear
();
132
}
133
134
135
/// copy operator
136
template
<
template
<
typename
>
class
ALLOC
>
137
DBRowGeneratorSet
<
ALLOC
>&
DBRowGeneratorSet
<
ALLOC
>::
operator
=(
138
const
DBRowGeneratorSet
<
ALLOC
>&
from
) {
139
if
(
this
!= &
from
) {
140
// produce the new generators
141
ALLOC
<
DBRowGenerator
<
ALLOC
> >
allocator
(
this
->
getAllocator
());
142
std
::
vector
<
DBRowGenerator
<
ALLOC
>*,
ALLOC
<
DBRowGenerator
<
ALLOC
>* > >
143
new_generators
(
from
.
nb_generators__
,
nullptr
,
allocator
);
144
for
(
std
::
size_t
i
=
std
::
size_t
(0);
i
<
from
.
nb_generators__
; ++
i
) {
145
try
{
146
new_generators
[
i
] =
from
.
generators__
[
i
]->
clone
(
allocator
);
147
}
catch
(...) {
148
for
(
std
::
size_t
j
=
std
::
size_t
(0);
j
<
i
; ++
j
) {
149
allocator
.
destroy
(
new_generators
[
j
]);
150
allocator
.
deallocate
(
new_generators
[
j
], 1);
151
}
152
throw
;
153
}
154
}
155
156
// create the setInputDBrow_performed vector
157
std
::
vector
<
int
,
ALLOC
<
int
> >
setInputDBrow_performed
(
158
from
.
nb_generators__
,
159
0,
160
getAllocator
());
161
162
// remove the old generators and copy the new ones
163
clear
();
164
generators__
=
std
::
move
(
new_generators
);
165
nb_generators__
=
from
.
nb_generators__
;
166
output_row__
=
nullptr
;
167
setInputRow_performed__
=
std
::
move
(
setInputDBrow_performed
);
168
}
169
170
return
*
this
;
171
}
172
173
174
/// move operator
175
template
<
template
<
typename
>
class
ALLOC
>
176
DBRowGeneratorSet
<
ALLOC
>&
177
DBRowGeneratorSet
<
ALLOC
>::
operator
=(
DBRowGeneratorSet
<
ALLOC
>&&
from
) {
178
if
(
this
!= &
from
) {
179
// remove the old generators and move the new ones
180
clear
();
181
generators__
=
std
::
move
(
from
.
generators__
);
182
nb_generators__
=
from
.
nb_generators__
;
183
output_row__
=
from
.
output_row__
;
184
setInputRow_performed__
=
std
::
move
(
from
.
setInputRow_performed__
);
185
}
186
return
*
this
;
187
}
188
189
190
/// returns the ith generator
191
template
<
template
<
typename
>
class
ALLOC
>
192
INLINE
DBRowGenerator
<
ALLOC
>&
193
DBRowGeneratorSet
<
ALLOC
>::
operator
[](
const
std
::
size_t
i
) {
194
return
*(
generators__
[
i
]);
195
}
196
197
198
/// returns the ith generator
199
template
<
template
<
typename
>
class
ALLOC
>
200
INLINE
const
DBRowGenerator
<
ALLOC
>&
201
DBRowGeneratorSet
<
ALLOC
>::
operator
[](
const
std
::
size_t
i
)
const
{
202
return
*(
generators__
[
i
]);
203
}
204
205
206
/// inserts a new generator at the end of the set
207
template
<
template
<
typename
>
class
ALLOC
>
208
template
<
template
<
template
<
typename
>
class
>
class
Generator
>
209
void
DBRowGeneratorSet
<
ALLOC
>::
insertGenerator
(
210
const
Generator
<
ALLOC
>&
generator
) {
211
// check that no output row generation is still active
212
if
(
output_row__
!=
nullptr
)
213
GUM_ERROR
(
OperationNotAllowed
,
214
"you cannot insert a new generator while a generation is "
215
"still being processed"
);
216
217
ALLOC
<
DBRowGenerator
<
ALLOC
> >
allocator
(
this
->
getAllocator
());
218
generators__
.
push_back
(
generator
.
clone
(
allocator
));
219
220
try
{
221
setInputRow_performed__
.
push_back
(0);
222
}
catch
(...) {
223
allocator
.
destroy
(
generators__
.
back
());
224
allocator
.
deallocate
(
generators__
.
back
(), 1);
225
throw
;
226
}
227
228
++
nb_generators__
;
229
output_row__
=
nullptr
;
230
}
231
232
233
/// inserts a new generator at the end of the set
234
template
<
template
<
typename
>
class
ALLOC
>
235
template
<
template
<
template
<
typename
>
class
>
class
Generator
>
236
void
DBRowGeneratorSet
<
ALLOC
>::
insertGenerator
(
237
const
Generator
<
ALLOC
>&
generator
,
238
const
std
::
size_t
i
) {
239
// check that no output row generation is still active
240
if
(
output_row__
!=
nullptr
)
241
GUM_ERROR
(
OperationNotAllowed
,
242
"you cannot insert a new generator while a generation is "
243
"still being processed"
);
244
245
ALLOC
<
DBRowGenerator
<
ALLOC
> >
allocator
(
this
->
getAllocator
());
246
generators__
.
insert
(
generators__
.
begin
() +
i
,
generator
.
clone
(
allocator
));
247
248
try
{
249
setInputRow_performed__
.
push_back
(0);
250
}
catch
(...) {
251
allocator
.
destroy
(*(
generators__
.
begin
() +
i
));
252
allocator
.
deallocate
(*(
generators__
.
begin
() +
i
));
253
throw
;
254
}
255
256
++
nb_generators__
;
257
output_row__
=
nullptr
;
258
}
259
260
261
/// returns the number of generators
262
template
<
template
<
typename
>
class
ALLOC
>
263
INLINE
std
::
size_t
DBRowGeneratorSet
<
ALLOC
>::
nbGenerators
()
const
noexcept
{
264
return
nb_generators__
;
265
}
266
267
268
/// assign a new Bayes net to all the generators that depend on a BN
269
template
<
template
<
typename
>
class
ALLOC
>
270
template
<
typename
GUM_SCALAR
>
271
void
DBRowGeneratorSet
<
ALLOC
>::
setBayesNet
(
272
const
BayesNet
<
GUM_SCALAR
>&
new_bn
) {
273
HashTable
<
DBRowGeneratorWithBN
<
GUM_SCALAR
,
ALLOC
>*,
274
const
BayesNet
<
GUM_SCALAR
>* >
275
old_bns
;
276
277
for
(
auto
xgen
:
generators__
) {
278
// check if the generator relies on a Bayes net
279
DBRowGeneratorWithBN
<
GUM_SCALAR
,
ALLOC
>*
gen
=
nullptr
;
280
try
{
281
gen
=
dynamic_cast
<
DBRowGeneratorWithBN
<
GUM_SCALAR
,
ALLOC
>* >(
xgen
);
282
}
catch
(
std
::
bad_cast
&) {}
283
284
if
(
gen
!=
nullptr
) {
285
// try to assign the new BN to the generator
286
try
{
287
const
BayesNet
<
GUM_SCALAR
>*
bn
= &(
gen
->
getBayesNet
());
288
old_bns
.
insert
(
gen
,
bn
);
289
gen
->
setBayesNet
(
new_bn
);
290
}
catch
(...) {
291
// if we could not assign the new BN to the generator, then
292
// make all the generators that were successfully assigned this
293
// BN revert to the old BN they had
294
for
(
auto
&
generator
:
old_bns
) {
295
generator
.
first
->
setBayesNet
(*(
generator
.
second
));
296
}
297
throw
;
298
}
299
}
300
}
301
}
302
303
304
/// returns the number of generators (alias for nbGenerators)
305
template
<
template
<
typename
>
class
ALLOC
>
306
INLINE
std
::
size_t
DBRowGeneratorSet
<
ALLOC
>::
size
()
const
noexcept
{
307
return
nb_generators__
;
308
}
309
310
311
/// returns true if there are still rows that can be output by the RowFilter
312
template
<
template
<
typename
>
class
ALLOC
>
313
INLINE
bool
DBRowGeneratorSet
<
ALLOC
>::
hasRows
() {
314
return
output_row__
!=
nullptr
;
315
}
316
317
318
// try to produce a new row
319
template
<
template
<
typename
>
class
ALLOC
>
320
bool
DBRowGeneratorSet
<
ALLOC
>::
produceNextRow__
(
321
const
DBRow
<
DBTranslatedValue
,
ALLOC
>*
input_row
,
322
std
::
size_t
i
) {
323
// the generation of output rows can be viewed as the traversal of a
324
// tree: each node of the tree correspond to the input row received by
325
// a generator. So the root node is the row passed in argument to
326
// the setInputDBrow() Method. From these input rows, generators produce
327
// through their generate() method new output rows, which correspond to
328
// the input rows of the next level of the tree. If we traverse this tree
329
// in terms of generators rather than in terms of input rows, we should
330
// call once Method setInputDBrow() in order to update the generators
331
// data structures, and then, to call method generate() to create new
332
// output rows. When some generators are unable to produce output rows,
333
// we just need to backtrack in the tree
334
335
// for the ith generator, we set the new input DBRow passed in
336
// argument of setInputDBRow. We ask it to generate a new output row.
337
// If this can be done, the new output row is passed as a new input DBRow
338
// for the next generator, and so on. If a generator cannot produce any
339
// output row, we ask its predecessors to generate new rows (backtrack),
340
// until all the generators have been able to generate at least one output
341
// row (or no output row can be produced from input_row).
342
const
DBRow
<
DBTranslatedValue
,
ALLOC
>*
row
=
input_row
;
343
while
(
i
!=
nb_generators__
) {
344
auto
generator
=
generators__
[
i
];
345
346
// if we did not pass any row yet to the ith generator, do it
347
// else use method generate() to generate a new output row
348
if
(
setInputRow_performed__
[
i
] == 0) {
349
// pass the current row
350
const
bool
has_rows
=
generator
->
setInputRow
(*
row
);
351
352
// if the generator could not create output rows, try to backtrack
353
if
(!
has_rows
) {
354
if
(
i
>
std
::
size_t
(0)) {
355
--
i
;
356
continue
;
357
}
else
{
358
// here we were unable to generate output rows
359
output_row__
=
nullptr
;
360
return
false
;
361
}
362
}
else
{
363
// here, the generator is able to generate output rows
364
// so, generate the first one
365
row
= &(
generator
->
generate
());
366
setInputRow_performed__
[
i
] = 1;
367
368
// pass to the next generator
369
++
i
;
370
}
371
}
else
{
372
// here, the generator has already performed its setInputDBRow call
373
// so we should ask it to generate a new row. If it cannot produce
374
// any more row, try to backtrack
375
if
(
generator
->
hasRows
()) {
376
// get the new row
377
row
= &(
generator
->
generate
());
378
379
// pass to the next generator
380
++
i
;
381
}
else
{
382
// indicate that the next time we use this generator, we will have
383
// to use method setInputDBRow and backtrack
384
setInputRow_performed__
[
i
] = 0;
385
if
(
i
>
std
::
size_t
(0)) {
386
--
i
;
387
continue
;
388
}
else
{
389
// here we were unable to generate output rows
390
output_row__
=
nullptr
;
391
return
false
;
392
}
393
}
394
}
395
}
396
397
// here row contains a row generated on a leaf of the row generation tree
398
// we should keep it when the user will ask for the next row to generate
399
output_row__
=
row
;
400
return
true
;
401
}
402
403
404
/// sets the input row from which the generator will create new rows
405
template
<
template
<
typename
>
class
ALLOC
>
406
INLINE
bool
DBRowGeneratorSet
<
ALLOC
>::
setInputRow
(
407
const
DBRow
<
DBTranslatedValue
,
ALLOC
>&
input_row
) {
408
// reset all the generators: ask them to use method setInputDBRow
409
if
(
hasRows
())
410
for
(
auto
&
performed
:
setInputRow_performed__
)
411
performed
= 0;
412
413
// now, parse the row generation tree
414
return
produceNextRow__
(&
input_row
,
std
::
size_t
(0));
415
}
416
417
418
/// generate new rows from the input row
419
template
<
template
<
typename
>
class
ALLOC
>
420
INLINE
const
DBRow
<
DBTranslatedValue
,
ALLOC
>&
421
DBRowGeneratorSet
<
ALLOC
>::
generate
() {
422
// get the row that we should return
423
const
auto
row
=
output_row__
;
424
425
// we should ask the last generator to produce the next output row
426
produceNextRow__
(
output_row__
,
nb_generators__
- 1);
427
428
return
*
row
;
429
}
430
431
432
/// resets the filter
433
template
<
template
<
typename
>
class
ALLOC
>
434
INLINE
void
DBRowGeneratorSet
<
ALLOC
>::
reset
() {
435
for
(
auto
gen
:
generators__
)
436
gen
->
reset
();
437
for
(
auto
&
performed
:
setInputRow_performed__
)
438
performed
= 0;
439
output_row__
=
nullptr
;
440
}
441
442
443
/// sets the columns of interest: the output DBRow needs only
444
/// contain values fot these columns
445
template
<
template
<
typename
>
class
ALLOC
>
446
INLINE
void
DBRowGeneratorSet
<
ALLOC
>::
setColumnsOfInterest
(
447
const
std
::
vector
<
std
::
size_t
,
ALLOC
<
std
::
size_t
> >&
cols_of_interest
) {
448
// check that no output row generation is still active
449
if
(
output_row__
!=
nullptr
)
450
GUM_ERROR
(
OperationNotAllowed
,
451
"you cannot change the columns of interest while a "
452
"generation is still being processed"
);
453
for
(
auto
gen
:
generators__
)
454
gen
->
setColumnsOfInterest
(
cols_of_interest
);
455
}
456
457
458
/// sets the columns of interest: the output DBRow needs only
459
/// contain values fot these columns
460
template
<
template
<
typename
>
class
ALLOC
>
461
INLINE
void
DBRowGeneratorSet
<
ALLOC
>::
setColumnsOfInterest
(
462
std
::
vector
<
std
::
size_t
,
ALLOC
<
std
::
size_t
> >&&
cols_of_interest
) {
463
if
(
output_row__
!=
nullptr
)
464
GUM_ERROR
(
OperationNotAllowed
,
465
"you cannot change the columns of interest while a "
466
"generation is still being processed"
);
467
for
(
auto
gen
:
generators__
)
468
gen
->
setColumnsOfInterest
(
cols_of_interest
);
469
}
470
471
472
/// returns the current set of columns of interest
473
template
<
template
<
typename
>
class
ALLOC
>
474
INLINE
const
std
::
vector
<
std
::
size_t
,
ALLOC
<
std
::
size_t
> >&
475
DBRowGeneratorSet
<
ALLOC
>::
columnsOfInterest
()
const
{
476
if
(
nb_generators__
==
std
::
size_t
(0)) {
477
GUM_ERROR
(
UndefinedElement
,
478
"there are no generators yet in the generator set, so "
479
"there are no columns of interest"
);
480
}
481
return
generators__
[0]->
columnsOfInterest
();
482
}
483
484
485
/// returns the allocator used
486
template
<
template
<
typename
>
class
ALLOC
>
487
INLINE
typename
DBRowGeneratorSet
<
ALLOC
>::
allocator_type
488
DBRowGeneratorSet
<
ALLOC
>::
getAllocator
()
const
{
489
return
allocator_type
(
generators__
.
get_allocator
());
490
}
491
492
493
}
/* namespace learning */
494
495
}
/* namespace gum */
496
497
#
endif
/* DOXYGEN_SHOULD_SKIP_THIS */
gum::Set::emplace
INLINE void emplace(Args &&... args)
Definition:
set_tpl.h:669
gum::learning::genericBNLearner::Database::Database
Database(const std::string &filename, const BayesNet< GUM_SCALAR > &bn, const std::vector< std::string > &missing_symbols)
Definition:
genericBNLearner_tpl.h:31