aGrUM 3.1.1
a C++ library for (probabilistic) graphical models
structuredInference_tpl.h
Go to the documentation of this file.
1/****************************************************************************
2 * This file is part of the aGrUM/pyAgrum library. *
3 * *
4 * Copyright (c) 2005-2026 by *
5 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
6 * - Christophe GONZALES(_at_AMU) *
7 * *
8 * The aGrUM/pyAgrum library is free software; you can redistribute it *
9 * and/or modify it under the terms of either : *
10 * *
11 * - the GNU Lesser General Public License as published by *
12 * the Free Software Foundation, either version 3 of the License, *
13 * or (at your option) any later version, *
14 * - the MIT license (MIT), *
15 * - or both in dual license, as here. *
16 * *
17 * (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html) *
18 * *
19 * This aGrUM/pyAgrum library is distributed in the hope that it will be *
20 * useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
21 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS *
22 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
25 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
26 * OTHER DEALINGS IN THE SOFTWARE. *
27 * *
28 * See LICENCES for more details. *
29 * *
30 * SPDX-FileCopyrightText: Copyright 2005-2026 *
31 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
32 * - Christophe GONZALES(_at_AMU) *
33 * SPDX-License-Identifier: LGPL-3.0-or-later OR MIT *
34 * *
35 * Contact : info_at_agrum_dot_org *
36 * homepage : http://agrum.gitlab.io *
37 * gitlab : https://gitlab.com/agrumery/agrum *
38 * *
39 ****************************************************************************/
40
41#pragma once
42
43
50
52
53namespace gum {
54 namespace prm {
55
56 template < GUM_Numeric GUM_SCALAR >
59 const PRMSystem< GUM_SCALAR >& system,
61 PRMInference< GUM_SCALAR >(prm, system), _gspan_(0), _pdata_(0), _mining_(false),
62 _dot_(".") {
63 GUM_CONSTRUCTOR(StructuredInference);
64 _gspan_ = new GSpan< GUM_SCALAR >(prm, system, strategy);
65 triang_time = 0.0;
66 mining_time = 0.0;
67 pattern_time = 0.0;
68 inner_time = 0.0;
69 obs_time = 0.0;
70 full_time = 0.0;
71 }
72
73 template < GUM_Numeric GUM_SCALAR >
76 PRMInference< GUM_SCALAR >(source), _gspan_(0), _pdata_(0), _mining_(source._mining_),
77 _found_query_(false), _dot_(".") {
78 GUM_CONS_CPY(StructuredInference);
79 _gspan_ = new GSpan< GUM_SCALAR >(*(this->prm_), *(this->sys_));
80 }
81
82 template < GUM_Numeric GUM_SCALAR >
84 GUM_DESTRUCTOR(StructuredInference);
85 delete this->_gspan_;
86
87 for (const auto& elt: _elim_map_)
88 delete elt.second;
89
90 for (const auto& elt: _cdata_map_)
91 delete elt.second;
92
93 for (const auto elt: _trash_)
94 delete (elt);
95
96 for (const auto& elt: _outputs_)
97 delete elt.second;
98
99 if (_pdata_) delete _pdata_;
100 }
101
102 template < GUM_Numeric GUM_SCALAR >
104 const StructuredInference< GUM_SCALAR >& source) {
105 this->prm_ = source.prm_;
106 this->sys_ = source.sys_;
107
108 if (this->_gspan_) delete this->_gspan_;
109
110 this->_gspan_ = new GSpan< GUM_SCALAR >(*(this->prm_), *(this->sys_));
111 return *this;
112 }
113
114 template < GUM_Numeric GUM_SCALAR >
117
118 template < GUM_Numeric GUM_SCALAR >
121
122 template < GUM_Numeric GUM_SCALAR >
124 const typename PRMInference< GUM_SCALAR >::Chain& chain,
125 Tensor< GUM_SCALAR >& m) {
126 timer.reset();
127 _found_query_ = false;
128 _query_ = chain;
130
131 if (!this->hasEvidence() && (chain.second->cpf().nbrDim() == 1)) {
132 Instantiation i(m);
133
134 for (i.setFirst(); !i.end(); i.inc())
135 m.set(i, chain.second->cpf().get(i));
136
137 return;
138 } else if (this->hasEvidence(chain)) {
139 Instantiation i(m);
140 const Tensor< GUM_SCALAR >* e = this->evidence(_query_.first)[_query_.second->id()];
141
142 for (i.setFirst(); !i.end(); i.inc())
143 m.set(i, e->get(i));
144
145 return;
146 }
147
148 _buildReduceGraph_(data);
150
151 if (data.pool.size() > 1) {
152 for (const auto pot: data.pool)
153 if (pot->contains(_query_.second->type().variable())) pots.insert(pot);
154
155 if (pots.size() == 1) {
156 Tensor< GUM_SCALAR >* pot = const_cast< Tensor< GUM_SCALAR >* >(*(pots.begin()));
157 GUM_ASSERT(pot->contains(_query_.second->type().variable()));
158 GUM_ASSERT(pot->variablesSequence().size() == 1);
159 Instantiation i(*pot), j(m);
160
161 for (i.setFirst(), j.setFirst(); !i.end(); i.inc(), j.inc())
162 m.set(j, pot->get(i));
163 } else {
165 Tensor< GUM_SCALAR >* tmp = Comb.execute(pots);
166 Instantiation i(m), j(*tmp);
167
168 for (i.setFirst(), j.setFirst(); !i.end(); i.inc(), j.inc())
169 m.set(i, tmp->get(j));
170
171 delete tmp;
172 }
173 } else {
174 Tensor< GUM_SCALAR >* pot = *(data.pool.begin());
175 GUM_ASSERT(pot->contains(_query_.second->type().variable()));
176 GUM_ASSERT(pot->variablesSequence().size() == 1);
177 Instantiation i(*pot), j(m);
178
179 for (i.setFirst(), j.setFirst(); !i.end(); i.inc(), j.inc())
180 m.set(j, pot->get(i));
181 }
182
183 m.normalize();
184
185 if (_pdata_) {
186 delete _pdata_;
187 _pdata_ = 0;
188 }
189
190 full_time = timer.step();
191 }
192
193 template < GUM_Numeric GUM_SCALAR >
195 const std::vector< typename PRMInference< GUM_SCALAR >::Chain >& queries,
196 Tensor< GUM_SCALAR >& j) {
197 GUM_ERROR(FatalError, "not implemented")
198 }
199
200 template < GUM_Numeric GUM_SCALAR >
202 std::stringstream s;
203 s << std::format("Triangulation time: {}\n", triang_time);
204 s << std::format("Pattern mining time: {}\n", mining_time);
205 s << std::format("Pattern elimination time: {}\n", pattern_time);
206 s << std::format("Inner node elimination time: {}\n", inner_time);
207 s << std::format("Observed node elimination time: {}\n", obs_time);
208 s << std::format("Full inference time: {}\n", full_time);
209 s << std::format("#patterns: {}\n", _gspan_->patterns().size());
210 Size count = 0;
211 using Iter = std::vector< gspan::Pattern* >::const_iterator;
212
213 for (Iter p = _gspan_->patterns().begin(); p != _gspan_->patterns().end(); ++p) {
214 if (_gspan_->matches(**p).size()) {
215 s << std::format("Pattern n°{} match count: {}\n", count++, _gspan_->matches(**p).size());
216 s << std::format("Pattern n°{} instance count: {}\n", count++, (**p).size());
217 }
218 }
219
220 return s.str();
221 }
222
223 template < GUM_Numeric GUM_SCALAR >
226 // Launch the pattern mining
227 plopTimer.reset();
228
229 if (_mining_) _gspan_->discoverPatterns();
230
231 mining_time = plopTimer.step();
232 // Reducing each used pattern
233 plopTimer.reset();
234 using Iter = std::vector< gspan::Pattern* >::const_iterator;
235
236 for (Iter p = _gspan_->patterns().begin(); p != _gspan_->patterns().end(); ++p)
237 if (_gspan_->matches(**p).size()) _reducePattern_(*p);
238
239 pattern_time = plopTimer.step();
240 // reducing instance not already reduced in a pattern
242 // Adding edges using the pools
244 // Placing the query where it belongs
245 NodeId id = data.var2node.second(&(_query_.second->type().variable()));
246 data.outputs().erase(id);
247 data.queries().insert(id);
248 // Triangulating, then eliminating
249 PartialOrderedTriangulation t(&(data.reducedGraph), &(data.mods), &(data.partial_order));
250 const std::vector< NodeId >& elim_order = t.eliminationOrder();
251
252 for (size_t i = 0; i < data.outputs().size(); ++i)
253 eliminateNode(data.var2node.first(elim_order[i]), data.pool, _trash_);
254 }
255
256 template < GUM_Numeric GUM_SCALAR >
259 typename StructuredInference< GUM_SCALAR >::PData data(*p, _gspan_->matches(*p));
260 _buildPatternGraph_(data, pool, **(data.matches.begin()));
261 _removeBarrenNodes_(data, pool);
262 PartialOrderedTriangulation t(&(data.graph), &(data.mod), data.partial_order());
263 const std::vector< NodeId >& elim_order = t.eliminationOrder();
264
265 for (size_t i = 0; i < data.inners().size(); ++i)
266 if (!data.barren.exists(elim_order[i]))
267 eliminateNode(data.vars.second(elim_order[i]), pool, _trash_);
268
269 typename GSpan< GUM_SCALAR >::MatchedInstances fake_patterns;
271
272 for (const auto elt: **iter)
273 _reducedInstances_.insert(elt);
274
275 if (data.obs().size())
276 _elim_map_.insert(*iter, _eliminateObservedNodesInSource_(data, pool, **iter, elim_order));
277 else _elim_map_.insert(*iter, new Set< Tensor< GUM_SCALAR >* >(pool));
278
279 ++iter;
280
281 if (data.obs().size()) {
282 for (; iter != data.matches.end(); ++iter) {
283 try {
284 _elim_map_.insert(*iter, _eliminateObservedNodes_(data, pool, **iter, elim_order));
285 } catch (OperationNotAllowed const&) { fake_patterns.insert(*iter); }
286 }
287 } else {
288 for (; iter != data.matches.end(); ++iter) {
289 try {
290 _elim_map_.insert(*iter, _translatePotSet_(data, pool, **iter));
291 } catch (OperationNotAllowed const&) { fake_patterns.insert(*iter); }
292 }
293 }
294
295 for (const auto pat: fake_patterns) {
296 for (const auto elt: *pat)
297 _reducedInstances_.erase(elt);
298
299 data.matches.erase(pat);
300 }
301
302 obs_time += plopTimer.step();
303
304 if (data.queries().size())
305 for (const auto m: data.matches)
306 if (!(m->exists(const_cast< PRMInstance< GUM_SCALAR >* >(_query_.first))))
308 &(m->atPos(_query_data_.first)->get(_query_data_.second).type().variable()),
309 *(_elim_map_[m]),
310 _trash_);
311 }
312
313 template < GUM_Numeric GUM_SCALAR >
316 const Sequence< PRMInstance< GUM_SCALAR >* >& match,
319 NodeId id,
320 std::pair< Idx, std::string >& v) {
321 if ((*inst).hasRefAttr((*inst).get(v.second).id())) {
322 std::vector< std::pair< PRMInstance< GUM_SCALAR >*, std::string > >& refs
323 = inst->getRefAttr(inst->get(v.second).id());
324
325 for (auto r = refs.begin(); r != refs.end(); ++r) {
326 if (!match.exists(r->first)) {
327 data.outputs().insert(id);
328 break;
329 }
330 }
331 }
332
333 if (!(data.outputs().size() && (data.outputs().exists(id)))) {
334 for (const auto m: data.matches) {
335 if (this->hasEvidence(std::make_pair((*m)[v.first], &((*m)[v.first]->get(v.second))))) {
336 GUM_ASSERT(inst->type().name() == (*m)[v.first]->type().name());
337 GUM_ASSERT(inst->get(v.second).safeName() == (*m)[v.first]->get(v.second).safeName());
338 data.obs().insert(id);
339 break;
340 }
341 }
342
343 if (!(data.obs().size() && (data.obs().exists(id)))) data.inners().insert(id);
344 }
345 }
346
347 template < GUM_Numeric GUM_SCALAR >
350 Set< Tensor< GUM_SCALAR >* >& pool,
351 const Sequence< PRMInstance< GUM_SCALAR >* >& match) {
352 std::pair< Idx, std::string > v;
353 Tensor< GUM_SCALAR >* pot = 0;
354
355 for (const auto inst: match) {
356 for (const auto& elt: *inst) {
357 NodeId id = data.graph.addNode();
358 v = std::make_pair(match.pos(inst), elt.second->safeName());
359 data.map.insert(id, v);
360 data.node2attr.insert(id, _str_(inst, elt.second));
361 data.mod.insert(id, elt.second->type()->domainSize());
362 data.vars.insert(id, &(elt.second->type().variable()));
363 pool.insert(const_cast< Tensor< GUM_SCALAR >* >(&(elt.second->cpf())));
364 pot = &(const_cast< Tensor< GUM_SCALAR >& >(inst->get(v.second).cpf()));
365
366 for (const auto var: pot->variablesSequence()) {
367 if (data.vars.existsSecond(var)) {
368 try {
369 if (id != data.vars.first(var)) data.graph.addEdge(id, data.vars.first(var));
370 } catch (DuplicateElement const&) {}
371 }
372 }
373
374 _insertNodeInElimLists_(data, match, inst, elt.second, id, v);
375
376 if (data.inners().exists(id)
377 && (inst->type().containerDag().children(elt.second->id()).size() == 0)
378 && _allInstanceNoRefAttr_(data, v))
379 data.barren.insert(id);
380 }
381 }
382
383 if (!_found_query_) {
384 for (const auto mat: data.matches) {
385 if (mat->exists(const_cast< PRMInstance< GUM_SCALAR >* >(_query_.first))) {
386 Idx pos = mat->pos(const_cast< PRMInstance< GUM_SCALAR >* >(_query_.first));
388 = match.atPos(pos)->get(_query_.second->safeName()).type().variable();
389 NodeId id = data.vars.first(&var);
390 data.barren.erase(id);
391 data.inners().erase(id);
392 data.obs().erase(id);
393 data.outputs().erase(id);
394 data.queries().insert(id);
395 _found_query_ = true;
396 _query_data_ = std::make_pair(pos, _query_.second->safeName());
397 break;
398 }
399 }
400 }
401 }
402
403 template < GUM_Numeric GUM_SCALAR >
406 std::pair< Idx, std::string > attr) {
407 for (const auto mat: data.matches)
408 if (mat->atPos(attr.first)->hasRefAttr(mat->atPos(attr.first)->get(attr.second).id()))
409 return false;
410
411 return true;
412 }
413
414 template < GUM_Numeric GUM_SCALAR >
417 Set< Tensor< GUM_SCALAR >* >& pool) {
418 Sequence< NodeId > candidates;
419
420 for (const auto node: data.barren) {
421 for (const auto pot: pool)
422 if (pot->contains(*data.vars.second(node))) {
423 pool.erase(pot);
424 break;
425 }
426
427 for (const auto nei: data.graph.neighbours(node))
428 if (data.inners().exists(nei)) {
429 try {
430 candidates.insert(nei);
431 } catch (DuplicateElement const&) {}
432 }
433 }
434
435 NodeId node;
436 Tensor< GUM_SCALAR >* my_pot = nullptr;
437 short count = 0;
438
439 while (candidates.size()) {
440 node = candidates.back();
441 candidates.erase(node);
442 count = 0;
443
444 for (const auto pot: pool) {
445 if (pot->contains(*data.vars.second(node))) {
446 ++count;
447 my_pot = pot;
448 }
449 }
450
451 if (count == 1) {
452 pool.erase(my_pot);
453 data.barren.insert(node);
454
455 for (const auto nei: data.graph.neighbours(node)) {
456 if (data.inners().exists(nei)) {
457 try {
458 candidates.insert(nei);
459 } catch (DuplicateElement const&) {}
460 }
461 }
462 }
463 }
464 }
465
466 template < GUM_Numeric GUM_SCALAR >
470 const Set< Tensor< GUM_SCALAR >* >& pool,
471 const Sequence< PRMInstance< GUM_SCALAR >* >& match,
472 const std::vector< NodeId >& elim_order) {
473 Set< Tensor< GUM_SCALAR >* >* my_pool = new Set< Tensor< GUM_SCALAR >* >(pool);
474 std::pair< Idx, std::string > target;
475 size_t end = data.inners().size() + data.obs().size();
476
477 for (size_t idx = data.inners().size(); idx < end; ++idx) {
478 target = data.map[data.vars.first(data.vars.second(elim_order[idx]))];
479 eliminateNode(&(match[target.first]->get(target.second).type().variable()),
480 *my_pool,
481 _trash_);
482 }
483
484 return my_pool;
485 }
486
487 template < GUM_Numeric GUM_SCALAR >
490 const Set< Tensor< GUM_SCALAR >* >& pool,
491 const Sequence< PRMInstance< GUM_SCALAR >* >& match,
492 const std::vector< NodeId >& elim_order) {
493 Set< Tensor< GUM_SCALAR >* >* my_pool = _translatePotSet_(data, pool, match);
494 std::pair< Idx, std::string > target;
495 size_t end = data.inners().size() + data.obs().size();
496
497 for (size_t idx = data.inners().size(); idx < end; ++idx) {
498 target = data.map[data.vars.first(data.vars.second(elim_order[idx]))];
499 eliminateNode(&(match[target.first]->get(target.second).type().variable()),
500 *my_pool,
501 _trash_);
502 }
503
504 return my_pool;
505 }
506
507 template < GUM_Numeric GUM_SCALAR >
510 const Set< Tensor< GUM_SCALAR >* >& pool,
511 const Sequence< PRMInstance< GUM_SCALAR >* >& match) {
512#ifdef DEBUG
513
514 for (const auto iter = data.matches.begin(); iter != data.matches.end(); ++iter) {
515 GUM_ASSERT((**iter).size() == match.size());
516
517 for (Size idx = 0; idx < match.size(); ++idx) {
518 GUM_ASSERT((**iter).atPos(idx)->type() == match.atPos(idx)->type());
519 }
520 }
521
522#endif
524 std::pair< Idx, std::string > target;
526 const Sequence< PRMInstance< GUM_SCALAR >* >& source = **(data.matches.begin());
527
528 for (Size idx = 0; idx < match.size(); ++idx) {
529 _reducedInstances_.insert(match[idx]);
530 const auto& chains = source[idx]->type().slotChains();
531
532 for (const auto sc: chains) {
533#ifdef DEBUG
534 GUM_ASSERT(!(sc->isMultiple()));
535#endif
536
537 try {
538 bij.insert(&(source[idx]
539 ->getInstance(sc->id())
540 .get(sc->lastElt().safeName())
541 .type()
542 .variable()),
543 &(match[idx]
544 ->getInstance(sc->id())
545 .get(sc->lastElt().safeName())
546 .type()
547 .variable()));
548 } catch (DuplicateElement const&) {
549 try {
550 if (bij.first(&(match[idx]
551 ->getInstance(sc->id())
552 .get(sc->lastElt().safeName())
553 .type()
554 .variable()))
555 != &(source[idx]
556 ->getInstance(sc->id())
557 .get(sc->lastElt().safeName())
558 .type()
559 .variable())) {
560 delete my_pool;
561 GUM_ERROR(OperationNotAllowed, "fake pattern")
562 }
563 } catch (NotFound const&) { // bijection lookup failed
564 delete my_pool;
565 GUM_ERROR(OperationNotAllowed, "fake pattern")
566 }
567 }
568 }
569 }
570
571 for (const auto p: pool) {
572 for (const auto v: p->variablesSequence()) {
573 if (data.vars.existsSecond(v)) {
574 auto varId = data.vars.first(v);
575 if (auto p_map = data.map.tryGet(varId)) {
576 target = *p_map;
577 try {
578 bij.insert(v, &(match[target.first]->get(target.second).type().variable()));
579 } catch (DuplicateElement const&) {}
580 }
581 }
582 }
583
584 try {
585 my_pool->insert(copyTensor(bij, *p));
586 } catch (Exception const&) {
587 for (const auto pot: *my_pool)
588 delete pot;
589
590 delete my_pool;
591 GUM_ERROR(OperationNotAllowed, "fake pattern")
592 }
593 }
594
595 return my_pool;
596 }
597
598 template < GUM_Numeric GUM_SCALAR >
602 Tensor< GUM_SCALAR >* pot = nullptr;
603 PRMInstance< GUM_SCALAR >* inst = nullptr;
604
605 for (const auto& elt: *this->sys_) {
606 inst = elt.second;
607
608 if (!_reducedInstances_.exists(inst)) {
609 // Checking if its not an empty class
610 if (inst->size()) {
612
613 if (auto p_cdata = _cdata_map_.tryGet(&(inst->type()))) {
614 data = *p_cdata;
615 } else {
617 _cdata_map_.insert(&(inst->type()), data);
618 }
619
620 data->instances.insert(inst);
621 // Filling up the partial ordering
622 List< NodeSet > partial_order;
623
624 if (data->inners().size()) partial_order.push_back(data->inners());
625
626 if (data->aggregators().size())
627 for (const auto agg: data->aggregators())
628 partial_order[0].insert(agg);
629
630 if (data->outputs().size()) partial_order.push_back(data->outputs());
631
632 if (_query_.first == inst) {
633 // First case, the instance contains the query
634 partial_order[0].erase(_query_.second->id());
635
636 if (partial_order[0].empty()) partial_order.erase(0);
637
638 if (partial_order.size() > 1) {
639 partial_order[1].erase(_query_.second->id());
640
641 if (partial_order[1].empty()) partial_order.erase(1);
642 }
643
644 NodeSet query_set;
645 query_set.insert(_query_.second->id());
646 partial_order.insert(query_set);
647
648 // Adding the tensors
649 for (auto attr = inst->begin(); attr != inst->end(); ++attr)
650 pool.insert(&(const_cast< Tensor< GUM_SCALAR >& >((*(attr.val())).cpf())));
651
652 // Adding evidences if any
653 if (this->hasEvidence(inst))
654 for (const auto& elt: this->evidence(inst))
655 pool.insert(const_cast< Tensor< GUM_SCALAR >* >(elt.second));
656
657 PartialOrderedTriangulation t(&(data->moral_graph), &(data->mods), &(partial_order));
658 const std::vector< NodeId >& v = t.eliminationOrder();
659
660 if (partial_order.size() > 1)
661 for (size_t idx = 0; idx < partial_order[0].size(); ++idx)
662 eliminateNode(&(inst->get(v[idx]).type().variable()), pool, _trash_);
663 } else if (this->hasEvidence(inst)) {
664 // Second case, the instance has evidences
665 // Adding the tensors
666 for (const auto& elt: *inst)
667 pool.insert(&const_cast< Tensor< GUM_SCALAR >& >(elt.second->cpf()));
668
669 // Adding evidences
670 for (const auto& elt: this->evidence(inst))
671 pool.insert(const_cast< Tensor< GUM_SCALAR >* >(elt.second));
672
673 PartialOrderedTriangulation t(&(data->moral_graph), &(data->mods), &(partial_order));
674
675 for (size_t idx = 0; idx < partial_order[0].size(); ++idx)
676 eliminateNode(&(inst->get(t.eliminationOrder()[idx]).type().variable()),
677 pool,
678 _trash_);
679 } else {
680 // Last cast, the instance neither contains evidences nor
681 // instances
682 // We translate the class level tensors into the instance ones
683 // and
684 // proceed with elimination
685 for (const auto srcPot: data->pool) {
686 pot = copyTensor(inst->bijection(), *srcPot);
687 pool.insert(pot);
688 _trash_.insert(pot);
689 }
690
691 for (const auto agg: data->c.aggregates())
692 pool.insert(&(const_cast< Tensor< GUM_SCALAR >& >(inst->get(agg->id()).cpf())));
693
694 // We eliminate inner aggregators with their parents if necessary
695 // (see
696 // CData constructor)
697 Size size = data->inners().size() + data->aggregators().size();
698
699 for (size_t idx = data->inners().size(); idx < size; ++idx)
700 eliminateNode(&(inst->get(data->elim_order()[idx]).type().variable()),
701 pool,
702 _trash_);
703 }
704
705 for (const auto pot: pool)
706 rg_data.pool.insert(pot);
707 }
708 }
709 }
710 }
711
712 template < GUM_Numeric GUM_SCALAR >
715 // We first add edges between variables already in pool (i.e. those of the
716 // reduced instances)
717 NodeId id_1, id_2;
718
719 for (const auto pot: data.pool) {
720 const Sequence< const DiscreteVariable* >& vars = pot->variablesSequence();
721
722 for (Size var_1 = 0; var_1 < vars.size(); ++var_1) {
723 if (data.var2node.existsFirst(vars.atPos(var_1))) {
724 id_1 = data.var2node.second(vars.atPos(var_1));
725 } else {
726 id_1 = data.reducedGraph.addNode();
727 data.var2node.insert(vars.atPos(var_1), id_1);
728 data.mods.insert(id_1, vars.atPos(var_1)->domainSize());
729 data.outputs().insert(id_1);
730 }
731
732 for (Size var_2 = var_1 + 1; var_2 < vars.size(); ++var_2) {
733 if (data.var2node.existsFirst(vars.atPos(var_2))) {
734 id_2 = data.var2node.second(vars.atPos(var_2));
735 } else {
736 id_2 = data.reducedGraph.addNode();
737 data.var2node.insert(vars.atPos(var_2), id_2);
738 data.mods.insert(id_2, vars.atPos(var_2)->domainSize());
739 data.outputs().insert(id_2);
740 }
741
742 try {
743 data.reducedGraph.addEdge(id_1, id_2);
744 } catch (DuplicateElement const&) {}
745 }
746 }
747 }
748
749 // Adding tensors obtained from reduced patterns
750 for (const auto& elt: _elim_map_) {
751 // We add edges between variables in the same reduced patterns
752 for (const auto pot: *elt.second) {
753 data.pool.insert(pot);
754 const Sequence< const DiscreteVariable* >& vars = pot->variablesSequence();
755
756 for (Size var_1 = 0; var_1 < vars.size(); ++var_1) {
757 if (data.var2node.existsFirst(vars.atPos(var_1))) {
758 id_1 = data.var2node.second(vars.atPos(var_1));
759 } else {
760 id_1 = data.reducedGraph.addNode();
761 data.var2node.insert(vars.atPos(var_1), id_1);
762 data.mods.insert(id_1, vars.atPos(var_1)->domainSize());
763 data.outputs().insert(id_1);
764 }
765
766 for (Size var_2 = var_1 + 1; var_2 < vars.size(); ++var_2) {
767 if (data.var2node.existsFirst(vars.atPos(var_2))) {
768 id_2 = data.var2node.second(vars.atPos(var_2));
769 } else {
770 id_2 = data.reducedGraph.addNode();
771 data.var2node.insert(vars.atPos(var_2), id_2);
772 data.mods.insert(id_2, vars.atPos(var_2)->domainSize());
773 data.outputs().insert(id_2);
774 }
775
776 try {
777 data.reducedGraph.addEdge(id_1, id_2);
778 } catch (DuplicateElement const&) {}
779 }
780 }
781 }
782 }
783 }
784
785 template < GUM_Numeric GUM_SCALAR >
791
792 template < GUM_Numeric GUM_SCALAR >
794 const gspan::Pattern& p,
796 pattern(p), matches(m), _real_order_(0) {
798
799 for (int i = 0; i < 4; ++i)
800 _partial_order_.push_front(NodeSet());
801 }
802
803 template < GUM_Numeric GUM_SCALAR >
805 const typename StructuredInference< GUM_SCALAR >::PData& source) :
806 pattern(source.pattern), matches(source.matches), graph(source.graph), mod(source.mod),
807 node2attr(source.node2attr), vars(source.vars), _partial_order_(source._partial_order_),
808 _real_order_(0) {
810 }
811
812 template < GUM_Numeric GUM_SCALAR >
814 if (!_real_order_) {
816
817 for (const auto& set: _partial_order_)
818 if (set.size() > 0) _real_order_->insert(set);
819 }
820
821 return _real_order_;
822 }
823
824 template < GUM_Numeric GUM_SCALAR >
826 c(a_class), _elim_order_(0) {
828
829 // First step we add Attributes and Aggregators
830 for (const auto node: c.containerDag().nodes()) {
831 switch (c.get(node).elt_type()) {
833 pool.insert(&(const_cast< Tensor< GUM_SCALAR >& >(c.get(node).cpf())));
834 // break omited : We want to execute the next block
835 // for attributes
836 }
837
839 moral_graph.addNodeWithId(node);
840 mods.insert(node, c.get(node).type()->domainSize());
841 break;
842 }
843
844 default : { /* do nothing */
845 }
846 }
847 }
848
849 // Second, we add edges, moralise the graph and build the partial ordering
850 for (const auto node: moral_graph.nodes()) {
851 const auto& parents = c.containerDag().parents(node);
852
853 // Adding edges and marrying parents
854 for (auto tail = parents.begin(); tail != parents.end(); ++tail) {
857 moral_graph.addEdge(*tail, node);
858 NodeSet::const_iterator marry = tail;
859 ++marry;
860
861 while (marry != parents.end()) {
864 moral_graph.addEdge(*tail, *marry);
865
866 ++marry;
867 }
868 }
869 }
870
871 // Adding nodes to the partial ordering
872 switch (c.get(node).elt_type()) {
874 if (c.isOutputNode(c.get(node))) outputs().insert(node);
875 else aggregators().insert(node);
876
877 // If the aggregators is not an output and have parents which are
878 // not outputs, we must eliminate the parents after adding the
879 // aggregator's CPT
880 for (const auto par: c.containerDag().parents(node)) {
881 const auto& prnt = c.get(par);
882
883 if ((!c.isOutputNode(prnt))
886 inners().erase(prnt.id());
887 aggregators().insert(prnt.id());
888 }
889 }
890
891 break;
892 }
893
895 pool.insert(const_cast< Tensor< GUM_SCALAR >* >(&(c.get(node).cpf())));
896
897 if (c.isOutputNode(c.get(node))) outputs().insert(node);
898 else if (!aggregators().exists(node)) inners().insert(node);
899
900 break;
901 }
902
903 default : { /* Do nothing */
904 }
905 }
906 }
907
908 if (inners().size()) partial_order.insert(inners());
909
910 if (aggregators().size()) partial_order.insert(aggregators());
911
912 if (outputs().size()) partial_order.insert(outputs());
913
914 GUM_ASSERT(partial_order.size());
917
918 for (size_t i = 0; i < inners().size(); ++i)
919 eliminateNode(&(c.get(_elim_order_[i]).type().variable()), pool, _trash_);
920 }
921
922 template < GUM_Numeric GUM_SCALAR >
925
926 for (const auto pot: _trash_)
927 delete pot;
928 }
929
930 template < GUM_Numeric GUM_SCALAR >
932 const PRMInstance< GUM_SCALAR >* i = (this->sys_->begin()).val();
933 _query_ = std::make_pair(i, i->begin().val());
934 _found_query_ = false;
936 _buildReduceGraph_(data);
937 }
938
939 template < GUM_Numeric GUM_SCALAR >
943
944 template < GUM_Numeric GUM_SCALAR >
945 std::string
950
951 template < GUM_Numeric GUM_SCALAR >
952 std::string
957
958 template < GUM_Numeric GUM_SCALAR >
959 std::string
961 const PRMSlotChain< GUM_SCALAR >& a) const {
962 return i->name() + _dot_ + a.lastElt().safeName();
963 }
964
965 template < GUM_Numeric GUM_SCALAR >
969
970 template < GUM_Numeric GUM_SCALAR >
974
975 template < GUM_Numeric GUM_SCALAR >
977 return "StructuredInference";
978 }
979
980 template < GUM_Numeric GUM_SCALAR >
984
985 template < GUM_Numeric GUM_SCALAR >
989
990 template < GUM_Numeric GUM_SCALAR >
993 NodeId id,
994 Set< Tensor< GUM_SCALAR >* >& pool) {
995 data.graph.eraseNode(id);
996 GUM_ASSERT(!data.graph.exists(id));
997 data.mod.erase(id);
998 GUM_ASSERT(!data.mod.exists(id));
999 data.node2attr.eraseFirst(id);
1000 GUM_ASSERT(!data.node2attr.existsFirst(id));
1001 data.map.erase(id);
1002 GUM_ASSERT(!data.map.exists(id));
1003 data.vars.eraseFirst(id);
1004 GUM_ASSERT(!data.vars.existsFirst(id));
1005 data.inners().erase(id);
1006 GUM_ASSERT(!data.inners().exists(id));
1007 pool.erase(data.pots[id]);
1008 GUM_ASSERT(!pool.exists(data.pots[id]));
1009 data.pots.erase(id);
1010 GUM_ASSERT(!data.pots.exists(id));
1011 }
1012
1013 template < GUM_Numeric GUM_SCALAR >
1017
1018 template < GUM_Numeric GUM_SCALAR >
1022
1023 template < GUM_Numeric GUM_SCALAR >
1027
1028 template < GUM_Numeric GUM_SCALAR >
1032
1033 template < GUM_Numeric GUM_SCALAR >
1037
1038 template < GUM_Numeric GUM_SCALAR >
1042
1043 template < GUM_Numeric GUM_SCALAR >
1047
1048 template < GUM_Numeric GUM_SCALAR >
1052
1053 template < GUM_Numeric GUM_SCALAR >
1057
1058 template < GUM_Numeric GUM_SCALAR >
1060 return _elim_order_;
1061 }
1062
1063 } /* namespace prm */
1064} /* namespace gum */
const T1 & first(const T2 &second) const
Returns the first value of a pair given its second value.
Set of pairs of elements with fast search for both elements.
Definition bijection.h:1640
Base class for discrete random variable.
Exception : a similar element already exists.
const NodeSet & neighbours(NodeId id) const
returns the set of node neighbours to a given node
Base class for all aGrUM's exceptions.
Definition exceptions.h:122
Exception : fatal (unknown ?) error.
Class for assigning/browsing values to tuples of discrete variables.
bool end() const
Returns true if the Instantiation reached the end.
void inc()
Operator increment.
void setFirst()
Assign the first values to the tuple of the Instantiation.
Generic doubly linked lists.
Definition list.h:378
Size size() const noexcept
Returns the number of elements in the list.
Definition list_tpl.h:1710
Val & push_back(Args &&... args)
An alias for pushBack used for STL compliance.
Definition list_tpl.h:1494
Val & insert(const Val &val)
Inserts a new element at the end of the chained list (alias of pushBack).
Definition list_tpl.h:1508
void erase(Size i)
Erases the ith element of the List (the first one is in position 0).
Definition list_tpl.h:1763
A class to combine efficiently several MultiDim tables.
TABLE * execute(const Set< const TABLE * > &set) const final
Creates and returns the result of the combination of the tables within set.
bool exists(const NodeId id) const
alias for existsNode
virtual NodeId addNode()
insert a new node and return its id
Exception : the element we looked for cannot be found.
Exception : operation not allowed.
class for graph triangulations for which we enforce a given partial ordering on the nodes elimination...
const Key & atPos(Idx i) const
Returns the object at the pos i.
The generic class for storing (ordered) sequences of objects.
Definition sequence.h:994
Representation of a set.
Definition set.h:129
SetIterator< Sequence< PRMInstance< GUM_SCALAR > * > * > const_iterator
Definition set.h:141
bool exists(const Key &k) const
Indicates whether a given elements belong to the set.
Definition set_tpl.h:504
iterator begin() const
The usual unsafe begin iterator to parse the set.
Definition set_tpl.h:409
void insert(const Key &k)
Inserts a new element into the set.
Definition set_tpl.h:510
void erase(const Key &k)
Erases an element from the set.
Definition set_tpl.h:553
Size size() const noexcept
Returns the number of elements in the set.
Definition set_tpl.h:607
const std::vector< NodeId > & eliminationOrder() override
returns an elimination ordering compatible with the triangulated graph
void addEdge(NodeId first, NodeId second) override
insert a new edge into the undirected graph
void eraseNode(NodeId id) override
remove a node and its adjacent edges from the graph
This class discovers pattern in a PRM<GUM_SCALAR>'s PRMSystem<GUM_SCALAR> to speed up structured infe...
Definition gspan.h:86
Set< Sequence< PRMInstance< GUM_SCALAR > * > * > MatchedInstances
Code alias.
Definition gspan.h:185
PRMAttribute is a member of a Class in a PRM.
static INLINE bool isAggregate(const PRMClassElement< GUM_SCALAR > &elt)
Return true if obj is of type PRMAggregate.
static INLINE bool isAttribute(const PRMClassElement< GUM_SCALAR > &elt)
Returns true if obj_ptr is of type PRMAttribute.
const std::string & safeName() const
Returns the safe name of this PRMClassElement, if any.
A PRMClass is an object of a PRM representing a fragment of a Bayesian network which can be instantia...
Definition PRMClass.h:77
std::pair< const PRMInstance< GUM_SCALAR > *, const PRMAttribute< GUM_SCALAR > * > Chain
Code alias.
PRMInference(const PRM< GUM_SCALAR > &prm, const PRMSystem< GUM_SCALAR > &system)
Default constructor.
EMap & evidence(const PRMInstance< GUM_SCALAR > &i)
Returns EMap of evidences over i.
PRMSystem< GUM_SCALAR > const * sys_
The Model on which inference is done.
PRM< GUM_SCALAR > const * prm_
The PRM<GUM_SCALAR> on which inference is done.
bool hasEvidence(const PRMInstance< GUM_SCALAR > &i) const
Returns true if i has evidence.
An PRMInstance is a Bayesian network fragment defined by a Class and used in a PRMSystem.
Definition PRMInstance.h:79
const iterator & end()
Returns a reference over the iterator at the end of the list of gum::prm::PRMAttribute<GUM_SCALAR> in...
Size size() const
Returns the number of attributes in this PRMInstance<GUM_SCALAR>.
PRMAttribute< GUM_SCALAR > & get(NodeId id)
Getter on an PRMAttribute<GUM_SCALAR> of this PRMInstance<GUM_SCALAR>.
const Bijection< const DiscreteVariable *, const DiscreteVariable * > & bijection() const
Returns a mapping between DiscreteVariable used in this and the ones used in this PRMInstance<GUM_SCA...
PRMClass< GUM_SCALAR > & type()
Returns the type of this instance.
std::vector< std::pair< PRMInstance< GUM_SCALAR > *, std::string > > & getRefAttr(NodeId id)
Returns a vector of pairs of refering attributes of id.
iterator begin()
Returns an iterator at the begining of the list of gum::prm::PRMAttribute<GUM_SCALAR> in this PRMInst...
const std::string & name() const
Returns the name of this object.
A PRMSlotChain represents a sequence of gum::prm::PRMClassElement<GUM_SCALAR> where the n-1 first gum...
PRMClassElement< GUM_SCALAR > & lastElt()
Returns the last element of the slot chain, typically this is an gum::PRMAttribute or a gum::PRMAggre...
A PRMSystem is a container of PRMInstance and describe a relational skeleton.
Definition PRMSystem.h:72
This class represents a Probabilistic Relational PRMSystem<GUM_SCALAR>.
Definition PRM.h:74
void searchPatterns()
Search for patterns without doing any computations.
Set< Tensor< GUM_SCALAR > * > * _eliminateObservedNodes_(typename StructuredInference::PData &data, const Set< Tensor< GUM_SCALAR > * > &pool, const Sequence< PRMInstance< GUM_SCALAR > * > &match, const std::vector< NodeId > &elim_order)
Add in data.queries() any queried variable in one of data.pattern matches.
HashTable< const PRMClass< GUM_SCALAR > *, CData * > _cdata_map_
Mapping between a Class<GUM_SCALAR> and data about instances reduced using only Class<GUM_SCALAR> lev...
void _reducePattern_(const gspan::Pattern *p)
Proceed with the elimination of all inner variables (observed or not) of all usable matches of Patter...
void _removeNode_(typename StructuredInference::PData &data, NodeId id, Set< Tensor< GUM_SCALAR > * > &pool)
std::string name() const override
Tells this algorithm to use pattern mining or not.
void posterior_(const typename PRMInference< GUM_SCALAR >::Chain &chain, Tensor< GUM_SCALAR > &m) override
See PRMInference::posterior_().
std::string _dot_
Unreduce the match containing the query.
void evidenceAdded_(const typename PRMInference< GUM_SCALAR >::Chain &chain) override
See PRMInference::evidenceAdded_().
void _buildReduceGraph_(RGData &data)
This calls reducePattern() over each pattern and then build the reduced graph which is used for infer...
void _removeBarrenNodes_(typename StructuredInference::PData &data, Set< Tensor< GUM_SCALAR > * > &pool)
HashTable< const Sequence< PRMInstance< GUM_SCALAR > * > *, Set< Tensor< GUM_SCALAR > * > * > _elim_map_
Mapping between a Pattern's match and its tensor pool after inner variables were eliminated.
HashTable< const PRMClass< GUM_SCALAR > *, std::vector< NodeId > * > _outputs_
std::string _str_(const PRMInstance< GUM_SCALAR > *i, const PRMAttribute< GUM_SCALAR > *a) const
PRMInference< GUM_SCALAR >::Chain _query_
The query.
void evidenceRemoved_(const typename PRMInference< GUM_SCALAR >::Chain &chain) override
See PRMInference::evidenceRemoved_().
GSpan< GUM_SCALAR > & gspan()
Returns the instance of gspan used to search patterns.
void _buildPatternGraph_(PData &data, Set< Tensor< GUM_SCALAR > * > &pool, const Sequence< PRMInstance< GUM_SCALAR > * > &match)
Build the DAG corresponding to Pattern data.pattern, initialize pool with all the Tensors of all vari...
PData * _pdata_
The pattern data of the pattern which one of its matches contains the query.
Set< Tensor< GUM_SCALAR > * > * _translatePotSet_(typename StructuredInference::PData &data, const Set< Tensor< GUM_SCALAR > * > &pool, const Sequence< PRMInstance< GUM_SCALAR > * > &match)
Translate a given Tensor Set into one w.r.t. variables in match.
void _insertNodeInElimLists_(typename StructuredInference::PData &data, const Sequence< PRMInstance< GUM_SCALAR > * > &match, PRMInstance< GUM_SCALAR > *inst, PRMAttribute< GUM_SCALAR > *attr, NodeId id, std::pair< Idx, std::string > &v)
StructuredInference & operator=(const StructuredInference &source)
Copy operator.
void _addEdgesInReducedGraph_(RGData &data)
Add the nodes in the reduced graph.
~StructuredInference() override
Destructor.
void setPatternMining(bool b)
Tells this algorithm to use pattern mining or not.
bool _mining_
Flag which tells to use pattern mining or not.
GSpan< GUM_SCALAR > * _gspan_
Pointer over th GSpan<GUM_SCALAR> instance used by this class.
bool _found_query_
Flag with an explicit name.
bool _allInstanceNoRefAttr_(typename StructuredInference::PData &data, std::pair< Idx, std::string > attr)
Set< const PRMInstance< GUM_SCALAR > * > _reducedInstances_
This keeps track of reduced instances.
StructuredInference(const PRM< GUM_SCALAR > &prm, const PRMSystem< GUM_SCALAR > &system, gspan::SearchStrategy< GUM_SCALAR > *strategy=0)
Default constructor.
Set< Tensor< GUM_SCALAR > * > * _eliminateObservedNodesInSource_(typename StructuredInference::PData &data, const Set< Tensor< GUM_SCALAR > * > &pool, const Sequence< PRMInstance< GUM_SCALAR > * > &match, const std::vector< NodeId > &elim_order)
Set< Tensor< GUM_SCALAR > * > _trash_
Keeping track of create tensors to delete them after inference.
void _reduceAloneInstances_(RGData &data)
Add the reduced tensors of instances not in any used patterns.
void joint_(const std::vector< typename PRMInference< GUM_SCALAR >::Chain > &queries, Tensor< GUM_SCALAR > &j) override
See PRMInference::joint_().
std::pair< Idx, std::string > _query_data_
This contains all the information we want for a node in a DFSTree.
Definition pattern.h:90
This is an abstract class used to tune search strategies in the gspan algorithm.
#define GUM_ERROR(type, msg)
Definition exceptions.h:76
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition types.h:74
Size Idx
Type for indexes.
Definition types.h:79
Size NodeId
Type for node ids.
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
namespace for all probabilistic relational models entities
Definition agrum.h:68
Tensor< GUM_SCALAR > multTensor(const Tensor< GUM_SCALAR > &t1, const Tensor< GUM_SCALAR > &t2)
void eliminateNode(const DiscreteVariable *var, Set< Tensor< GUM_SCALAR > * > &pool, Set< Tensor< GUM_SCALAR > * > &trash)
Proceeds with the elimination of var in pool.
Tensor< GUM_SCALAR > * copyTensor(const Bijection< const DiscreteVariable *, const DiscreteVariable * > &bij, const Tensor< GUM_SCALAR > &source)
Returns a copy of a Tensor after applying a bijection over the variables in source.
gum is the global namespace for all aGrUM entities
Definition agrum.h:46
Private structure to represent data about a Class<GUM_SCALAR>.
NodeSet & aggregators()
Returns the set of aggregators and their parents.
Set< Tensor< GUM_SCALAR > * > _trash_
List< NodeSet > partial_order
The partial order used of variable elimination.
Set< const PRMInstance< GUM_SCALAR > * > instances
The Set of Instances reduces at class level.
NodeSet & outputs()
Returns the set of outputs nodes.
UndiGraph moral_graph
The class moral graph. NodeId matches those in c.
CData(const PRMClass< GUM_SCALAR > &c)
Default constructor.
std::vector< NodeId > & elim_order()
The elimination order for nodes of this class.
Set< Tensor< GUM_SCALAR > * > pool
The tensor pool obtained by C elimination of inner nodes.
NodeSet & inners()
Returns the set of inner nodes.
const PRMClass< GUM_SCALAR > & c
The class about what this data is about.
NodeProperty< Size > mods
The class variables modalities.
Private structure to represent data about a pattern.
List< NodeSet > _partial_order_
We'll use a PartialOrderedTriangulation with three sets: output, nodes and obs with children outside ...
NodeSet & obs()
Returns the set of inner and observed nodes given all the matches of pattern.
NodeSet & queries()
Returns the set of queried nodes given all the matches of pattern.
Set< NodeId > barren
Set of barren nodes.
Bijection< NodeId, std::string > node2attr
A bijection to easily keep track between graph and attributes, its of the form instance_name DOT attr...
NodeProperty< std::pair< Idx, std::string > > map
To ease translating tensors from one match to another.
UndiGraph graph
A yet to be triangulated undigraph.
NodeSet & outputs()
Returns the set of outputs nodes given all the matches of pattern.
const gspan::Pattern & pattern
The pattern for which this represents data about it.
NodeSet & inners()
Returns the set of inner nodes.
NodeProperty< Tensor< GUM_SCALAR > * > pots
To handle barren nodes.
Bijection< NodeId, const DiscreteVariable * > vars
Bijection between graph's nodes and their corresponding DiscreteVariable, for inference purpose.
List< NodeSet > * _real_order_
A copy of partial_order without empty sets.
PData(const gspan::Pattern &p, typename GSpan< GUM_SCALAR >::MatchedInstances &m)
Default constructor.
NodeProperty< Size > mod
The pattern's variables modalities.
GSpan< GUM_SCALAR >::MatchedInstances & matches
A reference over the usable matches of pattern.
Private structure to represent data about a reduced graph.
List< NodeSet > partial_order
Partial order used for triangulation, first is outputs nodes, second query nodes.
Set< Tensor< GUM_SCALAR > * > pool
The pool of tensors matching the reduced graph.
Bijection< const DiscreteVariable *, NodeId > var2node
Mapping between DiscreteVariable and NodeId.
NodeSet & queries()
Returns the set of query nodes (which will not be eliminated).
UndiGraph reducedGraph
The reduced graph.
NodeSet & outputs()
Returns the set of outputs nodes (which will be eliminated).
NodeProperty< Size > mods
Mapping between NodeId and modalities.
Headers of StructuredInference.