53#ifndef DOXYGEN_SHOULD_SKIP_THIS
66 template < GUM_Numeric GUM_SCALAR >
71 bool use_binary_join_tree) :
73 _use_binary_join_tree_(use_binary_join_tree) {
76 = &ShaferShenoyInference< GUM_SCALAR >::_findRelevantTensorsWithdSeparation2_;
77 setRelevantTensorsFinderType(relevant_type);
78 setFindBarrenNodesType(barren_type);
81 _triangulation_ =
new DefaultTriangulation;
84 GUM_CONSTRUCTOR(ShaferShenoyInference);
88 template < GUM_Numeric GUM_SCALAR >
89 ShaferShenoyInference< GUM_SCALAR >::~ShaferShenoyInference() {
91 for (
const auto& pot: _arc_to_created_tensors_)
99 for (
auto pot: _clique_ss_tensor_) {
100 if (_clique_tensors_[pot.first].size() > 1)
delete pot.second;
103 for (
auto potset: _clique_tensors_) {
104 for (
auto pot: potset.second)
109 for (
const auto& pot: _target_posteriors_)
111 for (
const auto& pot: _joint_target_posteriors_)
115 if (_JT_ !=
nullptr)
delete _JT_;
116 if (_junctionTree_ !=
nullptr)
delete _junctionTree_;
117 delete _triangulation_;
120 GUM_DESTRUCTOR(ShaferShenoyInference);
124 template < GUM_Numeric GUM_SCALAR >
125 void ShaferShenoyInference< GUM_SCALAR >::setTriangulation(
126 const Triangulation& new_triangulation) {
127 delete _triangulation_;
128 _triangulation_ = new_triangulation.newFactory();
129 _is_new_jt_needed_ =
true;
130 this->setOutdatedStructureState_();
134 template < GUM_Numeric GUM_SCALAR >
135 const JoinTree* ShaferShenoyInference< GUM_SCALAR >::joinTree() {
136 if (_is_new_jt_needed_) _createNewJT_();
142 template < GUM_Numeric GUM_SCALAR >
143 const JunctionTree* ShaferShenoyInference< GUM_SCALAR >::junctionTree() {
144 if (_is_new_jt_needed_) _createNewJT_();
146 return _junctionTree_;
150 template < GUM_Numeric GUM_SCALAR >
151 void ShaferShenoyInference< GUM_SCALAR >::setRelevantTensorsFinderType(
152 RelevantTensorsFinderType type) {
153 if (type != _find_relevant_tensor_type_) {
155 case RelevantTensorsFinderType::DSEP_BAYESBALL_TENSORS :
156 _findRelevantTensors_
157 = &ShaferShenoyInference< GUM_SCALAR >::_findRelevantTensorsWithdSeparation2_;
160 case RelevantTensorsFinderType::DSEP_BAYESBALL_NODES :
161 _findRelevantTensors_
162 = &ShaferShenoyInference< GUM_SCALAR >::_findRelevantTensorsWithdSeparation_;
165 case RelevantTensorsFinderType::DSEP_KOLLER_FRIEDMAN_2009 :
166 _findRelevantTensors_
167 = &ShaferShenoyInference< GUM_SCALAR >::_findRelevantTensorsWithdSeparation3_;
170 case RelevantTensorsFinderType::FIND_ALL :
171 _findRelevantTensors_ = &ShaferShenoyInference< GUM_SCALAR >::_findRelevantTensorsGetAll_;
176 "setRelevantTensorsFinderType for type " << (
unsigned int)type
177 <<
" is not implemented yet");
180 _find_relevant_tensor_type_ = type;
184 _invalidateAllMessages_();
189 template < GUM_Numeric GUM_SCALAR >
190 void ShaferShenoyInference< GUM_SCALAR >::_setProjectionFunction_(
191 Tensor< GUM_SCALAR > (*proj)(
const Tensor< GUM_SCALAR >&,
const gum::VariableSet&)) {
192 _projection_op_ = proj;
196 _invalidateAllMessages_();
200 template < GUM_Numeric GUM_SCALAR >
201 void ShaferShenoyInference< GUM_SCALAR >::_setCombinationFunction_(
202 Tensor< GUM_SCALAR > (*comb)(
const Tensor< GUM_SCALAR >&,
const Tensor< GUM_SCALAR >&)) {
203 _combination_op_ = comb;
207 _invalidateAllMessages_();
211 template < GUM_Numeric GUM_SCALAR >
212 void ShaferShenoyInference< GUM_SCALAR >::_invalidateAllMessages_() {
214 for (
auto& pot: _separator_tensors_)
215 pot.second =
nullptr;
217 for (
auto& mess_computed: _messages_computed_)
218 mess_computed.second =
false;
221 for (
const auto& pot: _arc_to_created_tensors_)
222 if (pot.second !=
nullptr)
delete pot.second;
223 _arc_to_created_tensors_.clear();
226 for (
const auto& pot: _target_posteriors_)
228 _target_posteriors_.clear();
229 for (
const auto& pot: _joint_target_posteriors_)
231 _joint_target_posteriors_.clear();
234 if (this->isInferenceReady() || this->isInferenceDone()) this->setOutdatedTensorsState_();
238 template < GUM_Numeric GUM_SCALAR >
239 void ShaferShenoyInference< GUM_SCALAR >::setFindBarrenNodesType(FindBarrenNodesType type) {
240 if (type != _barren_nodes_type_) {
244 case FindBarrenNodesType::FIND_BARREN_NODES :
245 case FindBarrenNodesType::FIND_NO_BARREN_NODES :
break;
249 "setFindBarrenNodesType for type " << (
unsigned int)type
250 <<
" is not implemented yet");
253 _barren_nodes_type_ = type;
256 this->setOutdatedStructureState_();
261 template < GUM_Numeric GUM_SCALAR >
262 void ShaferShenoyInference< GUM_SCALAR >::onEvidenceAdded_(
const NodeId
id,
bool isHardEvidence) {
266 if (isHardEvidence || !_graph_.exists(
id)) _is_new_jt_needed_ =
true;
269 _evidence_changes_.insert(
id, EvidenceChangeType::EVIDENCE_ADDED);
275 _evidence_changes_[id] = EvidenceChangeType::EVIDENCE_MODIFIED;
281 template < GUM_Numeric GUM_SCALAR >
282 void ShaferShenoyInference< GUM_SCALAR >::onEvidenceErased_(
const NodeId
id,
283 bool isHardEvidence) {
286 if (isHardEvidence) _is_new_jt_needed_ =
true;
289 _evidence_changes_.insert(
id, EvidenceChangeType::EVIDENCE_ERASED);
296 if (_evidence_changes_[
id] == EvidenceChangeType::EVIDENCE_ADDED)
297 _evidence_changes_.erase(
id);
298 else _evidence_changes_[id] = EvidenceChangeType::EVIDENCE_ERASED;
304 template < GUM_Numeric GUM_SCALAR >
305 void ShaferShenoyInference< GUM_SCALAR >::onAllEvidenceErased_(
bool has_hard_evidence) {
306 if (has_hard_evidence || !this->hardEvidenceNodes().empty()) _is_new_jt_needed_ =
true;
308 for (
const auto node: this->softEvidenceNodes()) {
310 _evidence_changes_.insert(node, EvidenceChangeType::EVIDENCE_ERASED);
317 if (_evidence_changes_[node] == EvidenceChangeType::EVIDENCE_ADDED)
318 _evidence_changes_.erase(node);
319 else _evidence_changes_[node] = EvidenceChangeType::EVIDENCE_ERASED;
326 template < GUM_Numeric GUM_SCALAR >
327 void ShaferShenoyInference< GUM_SCALAR >::onEvidenceChanged_(
const NodeId
id,
328 bool hasChangedSoftHard) {
329 if (hasChangedSoftHard) _is_new_jt_needed_ =
true;
332 _evidence_changes_.insert(
id, EvidenceChangeType::EVIDENCE_MODIFIED);
342 template < GUM_Numeric GUM_SCALAR >
343 void ShaferShenoyInference< GUM_SCALAR >::onModelChanged_(
const GraphicalModel* bn) {}
346 template < GUM_Numeric GUM_SCALAR >
347 void ShaferShenoyInference< GUM_SCALAR >::onMarginalTargetAdded_(
const NodeId
id) {
352 if (!_graph_.exists(
id) && !_hard_ev_nodes_.contains(
id)) { _is_new_jt_needed_ =
true; }
356 template < GUM_Numeric GUM_SCALAR >
357 void ShaferShenoyInference< GUM_SCALAR >::onMarginalTargetErased_(
const NodeId
id) {}
360 template < GUM_Numeric GUM_SCALAR >
361 void ShaferShenoyInference< GUM_SCALAR >::onJointTargetAdded_(
const NodeSet& set) {
363 if (_JT_ ==
nullptr) {
364 _is_new_jt_needed_ =
true;
371 NodeId first_eliminated_node = std::numeric_limits< NodeId >::max();
372 int elim_number = std::numeric_limits< int >::max();
373 const std::vector< NodeId >& JT_elim_order = _triangulation_->eliminationOrder();
374 NodeProperty< int > elim_order(Size(JT_elim_order.size()));
375 for (std::size_t i = std::size_t(0), size = JT_elim_order.size(); i < size; ++i)
376 elim_order.insert(JT_elim_order[i], (
int)i);
377 NodeSet unobserved_set(set.size());
378 for (
const auto node: set) {
379 if (!_graph_.exists(node)) {
380 if (!_hard_ev_nodes_.contains(node)) {
381 _is_new_jt_needed_ =
true;
385 unobserved_set.insert(node);
386 if (elim_order[node] < elim_number) {
387 elim_number = elim_order[node];
388 first_eliminated_node = node;
393 if (!unobserved_set.empty()) {
397 const auto clique_id = _node_to_clique_[first_eliminated_node];
398 const auto& clique = _JT_->clique(clique_id);
399 for (
const auto node: unobserved_set) {
400 if (!clique.contains(node)) {
401 _is_new_jt_needed_ =
true;
409 template < GUM_Numeric GUM_SCALAR >
410 void ShaferShenoyInference< GUM_SCALAR >::onJointTargetErased_(
const NodeSet& set) {}
413 template < GUM_Numeric GUM_SCALAR >
414 void ShaferShenoyInference< GUM_SCALAR >::onAllMarginalTargetsAdded_() {
415 for (
const auto node: this->BN().internalDag()) {
420 if (!_graph_.exists(node) && !_hard_ev_nodes_.contains(node)) {
421 _is_new_jt_needed_ =
true;
428 template < GUM_Numeric GUM_SCALAR >
429 void ShaferShenoyInference< GUM_SCALAR >::onAllMarginalTargetsErased_() {}
432 template < GUM_Numeric GUM_SCALAR >
433 void ShaferShenoyInference< GUM_SCALAR >::onAllJointTargetsErased_() {}
436 template < GUM_Numeric GUM_SCALAR >
437 void ShaferShenoyInference< GUM_SCALAR >::onAllTargetsErased_() {}
440 template < GUM_Numeric GUM_SCALAR >
441 bool ShaferShenoyInference< GUM_SCALAR >::_isNewJTNeeded_()
const {
444 if ((_JT_ ==
nullptr) || _is_new_jt_needed_)
return true;
452 const auto& hard_ev_nodes = this->hardEvidenceNodes();
453 for (
const auto node: this->targets()) {
454 if (!_graph_.exists(node) && !hard_ev_nodes.exists(node))
return true;
458 const std::vector< NodeId >& JT_elim_order = _triangulation_->eliminationOrder();
459 NodeProperty< int > elim_order(Size(JT_elim_order.size()));
460 for (std::size_t i = std::size_t(0), size = JT_elim_order.size(); i < size; ++i)
461 elim_order.insert(JT_elim_order[i], (
int)i);
464 for (
const auto& joint_target: this->jointTargets()) {
467 NodeId first_eliminated_node = std::numeric_limits< NodeId >::max();
468 int elim_number = std::numeric_limits< int >::max();
469 unobserved_set.clear();
470 for (
const auto node: joint_target) {
471 if (!_graph_.exists(node)) {
472 if (!hard_ev_nodes.exists(node))
return true;
474 unobserved_set.insert(node);
475 if (elim_order[node] < elim_number) {
476 elim_number = elim_order[node];
477 first_eliminated_node = node;
481 if (!unobserved_set.empty()) {
485 const auto clique_id = _node_to_clique_[first_eliminated_node];
486 const auto& clique = _JT_->clique(clique_id);
487 for (
const auto node: unobserved_set) {
488 if (!clique.contains(node))
return true;
495 for (
const auto& change: _evidence_changes_) {
496 if ((change.second == EvidenceChangeType::EVIDENCE_ADDED) && !_graph_.exists(change.first))
505 template < GUM_Numeric GUM_SCALAR >
506 void ShaferShenoyInference< GUM_SCALAR >::_createNewJT_() {
522 const auto& bn = this->BN();
524 for (
const auto node: bn.internalDag())
525 _graph_.addNodeWithId(node);
528 NodeSet target_nodes = this->targets();
529 for (
const auto& nodeset: this->jointTargets()) {
530 target_nodes += nodeset;
538 if ((this->nbrTargets() != bn.internalDag().size())
539 && (_barren_nodes_type_ == FindBarrenNodesType::FIND_BARREN_NODES)) {
542 if (target_nodes.size() != bn.size()) {
543 BarrenNodesFinder finder(&bn.internalDag());
544 finder.setTargets(&target_nodes);
546 NodeSet evidence_nodes(this->evidence().size());
547 for (
const auto& pair: this->evidence()) {
548 evidence_nodes.insert(pair.first);
550 finder.setEvidence(&evidence_nodes);
552 NodeSet barren_nodes = finder.barrenNodes();
555 for (
const auto node: barren_nodes) {
556 _graph_.eraseNode(node);
564 if (this->nbrTargets() != bn.internalDag().size()) {
566 bool dsep_analysis =
false;
567 switch (_find_relevant_tensor_type_) {
568 case RelevantTensorsFinderType::DSEP_BAYESBALL_TENSORS :
569 case RelevantTensorsFinderType::DSEP_BAYESBALL_NODES : {
570 BayesBall::requisiteNodes(bn.internalDag(),
572 this->hardEvidenceNodes(),
573 this->softEvidenceNodes(),
575 dsep_analysis =
true;
578 case RelevantTensorsFinderType::DSEP_KOLLER_FRIEDMAN_2009 : {
579 dSeparationAlgorithm dsep;
580 dsep.requisiteNodes(bn.internalDag(),
582 this->hardEvidenceNodes(),
583 this->softEvidenceNodes(),
585 dsep_analysis =
true;
588 case RelevantTensorsFinderType::FIND_ALL :
break;
595 for (
auto iter = _graph_.beginSafe(); iter != _graph_.endSafe(); ++iter) {
596 if (!requisite_nodes.contains(*iter) && !this->hardEvidenceNodes().contains(*iter)) {
597 _graph_.eraseNode(*iter);
604 for (
const auto node: _graph_) {
605 const NodeSet& parents = bn.parents(node);
606 for (
auto iter1 = parents.cbegin(); iter1 != parents.cend(); ++iter1) {
611 if (_graph_.existsNode(*iter1)) {
612 _graph_.addEdge(*iter1, node);
615 for (++iter2; iter2 != parents.cend(); ++iter2) {
620 if (_graph_.existsNode(*iter2)) _graph_.addEdge(*iter1, *iter2);
629 for (
const auto& nodeset: this->jointTargets()) {
630 for (
auto iter1 = nodeset.cbegin(); iter1 != nodeset.cend(); ++iter1) {
632 for (++iter2; iter2 != nodeset.cend(); ++iter2) {
633 _graph_.addEdge(*iter1, *iter2);
639 _hard_ev_nodes_ = this->hardEvidenceNodes();
640 for (
const auto node: _hard_ev_nodes_) {
641 _graph_.eraseNode(node);
648 if (_JT_ !=
nullptr)
delete _JT_;
649 if (_junctionTree_ !=
nullptr)
delete _junctionTree_;
651 _triangulation_->setGraph(&_graph_, &(this->domainSizes()));
652 const JunctionTree& triang_jt = _triangulation_->junctionTree();
653 if (_use_binary_join_tree_) {
654 BinaryJoinTreeConverterDefault bjt_converter;
656 _JT_ =
new CliqueGraph(bjt_converter.convert(triang_jt, this->domainSizes(), emptyset));
658 _JT_ =
new CliqueGraph(triang_jt);
660 _junctionTree_ =
new CliqueGraph(triang_jt);
665 _node_to_clique_.clear();
666 const std::vector< NodeId >& JT_elim_order = _triangulation_->eliminationOrder();
667 NodeProperty< int > elim_order(Size(JT_elim_order.size()));
668 for (std::size_t i = std::size_t(0), size = JT_elim_order.size(); i < size; ++i)
669 elim_order.insert(JT_elim_order[i], (
int)i);
670 const DAG& dag = bn.internalDag();
671 for (
const auto node: _graph_) {
673 NodeId first_eliminated_node = node;
674 int elim_number = elim_order[first_eliminated_node];
676 for (
const auto parent: dag.parents(node)) {
677 if (_graph_.existsNode(parent) && (elim_order[parent] < elim_number)) {
678 elim_number = elim_order[parent];
679 first_eliminated_node = parent;
687 _node_to_clique_.insert(node,
688 _triangulation_->createdJunctionTreeClique(first_eliminated_node));
694 for (
const auto node: _hard_ev_nodes_) {
695 NodeId first_eliminated_node = std::numeric_limits< NodeId >::max();
696 int elim_number = std::numeric_limits< int >::max();
698 for (
const auto parent: dag.parents(node)) {
699 if (_graph_.exists(parent) && (elim_order[parent] < elim_number)) {
700 elim_number = elim_order[parent];
701 first_eliminated_node = parent;
709 if (elim_number != std::numeric_limits< int >::max()) {
710 _node_to_clique_.insert(node,
711 _triangulation_->createdJunctionTreeClique(first_eliminated_node));
716 _joint_target_to_clique_.clear();
717 for (
const auto& set: this->jointTargets()) {
718 NodeId first_eliminated_node = std::numeric_limits< NodeId >::max();
719 int elim_number = std::numeric_limits< int >::max();
723 for (
const auto node: set) {
724 if (!_hard_ev_nodes_.contains(node)) {
727 if (elim_order[node] < elim_number) {
728 elim_number = elim_order[node];
729 first_eliminated_node = node;
734 if (elim_number != std::numeric_limits< int >::max()) {
735 _joint_target_to_clique_.insert(
737 _triangulation_->createdJunctionTreeClique(first_eliminated_node));
742 _computeJoinTreeRoots_();
747 for (
const auto& pot: _clique_ss_tensor_) {
748 if (_clique_tensors_[pot.first].size() > 1)
delete pot.second;
750 _clique_ss_tensor_.clear();
751 for (
const auto& potlist: _clique_tensors_)
752 for (
const auto pot: potlist.second)
754 _clique_tensors_.clear();
757 for (
const auto& pot: _arc_to_created_tensors_)
759 _arc_to_created_tensors_.clear();
764 _node_to_hard_ev_projected_CPTs_.clear();
767 _node_to_soft_evidence_.clear();
771 _ScheduleMultiDimSet_ empty_set;
772 for (
const auto node: *_JT_) {
773 _clique_tensors_.insert(node, empty_set);
774 _clique_ss_tensor_.insert(node,
nullptr);
782 _separator_tensors_.clear();
783 _messages_computed_.clear();
784 for (
const auto& edge: _JT_->edges()) {
785 const Arc arc1(edge.first(), edge.second());
786 _separator_tensors_.insert(arc1,
nullptr);
787 _messages_computed_.insert(arc1,
false);
788 const Arc arc2(edge.second(), edge.first());
789 _separator_tensors_.insert(arc2,
nullptr);
790 _messages_computed_.insert(arc2,
false);
794 for (
const auto& pot: _target_posteriors_)
796 _target_posteriors_.clear();
797 for (
const auto& pot: _joint_target_posteriors_)
799 _joint_target_posteriors_.clear();
804 double overall_size = 0;
805 for (
const auto clique: *_JT_) {
806 double clique_size = 1.0;
807 for (
const auto node: _JT_->clique(clique))
808 clique_size *= this->domainSizes()[node];
809 overall_size += clique_size;
811 _use_schedules_ = (overall_size > _schedule_threshold_);
814 const NodeProperty< const Tensor< GUM_SCALAR >* >& evidence = this->evidence();
815 for (
const auto node: this->softEvidenceNodes()) {
816 if (
auto ptr_clique = _node_to_clique_.tryGet(node)) {
817 auto ev_pot =
new ScheduleMultiDim< Tensor< GUM_SCALAR > >(*evidence[node],
false);
818 _node_to_soft_evidence_.insert(node, ev_pot);
819 _clique_tensors_[*ptr_clique].insert(ev_pot);
827 if (_use_schedules_) {
829 _initializeJTCliques_(schedule);
831 _initializeJTCliques_();
836 _evidence_changes_.clear();
837 _is_new_jt_needed_ =
false;
841 template < GUM_Numeric GUM_SCALAR >
842 void ShaferShenoyInference< GUM_SCALAR >::_initializeJTCliques_() {
843 const auto& bn = this->BN();
844 const DAG& dag = bn.internalDag();
850 const NodeProperty< const Tensor< GUM_SCALAR >* >& evidence = this->evidence();
851 const NodeProperty< Idx >& hard_evidence = this->hardEvidence();
853 for (
const auto node: dag) {
854 if (_graph_.exists(node) || _hard_ev_nodes_.contains(node)) {
855 const Tensor< GUM_SCALAR >& cpt = bn.cpt(node);
859 const auto& variables = cpt.variablesSequence();
860 bool graph_contains_nodes =
false;
861 for (
const auto var: variables) {
862 NodeId xnode = bn.nodeId(*var);
863 if (_hard_ev_nodes_.contains(xnode)) hard_nodes.insert(xnode);
864 else if (_graph_.exists(xnode)) graph_contains_nodes =
true;
870 if (hard_nodes.empty()) {
871 auto sched_cpt =
new ScheduleMultiDim< Tensor< GUM_SCALAR > >(cpt,
false);
872 _clique_tensors_[_node_to_clique_[node]].insert(sched_cpt);
878 if (hard_nodes.size() == variables.size()) {
879 Instantiation inst(cpt);
880 for (Size i = 0; i < hard_nodes.size(); ++i) {
881 inst.chgVal(*variables[i], hard_evidence[bn.nodeId(*(variables[i]))]);
883 _constants_.insert(node, cpt.get(inst));
888 if (!graph_contains_nodes)
continue;
892 _TensorSet_ marg_cpt_set(1 + hard_nodes.size());
893 marg_cpt_set.insert(&cpt);
894 for (
const auto xnode: hard_nodes) {
895 marg_cpt_set.insert(evidence[xnode]);
896 hard_variables.
insert(&(bn.variable(xnode)));
900 MultiDimCombineAndProjectDefault< Tensor< GUM_SCALAR > > combine_and_project(
904 _TensorSet_ new_cpt_list = combine_and_project.execute(marg_cpt_set, hard_variables);
907 if (new_cpt_list.size() != 1) {
908 for (
const auto pot: new_cpt_list)
909 if (!marg_cpt_set.contains(pot))
delete pot;
912 "the projection of a tensor containing " <<
"hard evidence is empty!");
914 auto new_pot =
const_cast< Tensor< GUM_SCALAR >*
>(*(new_cpt_list.begin()));
915 auto projected_pot =
new ScheduleMultiDim< Tensor< GUM_SCALAR > >(std::move(*new_pot));
918 _clique_tensors_[_node_to_clique_[node]].insert(projected_pot);
919 _node_to_hard_ev_projected_CPTs_.insert(node, projected_pot);
929 MultiDimCombinationDefault< Tensor< GUM_SCALAR > > fast_combination(_combination_op_);
930 for (
const auto& xpotset: _clique_tensors_) {
931 const auto& potset = xpotset.second;
932 if (potset.size() > 0) {
937 if (potset.size() == 1) {
938 _clique_ss_tensor_[xpotset.first] = *(potset.cbegin());
940 _TensorSet_ p_potset(potset.size());
941 for (
const auto pot: potset)
943 &(
static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(pot)->multiDim()));
945 Tensor< GUM_SCALAR >* joint
946 =
const_cast< Tensor< GUM_SCALAR >*
>(fast_combination.execute(p_potset));
947 _clique_ss_tensor_[xpotset.first]
948 =
new ScheduleMultiDim< Tensor< GUM_SCALAR > >(std::move(*joint));
956 template < GUM_Numeric GUM_SCALAR >
957 void ShaferShenoyInference< GUM_SCALAR >::_initializeJTCliques_(Schedule& schedule) {
958 const auto& bn = this->BN();
959 const DAG& dag = bn.internalDag();
965 const NodeProperty< const Tensor< GUM_SCALAR >* >& evidence = this->evidence();
966 const NodeProperty< Idx >& hard_evidence = this->hardEvidence();
968 for (
const auto node: dag) {
969 if (_graph_.exists(node) || _hard_ev_nodes_.contains(node)) {
970 const Tensor< GUM_SCALAR >& cpt = bn.cpt(node);
974 const auto& variables = cpt.variablesSequence();
975 bool graph_contains_nodes =
false;
976 for (
const auto var: variables) {
977 NodeId xnode = bn.nodeId(*var);
978 if (_hard_ev_nodes_.contains(xnode)) hard_nodes.insert(xnode);
979 else if (_graph_.exists(xnode)) graph_contains_nodes =
true;
985 if (hard_nodes.empty()) {
986 auto sched_cpt =
new ScheduleMultiDim< Tensor< GUM_SCALAR > >(cpt,
false);
987 _clique_tensors_[_node_to_clique_[node]].insert(sched_cpt);
993 if (hard_nodes.size() == variables.size()) {
994 Instantiation inst(cpt);
995 for (Size i = 0; i < hard_nodes.size(); ++i) {
996 inst.chgVal(*variables[i], hard_evidence[bn.nodeId(*(variables[i]))]);
998 _constants_.insert(node, cpt.get(inst));
1003 if (!graph_contains_nodes)
continue;
1007 _ScheduleMultiDimSet_ marg_cpt_set(1 + hard_nodes.size());
1008 const IScheduleMultiDim* sched_cpt
1009 = schedule.insertTable< Tensor< GUM_SCALAR > >(cpt,
false);
1010 marg_cpt_set.insert(sched_cpt);
1012 for (
const auto xnode: hard_nodes) {
1013 const IScheduleMultiDim* pot
1014 = schedule.insertTable< Tensor< GUM_SCALAR > >(*evidence[xnode],
false);
1015 marg_cpt_set.insert(pot);
1016 hard_variables.
insert(&(bn.variable(xnode)));
1020 MultiDimCombineAndProjectDefault< Tensor< GUM_SCALAR > > combine_and_project(
1024 _ScheduleMultiDimSet_ new_cpt_list
1025 = combine_and_project.schedule(schedule, marg_cpt_set, hard_variables);
1028 if (new_cpt_list.size() != 1) {
1030 "the projection of a tensor containing " <<
"hard evidence is empty!");
1032 auto projected_pot =
const_cast< ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
1033 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
1034 *new_cpt_list.begin()));
1035 const_cast< ScheduleOperator*
>(schedule.scheduleMultiDimCreator(projected_pot))
1036 ->makeResultsPersistent(
true);
1037 _clique_tensors_[_node_to_clique_[node]].insert(projected_pot);
1038 _node_to_hard_ev_projected_CPTs_.insert(node, projected_pot);
1043 this->scheduler().execute(schedule);
1050 MultiDimCombinationDefault< Tensor< GUM_SCALAR > > fast_combination(_combination_op_);
1051 for (
const auto& xpotset: _clique_tensors_) {
1052 const auto& potset = xpotset.second;
1053 if (potset.size() > 0) {
1058 if (potset.size() == 1) {
1059 _clique_ss_tensor_[xpotset.first] = *(potset.cbegin());
1062 for (
const auto pot: potset) {
1063 schedule.emplaceScheduleMultiDim(*pot);
1066 auto joint =
const_cast< ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
1067 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
1068 fast_combination.schedule(schedule, potset)));
1069 const_cast< ScheduleOperator*
>(schedule.scheduleMultiDimCreator(joint))
1070 ->makeResultsPersistent(
true);
1071 _clique_ss_tensor_[xpotset.first] = joint;
1075 this->scheduler().execute(schedule);
1079 template < GUM_Numeric GUM_SCALAR >
1080 void ShaferShenoyInference< GUM_SCALAR >::updateOutdatedStructure_() {
1082 if (_isNewJTNeeded_()) {
1088 updateOutdatedTensors_();
1093 template < GUM_Numeric GUM_SCALAR >
1094 void ShaferShenoyInference< GUM_SCALAR >::_diffuseMessageInvalidations_(
1097 NodeSet& invalidated_cliques) {
1099 invalidated_cliques.insert(to_id);
1102 const Arc arc(from_id, to_id);
1103 bool& message_computed = _messages_computed_[arc];
1104 if (message_computed) {
1105 message_computed =
false;
1106 _separator_tensors_[arc] =
nullptr;
1107 if (_arc_to_created_tensors_.exists(arc)) {
1108 delete _arc_to_created_tensors_[arc];
1109 _arc_to_created_tensors_.erase(arc);
1113 for (
const auto node_id: _JT_->neighbours(to_id)) {
1114 if (node_id != from_id) _diffuseMessageInvalidations_(to_id, node_id, invalidated_cliques);
1121 template < GUM_Numeric GUM_SCALAR >
1122 void ShaferShenoyInference< GUM_SCALAR >::updateOutdatedTensors_() {
1127 NodeProperty< bool > ss_tensor_to_deallocate(_clique_tensors_.size());
1128 for (
const auto& potset: _clique_tensors_) {
1129 ss_tensor_to_deallocate.insert(potset.first, (potset.second.size() > 1));
1139 NodeSet hard_nodes_changed(_hard_ev_nodes_.size());
1140 for (
const auto node: _hard_ev_nodes_)
1141 if (_evidence_changes_.exists(node)) hard_nodes_changed.insert(node);
1143 NodeSet nodes_with_projected_CPTs_changed;
1144 const auto& bn = this->BN();
1145 for (
auto pot_iter = _node_to_hard_ev_projected_CPTs_.beginSafe();
1146 pot_iter != _node_to_hard_ev_projected_CPTs_.endSafe();
1148 for (
const auto var: bn.cpt(pot_iter.key()).variablesSequence()) {
1149 if (hard_nodes_changed.contains(bn.nodeId(*var))) {
1150 nodes_with_projected_CPTs_changed.insert(pot_iter.key());
1151 delete pot_iter.val();
1152 _clique_tensors_[_node_to_clique_[pot_iter.key()]].erase(pot_iter.val());
1153 _node_to_hard_ev_projected_CPTs_.erase(pot_iter);
1167 NodeSet invalidated_cliques(_JT_->size());
1168 for (
const auto& pair: _evidence_changes_) {
1169 if (
auto ptr_clique = _node_to_clique_.tryGet(pair.first)) {
1170 const auto clique = *ptr_clique;
1171 invalidated_cliques.insert(clique);
1172 for (
const auto neighbor: _JT_->neighbours(clique)) {
1173 _diffuseMessageInvalidations_(clique, neighbor, invalidated_cliques);
1180 for (
const auto node: nodes_with_projected_CPTs_changed) {
1181 const auto clique = _node_to_clique_[node];
1182 invalidated_cliques.insert(clique);
1183 for (
const auto neighbor: _JT_->neighbours(clique)) {
1184 _diffuseMessageInvalidations_(clique, neighbor, invalidated_cliques);
1190 for (
const auto clique: invalidated_cliques) {
1191 if (ss_tensor_to_deallocate[clique]) {
1192 delete _clique_ss_tensor_[clique];
1193 _clique_ss_tensor_[clique] =
nullptr;
1202 for (
auto iter = _target_posteriors_.beginSafe(); iter != _target_posteriors_.endSafe();
1204 if (_graph_.exists(iter.key())
1205 && (invalidated_cliques.exists(_node_to_clique_[iter.key()]))) {
1207 _target_posteriors_.erase(iter);
1212 for (
auto iter = _target_posteriors_.beginSafe(); iter != _target_posteriors_.endSafe();
1214 if (hard_nodes_changed.contains(iter.key())) {
1216 _target_posteriors_.erase(iter);
1222 for (
auto iter = _joint_target_posteriors_.beginSafe();
1223 iter != _joint_target_posteriors_.endSafe();
1225 if (invalidated_cliques.exists(_joint_target_to_clique_[iter.key()])) {
1227 _joint_target_posteriors_.erase(iter);
1230 bool has_unevidenced_node =
false;
1231 for (
const auto node: iter.key()) {
1232 if (!hard_nodes_changed.exists(node)) {
1233 has_unevidenced_node =
true;
1237 if (!has_unevidenced_node) {
1239 _joint_target_posteriors_.erase(iter);
1246 for (
const auto& pot_pair: _node_to_soft_evidence_) {
1247 delete pot_pair.second;
1248 _clique_tensors_[_node_to_clique_[pot_pair.first]].erase(pot_pair.second);
1250 _node_to_soft_evidence_.clear();
1252 const auto& evidence = this->evidence();
1253 for (
const auto node: this->softEvidenceNodes()) {
1254 auto ev_pot =
new ScheduleMultiDim< Tensor< GUM_SCALAR > >(*evidence[node],
false);
1255 _node_to_soft_evidence_.insert(node, ev_pot);
1256 _clique_tensors_[_node_to_clique_[node]].insert(ev_pot);
1266 if (_use_schedules_) {
1268 for (
const auto node: nodes_with_projected_CPTs_changed) {
1270 const Tensor< GUM_SCALAR >& cpt = bn.cpt(node);
1271 const auto& variables = cpt.variablesSequence();
1272 _ScheduleMultiDimSet_ marg_cpt_set;
1273 const auto sched_cpt = schedule.insertTable< Tensor< GUM_SCALAR > >(cpt,
false);
1274 marg_cpt_set.insert(sched_cpt);
1277 for (
const auto var: variables) {
1278 NodeId xnode = bn.nodeId(*var);
1279 if (_hard_ev_nodes_.exists(xnode)) {
1280 const auto pot = schedule.insertTable< Tensor< GUM_SCALAR > >(*evidence[xnode],
false);
1281 marg_cpt_set.insert(pot);
1282 hard_variables.
insert(var);
1287 MultiDimCombineAndProjectDefault< Tensor< GUM_SCALAR > > combine_and_project(
1291 _ScheduleMultiDimSet_ new_cpt_list
1292 = combine_and_project.schedule(schedule, marg_cpt_set, hard_variables);
1295 if (new_cpt_list.size() != 1) {
1297 "the projection of a tensor containing " <<
"hard evidence is empty!");
1299 auto projected_pot =
const_cast< ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
1300 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(*new_cpt_list.begin()));
1301 const_cast< ScheduleOperator*
>(schedule.scheduleMultiDimCreator(projected_pot))
1302 ->makeResultsPersistent(
true);
1303 _clique_tensors_[_node_to_clique_[node]].insert(projected_pot);
1304 _node_to_hard_ev_projected_CPTs_.insert(node, projected_pot);
1310 MultiDimCombinationDefault< Tensor< GUM_SCALAR > > fast_combination(_combination_op_);
1311 for (
const auto clique: invalidated_cliques) {
1312 const auto& potset = _clique_tensors_[clique];
1314 if (potset.size() > 0) {
1319 if (potset.size() == 1) {
1320 _clique_ss_tensor_[clique] = *(potset.cbegin());
1322 for (
const auto pot: potset)
1323 if (!schedule.existsScheduleMultiDim(pot->id()))
1324 schedule.emplaceScheduleMultiDim(*pot);
1325 auto joint =
const_cast< ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
1326 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
1327 fast_combination.schedule(schedule, potset)));
1328 const_cast< ScheduleOperator*
>(schedule.scheduleMultiDimCreator(joint))
1329 ->makeResultsPersistent(
true);
1330 _clique_ss_tensor_[clique] = joint;
1334 this->scheduler().execute(schedule);
1336 for (
const auto node: nodes_with_projected_CPTs_changed) {
1338 const Tensor< GUM_SCALAR >& cpt = bn.cpt(node);
1339 const auto& variables = cpt.variablesSequence();
1340 _TensorSet_ marg_cpt_set(1 + variables.size());
1341 marg_cpt_set.insert(&cpt);
1344 for (
const auto var: variables) {
1345 NodeId xnode = bn.nodeId(*var);
1346 if (_hard_ev_nodes_.exists(xnode)) {
1347 marg_cpt_set.insert(evidence[xnode]);
1348 hard_variables.
insert(var);
1353 MultiDimCombineAndProjectDefault< Tensor< GUM_SCALAR > > combine_and_project(
1357 _TensorSet_ new_cpt_list = combine_and_project.execute(marg_cpt_set, hard_variables);
1360 if (new_cpt_list.size() != 1) {
1361 for (
const auto pot: new_cpt_list)
1362 if (!marg_cpt_set.contains(pot))
delete pot;
1365 "the projection of a tensor containing " <<
"hard evidence is empty!");
1367 Tensor< GUM_SCALAR >* xprojected_pot
1368 =
const_cast< Tensor< GUM_SCALAR >*
>(*new_cpt_list.begin());
1370 =
new ScheduleMultiDim< Tensor< GUM_SCALAR > >(std::move(*xprojected_pot));
1371 delete xprojected_pot;
1372 _clique_tensors_[_node_to_clique_[node]].insert(projected_pot);
1373 _node_to_hard_ev_projected_CPTs_.insert(node, projected_pot);
1379 MultiDimCombinationDefault< Tensor< GUM_SCALAR > > fast_combination(_combination_op_);
1380 for (
const auto clique: invalidated_cliques) {
1381 const auto& potset = _clique_tensors_[clique];
1383 if (potset.size() > 0) {
1388 if (potset.size() == 1) {
1389 _clique_ss_tensor_[clique] = *(potset.cbegin());
1391 _TensorSet_ p_potset(potset.size());
1392 for (
const auto pot: potset)
1394 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(pot)->multiDim()));
1396 Tensor< GUM_SCALAR >* joint
1397 =
const_cast< Tensor< GUM_SCALAR >*
>(fast_combination.execute(p_potset));
1398 _clique_ss_tensor_[clique]
1399 =
new ScheduleMultiDim< Tensor< GUM_SCALAR > >(std::move(*joint));
1407 const auto& hard_evidence = this->hardEvidence();
1408 for (
auto& node_cst: _constants_) {
1409 const Tensor< GUM_SCALAR >& cpt = bn.cpt(node_cst.first);
1410 const auto& variables = cpt.variablesSequence();
1412 for (
const auto var: variables)
1414 for (
const auto var: variables) {
1415 inst.chgVal(*var, hard_evidence[bn.nodeId(*var)]);
1417 node_cst.second = cpt.get(inst);
1421 _evidence_changes_.clear();
1425 template < GUM_Numeric GUM_SCALAR >
1426 void ShaferShenoyInference< GUM_SCALAR >::_computeJoinTreeRoots_() {
1431 for (
const auto node: this->targets()) {
1432 if (
auto ptr_clique = _node_to_clique_.tryGet(node)) { clique_targets.insert(*ptr_clique); }
1434 for (
const auto& set: this->jointTargets()) {
1435 if (
auto ptr_clique = _joint_target_to_clique_.tryGet(set)) {
1436 clique_targets.insert(*ptr_clique);
1441 std::vector< std::pair< NodeId, Size > > possible_roots(clique_targets.size());
1442 const auto& bn = this->BN();
1444 for (
const auto clique_id: clique_targets) {
1445 const auto& clique = _JT_->clique(clique_id);
1447 for (
const auto node: clique) {
1448 dom_size *= bn.variable(node).domainSize();
1450 possible_roots[i] = std::pair< NodeId, Size >(clique_id, dom_size);
1455 std::sort(possible_roots.begin(),
1456 possible_roots.end(),
1457 [](
const std::pair< NodeId, Size >& a,
const std::pair< NodeId, Size >& b) ->
bool {
1458 return a.second < b.second;
1462 NodeProperty< bool > marked = _JT_->nodesPropertyFromVal(
false);
1463 std::function< void(NodeId, NodeId) > diffuse_marks
1464 = [&marked, &diffuse_marks,
this](NodeId node, NodeId from) {
1465 if (!marked[node]) {
1466 marked[node] =
true;
1467 for (
const auto neigh: _JT_->neighbours(node))
1468 if ((neigh != from) && !marked[neigh]) diffuse_marks(neigh, node);
1472 for (
const auto& xclique: possible_roots) {
1473 NodeId clique = xclique.first;
1474 if (!marked[clique]) {
1475 _roots_.insert(clique);
1476 diffuse_marks(clique, clique);
1482 template < GUM_Numeric GUM_SCALAR >
1483 void ShaferShenoyInference< GUM_SCALAR >::_findRelevantTensorsGetAll_(
1484 Set< const IScheduleMultiDim* >& pot_list,
1488 template < GUM_Numeric GUM_SCALAR >
1489 void ShaferShenoyInference< GUM_SCALAR >::_findRelevantTensorsWithdSeparation_(
1490 Set< const IScheduleMultiDim* >& pot_list,
1494 const auto& bn = this->BN();
1495 for (
const auto var: kept_vars) {
1496 kept_ids.insert(bn.nodeId(*var));
1501 BayesBall::requisiteNodes(bn.internalDag(),
1503 this->hardEvidenceNodes(),
1504 this->softEvidenceNodes(),
1506 for (
auto iter = pot_list.beginSafe(); iter != pot_list.endSafe(); ++iter) {
1507 const Sequence< const DiscreteVariable* >& vars = (*iter)->variablesSequence();
1509 for (
const auto var: vars) {
1510 if (requisite_nodes.exists(bn.nodeId(*var))) {
1516 if (!found) { pot_list.erase(iter); }
1521 template < GUM_Numeric GUM_SCALAR >
1522 void ShaferShenoyInference< GUM_SCALAR >::_findRelevantTensorsWithdSeparation2_(
1523 Set< const IScheduleMultiDim* >& pot_list,
1527 const auto& bn = this->BN();
1528 for (
const auto var: kept_vars) {
1529 kept_ids.insert(bn.nodeId(*var));
1533 BayesBall::relevantTensors(bn,
1535 this->hardEvidenceNodes(),
1536 this->softEvidenceNodes(),
1541 template < GUM_Numeric GUM_SCALAR >
1542 void ShaferShenoyInference< GUM_SCALAR >::_findRelevantTensorsWithdSeparation3_(
1543 Set< const IScheduleMultiDim* >& pot_list,
1547 const auto& bn = this->BN();
1548 for (
const auto var: kept_vars) {
1549 kept_ids.insert(bn.nodeId(*var));
1553 dSeparationAlgorithm dsep;
1554 dsep.relevantTensors(bn,
1556 this->hardEvidenceNodes(),
1557 this->softEvidenceNodes(),
1562 template < GUM_Numeric GUM_SCALAR >
1563 void ShaferShenoyInference< GUM_SCALAR >::_findRelevantTensorsXX_(
1564 Set< const IScheduleMultiDim* >& pot_list,
1566 switch (_find_relevant_tensor_type_) {
1567 case RelevantTensorsFinderType::DSEP_BAYESBALL_TENSORS :
1568 _findRelevantTensorsWithdSeparation2_(pot_list, kept_vars);
1571 case RelevantTensorsFinderType::DSEP_BAYESBALL_NODES :
1572 _findRelevantTensorsWithdSeparation_(pot_list, kept_vars);
1575 case RelevantTensorsFinderType::DSEP_KOLLER_FRIEDMAN_2009 :
1576 _findRelevantTensorsWithdSeparation3_(pot_list, kept_vars);
1579 case RelevantTensorsFinderType::FIND_ALL :
1580 _findRelevantTensorsGetAll_(pot_list, kept_vars);
1588 template < GUM_Numeric GUM_SCALAR >
1589 Set< const IScheduleMultiDim* >
1590 ShaferShenoyInference< GUM_SCALAR >::_removeBarrenVariables_(Schedule& schedule,
1591 _ScheduleMultiDimSet_& pot_list,
1596 for (
auto iter = the_del_vars.
beginSafe(); iter != the_del_vars.
endSafe(); ++iter) {
1597 NodeId
id = this->BN().nodeId(**iter);
1598 if (this->hardEvidenceNodes().exists(
id) || this->softEvidenceNodes().exists(
id)) {
1599 the_del_vars.
erase(iter);
1604 HashTable< const DiscreteVariable*, _ScheduleMultiDimSet_ > var2pots(the_del_vars.
size());
1605 _ScheduleMultiDimSet_ empty_pot_set;
1606 for (
const auto pot: pot_list) {
1607 const auto& vars = pot->variablesSequence();
1608 for (
const auto var: vars) {
1609 if (the_del_vars.
exists(var)) {
1610 if (!var2pots.exists(var)) { var2pots.insert(var, empty_pot_set); }
1611 var2pots[var].insert(pot);
1618 HashTable< const IScheduleMultiDim*, gum::VariableSet > pot2barren_var;
1620 for (
const auto& elt: var2pots) {
1621 if (elt.second.size() == 1) {
1622 const IScheduleMultiDim* pot = *(elt.second.begin());
1623 if (!pot2barren_var.exists(pot)) { pot2barren_var.insert(pot, empty_var_set); }
1624 pot2barren_var[pot].insert(elt.first);
1631 MultiDimProjection< Tensor< GUM_SCALAR > > projector(_projection_op_);
1632 _ScheduleMultiDimSet_ projected_pots;
1633 for (
const auto& elt: pot2barren_var) {
1635 const IScheduleMultiDim* pot = elt.first;
1636 pot_list.erase(pot);
1640 if (pot->variablesSequence().size() != elt.second.size()) {
1641 const IScheduleMultiDim* new_pot = projector.schedule(schedule, pot, elt.second);
1645 pot_list.insert(new_pot);
1646 projected_pots.insert(new_pot);
1650 return projected_pots;
1654 template < GUM_Numeric GUM_SCALAR >
1655 Set< const Tensor< GUM_SCALAR >* >
1656 ShaferShenoyInference< GUM_SCALAR >::_removeBarrenVariables_(_TensorSet_& pot_list,
1661 for (
auto iter = the_del_vars.
beginSafe(); iter != the_del_vars.
endSafe(); ++iter) {
1662 NodeId
id = this->BN().nodeId(**iter);
1663 if (this->hardEvidenceNodes().exists(
id) || this->softEvidenceNodes().exists(
id)) {
1664 the_del_vars.
erase(iter);
1669 HashTable< const DiscreteVariable*, _TensorSet_ > var2pots(the_del_vars.
size());
1670 _TensorSet_ empty_pot_set;
1671 for (
const auto pot: pot_list) {
1672 const Sequence< const DiscreteVariable* >& vars = pot->variablesSequence();
1673 for (
const auto var: vars) {
1674 if (the_del_vars.
exists(var)) {
1675 if (!var2pots.exists(var)) { var2pots.insert(var, empty_pot_set); }
1676 var2pots[var].insert(pot);
1685 for (
const auto& elt: var2pots) {
1686 if (elt.second.size() == 1) {
1687 const Tensor< GUM_SCALAR >* pot = *(elt.second.begin());
1688 if (!pot2barren_var.exists(pot)) { pot2barren_var.insert(pot, empty_var_set); }
1689 pot2barren_var[pot].insert(elt.first);
1696 MultiDimProjection< Tensor< GUM_SCALAR > > projector(_projection_op_);
1697 _TensorSet_ projected_pots;
1698 for (
const auto& elt: pot2barren_var) {
1700 const Tensor< GUM_SCALAR >* pot = elt.first;
1701 pot_list.erase(pot);
1705 if (pot->variablesSequence().size() != elt.second.size()) {
1706 const Tensor< GUM_SCALAR >* new_pot = projector.execute(*pot, elt.second);
1707 pot_list.insert(new_pot);
1708 projected_pots.insert(new_pot);
1712 return projected_pots;
1716 template < GUM_Numeric GUM_SCALAR >
1717 void ShaferShenoyInference< GUM_SCALAR >::_collectMessage_(Schedule& schedule,
1720 for (
const auto other: _JT_->neighbours(
id)) {
1721 if ((other != from) && !_messages_computed_[
Arc(other,
id)])
1722 _collectMessage_(schedule, other,
id);
1725 if ((
id != from) && !_messages_computed_[
Arc(
id, from)]) {
1726 _produceMessage_(schedule,
id, from);
1731 template < GUM_Numeric GUM_SCALAR >
1732 void ShaferShenoyInference< GUM_SCALAR >::_collectMessage_(NodeId
id, NodeId from) {
1733 for (
const auto other: _JT_->neighbours(
id)) {
1734 if ((other != from) && !_messages_computed_[
Arc(other,
id)]) _collectMessage_(other,
id);
1737 if ((
id != from) && !_messages_computed_[
Arc(
id, from)]) { _produceMessage_(
id, from); }
1741 template < GUM_Numeric GUM_SCALAR >
1742 const IScheduleMultiDim* ShaferShenoyInference< GUM_SCALAR >::_marginalizeOut_(
1744 Set< const IScheduleMultiDim* > pot_list,
1751 if (pot_list.empty()) {
1752 return new ScheduleMultiDim< Tensor< GUM_SCALAR > >(Tensor< GUM_SCALAR >());
1757 for (
const auto pot: pot_list) {
1758 if (!schedule.existsScheduleMultiDim(pot->id())) schedule.emplaceScheduleMultiDim(*pot);
1763 _ScheduleMultiDimSet_ barren_projected_tensors;
1764 if (_barren_nodes_type_ == FindBarrenNodesType::FIND_BARREN_NODES) {
1765 barren_projected_tensors = _removeBarrenVariables_(schedule, pot_list, del_vars);
1769 _ScheduleMultiDimSet_ new_pot_list;
1770 if (pot_list.size() == 1) {
1771 MultiDimProjection< Tensor< GUM_SCALAR > > projector(_projection_op_);
1772 auto xpot = projector.schedule(schedule, *(pot_list.begin()), del_vars);
1773 new_pot_list.insert(xpot);
1774 }
else if (pot_list.size() > 1) {
1777 MultiDimCombineAndProjectDefault< Tensor< GUM_SCALAR > > combine_and_project(_combination_op_,
1779 new_pot_list = combine_and_project.schedule(schedule, pot_list, del_vars);
1785 for (
auto barren_pot: barren_projected_tensors) {
1786 if (!new_pot_list.exists(barren_pot))
1787 schedule.emplaceDeletion(
1788 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>& >(*barren_pot));
1792 if (new_pot_list.empty())
1793 return new ScheduleMultiDim< Tensor< GUM_SCALAR > >(Tensor< GUM_SCALAR >());
1794 if (new_pot_list.size() == 1)
return *(new_pot_list.begin());
1795 MultiDimCombinationDefault< Tensor< GUM_SCALAR > > fast_combination(_combination_op_);
1796 return fast_combination.schedule(schedule, new_pot_list);
1800 template < GUM_Numeric GUM_SCALAR >
1801 const IScheduleMultiDim* ShaferShenoyInference< GUM_SCALAR >::_marginalizeOut_(
1802 Set< const IScheduleMultiDim* >& pot_list,
1806 if (pot_list.empty()) {
1807 return new ScheduleMultiDim< Tensor< GUM_SCALAR > >(Tensor< GUM_SCALAR >());
1810 _TensorSet_ xpot_list(pot_list.size());
1811 for (
auto pot: pot_list)
1813 &(
static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(pot)->multiDim()));
1820 _TensorSet_ barren_projected_tensors;
1821 if (_barren_nodes_type_ == FindBarrenNodesType::FIND_BARREN_NODES) {
1822 barren_projected_tensors = _removeBarrenVariables_(xpot_list, del_vars);
1826 _TensorSet_ xnew_pot_list;
1827 if (xpot_list.size() == 1) {
1828 MultiDimProjection< Tensor< GUM_SCALAR > > projector(_projection_op_);
1829 auto xpot = projector.execute(**(xpot_list.begin()), del_vars);
1830 xnew_pot_list.insert(xpot);
1831 }
else if (xpot_list.size() > 1) {
1834 MultiDimCombineAndProjectDefault< Tensor< GUM_SCALAR > > combine_and_project(_combination_op_,
1836 xnew_pot_list = combine_and_project.execute(xpot_list, del_vars);
1840 const Tensor< GUM_SCALAR >* xres_pot;
1841 ScheduleMultiDim< Tensor< GUM_SCALAR > >* res_pot;
1842 if (xnew_pot_list.size() == 1) {
1843 xres_pot = *(xnew_pot_list.begin());
1844 }
else if (xnew_pot_list.size() > 1) {
1847 MultiDimCombinationDefault< Tensor< GUM_SCALAR > > fast_combination(_combination_op_);
1848 xres_pot = fast_combination.execute(xnew_pot_list);
1849 for (
const auto pot: xnew_pot_list) {
1850 if (!xpot_list.contains(pot) && (pot != xres_pot))
delete pot;
1853 xres_pot =
new Tensor< GUM_SCALAR >();
1857 if (xpot_list.contains(xres_pot))
1858 res_pot =
new ScheduleMultiDim< Tensor< GUM_SCALAR > >(*xres_pot,
false);
1860 res_pot =
new ScheduleMultiDim< Tensor< GUM_SCALAR > >(
1861 std::move(
const_cast< Tensor< GUM_SCALAR >&
>(*xres_pot)));
1868 for (
const auto barren_pot: barren_projected_tensors) {
1869 if (!xnew_pot_list.exists(barren_pot))
delete barren_pot;
1876 template < GUM_Numeric GUM_SCALAR >
1877 void ShaferShenoyInference< GUM_SCALAR >::_produceMessage_(Schedule& schedule,
1881 _ScheduleMultiDimSet_ pot_list;
1882 if (_clique_ss_tensor_[from_id] !=
nullptr) pot_list.insert(_clique_ss_tensor_[from_id]);
1885 for (
const auto other_id: _JT_->neighbours(from_id)) {
1886 if (other_id != to_id) {
1887 const auto separator_pot = _separator_tensors_[
Arc(other_id, from_id)];
1888 if (separator_pot !=
nullptr) pot_list.insert(separator_pot);
1893 const NodeSet& from_clique = _JT_->clique(from_id);
1894 const NodeSet& separator = _JT_->separator(from_id, to_id);
1897 const auto& bn = this->BN();
1899 for (
const auto node: from_clique) {
1900 if (!separator.contains(node)) {
1901 del_vars.
insert(&(bn.variable(node)));
1903 kept_vars.
insert(&(bn.variable(node)));
1909 const IScheduleMultiDim* new_pot = _marginalizeOut_(schedule, pot_list, del_vars, kept_vars);
1912 const Arc arc(from_id, to_id);
1913 if (!pot_list.exists(new_pot)) {
1914 if (!_arc_to_created_tensors_.exists(arc)) {
1915 _arc_to_created_tensors_.insert(arc, new_pot);
1918 auto op = schedule.scheduleMultiDimCreator(new_pot);
1919 if (op !=
nullptr)
const_cast< ScheduleOperator*
>(op)->makeResultsPersistent(
true);
1923 _separator_tensors_[arc] = new_pot;
1924 _messages_computed_[arc] =
true;
1928 template < GUM_Numeric GUM_SCALAR >
1929 void ShaferShenoyInference< GUM_SCALAR >::_produceMessage_(NodeId from_id, NodeId to_id) {
1931 _ScheduleMultiDimSet_ pot_list;
1932 if (_clique_ss_tensor_[from_id] !=
nullptr) pot_list.insert(_clique_ss_tensor_[from_id]);
1935 for (
const auto other_id: _JT_->neighbours(from_id)) {
1936 if (other_id != to_id) {
1937 const auto separator_pot = _separator_tensors_[
Arc(other_id, from_id)];
1938 if (separator_pot !=
nullptr) pot_list.insert(separator_pot);
1943 const NodeSet& from_clique = _JT_->clique(from_id);
1944 const NodeSet& separator = _JT_->separator(from_id, to_id);
1947 const auto& bn = this->BN();
1949 for (
const auto node: from_clique) {
1950 if (!separator.contains(node)) {
1951 del_vars.
insert(&(bn.variable(node)));
1953 kept_vars.
insert(&(bn.variable(node)));
1959 const IScheduleMultiDim* new_pot = _marginalizeOut_(pot_list, del_vars, kept_vars);
1962 const Arc arc(from_id, to_id);
1963 if (!pot_list.exists(new_pot)) {
1964 if (!_arc_to_created_tensors_.exists(arc)) { _arc_to_created_tensors_.insert(arc, new_pot); }
1967 _separator_tensors_[arc] = new_pot;
1968 _messages_computed_[arc] =
true;
1972 template < GUM_Numeric GUM_SCALAR >
1973 void ShaferShenoyInference< GUM_SCALAR >::makeInference_() {
1974 if (_use_schedules_) {
1978 for (
const auto node: this->targets()) {
1982 if (_graph_.exists(node)) {
1983 _collectMessage_(schedule, _node_to_clique_[node], _node_to_clique_[node]);
1991 for (
const auto& set: _joint_target_to_clique_)
1992 _collectMessage_(schedule, set.second, set.second);
1995 this->scheduler().execute(schedule);
1998 for (
const auto node: this->targets()) {
2002 if (_graph_.exists(node)) {
2003 _collectMessage_(_node_to_clique_[node], _node_to_clique_[node]);
2011 for (
const auto& set: _joint_target_to_clique_)
2012 _collectMessage_(set.second, set.second);
2017 template < GUM_Numeric GUM_SCALAR >
2018 Tensor< GUM_SCALAR >*
2019 ShaferShenoyInference< GUM_SCALAR >::unnormalizedJointPosterior_(NodeId
id) {
2020 if (_use_schedules_) {
2022 return _unnormalizedJointPosterior_(schedule,
id);
2024 return _unnormalizedJointPosterior_(
id);
2029 template < GUM_Numeric GUM_SCALAR >
2030 Tensor< GUM_SCALAR >*
2031 ShaferShenoyInference< GUM_SCALAR >::_unnormalizedJointPosterior_(Schedule& schedule,
2033 const auto& bn = this->BN();
2037 if (this->hardEvidenceNodes().
contains(
id)) {
2038 return new Tensor< GUM_SCALAR >(*(this->evidence()[
id]));
2041 auto& scheduler = this->scheduler();
2045 const NodeId clique_of_id = _node_to_clique_[id];
2046 _collectMessage_(schedule, clique_of_id, clique_of_id);
2051 _ScheduleMultiDimSet_ pot_list;
2052 if (_clique_ss_tensor_[clique_of_id] !=
nullptr)
2053 pot_list.insert(_clique_ss_tensor_[clique_of_id]);
2056 for (
const auto other: _JT_->neighbours(clique_of_id))
2057 pot_list.insert(_separator_tensors_[
Arc(other, clique_of_id)]);
2060 const NodeSet& nodes = _JT_->clique(clique_of_id);
2063 for (
const auto node: nodes) {
2064 if (node !=
id) del_vars.
insert(&(bn.variable(node)));
2069 auto resulting_pot =
const_cast< ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
2070 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
2071 _marginalizeOut_(schedule, pot_list, del_vars, kept_vars)));
2072 Tensor< GUM_SCALAR >* joint =
nullptr;
2074 scheduler.execute(schedule);
2078 if (pot_list.exists(resulting_pot)) {
2079 joint =
new Tensor< GUM_SCALAR >(resulting_pot->multiDim());
2081 joint = resulting_pot->exportMultiDim();
2087 bool nonzero_found =
false;
2088 for (Instantiation inst(*joint); !inst.end(); ++inst) {
2089 if (joint->get(inst)) {
2090 nonzero_found =
true;
2094 if (!nonzero_found) {
2098 "some evidence entered into the Bayes "
2099 "net are incompatible (their joint proba = 0)");
2105 template < GUM_Numeric GUM_SCALAR >
2106 Tensor< GUM_SCALAR >*
2107 ShaferShenoyInference< GUM_SCALAR >::_unnormalizedJointPosterior_(NodeId
id) {
2108 const auto& bn = this->BN();
2112 if (this->hardEvidenceNodes().
contains(
id)) {
2113 return new Tensor< GUM_SCALAR >(*(this->evidence()[
id]));
2118 NodeId clique_of_id = _node_to_clique_[id];
2119 _collectMessage_(clique_of_id, clique_of_id);
2124 _ScheduleMultiDimSet_ pot_list;
2125 if (_clique_ss_tensor_[clique_of_id] !=
nullptr)
2126 pot_list.insert(_clique_ss_tensor_[clique_of_id]);
2129 for (
const auto other: _JT_->neighbours(clique_of_id))
2130 pot_list.insert(_separator_tensors_[
Arc(other, clique_of_id)]);
2133 const NodeSet& nodes = _JT_->clique(clique_of_id);
2136 for (
const auto node: nodes) {
2137 if (node !=
id) del_vars.
insert(&(bn.variable(node)));
2142 auto resulting_pot =
const_cast< ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
2143 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
2144 _marginalizeOut_(pot_list, del_vars, kept_vars)));
2145 Tensor< GUM_SCALAR >* joint =
nullptr;
2149 if (pot_list.exists(resulting_pot)) {
2150 joint =
new Tensor< GUM_SCALAR >(resulting_pot->multiDim());
2152 joint = resulting_pot->exportMultiDim();
2153 delete resulting_pot;
2159 bool nonzero_found =
false;
2160 for (Instantiation inst(*joint); !inst.end(); ++inst) {
2161 if (joint->get(inst)) {
2162 nonzero_found =
true;
2166 if (!nonzero_found) {
2170 "some evidence entered into the Bayes "
2171 "net are incompatible (their joint proba = 0)");
2177 template < GUM_Numeric GUM_SCALAR >
2178 const Tensor< GUM_SCALAR >& ShaferShenoyInference< GUM_SCALAR >::posterior_(NodeId
id) {
2180 if (_target_posteriors_.exists(
id)) {
return *(_target_posteriors_[id]); }
2183 auto joint = unnormalizedJointPosterior_(
id);
2184 if (joint->sum() != 1)
2186 _target_posteriors_.insert(
id, joint);
2192 template < GUM_Numeric GUM_SCALAR >
2193 Tensor< GUM_SCALAR >*
2194 ShaferShenoyInference< GUM_SCALAR >::unnormalizedJointPosterior_(
const NodeSet& set) {
2195 if (_use_schedules_) {
2197 return _unnormalizedJointPosterior_(schedule, set);
2199 return _unnormalizedJointPosterior_(set);
2204 template < GUM_Numeric GUM_SCALAR >
2205 Tensor< GUM_SCALAR >*
2206 ShaferShenoyInference< GUM_SCALAR >::_unnormalizedJointPosterior_(Schedule& schedule,
2207 const NodeSet& set) {
2210 NodeSet targets = set, hard_ev_nodes;
2211 for (
const auto node: this->hardEvidenceNodes()) {
2212 if (targets.contains(node)) {
2213 targets.erase(node);
2214 hard_ev_nodes.insert(node);
2218 auto& scheduler = this->scheduler();
2222 const auto& evidence = this->evidence();
2223 if (targets.empty()) {
2224 if (set.size() == 1) {
2225 return new Tensor< GUM_SCALAR >(*evidence[*set.begin()]);
2227 _ScheduleMultiDimSet_ pot_list;
2228 for (
const auto node: set) {
2229 auto new_pot_ev = schedule.insertTable< Tensor< GUM_SCALAR > >(*evidence[node],
false);
2230 pot_list.insert(new_pot_ev);
2234 MultiDimCombinationDefault< Tensor< GUM_SCALAR > > fast_combination(_combination_op_);
2235 const IScheduleMultiDim* pot = fast_combination.schedule(schedule, pot_list);
2236 auto schedule_pot =
const_cast< ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
2237 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(pot));
2238 scheduler.execute(schedule);
2239 auto result = schedule_pot->exportMultiDim();
2249 NodeId clique_of_set;
2250 if (
auto ptr_clique = _joint_target_to_clique_.tryGet(set)) {
2251 clique_of_set = *ptr_clique;
2258 for (
const auto node: targets) {
2259 if (!_graph_.exists(node)) {
2261 "The variable " << this->BN().variable(node).name() <<
"(" << node
2262 <<
") does not belong to this optimized inference.")
2268 const std::vector< NodeId >& JT_elim_order = _triangulation_->eliminationOrder();
2270 NodeProperty< int > elim_order(Size(JT_elim_order.size()));
2271 for (std::size_t i = std::size_t(0), size = JT_elim_order.size(); i < size; ++i)
2272 elim_order.insert(JT_elim_order[i], (
int)i);
2273 NodeId first_eliminated_node = *(targets.begin());
2274 int elim_number = elim_order[first_eliminated_node];
2275 for (
const auto node: targets) {
2276 if (elim_order[node] < elim_number) {
2277 elim_number = elim_order[node];
2278 first_eliminated_node = node;
2282 clique_of_set = _triangulation_->createdJunctionTreeClique(first_eliminated_node);
2286 const NodeSet& clique_nodes = _JT_->clique(clique_of_set);
2287 for (
const auto node: targets) {
2288 if (!clique_nodes.contains(node)) {
2290 this->BN().names(set) <<
"(" << set <<
")"
2291 <<
" is not addressable in this optimized inference.")
2296 _joint_target_to_clique_.
insert(set, clique_of_set);
2300 _collectMessage_(schedule, clique_of_set, clique_of_set);
2305 _ScheduleMultiDimSet_ pot_list;
2306 if (_clique_ss_tensor_[clique_of_set] !=
nullptr) {
2307 auto pot = _clique_ss_tensor_[clique_of_set];
2308 if (!schedule.existsScheduleMultiDim(pot->id())) schedule.emplaceScheduleMultiDim(*pot);
2309 pot_list.insert(_clique_ss_tensor_[clique_of_set]);
2313 for (
const auto other: _JT_->neighbours(clique_of_set)) {
2314 const auto pot = _separator_tensors_[
Arc(other, clique_of_set)];
2315 if (pot !=
nullptr) pot_list.insert(pot);
2320 const NodeSet& nodes = _JT_->clique(clique_of_set);
2323 const auto& bn = this->BN();
2324 for (
const auto node: nodes) {
2325 if (!targets.contains(node)) {
2326 del_vars.
insert(&(bn.variable(node)));
2328 kept_vars.
insert(&(bn.variable(node)));
2334 const IScheduleMultiDim* new_pot = _marginalizeOut_(schedule, pot_list, del_vars, kept_vars);
2335 scheduler.execute(schedule);
2336 ScheduleMultiDim< Tensor< GUM_SCALAR > >* resulting_pot
2337 =
const_cast< ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
2338 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(new_pot));
2342 Tensor< GUM_SCALAR >* joint =
nullptr;
2343 if (pot_list.exists(resulting_pot)) {
2344 joint =
new Tensor< GUM_SCALAR >(resulting_pot->multiDim());
2346 joint = resulting_pot->exportMultiDim();
2351 bool nonzero_found =
false;
2352 for (Instantiation inst(*joint); !inst.end(); ++inst) {
2353 if ((*joint)[inst]) {
2354 nonzero_found =
true;
2358 if (!nonzero_found) {
2362 "some evidence entered into the Bayes "
2363 "net are incompatible (their joint proba = 0)");
2369 if (!hard_ev_nodes.empty()) {
2370 _TensorSet_ pot_list;
2371 pot_list.insert(joint);
2372 const auto& hard_evidence = this->evidence();
2373 for (
const auto node: hard_ev_nodes)
2374 pot_list.insert(hard_evidence[node]);
2375 MultiDimCombinationDefault< Tensor< GUM_SCALAR > > combine(_combination_op_);
2376 Tensor< GUM_SCALAR >* new_joint = combine.execute(pot_list);
2385 template < GUM_Numeric GUM_SCALAR >
2386 Tensor< GUM_SCALAR >*
2387 ShaferShenoyInference< GUM_SCALAR >::_unnormalizedJointPosterior_(
const NodeSet& set) {
2390 NodeSet targets = set, hard_ev_nodes;
2391 for (
const auto node: this->hardEvidenceNodes()) {
2392 if (targets.contains(node)) {
2393 targets.erase(node);
2394 hard_ev_nodes.insert(node);
2400 const auto& evidence = this->evidence();
2401 if (targets.empty()) {
2402 if (set.size() == 1) {
2403 return new Tensor< GUM_SCALAR >(*evidence[*set.begin()]);
2405 _TensorSet_ pot_list;
2406 for (
const auto node: set) {
2407 pot_list.insert(evidence[node]);
2411 MultiDimCombinationDefault< Tensor< GUM_SCALAR > > fast_combination(_combination_op_);
2412 const Tensor< GUM_SCALAR >* pot = fast_combination.execute(pot_list);
2414 return const_cast< Tensor< GUM_SCALAR >*
>(pot);
2422 NodeId clique_of_set;
2423 if (
auto ptr_clique = _joint_target_to_clique_.tryGet(set)) {
2424 clique_of_set = *ptr_clique;
2431 for (
const auto node: targets) {
2432 if (!_graph_.exists(node)) {
2434 node <<
" cannot be a query in the optimized inference (w.r.t the declared "
2435 "targets/evidence)")
2441 const std::vector< NodeId >& JT_elim_order = _triangulation_->eliminationOrder();
2443 NodeProperty< int > elim_order(Size(JT_elim_order.size()));
2444 for (std::size_t i = std::size_t(0), size = JT_elim_order.size(); i < size; ++i)
2445 elim_order.insert(JT_elim_order[i], (
int)i);
2446 NodeId first_eliminated_node = *(targets.begin());
2447 int elim_number = elim_order[first_eliminated_node];
2448 for (
const auto node: targets) {
2449 if (elim_order[node] < elim_number) {
2450 elim_number = elim_order[node];
2451 first_eliminated_node = node;
2455 clique_of_set = _triangulation_->createdJunctionTreeClique(first_eliminated_node);
2458 const NodeSet& clique_nodes = _JT_->clique(clique_of_set);
2459 for (
const auto node: targets) {
2460 if (!clique_nodes.contains(node)) {
2462 this->BN().names(set) <<
"(" << set <<
")"
2463 <<
" is not addressable in this optimized inference.")
2468 _joint_target_to_clique_.
insert(set, clique_of_set);
2472 _collectMessage_(clique_of_set, clique_of_set);
2477 _ScheduleMultiDimSet_ pot_list;
2478 if (_clique_ss_tensor_[clique_of_set] !=
nullptr) {
2479 auto pot = _clique_ss_tensor_[clique_of_set];
2480 if (pot !=
nullptr) pot_list.insert(_clique_ss_tensor_[clique_of_set]);
2484 for (
const auto other: _JT_->neighbours(clique_of_set)) {
2485 const auto pot = _separator_tensors_[
Arc(other, clique_of_set)];
2486 if (pot !=
nullptr) pot_list.insert(pot);
2490 const NodeSet& nodes = _JT_->clique(clique_of_set);
2493 const auto& bn = this->BN();
2494 for (
const auto node: nodes) {
2495 if (!targets.contains(node)) {
2496 del_vars.
insert(&(bn.variable(node)));
2498 kept_vars.
insert(&(bn.variable(node)));
2504 const IScheduleMultiDim* new_pot = _marginalizeOut_(pot_list, del_vars, kept_vars);
2505 ScheduleMultiDim< Tensor< GUM_SCALAR > >* resulting_pot
2506 =
const_cast< ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
2507 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(new_pot));
2511 Tensor< GUM_SCALAR >* joint =
nullptr;
2512 if (pot_list.exists(resulting_pot)) {
2513 joint =
new Tensor< GUM_SCALAR >(resulting_pot->multiDim());
2515 joint = resulting_pot->exportMultiDim();
2521 bool nonzero_found =
false;
2522 for (Instantiation inst(*joint); !inst.end(); ++inst) {
2523 if ((*joint)[inst]) {
2524 nonzero_found =
true;
2528 if (!nonzero_found) {
2532 "some evidence entered into the Bayes "
2533 "net are incompatible (their joint proba = 0)");
2539 if (!hard_ev_nodes.empty()) {
2540 _TensorSet_ pot_list;
2541 pot_list.insert(joint);
2542 const auto& hard_evidence = this->evidence();
2543 for (
const auto node: hard_ev_nodes)
2544 pot_list.insert(hard_evidence[node]);
2545 MultiDimCombinationDefault< Tensor< GUM_SCALAR > > combine(_combination_op_);
2546 Tensor< GUM_SCALAR >* new_joint = combine.execute(pot_list);
2555 template < GUM_Numeric GUM_SCALAR >
2556 const Tensor< GUM_SCALAR >&
2557 ShaferShenoyInference< GUM_SCALAR >::jointPosterior_(
const NodeSet& set) {
2559 if (_joint_target_posteriors_.exists(set)) {
return *(_joint_target_posteriors_[set]); }
2562 auto joint = unnormalizedJointPosterior_(set);
2564 _joint_target_posteriors_.insert(set, joint);
2570 template < GUM_Numeric GUM_SCALAR >
2571 const Tensor< GUM_SCALAR >&
2572 ShaferShenoyInference< GUM_SCALAR >::jointPosterior_(
const NodeSet& wanted_target,
2573 const NodeSet& declared_target) {
2575 if (_joint_target_posteriors_.exists(wanted_target))
2576 return *(_joint_target_posteriors_[wanted_target]);
2582 if (!_joint_target_posteriors_.exists(declared_target)) { jointPosterior_(declared_target); }
2585 const auto& bn = this->BN();
2587 for (
const auto node: declared_target)
2588 if (!wanted_target.contains(node)) del_vars.
insert(&(bn.variable(node)));
2590 =
new Tensor< GUM_SCALAR >(_joint_target_posteriors_[declared_target]->sumOut(del_vars));
2593 _joint_target_posteriors_.insert(wanted_target, pot);
2598 template < GUM_Numeric GUM_SCALAR >
2599 GUM_SCALAR ShaferShenoyInference< GUM_SCALAR >::evidenceProbability() {
2602 RelevantTensorsFinderType old_relevant_type = _find_relevant_tensor_type_;
2607 if (old_relevant_type != RelevantTensorsFinderType::FIND_ALL) {
2608 _find_relevant_tensor_type_ = RelevantTensorsFinderType::FIND_ALL;
2609 _is_new_jt_needed_ =
true;
2610 this->setOutdatedStructureState_();
2614 this->makeInference();
2622 GUM_SCALAR prob_ev = 1;
2623 for (
const auto root: _roots_) {
2625 NodeId node = *(_JT_->clique(root).begin());
2626 Tensor< GUM_SCALAR >* tmp = unnormalizedJointPosterior_(node);
2627 prob_ev *= tmp->sum();
2631 for (
const auto& projected_cpt: _constants_)
2632 prob_ev *= projected_cpt.second;
2635 _find_relevant_tensor_type_ = old_relevant_type;
2640 template < GUM_Numeric GUM_SCALAR >
2641 Tensor< GUM_SCALAR > SSNewmultiTensor(
const Tensor< GUM_SCALAR >& t1,
2642 const Tensor< GUM_SCALAR >& t2) {
2646 template < GUM_Numeric GUM_SCALAR >
2647 Tensor< GUM_SCALAR > SSNewprojTensor(
const Tensor< GUM_SCALAR >& t1,
2649 return t1.sumOut(del_vars);
2652 template < GUM_Numeric GUM_SCALAR >
2653 void ShaferShenoyInference< GUM_SCALAR >::onStateChanged_() {}
The BayesBall algorithm (as described by Schachter).
Implementation of Shafer-Shenoy's algorithm for inference in Bayesian networks.
Detect barren nodes for inference in Bayesian networks.
An algorithm for converting a join tree into a binary join tree.
Exception : a similar element already exists.
<agrum/BN/inference/evidenceInference.h>
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.
ShaferShenoyInference(const IBayesNet< GUM_SCALAR > *BN, RelevantTensorsFinderType=RelevantTensorsFinderType::DSEP_BAYESBALL_TENSORS, FindBarrenNodesType barren_type=FindBarrenNodesType::FIND_BARREN_NODES, bool use_binary_join_tree=true)
default constructor
Exception : a looked-for element could not be found.
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 JoinTree
a join tree is a clique graph satisfying the running intersection property (but some cliques may be i...
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...