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