53#ifndef DOXYGEN_SHOULD_SKIP_THIS
69 template < GUM_Numeric GUM_SCALAR >
76 = &VariableElimination< GUM_SCALAR >::_findRelevantTensorsWithdSeparation2_;
77 setRelevantTensorsFinderType(relevant_type);
78 setFindBarrenNodesType(barren_type);
81 _triangulation_ =
new DefaultTriangulation;
84 GUM_CONSTRUCTOR(VariableElimination);
88 template < GUM_Numeric GUM_SCALAR >
89 VariableElimination< GUM_SCALAR >::~VariableElimination() {
91 if (_JT_ !=
nullptr)
delete _JT_;
92 delete _triangulation_;
93 if (_target_posterior_ !=
nullptr)
delete _target_posterior_;
96 GUM_DESTRUCTOR(VariableElimination);
100 template < GUM_Numeric GUM_SCALAR >
101 void VariableElimination< GUM_SCALAR >::setTriangulation(
const Triangulation& new_triangulation) {
102 delete _triangulation_;
103 _triangulation_ = new_triangulation.newFactory();
107 template < GUM_Numeric GUM_SCALAR >
108 const JunctionTree* VariableElimination< GUM_SCALAR >::junctionTree(NodeId
id) {
109 _createNewJT_(NodeSet{
id});
115 template < GUM_Numeric GUM_SCALAR >
116 void VariableElimination< GUM_SCALAR >::setRelevantTensorsFinderType(
117 RelevantTensorsFinderType type) {
118 if (type != _find_relevant_tensor_type_) {
120 case RelevantTensorsFinderType::DSEP_BAYESBALL_TENSORS :
121 _findRelevantTensors_
122 = &VariableElimination< GUM_SCALAR >::_findRelevantTensorsWithdSeparation2_;
125 case RelevantTensorsFinderType::DSEP_BAYESBALL_NODES :
126 _findRelevantTensors_
127 = &VariableElimination< GUM_SCALAR >::_findRelevantTensorsWithdSeparation_;
130 case RelevantTensorsFinderType::DSEP_KOLLER_FRIEDMAN_2009 :
131 _findRelevantTensors_
132 = &VariableElimination< GUM_SCALAR >::_findRelevantTensorsWithdSeparation3_;
135 case RelevantTensorsFinderType::FIND_ALL :
136 _findRelevantTensors_ = &VariableElimination< GUM_SCALAR >::_findRelevantTensorsGetAll_;
141 "setRelevantTensorsFinderType for type " << (
unsigned int)type
142 <<
" is not implemented yet");
145 _find_relevant_tensor_type_ = type;
150 template < GUM_Numeric GUM_SCALAR >
151 void VariableElimination< GUM_SCALAR >::_setProjectionFunction_(
152 Tensor< GUM_SCALAR > (*proj)(
const Tensor< GUM_SCALAR >&,
const gum::VariableSet&)) {
153 _projection_op_ = proj;
157 template < GUM_Numeric GUM_SCALAR >
158 void VariableElimination< GUM_SCALAR >::_setCombinationFunction_(
159 Tensor< GUM_SCALAR > (*comb)(
const Tensor< GUM_SCALAR >&,
const Tensor< GUM_SCALAR >&)) {
160 _combination_op_ = comb;
164 template < GUM_Numeric GUM_SCALAR >
165 void VariableElimination< GUM_SCALAR >::setFindBarrenNodesType(FindBarrenNodesType type) {
166 if (type != _barren_nodes_type_) {
170 case FindBarrenNodesType::FIND_BARREN_NODES :
171 case FindBarrenNodesType::FIND_NO_BARREN_NODES :
break;
175 "setFindBarrenNodesType for type " << (
unsigned int)type
176 <<
" is not implemented yet");
179 _barren_nodes_type_ = type;
184 template < GUM_Numeric GUM_SCALAR >
185 void VariableElimination< GUM_SCALAR >::onEvidenceAdded_(
const NodeId,
bool) {}
188 template < GUM_Numeric GUM_SCALAR >
189 void VariableElimination< GUM_SCALAR >::onEvidenceErased_(
const NodeId,
bool) {}
192 template < GUM_Numeric GUM_SCALAR >
193 void VariableElimination< GUM_SCALAR >::onAllEvidenceErased_(
bool) {}
196 template < GUM_Numeric GUM_SCALAR >
197 void VariableElimination< GUM_SCALAR >::onEvidenceChanged_(
const NodeId,
bool) {}
200 template < GUM_Numeric GUM_SCALAR >
201 void VariableElimination< GUM_SCALAR >::onMarginalTargetAdded_(
const NodeId) {}
204 template < GUM_Numeric GUM_SCALAR >
205 void VariableElimination< GUM_SCALAR >::onMarginalTargetErased_(
const NodeId) {}
208 template < GUM_Numeric GUM_SCALAR >
209 void VariableElimination< GUM_SCALAR >::onModelChanged_(
const GraphicalModel* bn) {}
212 template < GUM_Numeric GUM_SCALAR >
213 void VariableElimination< GUM_SCALAR >::onJointTargetAdded_(
const NodeSet&) {}
216 template < GUM_Numeric GUM_SCALAR >
217 void VariableElimination< GUM_SCALAR >::onJointTargetErased_(
const NodeSet&) {}
220 template < GUM_Numeric GUM_SCALAR >
221 void VariableElimination< GUM_SCALAR >::onAllMarginalTargetsAdded_() {}
224 template < GUM_Numeric GUM_SCALAR >
225 void VariableElimination< GUM_SCALAR >::onAllMarginalTargetsErased_() {}
228 template < GUM_Numeric GUM_SCALAR >
229 void VariableElimination< GUM_SCALAR >::onAllJointTargetsErased_() {}
232 template < GUM_Numeric GUM_SCALAR >
233 void VariableElimination< GUM_SCALAR >::onAllTargetsErased_() {}
236 template < GUM_Numeric GUM_SCALAR >
237 void VariableElimination< GUM_SCALAR >::_createNewJT_(
const NodeSet& targets) {
253 const auto& bn = this->BN();
255 for (
const auto node: bn.internalDag())
256 _graph_.addNodeWithId(node);
263 if (_barren_nodes_type_ == FindBarrenNodesType::FIND_BARREN_NODES) {
266 if (targets.size() != bn.size()) {
267 BarrenNodesFinder finder(&bn.internalDag());
268 finder.setTargets(&targets);
270 NodeSet evidence_nodes(this->evidence().size());
271 for (
const auto& pair: this->evidence()) {
272 evidence_nodes.insert(pair.first);
274 finder.setEvidence(&evidence_nodes);
276 NodeSet barren_nodes = finder.barrenNodes();
279 for (
const auto node: barren_nodes) {
280 _graph_.eraseNode(node);
289 bool dsep_analysis =
false;
290 switch (_find_relevant_tensor_type_) {
291 case RelevantTensorsFinderType::DSEP_BAYESBALL_TENSORS :
292 case RelevantTensorsFinderType::DSEP_BAYESBALL_NODES : {
293 BayesBall::requisiteNodes(bn.internalDag(),
295 this->hardEvidenceNodes(),
296 this->softEvidenceNodes(),
298 dsep_analysis =
true;
301 case RelevantTensorsFinderType::DSEP_KOLLER_FRIEDMAN_2009 : {
302 dSeparationAlgorithm dsep;
303 dsep.requisiteNodes(bn.internalDag(),
305 this->hardEvidenceNodes(),
306 this->softEvidenceNodes(),
308 dsep_analysis =
true;
311 case RelevantTensorsFinderType::FIND_ALL :
break;
318 for (
auto iter = _graph_.beginSafe(); iter != _graph_.endSafe(); ++iter) {
319 if (!requisite_nodes.contains(*iter) && !this->hardEvidenceNodes().contains(*iter)) {
320 _graph_.eraseNode(*iter);
327 for (
const auto node: _graph_) {
328 const NodeSet& parents = bn.parents(node);
329 for (
auto iter1 = parents.cbegin(); iter1 != parents.cend(); ++iter1) {
334 if (_graph_.existsNode(*iter1)) {
335 _graph_.addEdge(*iter1, node);
338 for (++iter2; iter2 != parents.cend(); ++iter2) {
343 if (_graph_.existsNode(*iter2)) _graph_.addEdge(*iter1, *iter2);
352 for (
auto iter1 = targets.cbegin(); iter1 != targets.cend(); ++iter1) {
354 for (++iter2; iter2 != targets.cend(); ++iter2) {
355 _graph_.addEdge(*iter1, *iter2);
360 const auto& hard_ev_nodes = this->hardEvidenceNodes();
361 for (
const auto node: hard_ev_nodes) {
362 _graph_.eraseNode(node);
367 if (_JT_ !=
nullptr)
delete _JT_;
368 _triangulation_->setGraph(&_graph_, &(this->domainSizes()));
369 const JunctionTree& triang_jt = _triangulation_->junctionTree();
370 _JT_ =
new CliqueGraph(triang_jt);
374 _node_to_clique_.clear();
375 _clique_to_nodes_.clear();
376 NodeSet emptyset(_JT_->size());
377 for (
auto clique: *_JT_)
378 _clique_to_nodes_.insert(clique, emptyset);
379 const std::vector< NodeId >& JT_elim_order = _triangulation_->eliminationOrder();
380 NodeProperty< int > elim_order(Size(JT_elim_order.size()));
381 for (std::size_t i = std::size_t(0), size = JT_elim_order.size(); i < size; ++i)
382 elim_order.insert(JT_elim_order[i], (
int)i);
383 const DAG& dag = bn.internalDag();
385 for (
const auto node: _graph_) {
387 NodeId first_eliminated_node = node;
388 int elim_number = elim_order[first_eliminated_node];
390 for (
const auto parent: dag.parents(node)) {
391 if (_graph_.existsNode(parent) && (elim_order[parent] < elim_number)) {
392 elim_number = elim_order[parent];
393 first_eliminated_node = parent;
401 NodeId clique = _triangulation_->createdJunctionTreeClique(first_eliminated_node);
402 _node_to_clique_.insert(node, clique);
403 _clique_to_nodes_[clique].insert(node);
409 for (
const auto node: hard_ev_nodes) {
410 NodeId first_eliminated_node = std::numeric_limits< NodeId >::max();
411 int elim_number = std::numeric_limits< int >::max();
413 for (
const auto parent: dag.parents(node)) {
414 if (_graph_.exists(parent) && (elim_order[parent] < elim_number)) {
415 elim_number = elim_order[parent];
416 first_eliminated_node = parent;
424 if (elim_number != std::numeric_limits< int >::max()) {
425 NodeId clique = _triangulation_->createdJunctionTreeClique(first_eliminated_node);
426 _node_to_clique_.insert(node, clique);
427 _clique_to_nodes_[clique].insert(node);
433 _targets2clique_ = std::numeric_limits< NodeId >::max();
437 NodeId first_eliminated_node = std::numeric_limits< NodeId >::max();
438 int elim_number = std::numeric_limits< int >::max();
440 for (
const auto node: targets) {
441 if (!hard_ev_nodes.contains(node) && (elim_order[node] < elim_number)) {
442 elim_number = elim_order[node];
443 first_eliminated_node = node;
447 if (elim_number != std::numeric_limits< int >::max()) {
448 _targets2clique_ = _triangulation_->createdJunctionTreeClique(first_eliminated_node);
454 template < GUM_Numeric GUM_SCALAR >
455 void VariableElimination< GUM_SCALAR >::updateOutdatedStructure_() {}
459 template < GUM_Numeric GUM_SCALAR >
460 void VariableElimination< GUM_SCALAR >::updateOutdatedTensors_() {}
463 template < GUM_Numeric GUM_SCALAR >
464 void VariableElimination< GUM_SCALAR >::_findRelevantTensorsGetAll_(
465 Set< const IScheduleMultiDim* >& pot_list,
469 template < GUM_Numeric GUM_SCALAR >
470 void VariableElimination< GUM_SCALAR >::_findRelevantTensorsWithdSeparation_(
471 Set< const IScheduleMultiDim* >& pot_list,
475 const auto& bn = this->BN();
476 for (
const auto var: kept_vars) {
477 kept_ids.insert(bn.nodeId(*var));
482 BayesBall::requisiteNodes(bn.internalDag(),
484 this->hardEvidenceNodes(),
485 this->softEvidenceNodes(),
487 for (
auto iter = pot_list.beginSafe(); iter != pot_list.endSafe(); ++iter) {
488 const Sequence< const DiscreteVariable* >& vars = (*iter)->variablesSequence();
490 for (
const auto var: vars) {
491 if (requisite_nodes.exists(bn.nodeId(*var))) {
497 if (!found) { pot_list.erase(iter); }
502 template < GUM_Numeric GUM_SCALAR >
503 void VariableElimination< GUM_SCALAR >::_findRelevantTensorsWithdSeparation2_(
504 Set< const IScheduleMultiDim* >& pot_list,
508 const auto& bn = this->BN();
509 for (
const auto var: kept_vars) {
510 kept_ids.insert(bn.nodeId(*var));
514 BayesBall::relevantTensors(bn,
516 this->hardEvidenceNodes(),
517 this->softEvidenceNodes(),
522 template < GUM_Numeric GUM_SCALAR >
523 void VariableElimination< GUM_SCALAR >::_findRelevantTensorsWithdSeparation3_(
524 Set< const IScheduleMultiDim* >& pot_list,
528 const auto& bn = this->BN();
529 for (
const auto var: kept_vars) {
530 kept_ids.insert(bn.nodeId(*var));
534 dSeparationAlgorithm dsep;
535 dsep.relevantTensors(bn,
537 this->hardEvidenceNodes(),
538 this->softEvidenceNodes(),
543 template < GUM_Numeric GUM_SCALAR >
544 void VariableElimination< GUM_SCALAR >::_findRelevantTensorsXX_(
545 Set< const IScheduleMultiDim* >& pot_list,
547 switch (_find_relevant_tensor_type_) {
548 case RelevantTensorsFinderType::DSEP_BAYESBALL_TENSORS :
549 _findRelevantTensorsWithdSeparation2_(pot_list, kept_vars);
552 case RelevantTensorsFinderType::DSEP_BAYESBALL_NODES :
553 _findRelevantTensorsWithdSeparation_(pot_list, kept_vars);
556 case RelevantTensorsFinderType::DSEP_KOLLER_FRIEDMAN_2009 :
557 _findRelevantTensorsWithdSeparation3_(pot_list, kept_vars);
560 case RelevantTensorsFinderType::FIND_ALL :
561 _findRelevantTensorsGetAll_(pot_list, kept_vars);
569 template < GUM_Numeric GUM_SCALAR >
570 Set< const IScheduleMultiDim* >
571 VariableElimination< GUM_SCALAR >::_removeBarrenVariables_(Schedule& schedule,
572 _ScheduleMultiDimSet_& pot_list,
577 for (
auto iter = the_del_vars.
beginSafe(); iter != the_del_vars.
endSafe(); ++iter) {
578 NodeId
id = this->BN().nodeId(**iter);
579 if (this->hardEvidenceNodes().exists(
id) || this->softEvidenceNodes().exists(
id)) {
580 the_del_vars.
erase(iter);
585 HashTable< const DiscreteVariable*, _ScheduleMultiDimSet_ > var2pots(the_del_vars.
size());
586 _ScheduleMultiDimSet_ empty_pot_set;
587 for (
const auto pot: pot_list) {
588 const auto& vars = pot->variablesSequence();
589 for (
const auto var: vars) {
590 if (the_del_vars.
exists(var)) {
591 if (!var2pots.exists(var)) { var2pots.insert(var, empty_pot_set); }
592 var2pots[var].insert(pot);
599 HashTable< const IScheduleMultiDim*, gum::VariableSet > pot2barren_var;
601 for (
const auto& elt: var2pots) {
602 if (elt.second.size() == 1) {
603 const IScheduleMultiDim* pot = *(elt.second.begin());
604 if (!pot2barren_var.exists(pot)) { pot2barren_var.insert(pot, empty_var_set); }
605 pot2barren_var[pot].insert(elt.first);
612 MultiDimProjection< Tensor< GUM_SCALAR > > projector(_projection_op_);
613 _ScheduleMultiDimSet_ projected_pots;
614 for (
const auto& elt: pot2barren_var) {
616 const IScheduleMultiDim* pot = elt.first;
621 if (pot->variablesSequence().size() != elt.second.size()) {
622 const IScheduleMultiDim* new_pot = projector.schedule(schedule, pot, elt.second);
626 pot_list.insert(new_pot);
627 projected_pots.insert(new_pot);
631 return projected_pots;
635 template < GUM_Numeric GUM_SCALAR >
636 Set< const Tensor< GUM_SCALAR >* >
637 VariableElimination< GUM_SCALAR >::_removeBarrenVariables_(_TensorSet_& pot_list,
642 for (
auto iter = the_del_vars.
beginSafe(); iter != the_del_vars.
endSafe(); ++iter) {
643 NodeId
id = this->BN().nodeId(**iter);
644 if (this->hardEvidenceNodes().exists(
id) || this->softEvidenceNodes().exists(
id)) {
645 the_del_vars.
erase(iter);
650 HashTable< const DiscreteVariable*, _TensorSet_ > var2pots;
651 _TensorSet_ empty_pot_set;
652 for (
const auto pot: pot_list) {
653 const Sequence< const DiscreteVariable* >& vars = pot->variablesSequence();
654 for (
const auto var: vars) {
655 if (the_del_vars.
exists(var)) {
656 if (!var2pots.exists(var)) { var2pots.insert(var, empty_pot_set); }
657 var2pots[var].insert(pot);
666 for (
const auto& elt: var2pots) {
667 if (elt.second.size() == 1) {
668 const Tensor< GUM_SCALAR >* pot = *(elt.second.begin());
669 if (!pot2barren_var.exists(pot)) { pot2barren_var.insert(pot, empty_var_set); }
670 pot2barren_var[pot].insert(elt.first);
677 MultiDimProjection< Tensor< GUM_SCALAR > > projector(_projection_op_);
678 _TensorSet_ projected_pots;
679 for (
const auto& elt: pot2barren_var) {
681 const Tensor< GUM_SCALAR >* pot = elt.first;
686 if (pot->variablesSequence().size() != elt.second.size()) {
687 const Tensor< GUM_SCALAR >* new_pot = projector.execute(*pot, elt.second);
688 pot_list.insert(new_pot);
689 projected_pots.insert(new_pot);
693 return projected_pots;
697 template < GUM_Numeric GUM_SCALAR >
698 Set< const IScheduleMultiDim* >
699 VariableElimination< GUM_SCALAR >::_collectMessage_(Schedule& schedule,
703 _ScheduleMultiDimSet_ collected_messages;
704 for (
const auto other: _JT_->neighbours(
id)) {
706 _ScheduleMultiDimSet_ message(_collectMessage_(schedule, other,
id));
707 collected_messages += message;
712 return _produceMessage_(schedule,
id, from, std::move(collected_messages));
716 template < GUM_Numeric GUM_SCALAR >
717 std::pair< Set< const Tensor< GUM_SCALAR >* >, Set< const Tensor< GUM_SCALAR >* > >
718 VariableElimination< GUM_SCALAR >::_collectMessage_(NodeId
id, NodeId from) {
720 std::pair< _TensorSet_, _TensorSet_ > collected_messages;
721 for (
const auto other: _JT_->neighbours(
id)) {
723 std::pair< _TensorSet_, _TensorSet_ > message(_collectMessage_(other,
id));
724 collected_messages.first += message.first;
725 collected_messages.second += message.second;
730 return _produceMessage_(
id, from, std::move(collected_messages));
734 template < GUM_Numeric GUM_SCALAR >
735 Set< const IScheduleMultiDim* >
736 VariableElimination< GUM_SCALAR >::_NodeTensors_(Schedule& schedule, NodeId node) {
737 _ScheduleMultiDimSet_ res;
738 const auto& bn = this->BN();
749 const auto& evidence = this->evidence();
750 const auto& hard_evidence = this->hardEvidence();
751 const auto& hard_ev_nodes = this->hardEvidenceNodes();
752 if (_graph_.exists(node) || hard_ev_nodes.contains(node)) {
753 const Tensor< GUM_SCALAR >& cpt = bn.cpt(node);
754 const auto& variables = cpt.variablesSequence();
759 if (hard_ev_nodes.contains(node)) {
760 for (
const auto var: variables) {
761 NodeId xnode = bn.nodeId(*var);
762 if (!hard_ev_nodes.contains(xnode) && !_graph_.existsNode(xnode))
return res;
767 NodeSet hard_nodes(variables.size());
768 for (
const auto var: variables) {
769 NodeId xnode = bn.nodeId(*var);
770 if (hard_ev_nodes.contains(xnode)) hard_nodes.insert(xnode);
776 if (hard_nodes.empty()) {
777 const IScheduleMultiDim* sched_cpt
778 = schedule.insertTable< Tensor< GUM_SCALAR > >(cpt,
false);
779 res.insert(sched_cpt);
784 if (hard_nodes.size() != variables.size()) {
787 _ScheduleMultiDimSet_ marg_cpt_set(1 + hard_nodes.size());
788 const IScheduleMultiDim* sched_cpt
789 = schedule.insertTable< Tensor< GUM_SCALAR > >(cpt,
false);
790 marg_cpt_set.insert(sched_cpt);
792 for (
const auto xnode: hard_nodes) {
793 const IScheduleMultiDim* pot
794 = schedule.insertTable< Tensor< GUM_SCALAR > >(*evidence[xnode],
false);
795 marg_cpt_set.insert(pot);
796 hard_variables.
insert(&(bn.variable(xnode)));
800 MultiDimCombineAndProjectDefault< Tensor< GUM_SCALAR > > combine_and_project(
803 _ScheduleMultiDimSet_ new_cpt_list
804 = combine_and_project.schedule(schedule, marg_cpt_set, hard_variables);
807 if (new_cpt_list.size() != 1) {
809 "the projection of a tensor containing " <<
"hard evidence is empty!");
811 auto projected_pot =
const_cast< ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
812 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
813 *new_cpt_list.begin()));
814 res.insert(projected_pot);
819 if (evidence.exists(node) && !hard_evidence.exists(node)) {
820 const IScheduleMultiDim* pot
821 = schedule.insertTable< Tensor< GUM_SCALAR > >(*evidence[node],
false);
830 template < GUM_Numeric GUM_SCALAR >
831 std::pair< Set< const Tensor< GUM_SCALAR >* >, Set< const Tensor< GUM_SCALAR >* > >
832 VariableElimination< GUM_SCALAR >::_NodeTensors_(NodeId node) {
833 std::pair< _TensorSet_, _TensorSet_ > res;
834 const auto& bn = this->BN();
845 const auto& evidence = this->evidence();
846 const auto& hard_evidence = this->hardEvidence();
847 const auto& hard_ev_nodes = this->hardEvidenceNodes();
848 if (_graph_.exists(node) || hard_ev_nodes.contains(node)) {
849 const Tensor< GUM_SCALAR >& cpt = bn.cpt(node);
850 const auto& variables = cpt.variablesSequence();
855 if (hard_ev_nodes.contains(node)) {
856 for (
const auto var: variables) {
857 NodeId xnode = bn.nodeId(*var);
858 if (!hard_ev_nodes.contains(xnode) && !_graph_.existsNode(xnode))
return res;
863 NodeSet hard_nodes(variables.size());
864 for (
const auto var: variables) {
865 NodeId xnode = bn.nodeId(*var);
866 if (hard_ev_nodes.contains(xnode)) hard_nodes.insert(xnode);
872 if (hard_nodes.empty()) {
873 res.first.insert(&cpt);
878 if (hard_nodes.size() != variables.size()) {
881 _TensorSet_ marg_cpt_set(1 + hard_nodes.size());
882 marg_cpt_set.insert(&cpt);
884 for (
const auto xnode: hard_nodes) {
885 marg_cpt_set.insert(evidence[xnode]);
886 hard_variables.
insert(&(bn.variable(xnode)));
889 MultiDimCombineAndProjectDefault< Tensor< GUM_SCALAR > > combine_and_project(
892 _TensorSet_ new_cpt_list = combine_and_project.execute(marg_cpt_set, hard_variables);
895 if (new_cpt_list.size() != 1) {
897 for (
auto pot: new_cpt_list) {
898 if (!marg_cpt_set.contains(pot))
delete pot;
901 "the projection of a tensor containing " <<
"hard evidence is empty!");
903 const Tensor< GUM_SCALAR >* projected_cpt = *(new_cpt_list.begin());
904 res.first.insert(projected_cpt);
905 res.second.insert(projected_cpt);
910 if (evidence.exists(node) && !hard_evidence.exists(node)) {
911 res.first.insert(this->evidence()[node]);
919 template < GUM_Numeric GUM_SCALAR >
920 std::pair< Set< const Tensor< GUM_SCALAR >* >, Set< const Tensor< GUM_SCALAR >* > >
921 VariableElimination< GUM_SCALAR >::_produceMessage_(
924 std::pair< Set<
const Tensor< GUM_SCALAR >* >, Set<
const Tensor< GUM_SCALAR >* > >&&
927 std::pair< _TensorSet_, _TensorSet_ > pot_list(std::move(incoming_messages));
930 for (
const auto node: _clique_to_nodes_[from_id]) {
931 auto new_pots = _NodeTensors_(node);
932 pot_list.first += new_pots.first;
933 pot_list.second += new_pots.second;
937 if (!_JT_->existsEdge(from_id, to_id)) {
941 const NodeSet& from_clique = _JT_->clique(from_id);
942 const NodeSet& separator = _JT_->separator(from_id, to_id);
945 const auto& bn = this->BN();
947 for (
const auto node: from_clique) {
948 if (!separator.contains(node)) {
949 del_vars.
insert(&(bn.variable(node)));
951 kept_vars.
insert(&(bn.variable(node)));
957 _TensorSet_ new_pot_list = _marginalizeOut_(pot_list.first, del_vars, kept_vars);
960 for (
auto iter = pot_list.second.beginSafe(); iter != pot_list.second.endSafe(); ++iter) {
961 if (!new_pot_list.contains(*iter)) {
963 pot_list.second.erase(iter);
968 for (
const auto pot: new_pot_list) {
969 if (!pot_list.first.contains(pot)) { pot_list.second.insert(pot); }
973 return std::pair< _TensorSet_, _TensorSet_ >(std::move(new_pot_list),
974 std::move(pot_list.second));
979 template < GUM_Numeric GUM_SCALAR >
980 Set< const IScheduleMultiDim* > VariableElimination< GUM_SCALAR >::_produceMessage_(
984 Set< const IScheduleMultiDim* >&& incoming_messages) {
986 _ScheduleMultiDimSet_ pot_list(std::move(incoming_messages));
989 for (
const auto node: _clique_to_nodes_[from_id]) {
990 pot_list += _NodeTensors_(schedule, node);
994 if (!_JT_->existsEdge(from_id, to_id)) {
998 const NodeSet& from_clique = _JT_->clique(from_id);
999 const NodeSet& separator = _JT_->separator(from_id, to_id);
1002 const auto& bn = this->BN();
1004 for (
const auto node: from_clique) {
1005 if (!separator.contains(node)) {
1006 del_vars.
insert(&(bn.variable(node)));
1008 kept_vars.
insert(&(bn.variable(node)));
1014 _ScheduleMultiDimSet_ new_pot_list
1015 = _marginalizeOut_(schedule, pot_list, del_vars, kept_vars);
1018 for (
auto pot: pot_list) {
1019 if (!new_pot_list.contains(pot)) {
1020 const auto sched_pot
1021 =
static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(pot);
1022 schedule.emplaceDeletion(*sched_pot);
1027 return new_pot_list;
1032 template < GUM_Numeric GUM_SCALAR >
1033 Set< const Tensor< GUM_SCALAR >* > VariableElimination< GUM_SCALAR >::_marginalizeOut_(
1034 Set<
const Tensor< GUM_SCALAR >* > pot_list,
1038 if (pot_list.empty()) {
return _TensorSet_(); }
1045 _TensorSet_ barren_projected_tensors;
1046 if (_barren_nodes_type_ == FindBarrenNodesType::FIND_BARREN_NODES) {
1047 barren_projected_tensors = _removeBarrenVariables_(pot_list, del_vars);
1051 _TensorSet_ new_pot_list;
1052 if (pot_list.size() == 1) {
1053 MultiDimProjection< Tensor< GUM_SCALAR > > projector(_projection_op_);
1054 auto pot = projector.execute(**(pot_list.begin()), del_vars);
1055 new_pot_list.insert(pot);
1056 }
else if (pot_list.size() > 1) {
1059 MultiDimCombineAndProjectDefault< Tensor< GUM_SCALAR > > combine_and_project(_combination_op_,
1061 new_pot_list = combine_and_project.execute(pot_list, del_vars);
1067 for (
auto iter = barren_projected_tensors.beginSafe();
1068 iter != barren_projected_tensors.endSafe();
1070 if (!new_pot_list.exists(*iter))
delete *iter;
1073 return new_pot_list;
1077 template < GUM_Numeric GUM_SCALAR >
1078 Set< const IScheduleMultiDim* >
1079 VariableElimination< GUM_SCALAR >::_marginalizeOut_(Schedule& schedule,
1080 Set< const IScheduleMultiDim* > pot_list,
1084 if (pot_list.empty()) {
return _ScheduleMultiDimSet_(); }
1091 for (
const auto pot: pot_list) {
1092 if (!schedule.existsScheduleMultiDim(pot->id())) schedule.emplaceScheduleMultiDim(*pot);
1097 _ScheduleMultiDimSet_ barren_projected_tensors;
1098 if (_barren_nodes_type_ == FindBarrenNodesType::FIND_BARREN_NODES) {
1099 barren_projected_tensors = _removeBarrenVariables_(schedule, pot_list, del_vars);
1103 _ScheduleMultiDimSet_ new_pot_list;
1104 if (pot_list.size() == 1) {
1105 MultiDimProjection< Tensor< GUM_SCALAR > > projector(_projection_op_);
1106 auto xpot = projector.schedule(schedule, *(pot_list.begin()), del_vars);
1107 new_pot_list.insert(xpot);
1108 }
else if (pot_list.size() > 1) {
1111 MultiDimCombineAndProjectDefault< Tensor< GUM_SCALAR > > combine_and_project(_combination_op_,
1113 new_pot_list = combine_and_project.schedule(schedule, pot_list, del_vars);
1119 for (
auto pot: barren_projected_tensors) {
1120 if (!new_pot_list.exists(pot)) {
1121 const auto sched_pot =
static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(pot);
1122 schedule.emplaceDeletion(*sched_pot);
1126 return new_pot_list;
1130 template < GUM_Numeric GUM_SCALAR >
1131 void VariableElimination< GUM_SCALAR >::makeInference_() {}
1134 template < GUM_Numeric GUM_SCALAR >
1135 Tensor< GUM_SCALAR >* VariableElimination< GUM_SCALAR >::unnormalizedJointPosterior_(NodeId
id) {
1138 if (this->hardEvidenceNodes().
contains(
id)) {
1139 return new Tensor< GUM_SCALAR >(*(this->evidence()[
id]));
1143 _createNewJT_(NodeSet{
id});
1148 double overall_size = 0;
1149 for (
const auto clique: *_JT_) {
1150 double clique_size = 1.0;
1151 for (
const auto node: _JT_->clique(clique))
1152 clique_size *= this->domainSizes()[node];
1153 overall_size += clique_size;
1155 const bool use_schedules = (overall_size > _schedule_threshold_);
1157 if (use_schedules) {
1159 return _unnormalizedJointPosterior_(schedule,
id);
1161 return _unnormalizedJointPosterior_(
id);
1166 template < GUM_Numeric GUM_SCALAR >
1167 Tensor< GUM_SCALAR >* VariableElimination< GUM_SCALAR >::_unnormalizedJointPosterior_(NodeId
id) {
1168 const auto& bn = this->BN();
1170 NodeId clique_of_id = _node_to_clique_[id];
1171 std::pair< _TensorSet_, _TensorSet_ > pot_list = _collectMessage_(clique_of_id, clique_of_id);
1174 const NodeSet& nodes = _JT_->clique(clique_of_id);
1177 for (
const auto node: nodes) {
1178 if (node !=
id) del_vars.
insert(&(bn.variable(node)));
1183 _TensorSet_ new_pot_list = _marginalizeOut_(pot_list.first, del_vars, kept_vars);
1184 Tensor< GUM_SCALAR >* joint =
nullptr;
1186 if (new_pot_list.size() == 0) {
1187 joint =
new Tensor< GUM_SCALAR >;
1188 for (
const auto var: kept_vars)
1191 if (new_pot_list.size() == 1) {
1192 joint =
const_cast< Tensor< GUM_SCALAR >*
>(*(new_pot_list.begin()));
1195 if (pot_list.first.exists(joint)) {
1196 joint =
new Tensor< GUM_SCALAR >(*joint);
1200 new_pot_list.clear();
1203 MultiDimCombinationDefault< Tensor< GUM_SCALAR > > fast_combination(_combination_op_);
1204 joint = fast_combination.execute(new_pot_list);
1209 for (
auto pot: new_pot_list)
1210 if (!pot_list.first.exists(pot))
delete pot;
1213 for (
auto pot: pot_list.second)
1219 bool nonzero_found =
false;
1220 for (Instantiation inst(*joint); !inst.end(); ++inst) {
1221 if ((*joint)[inst]) {
1222 nonzero_found =
true;
1226 if (!nonzero_found) {
1230 "some evidence entered into the Bayes "
1231 "net are incompatible (their joint proba = 0)");
1238 template < GUM_Numeric GUM_SCALAR >
1239 Tensor< GUM_SCALAR >*
1240 VariableElimination< GUM_SCALAR >::_unnormalizedJointPosterior_(Schedule& schedule,
1242 const auto& bn = this->BN();
1244 NodeId clique_of_id = _node_to_clique_[id];
1245 _ScheduleMultiDimSet_ pot_list = _collectMessage_(schedule, clique_of_id, clique_of_id);
1248 const NodeSet& nodes = _JT_->clique(clique_of_id);
1251 for (
const auto node: nodes) {
1252 if (node !=
id) del_vars.
insert(&(bn.variable(node)));
1257 _ScheduleMultiDimSet_ new_pot_list = _marginalizeOut_(schedule, pot_list, del_vars, kept_vars);
1258 Tensor< GUM_SCALAR >* joint =
nullptr;
1259 ScheduleMultiDim< Tensor< GUM_SCALAR > >* resulting_pot =
nullptr;
1261 if (new_pot_list.size() == 0) {
1262 joint =
new Tensor< GUM_SCALAR >;
1263 for (
const auto var: kept_vars)
1266 auto& scheduler = this->scheduler();
1267 if (new_pot_list.size() == 1) {
1268 scheduler.execute(schedule);
1269 resulting_pot =
const_cast< ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
1270 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(*new_pot_list.begin()));
1272 MultiDimCombinationDefault< Tensor< GUM_SCALAR > > fast_combination(_combination_op_);
1273 const IScheduleMultiDim* pot = fast_combination.schedule(schedule, new_pot_list);
1274 scheduler.execute(schedule);
1275 resulting_pot =
const_cast< ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
1276 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(pot));
1281 if (pot_list.exists(resulting_pot)) {
1282 joint =
new Tensor< GUM_SCALAR >(resulting_pot->multiDim());
1284 joint = resulting_pot->exportMultiDim();
1291 bool nonzero_found =
false;
1292 for (Instantiation inst(*joint); !inst.end(); ++inst) {
1293 if ((*joint)[inst]) {
1294 nonzero_found =
true;
1298 if (!nonzero_found) {
1302 "some evidence entered into the Bayes "
1303 "net are incompatible (their joint proba = 0)");
1310 template < GUM_Numeric GUM_SCALAR >
1311 const Tensor< GUM_SCALAR >& VariableElimination< GUM_SCALAR >::posterior_(NodeId
id) {
1313 auto joint = unnormalizedJointPosterior_(
id);
1314 if (joint->sum() != 1)
1317 if (_target_posterior_ !=
nullptr)
delete _target_posterior_;
1318 _target_posterior_ = joint;
1324 template < GUM_Numeric GUM_SCALAR >
1325 Tensor< GUM_SCALAR >*
1326 VariableElimination< GUM_SCALAR >::unnormalizedJointPosterior_(
const NodeSet& set) {
1329 NodeSet targets = set, hard_ev_nodes(this->hardEvidenceNodes().size());
1330 for (
const auto node: this->hardEvidenceNodes()) {
1331 if (targets.contains(node)) {
1332 targets.erase(node);
1333 hard_ev_nodes.insert(node);
1339 const auto& evidence = this->evidence();
1340 if (targets.empty()) {
1341 _TensorSet_ pot_list;
1342 for (
const auto node: set) {
1343 pot_list.insert(evidence[node]);
1345 if (pot_list.size() == 1) {
1346 return new Tensor< GUM_SCALAR >(**(pot_list.begin()));
1348 MultiDimCombinationDefault< Tensor< GUM_SCALAR > > fast_combination(_combination_op_);
1349 return fast_combination.execute(pot_list);
1359 double overall_size = 0;
1360 for (
const auto clique: *_JT_) {
1361 double clique_size = 1.0;
1362 for (
const auto node: _JT_->clique(clique))
1363 clique_size *= this->domainSizes()[node];
1364 overall_size += clique_size;
1366 const bool use_schedules = (overall_size > _schedule_threshold_);
1368 if (use_schedules) {
1370 return _unnormalizedJointPosterior_(schedule, set, targets, hard_ev_nodes);
1372 return _unnormalizedJointPosterior_(set, targets, hard_ev_nodes);
1377 template < GUM_Numeric GUM_SCALAR >
1378 Tensor< GUM_SCALAR >* VariableElimination< GUM_SCALAR >::_unnormalizedJointPosterior_(
1380 const NodeSet& targets,
1381 const NodeSet& hard_ev_nodes) {
1382 std::pair< _TensorSet_, _TensorSet_ > pot_list
1383 = _collectMessage_(_targets2clique_, _targets2clique_);
1386 const NodeSet& nodes = _JT_->clique(_targets2clique_);
1389 const auto& bn = this->BN();
1390 for (
const auto node: nodes) {
1391 if (!targets.contains(node)) {
1392 del_vars.
insert(&(bn.variable(node)));
1394 kept_vars.
insert(&(bn.variable(node)));
1400 _TensorSet_ new_pot_list = _marginalizeOut_(pot_list.first, del_vars, kept_vars);
1401 Tensor< GUM_SCALAR >* joint =
nullptr;
1403 if ((new_pot_list.size() == 1) && hard_ev_nodes.empty()) {
1404 joint =
const_cast< Tensor< GUM_SCALAR >*
>(*(new_pot_list.begin()));
1407 if (pot_list.first.exists(joint)) {
1408 joint =
new Tensor< GUM_SCALAR >(*joint);
1412 new_pot_list.clear();
1417 const auto& evidence = this->evidence();
1418 _TensorSet_ new_new_pot_list = new_pot_list;
1419 for (
const auto node: hard_ev_nodes) {
1420 new_new_pot_list.insert(evidence[node]);
1422 MultiDimCombinationDefault< Tensor< GUM_SCALAR > > fast_combination(_combination_op_);
1423 joint = fast_combination.execute(new_new_pot_list);
1427 for (
auto pot: new_pot_list)
1428 if (!pot_list.first.exists(pot))
delete pot;
1431 for (
auto pot: pot_list.second)
1436 bool nonzero_found =
false;
1437 for (Instantiation inst(*joint); !inst.end(); ++inst) {
1438 if ((*joint)[inst]) {
1439 nonzero_found =
true;
1443 if (!nonzero_found) {
1447 "some evidence entered into the Bayes "
1448 "net are incompatible (their joint proba = 0)");
1455 template < GUM_Numeric GUM_SCALAR >
1456 Tensor< GUM_SCALAR >* VariableElimination< GUM_SCALAR >::_unnormalizedJointPosterior_(
1459 const NodeSet& targets,
1460 const NodeSet& hard_ev_nodes) {
1461 _ScheduleMultiDimSet_ pot_list = _collectMessage_(schedule, _targets2clique_, _targets2clique_);
1464 const NodeSet& nodes = _JT_->clique(_targets2clique_);
1467 const auto& bn = this->BN();
1468 for (
const auto node: nodes) {
1469 if (!targets.contains(node)) {
1470 del_vars.
insert(&(bn.variable(node)));
1472 kept_vars.
insert(&(bn.variable(node)));
1478 _ScheduleMultiDimSet_ new_pot_list = _marginalizeOut_(schedule, pot_list, del_vars, kept_vars);
1479 ScheduleMultiDim< Tensor< GUM_SCALAR > >* resulting_pot =
nullptr;
1480 auto& scheduler = this->scheduler();
1482 if ((new_pot_list.size() == 1) && hard_ev_nodes.empty()) {
1483 scheduler.execute(schedule);
1484 resulting_pot =
const_cast< ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
1485 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(*new_pot_list.begin()));
1489 const auto& evidence = this->evidence();
1490 for (
const auto node: hard_ev_nodes) {
1491 auto new_pot_ev = schedule.insertTable< Tensor< GUM_SCALAR > >(*evidence[node],
false);
1492 new_pot_list.insert(new_pot_ev);
1494 MultiDimCombinationDefault< Tensor< GUM_SCALAR > > fast_combination(_combination_op_);
1495 const auto pot = fast_combination.schedule(schedule, new_pot_list);
1496 scheduler.execute(schedule);
1497 resulting_pot =
const_cast< ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
1498 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(pot));
1503 Tensor< GUM_SCALAR >* joint =
nullptr;
1504 if (pot_list.exists(resulting_pot)) {
1505 joint =
new Tensor< GUM_SCALAR >(resulting_pot->multiDim());
1507 joint = resulting_pot->exportMultiDim();
1512 bool nonzero_found =
false;
1513 for (Instantiation inst(*joint); !inst.end(); ++inst) {
1514 if ((*joint)[inst]) {
1515 nonzero_found =
true;
1519 if (!nonzero_found) {
1523 "some evidence entered into the Bayes "
1524 "net are incompatible (their joint proba = 0)");
1531 template < GUM_Numeric GUM_SCALAR >
1532 const Tensor< GUM_SCALAR >&
1533 VariableElimination< GUM_SCALAR >::jointPosterior_(
const NodeSet& set) {
1535 auto joint = unnormalizedJointPosterior_(set);
1538 if (_target_posterior_ !=
nullptr)
delete _target_posterior_;
1539 _target_posterior_ = joint;
1545 template < GUM_Numeric GUM_SCALAR >
1546 const Tensor< GUM_SCALAR >&
1547 VariableElimination< GUM_SCALAR >::jointPosterior_(
const NodeSet& wanted_target,
1548 const NodeSet& declared_target) {
1549 return jointPosterior_(wanted_target);
1552 template < GUM_Numeric GUM_SCALAR >
1553 Tensor< GUM_SCALAR > VENewmultiTensor(
const Tensor< GUM_SCALAR >& t1,
1554 const Tensor< GUM_SCALAR >& t2) {
1558 template < GUM_Numeric GUM_SCALAR >
1559 Tensor< GUM_SCALAR > VENewprojTensor(
const Tensor< GUM_SCALAR >& t1,
1561 return t1.sumOut(del_vars);
1564 template < GUM_Numeric GUM_SCALAR >
1565 void VariableElimination< GUM_SCALAR >::onStateChanged_() {}
The BayesBall algorithm (as described by Schachter).
Detect barren nodes for inference in Bayesian networks.
An algorithm for converting a join tree into a binary join tree.
Exception : fatal (unknown ?) error.
Class representing the minimal interface for Bayesian network with no numerical data.
Exception : several evidence are incompatible together (proba=0).
Exception: at least one argument passed to a function is not what was expected.
<agrum/BN/inference/jointTargetedInference.h>
bool exists(const Key &k) const
Indicates whether a given elements belong to the set.
void insert(const Key &k)
Inserts a new element into the set.
iterator_safe beginSafe() const
The usual safe begin iterator to parse the set.
void erase(const Key &k)
Erases an element from the set.
Size size() const noexcept
Returns the number of elements in the set.
static const iterator_safe & endSafe() noexcept
The usual safe end iterator to parse the set.
VariableElimination(const IBayesNet< GUM_SCALAR > *BN, RelevantTensorsFinderType=RelevantTensorsFinderType::DSEP_BAYESBALL_TENSORS, FindBarrenNodesType=FindBarrenNodesType::FIND_BARREN_NODES)
default constructor
d-separation analysis (as described in Koller & Friedman 2009)
#define GUM_ERROR(type, msg)
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
bool contains(std::string_view s, std::string_view needle)
true if needle in s
Header files of gum::Instantiation.
gum is the global namespace for all aGrUM entities
FindBarrenNodesType
type of algorithm to determine barren nodes
Set< const DiscreteVariable * > VariableSet
CliqueGraph JunctionTree
a junction tree is a clique graph satisfying the running intersection property and such that no cliqu...
RelevantTensorsFinderType
type of algorithm for determining the relevant tensors for combinations using some d-separation analy...
Implementation of a variable elimination algorithm for inference in Bayesian networks.