53#ifndef DOXYGEN_SHOULD_SKIP_THIS
64 template < GUM_Numeric GUM_SCALAR >
67 bool use_binary_join_tree) :
69 _use_binary_join_tree_(use_binary_join_tree) {
71 _triangulation_ =
new DefaultTriangulation;
74 const auto& graph = this->MRF().internalGraph();
75 _node_to_factors_.resize(graph.size());
77 for (
const auto node: graph)
78 _node_to_factors_.insert(node, empty);
79 for (
const auto& factor: this->MRF().factors()) {
80 for (
const auto node: factor.first) {
81 _node_to_factors_[node].insert(factor.second);
86 GUM_CONSTRUCTOR(ShaferShenoyMRFInference);
90 template < GUM_Numeric GUM_SCALAR >
91 ShaferShenoyMRFInference< GUM_SCALAR >::~ShaferShenoyMRFInference() {
93 for (
const auto& pot: _arc_to_created_tensors_)
101 for (
auto pot: _clique_ss_tensor_) {
102 if (_clique_tensors_[pot.first].size() > 1)
delete pot.second;
105 for (
auto potset: _clique_tensors_) {
106 for (
auto pot: potset.second)
111 for (
const auto& pot: _target_posteriors_)
113 for (
const auto& pot: _joint_target_posteriors_)
117 if (_JT_ !=
nullptr)
delete _JT_;
118 if (_junctionTree_ !=
nullptr)
delete _junctionTree_;
119 delete _triangulation_;
122 GUM_DESTRUCTOR(ShaferShenoyMRFInference);
126 template < GUM_Numeric GUM_SCALAR >
127 void ShaferShenoyMRFInference< GUM_SCALAR >::setTriangulation(
128 const Triangulation& new_triangulation) {
129 delete _triangulation_;
130 _triangulation_ = new_triangulation.newFactory();
131 _is_new_jt_needed_ =
true;
132 this->setOutdatedStructureState_();
136 template < GUM_Numeric GUM_SCALAR >
137 const JoinTree* ShaferShenoyMRFInference< GUM_SCALAR >::joinTree() {
138 if (_is_new_jt_needed_) _createNewJT_();
144 template < GUM_Numeric GUM_SCALAR >
145 const JunctionTree* ShaferShenoyMRFInference< GUM_SCALAR >::junctionTree() {
146 if (_is_new_jt_needed_) _createNewJT_();
148 return _junctionTree_;
152 template < GUM_Numeric GUM_SCALAR >
153 void ShaferShenoyMRFInference< GUM_SCALAR >::_setProjectionFunction_(
154 Tensor< GUM_SCALAR > (*proj)(
const Tensor< GUM_SCALAR >&,
const gum::VariableSet&)) {
155 _projection_op_ = proj;
159 _invalidateAllMessages_();
163 template < GUM_Numeric GUM_SCALAR >
164 void ShaferShenoyMRFInference< GUM_SCALAR >::_setCombinationFunction_(
165 Tensor< GUM_SCALAR > (*comb)(
const Tensor< GUM_SCALAR >&,
const Tensor< GUM_SCALAR >&)) {
166 _combination_op_ = comb;
170 _invalidateAllMessages_();
174 template < GUM_Numeric GUM_SCALAR >
175 void ShaferShenoyMRFInference< GUM_SCALAR >::_invalidateAllMessages_() {
177 for (
auto& pot: _separator_tensors_)
178 pot.second =
nullptr;
180 for (
auto& mess_computed: _messages_computed_)
181 mess_computed.second =
false;
184 for (
const auto& pot: _arc_to_created_tensors_)
185 if (pot.second !=
nullptr)
delete pot.second;
186 _arc_to_created_tensors_.clear();
189 for (
const auto& pot: _target_posteriors_)
191 _target_posteriors_.clear();
192 for (
const auto& pot: _joint_target_posteriors_)
194 _joint_target_posteriors_.clear();
197 if (this->isInferenceReady() || this->isInferenceDone()) this->setOutdatedTensorsState_();
201 template < GUM_Numeric GUM_SCALAR >
202 void ShaferShenoyMRFInference< GUM_SCALAR >::onEvidenceAdded_(
const NodeId
id,
203 bool isHardEvidence) {
207 if (isHardEvidence || !_graph_.exists(
id)) _is_new_jt_needed_ =
true;
210 _evidence_changes_.insert(
id, EvidenceChangeType::EVIDENCE_ADDED);
216 _evidence_changes_[id] = EvidenceChangeType::EVIDENCE_MODIFIED;
222 template < GUM_Numeric GUM_SCALAR >
223 void ShaferShenoyMRFInference< GUM_SCALAR >::onEvidenceErased_(
const NodeId
id,
224 bool isHardEvidence) {
227 if (isHardEvidence) _is_new_jt_needed_ =
true;
230 _evidence_changes_.insert(
id, EvidenceChangeType::EVIDENCE_ERASED);
237 if (_evidence_changes_[
id] == EvidenceChangeType::EVIDENCE_ADDED)
238 _evidence_changes_.erase(
id);
239 else _evidence_changes_[id] = EvidenceChangeType::EVIDENCE_ERASED;
245 template < GUM_Numeric GUM_SCALAR >
246 void ShaferShenoyMRFInference< GUM_SCALAR >::onAllEvidenceErased_(
bool has_hard_evidence) {
247 if (has_hard_evidence || !this->hardEvidenceNodes().empty()) _is_new_jt_needed_ =
true;
249 for (
const auto node: this->softEvidenceNodes()) {
251 _evidence_changes_.insert(node, EvidenceChangeType::EVIDENCE_ERASED);
258 if (_evidence_changes_[node] == EvidenceChangeType::EVIDENCE_ADDED)
259 _evidence_changes_.erase(node);
260 else _evidence_changes_[node] = EvidenceChangeType::EVIDENCE_ERASED;
267 template < GUM_Numeric GUM_SCALAR >
268 void ShaferShenoyMRFInference< GUM_SCALAR >::onEvidenceChanged_(
const NodeId
id,
269 bool hasChangedSoftHard) {
270 if (hasChangedSoftHard) _is_new_jt_needed_ =
true;
273 _evidence_changes_.insert(
id, EvidenceChangeType::EVIDENCE_MODIFIED);
283 template < GUM_Numeric GUM_SCALAR >
284 void ShaferShenoyMRFInference< GUM_SCALAR >::onMarginalTargetAdded_(
const NodeId
id) {}
287 template < GUM_Numeric GUM_SCALAR >
288 void ShaferShenoyMRFInference< GUM_SCALAR >::onMarginalTargetErased_(
const NodeId
id) {}
291 template < GUM_Numeric GUM_SCALAR >
292 void ShaferShenoyMRFInference< GUM_SCALAR >::onJointTargetAdded_(
const NodeSet& set) {}
295 template < GUM_Numeric GUM_SCALAR >
296 void ShaferShenoyMRFInference< GUM_SCALAR >::onJointTargetErased_(
const NodeSet& set) {}
299 template < GUM_Numeric GUM_SCALAR >
300 void ShaferShenoyMRFInference< GUM_SCALAR >::onAllMarginalTargetsAdded_() {}
303 template < GUM_Numeric GUM_SCALAR >
304 void ShaferShenoyMRFInference< GUM_SCALAR >::onAllMarginalTargetsErased_() {}
307 template < GUM_Numeric GUM_SCALAR >
308 void ShaferShenoyMRFInference< GUM_SCALAR >::onMRFChanged_(
309 const IMarkovRandomField< GUM_SCALAR >* mn) {}
312 template < GUM_Numeric GUM_SCALAR >
313 void ShaferShenoyMRFInference< GUM_SCALAR >::onAllJointTargetsErased_() {}
316 template < GUM_Numeric GUM_SCALAR >
317 void ShaferShenoyMRFInference< GUM_SCALAR >::onAllTargetsErased_() {}
320 template < GUM_Numeric GUM_SCALAR >
321 bool ShaferShenoyMRFInference< GUM_SCALAR >::_isNewJTNeeded_()
const {
324 if ((_JT_ ==
nullptr) || _is_new_jt_needed_)
return true;
330 const auto& hard_ev_nodes = this->hardEvidenceNodes();
331 for (
const auto node: this->targets()) {
332 if (!_graph_.exists(node) && !hard_ev_nodes.exists(node))
return true;
336 const std::vector< NodeId >& JT_elim_order = _triangulation_->eliminationOrder();
337 NodeProperty< int > elim_order(Size(JT_elim_order.size()));
338 for (std::size_t i = std::size_t(0), size = JT_elim_order.size(); i < size; ++i)
339 elim_order.insert(JT_elim_order[i], (
int)i);
342 for (
const auto& joint_target: this->jointTargets()) {
345 NodeId first_eliminated_node = std::numeric_limits< NodeId >::max();
346 int elim_number = std::numeric_limits< int >::max();
347 unobserved_set.clear();
348 for (
const auto node: joint_target) {
349 if (!_graph_.exists(node)) {
350 if (!hard_ev_nodes.exists(node))
return true;
352 unobserved_set.insert(node);
353 if (elim_order[node] < elim_number) {
354 elim_number = elim_order[node];
355 first_eliminated_node = node;
359 if (!unobserved_set.empty()) {
363 const auto clique_id = _node_to_clique_[first_eliminated_node];
364 const auto& clique = _JT_->clique(clique_id);
365 for (
const auto node: unobserved_set) {
366 if (!clique.contains(node))
return true;
373 for (
const auto& change: _evidence_changes_) {
374 if ((change.second == EvidenceChangeType::EVIDENCE_ADDED) && !_graph_.exists(change.first))
383 template < GUM_Numeric GUM_SCALAR >
384 void ShaferShenoyMRFInference< GUM_SCALAR >::_createNewJT_() {
396 const auto& mn = this->MRF();
397 _graph_ = mn.internalGraph();
402 for (
const auto& nodeset: this->jointTargets()) {
403 for (
auto iter1 = nodeset.cbegin(); iter1 != nodeset.cend(); ++iter1) {
405 for (++iter2; iter2 != nodeset.cend(); ++iter2) {
406 _graph_.addEdge(*iter1, *iter2);
412 _hard_ev_nodes_ = this->hardEvidenceNodes();
413 for (
const auto node: _hard_ev_nodes_) {
414 _graph_.eraseNode(node);
421 if (_JT_ !=
nullptr)
delete _JT_;
422 if (_junctionTree_ !=
nullptr)
delete _junctionTree_;
424 const auto& domain_sizes = this->domainSizes();
425 _triangulation_->setGraph(&_graph_, &domain_sizes);
426 const JunctionTree& triang_jt = _triangulation_->junctionTree();
427 if (_use_binary_join_tree_) {
428 BinaryJoinTreeConverterDefault bjt_converter;
430 _JT_ =
new CliqueGraph(bjt_converter.convert(triang_jt, domain_sizes, emptyset));
432 _JT_ =
new CliqueGraph(triang_jt);
434 _junctionTree_ =
new CliqueGraph(triang_jt);
438 const std::vector< NodeId >& JT_elim_order = _triangulation_->eliminationOrder();
439 Size size_elim_order = JT_elim_order.size();
440 NodeProperty< int > elim_order(size_elim_order);
441 for (Idx i = Idx(0); i < size_elim_order; ++i)
442 elim_order.insert(JT_elim_order[i], (
int)i);
446 _factor_to_clique_.clear();
447 _factor_to_clique_.resize(mn.factors().size());
448 for (
const auto& factor: mn.factors()) {
449 const auto& nodes = factor.first;
450 NodeId first_eliminated_node = std::numeric_limits< NodeId >::max();
451 int elim_number = std::numeric_limits< int >::max();
452 for (
const auto node: nodes) {
453 if (_graph_.exists(node) && (elim_order[node] < elim_number)) {
454 elim_number = elim_order[node];
455 first_eliminated_node = node;
459 if (elim_number != std::numeric_limits< int >::max()) {
463 _factor_to_clique_.insert(
465 _triangulation_->createdJunctionTreeClique(first_eliminated_node));
471 _node_to_clique_.clear();
472 _node_to_clique_.resize(_graph_.size());
473 NodeProperty< double > node_to_clique_size(_graph_.size());
474 for (
const auto node: _graph_) {
475 _node_to_clique_.insert(node, std::numeric_limits< NodeId >::max());
476 node_to_clique_size.insert(node, std::numeric_limits< double >::max());
478 double overall_size = 0;
479 for (
const auto clique_id: *_JT_) {
481 const auto& clique_nodes = _JT_->clique(clique_id);
482 double clique_size = 1.0;
483 for (
const auto node: clique_nodes)
484 clique_size *=
double(domain_sizes[node]);
485 overall_size += clique_size;
489 for (
const auto node: clique_nodes) {
490 if (clique_size < node_to_clique_size[node]) {
491 _node_to_clique_[node] = clique_id;
492 node_to_clique_size[node] = clique_size;
498 _joint_target_to_clique_.clear();
499 for (
const auto& set: this->jointTargets()) {
500 NodeId first_eliminated_node = std::numeric_limits< NodeId >::max();
501 int elim_number = std::numeric_limits< int >::max();
505 for (
const auto node: set) {
506 if (!_hard_ev_nodes_.contains(node)) {
509 if (elim_order[node] < elim_number) {
510 elim_number = elim_order[node];
511 first_eliminated_node = node;
516 if (elim_number != std::numeric_limits< int >::max()) {
517 _joint_target_to_clique_.insert(
519 _triangulation_->createdJunctionTreeClique(first_eliminated_node));
524 _computeJoinTreeRoots_();
529 for (
const auto& pot: _clique_ss_tensor_) {
530 if (_clique_tensors_[pot.first].size() > 1)
delete pot.second;
532 _clique_ss_tensor_.clear();
533 for (
const auto& potlist: _clique_tensors_)
534 for (
const auto pot: potlist.second)
536 _clique_tensors_.clear();
539 for (
const auto& pot: _arc_to_created_tensors_)
541 _arc_to_created_tensors_.clear();
546 _hard_ev_projected_factors_.clear();
549 _node_to_soft_evidence_.clear();
553 _ScheduleMultiDimSet_ empty_set;
554 for (
const auto node: *_JT_) {
555 _clique_tensors_.insert(node, empty_set);
556 _clique_ss_tensor_.insert(node,
nullptr);
564 _separator_tensors_.clear();
565 _messages_computed_.clear();
566 for (
const auto& edge: _JT_->edges()) {
567 const Arc arc1(edge.first(), edge.second());
568 _separator_tensors_.insert(arc1,
nullptr);
569 _messages_computed_.insert(arc1,
false);
570 const Arc arc2(edge.second(), edge.first());
571 _separator_tensors_.insert(arc2,
nullptr);
572 _messages_computed_.insert(arc2,
false);
576 for (
const auto& pot: _target_posteriors_)
578 _target_posteriors_.clear();
579 for (
const auto& pot: _joint_target_posteriors_)
581 _joint_target_posteriors_.clear();
586 _use_schedules_ = (overall_size > _schedule_threshold_);
589 const NodeProperty< const Tensor< GUM_SCALAR >* >& evidence = this->evidence();
590 for (
const auto node: this->softEvidenceNodes()) {
591 if (
auto p = _node_to_clique_.tryGet(node)) {
592 auto ev_pot =
new ScheduleMultiDim< Tensor< GUM_SCALAR > >(*evidence[node],
false);
593 _node_to_soft_evidence_.insert(node, ev_pot);
594 _clique_tensors_[*p].insert(ev_pot);
602 if (_use_schedules_) {
604 _initializeJTCliques_(schedule);
606 _initializeJTCliques_();
611 _evidence_changes_.clear();
612 _is_new_jt_needed_ =
false;
616 template < GUM_Numeric GUM_SCALAR >
617 void ShaferShenoyMRFInference< GUM_SCALAR >::_initializeJTCliques_() {
618 const auto& mn = this->MRF();
624 const NodeProperty< const Tensor< GUM_SCALAR >* >& evidence = this->evidence();
625 const NodeProperty< Idx >& hard_evidence = this->hardEvidence();
627 for (
const auto& factor: mn.factors()) {
628 const auto& factor_nodes = factor.first;
629 const auto& pot = *(factor.second);
630 const auto& variables = pot.variablesSequence();
633 NodeSet hard_nodes(factor_nodes.size());
634 bool graph_contains_nodes =
false;
635 for (
const auto node: factor_nodes) {
636 if (_hard_ev_nodes_.contains(node)) hard_nodes.insert(node);
637 else if (_graph_.exists(node)) graph_contains_nodes =
true;
643 if (hard_nodes.empty()) {
644 auto sched_cpt =
new ScheduleMultiDim< Tensor< GUM_SCALAR > >(pot,
false);
645 _clique_tensors_[_factor_to_clique_[&pot]].insert(sched_cpt);
651 if (hard_nodes.size() == factor_nodes.size()) {
652 Instantiation inst(pot);
653 for (Size i = 0; i < hard_nodes.size(); ++i) {
654 inst.chgVal(*variables[i], hard_evidence[mn.nodeId(*(variables[i]))]);
656 _constants_.insert(&pot, pot.get(inst));
661 if (!graph_contains_nodes)
continue;
665 _TensorSet_ marg_factor_set(1 + hard_nodes.size());
666 marg_factor_set.insert(&pot);
667 for (
const auto node: hard_nodes) {
668 marg_factor_set.insert(evidence[node]);
669 hard_variables.
insert(&(mn.variable(node)));
673 MultiDimCombineAndProjectDefault< Tensor< GUM_SCALAR > > combine_and_project(
677 _TensorSet_ new_factor_list
678 = combine_and_project.execute(marg_factor_set, hard_variables);
681 if (new_factor_list.size() != 1) {
682 for (
const auto pot: new_factor_list) {
683 if (!marg_factor_set.contains(pot))
delete pot;
686 "the projection of a tensor containing " <<
"hard evidence is empty!");
688 auto new_factor =
const_cast< Tensor< GUM_SCALAR >*
>(*(new_factor_list.begin()));
689 auto projected_factor
690 =
new ScheduleMultiDim< Tensor< GUM_SCALAR > >(std::move(*new_factor));
693 _clique_tensors_[_factor_to_clique_[&pot]].insert(projected_factor);
694 _hard_ev_projected_factors_.insert(&pot, projected_factor);
703 MultiDimCombinationDefault< Tensor< GUM_SCALAR > > fast_combination(_combination_op_);
704 for (
const auto& xpotset: _clique_tensors_) {
705 const auto& potset = xpotset.second;
706 if (potset.size() > 0) {
711 if (potset.size() == 1) {
712 _clique_ss_tensor_[xpotset.first] = *(potset.cbegin());
714 _TensorSet_ p_potset(potset.size());
715 for (
const auto pot: potset)
717 &(
static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(pot)->multiDim()));
719 Tensor< GUM_SCALAR >* joint
720 =
const_cast< Tensor< GUM_SCALAR >*
>(fast_combination.execute(p_potset));
721 _clique_ss_tensor_[xpotset.first]
722 =
new ScheduleMultiDim< Tensor< GUM_SCALAR > >(std::move(*joint));
730 template < GUM_Numeric GUM_SCALAR >
731 void ShaferShenoyMRFInference< GUM_SCALAR >::_initializeJTCliques_(Schedule& schedule) {
732 const auto& mn = this->MRF();
738 const NodeProperty< const Tensor< GUM_SCALAR >* >& evidence = this->evidence();
739 const NodeProperty< Idx >& hard_evidence = this->hardEvidence();
741 for (
const auto& factor: mn.factors()) {
742 const auto& factor_nodes = factor.first;
743 const auto& pot = *(factor.second);
744 const auto& variables = pot.variablesSequence();
748 bool graph_contains_nodes =
false;
749 for (
const auto node: factor_nodes) {
750 if (_hard_ev_nodes_.contains(node)) hard_nodes.insert(node);
751 else if (_graph_.exists(node)) graph_contains_nodes =
true;
757 if (hard_nodes.empty()) {
758 auto sched_cpt =
new ScheduleMultiDim< Tensor< GUM_SCALAR > >(pot,
false);
759 _clique_tensors_[_factor_to_clique_[&pot]].insert(sched_cpt);
765 if (hard_nodes.size() == factor_nodes.size()) {
766 Instantiation inst(pot);
767 for (Size i = 0; i < hard_nodes.size(); ++i) {
768 inst.chgVal(*variables[i], hard_evidence[mn.nodeId(*(variables[i]))]);
770 _constants_.insert(&pot, pot.get(inst));
775 if (!graph_contains_nodes)
continue;
779 _ScheduleMultiDimSet_ marg_factor_set(1 + hard_nodes.size());
780 const IScheduleMultiDim* sched_pot
781 = schedule.insertTable< Tensor< GUM_SCALAR > >(pot,
false);
782 marg_factor_set.insert(sched_pot);
784 for (
const auto node: hard_nodes) {
785 const IScheduleMultiDim* pot
786 = schedule.insertTable< Tensor< GUM_SCALAR > >(*evidence[node],
false);
787 marg_factor_set.insert(pot);
788 hard_variables.
insert(&(mn.variable(node)));
792 MultiDimCombineAndProjectDefault< Tensor< GUM_SCALAR > > combine_and_project(
796 _ScheduleMultiDimSet_ new_factor_list
797 = combine_and_project.schedule(schedule, marg_factor_set, hard_variables);
800 if (new_factor_list.size() != 1) {
802 "the projection of a tensor containing " <<
"hard evidence is empty!");
804 auto projected_factor =
const_cast< ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
805 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
806 *new_factor_list.begin()));
807 const_cast< ScheduleOperator*
>(schedule.scheduleMultiDimCreator(projected_factor))
808 ->makeResultsPersistent(
true);
810 _clique_tensors_[_factor_to_clique_[&pot]].insert(projected_factor);
811 _hard_ev_projected_factors_.insert(&pot, projected_factor);
815 this->scheduler().execute(schedule);
822 MultiDimCombinationDefault< Tensor< GUM_SCALAR > > fast_combination(_combination_op_);
823 for (
const auto& xpotset: _clique_tensors_) {
824 const auto& potset = xpotset.second;
825 if (potset.size() > 0) {
830 if (potset.size() == 1) {
831 _clique_ss_tensor_[xpotset.first] = *(potset.cbegin());
834 for (
const auto pot: potset) {
835 schedule.emplaceScheduleMultiDim(*pot);
838 auto joint =
const_cast< ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
839 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
840 fast_combination.schedule(schedule, potset)));
841 const_cast< ScheduleOperator*
>(schedule.scheduleMultiDimCreator(joint))
842 ->makeResultsPersistent(
true);
843 _clique_ss_tensor_[xpotset.first] = joint;
847 this->scheduler().execute(schedule);
851 template < GUM_Numeric GUM_SCALAR >
852 void ShaferShenoyMRFInference< GUM_SCALAR >::updateOutdatedStructure_() {
854 if (_isNewJTNeeded_()) {
860 updateOutdatedTensors_();
865 template < GUM_Numeric GUM_SCALAR >
866 void ShaferShenoyMRFInference< GUM_SCALAR >::_diffuseMessageInvalidations_(
869 NodeSet& invalidated_cliques) {
871 invalidated_cliques.insert(to_id);
874 const Arc arc(from_id, to_id);
875 bool& message_computed = _messages_computed_[arc];
876 if (message_computed) {
877 message_computed =
false;
878 _separator_tensors_[arc] =
nullptr;
879 if (_arc_to_created_tensors_.exists(arc)) {
880 delete _arc_to_created_tensors_[arc];
881 _arc_to_created_tensors_.erase(arc);
885 for (
const auto node_id: _JT_->neighbours(to_id)) {
886 if (node_id != from_id) _diffuseMessageInvalidations_(to_id, node_id, invalidated_cliques);
893 template < GUM_Numeric GUM_SCALAR >
894 void ShaferShenoyMRFInference< GUM_SCALAR >::updateOutdatedTensors_() {
899 NodeProperty< bool > ss_tensor_to_deallocate(_clique_tensors_.size());
900 for (
const auto& potset: _clique_tensors_) {
901 ss_tensor_to_deallocate.insert(potset.first, (potset.second.size() > 1));
911 const auto& mn = this->MRF();
912 NodeSet hard_nodes_changed(_hard_ev_nodes_.size());
913 Set< const Tensor< GUM_SCALAR >* > hard_projected_factors_changed(mn.factors().size());
914 for (
const auto node: _hard_ev_nodes_) {
915 if (_evidence_changes_.exists(node)) {
916 hard_nodes_changed.insert(node);
917 for (
const auto pot: _node_to_factors_[node]) {
918 if (_hard_ev_projected_factors_.exists(pot)
919 && !hard_projected_factors_changed.exists(pot)) {
920 hard_projected_factors_changed.insert(pot);
926 NodeSet hard_cliques_changed(hard_projected_factors_changed.size());
927 for (
const auto pot: hard_projected_factors_changed) {
928 const auto chgPot = _hard_ev_projected_factors_[pot];
929 const NodeId chgClique = _factor_to_clique_[pot];
930 _clique_tensors_[chgClique].erase(chgPot);
931 _hard_ev_projected_factors_.erase(pot);
932 if (!hard_cliques_changed.contains(chgClique)) hard_cliques_changed.insert(chgClique);
944 NodeSet invalidated_cliques(_JT_->size());
945 for (
const auto& pair: _evidence_changes_) {
946 if (
auto p = _node_to_clique_.tryGet(pair.first)) {
947 const auto clique = *p;
948 invalidated_cliques.insert(clique);
949 for (
const auto neighbor: _JT_->neighbours(clique)) {
950 _diffuseMessageInvalidations_(clique, neighbor, invalidated_cliques);
957 for (
const auto clique: hard_cliques_changed) {
958 invalidated_cliques.insert(clique);
959 for (
const auto neighbor: _JT_->neighbours(clique)) {
960 _diffuseMessageInvalidations_(clique, neighbor, invalidated_cliques);
966 for (
const auto clique: invalidated_cliques) {
967 if (ss_tensor_to_deallocate[clique]) {
968 delete _clique_ss_tensor_[clique];
969 _clique_ss_tensor_[clique] =
nullptr;
976 if (!_target_posteriors_.empty()) {
977 for (
auto iter = _target_posteriors_.beginSafe(); iter != _target_posteriors_.endSafe();
981 if (_graph_.exists(iter.key())
982 && (invalidated_cliques.exists(_node_to_clique_[iter.key()]))) {
984 _target_posteriors_.erase(iter);
987 else if (hard_nodes_changed.contains(iter.key())) {
989 _target_posteriors_.erase(iter);
996 for (
auto iter = _joint_target_posteriors_.beginSafe();
997 iter != _joint_target_posteriors_.endSafe();
999 if (invalidated_cliques.exists(_joint_target_to_clique_[iter.key()])) {
1001 _joint_target_posteriors_.erase(iter);
1004 bool has_unevidenced_node =
false;
1005 for (
const auto node: iter.key()) {
1006 if (!hard_nodes_changed.exists(node)) {
1007 has_unevidenced_node =
true;
1011 if (!has_unevidenced_node) {
1013 _joint_target_posteriors_.erase(iter);
1020 for (
const auto& pot_pair: _node_to_soft_evidence_) {
1021 delete pot_pair.second;
1022 _clique_tensors_[_node_to_clique_[pot_pair.first]].erase(pot_pair.second);
1024 _node_to_soft_evidence_.clear();
1026 const auto& evidence = this->evidence();
1027 for (
const auto node: this->softEvidenceNodes()) {
1028 auto ev_pot =
new ScheduleMultiDim< Tensor< GUM_SCALAR > >(*evidence[node],
false);
1029 _node_to_soft_evidence_.insert(node, ev_pot);
1030 _clique_tensors_[_node_to_clique_[node]].insert(ev_pot);
1040 if (_use_schedules_) {
1042 for (
const auto pot: hard_projected_factors_changed) {
1043 _ScheduleMultiDimSet_ marg_pot_set;
1044 const auto sched_pot = schedule.insertTable< Tensor< GUM_SCALAR > >(*pot,
false);
1045 marg_pot_set.insert(sched_pot);
1046 const auto& variables = pot->variablesSequence();
1048 for (
const auto var: variables) {
1049 NodeId xnode = mn.nodeId(*var);
1050 if (_hard_ev_nodes_.exists(xnode)) {
1052 = schedule.insertTable< Tensor< GUM_SCALAR > >(*evidence[xnode],
false);
1053 marg_pot_set.insert(ev_pot);
1054 hard_variables.
insert(var);
1059 MultiDimCombineAndProjectDefault< Tensor< GUM_SCALAR > > combine_and_project(
1063 _ScheduleMultiDimSet_ new_pot_list
1064 = combine_and_project.schedule(schedule, marg_pot_set, hard_variables);
1067 if (new_pot_list.size() != 1) {
1069 "the projection of a tensor containing " <<
"hard evidence is empty!");
1071 auto projected_pot =
const_cast< ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
1072 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(*new_pot_list.begin()));
1073 const_cast< ScheduleOperator*
>(schedule.scheduleMultiDimCreator(projected_pot))
1074 ->makeResultsPersistent(
true);
1075 _clique_tensors_[_factor_to_clique_[pot]].insert(projected_pot);
1076 _hard_ev_projected_factors_.insert(pot, projected_pot);
1082 MultiDimCombinationDefault< Tensor< GUM_SCALAR > > fast_combination(_combination_op_);
1083 for (
const auto clique: invalidated_cliques) {
1084 const auto& potset = _clique_tensors_[clique];
1086 if (potset.size() > 0) {
1091 if (potset.size() == 1) {
1092 _clique_ss_tensor_[clique] = *(potset.cbegin());
1094 for (
const auto pot: potset)
1095 if (!schedule.existsScheduleMultiDim(pot->id()))
1096 schedule.emplaceScheduleMultiDim(*pot);
1097 auto joint =
const_cast< ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
1098 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
1099 fast_combination.schedule(schedule, potset)));
1100 const_cast< ScheduleOperator*
>(schedule.scheduleMultiDimCreator(joint))
1101 ->makeResultsPersistent(
true);
1102 _clique_ss_tensor_[clique] = joint;
1106 this->scheduler().execute(schedule);
1108 for (
const auto pot: hard_projected_factors_changed) {
1109 _TensorSet_ marg_pot_set;
1110 marg_pot_set.insert(pot);
1111 const auto& variables = pot->variablesSequence();
1114 for (
const auto var: variables) {
1115 NodeId xnode = mn.nodeId(*var);
1116 if (_hard_ev_nodes_.exists(xnode)) {
1117 marg_pot_set.insert(evidence[xnode]);
1118 hard_variables.
insert(var);
1123 MultiDimCombineAndProjectDefault< Tensor< GUM_SCALAR > > combine_and_project(
1127 _TensorSet_ new_pot_list = combine_and_project.execute(marg_pot_set, hard_variables);
1130 if (new_pot_list.size() != 1) {
1132 "the projection of a tensor containing " <<
"hard evidence is empty!");
1134 Tensor< GUM_SCALAR >* xprojected_pot
1135 =
const_cast< Tensor< GUM_SCALAR >*
>(*new_pot_list.begin());
1137 =
new ScheduleMultiDim< Tensor< GUM_SCALAR > >(std::move(*xprojected_pot));
1138 delete xprojected_pot;
1139 _clique_tensors_[_factor_to_clique_[pot]].insert(projected_pot);
1140 _hard_ev_projected_factors_.insert(pot, projected_pot);
1146 MultiDimCombinationDefault< Tensor< GUM_SCALAR > > fast_combination(_combination_op_);
1147 for (
const auto clique: invalidated_cliques) {
1148 const auto& potset = _clique_tensors_[clique];
1150 if (potset.size() > 0) {
1155 if (potset.size() == 1) {
1156 _clique_ss_tensor_[clique] = *(potset.cbegin());
1158 _TensorSet_ p_potset(potset.size());
1159 for (
const auto pot: potset)
1161 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(pot)->multiDim()));
1163 Tensor< GUM_SCALAR >* joint
1164 =
const_cast< Tensor< GUM_SCALAR >*
>(fast_combination.execute(p_potset));
1165 _clique_ss_tensor_[clique]
1166 =
new ScheduleMultiDim< Tensor< GUM_SCALAR > >(std::move(*joint));
1174 const auto& hard_evidence = this->hardEvidence();
1175 for (
auto& node_cst: _constants_) {
1176 const Tensor< GUM_SCALAR >& pot = *(node_cst.first);
1177 Instantiation inst(pot);
1178 for (
const auto var: pot.variablesSequence()) {
1179 inst.chgVal(*var, hard_evidence[mn.nodeId(*var)]);
1181 node_cst.second = pot.get(inst);
1185 _evidence_changes_.clear();
1189 template < GUM_Numeric GUM_SCALAR >
1190 void ShaferShenoyMRFInference< GUM_SCALAR >::_computeJoinTreeRoots_() {
1195 for (
const auto node: this->targets()) {
1196 if (
auto p = _node_to_clique_.tryGet(node)) { clique_targets.insert(*p); }
1198 for (
const auto& set: this->jointTargets()) {
1199 if (
auto p = _joint_target_to_clique_.tryGet(set)) { clique_targets.insert(*p); }
1203 std::vector< std::pair< NodeId, Size > > possible_roots(clique_targets.size());
1204 const auto& mn = this->MRF();
1206 for (
const auto clique_id: clique_targets) {
1207 const auto& clique = _JT_->clique(clique_id);
1209 for (
const auto node: clique) {
1210 dom_size *= mn.variable(node).domainSize();
1212 possible_roots[i] = std::pair< NodeId, Size >(clique_id, dom_size);
1217 std::sort(possible_roots.begin(),
1218 possible_roots.end(),
1219 [](
const std::pair< NodeId, Size >& a,
const std::pair< NodeId, Size >& b) ->
bool {
1220 return a.second < b.second;
1224 NodeProperty< bool > marked = _JT_->nodesPropertyFromVal(
false);
1225 std::function< void(NodeId, NodeId) > diffuse_marks
1226 = [&marked, &diffuse_marks,
this](NodeId node, NodeId from) {
1227 if (!marked[node]) {
1228 marked[node] =
true;
1229 for (
const auto neigh: _JT_->neighbours(node))
1230 if ((neigh != from) && !marked[neigh]) diffuse_marks(neigh, node);
1234 for (
const auto& xclique: possible_roots) {
1235 NodeId clique = xclique.first;
1236 if (!marked[clique]) {
1237 _roots_.insert(clique);
1238 diffuse_marks(clique, clique);
1244 template < GUM_Numeric GUM_SCALAR >
1245 void ShaferShenoyMRFInference< GUM_SCALAR >::_collectMessage_(Schedule& schedule,
1248 for (
const auto other: _JT_->neighbours(
id)) {
1249 if ((other != from) && !_messages_computed_[Arc(other,
id)])
1250 _collectMessage_(schedule, other,
id);
1253 if ((
id != from) && !_messages_computed_[Arc(
id, from)]) {
1254 _produceMessage_(schedule,
id, from);
1259 template < GUM_Numeric GUM_SCALAR >
1260 void ShaferShenoyMRFInference< GUM_SCALAR >::_collectMessage_(NodeId
id, NodeId from) {
1261 for (
const auto other: _JT_->neighbours(
id)) {
1262 if ((other != from) && !_messages_computed_[Arc(other,
id)]) _collectMessage_(other,
id);
1265 if ((
id != from) && !_messages_computed_[Arc(
id, from)]) { _produceMessage_(
id, from); }
1269 template < GUM_Numeric GUM_SCALAR >
1270 const IScheduleMultiDim* ShaferShenoyMRFInference< GUM_SCALAR >::_marginalizeOut_(
1272 Set< const IScheduleMultiDim* > pot_list,
1277 for (
const auto pot: pot_list) {
1278 if (!schedule.existsScheduleMultiDim(pot->id())) schedule.emplaceScheduleMultiDim(*pot);
1283 MultiDimCombineAndProjectDefault< Tensor< GUM_SCALAR > > combine_and_project(_combination_op_,
1285 _ScheduleMultiDimSet_ new_pot_list = combine_and_project.schedule(schedule, pot_list, del_vars);
1288 if (new_pot_list.size() == 1)
return *(new_pot_list.begin());
1289 MultiDimCombinationDefault< Tensor< GUM_SCALAR > > fast_combination(_combination_op_);
1290 return fast_combination.schedule(schedule, new_pot_list);
1294 template < GUM_Numeric GUM_SCALAR >
1295 const IScheduleMultiDim* ShaferShenoyMRFInference< GUM_SCALAR >::_marginalizeOut_(
1296 Set< const IScheduleMultiDim* >& pot_list,
1299 _TensorSet_ xpot_list(pot_list.size());
1300 for (
auto pot: pot_list)
1302 &(
static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(pot)->multiDim()));
1306 MultiDimCombineAndProjectDefault< Tensor< GUM_SCALAR > > combine_and_project(_combination_op_,
1308 _TensorSet_ xnew_pot_list = combine_and_project.execute(xpot_list, del_vars);
1311 const Tensor< GUM_SCALAR >* xres_pot;
1312 if (xnew_pot_list.size() == 1) {
1313 xres_pot = *(xnew_pot_list.begin());
1317 MultiDimCombinationDefault< Tensor< GUM_SCALAR > > fast_combination(_combination_op_);
1318 xres_pot = fast_combination.execute(xnew_pot_list);
1319 for (
const auto pot: xnew_pot_list) {
1320 if (!xpot_list.contains(pot) && (pot != xres_pot))
delete pot;
1325 ScheduleMultiDim< Tensor< GUM_SCALAR > >* res_pot;
1326 if (xpot_list.contains(xres_pot))
1327 res_pot =
new ScheduleMultiDim< Tensor< GUM_SCALAR > >(*xres_pot,
false);
1329 res_pot =
new ScheduleMultiDim< Tensor< GUM_SCALAR > >(
1330 std::move(
const_cast< Tensor< GUM_SCALAR >&
>(*xres_pot)));
1338 template < GUM_Numeric GUM_SCALAR >
1339 void ShaferShenoyMRFInference< GUM_SCALAR >::_produceMessage_(Schedule& schedule,
1343 _ScheduleMultiDimSet_ pot_list;
1344 if (_clique_ss_tensor_[from_id] !=
nullptr) pot_list.insert(_clique_ss_tensor_[from_id]);
1347 for (
const auto other_id: _JT_->neighbours(from_id)) {
1348 if (other_id != to_id) {
1349 const auto separator_pot = _separator_tensors_[Arc(other_id, from_id)];
1350 if (separator_pot !=
nullptr) pot_list.insert(separator_pot);
1355 const NodeSet& from_clique = _JT_->clique(from_id);
1356 const NodeSet& separator = _JT_->separator(from_id, to_id);
1359 const auto& mn = this->MRF();
1361 for (
const auto node: from_clique) {
1362 if (!separator.contains(node)) {
1363 del_vars.
insert(&(mn.variable(node)));
1365 kept_vars.
insert(&(mn.variable(node)));
1371 const IScheduleMultiDim* new_pot = _marginalizeOut_(schedule, pot_list, del_vars, kept_vars);
1374 const Arc arc(from_id, to_id);
1375 if (!pot_list.exists(new_pot)) {
1376 if (!_arc_to_created_tensors_.exists(arc)) {
1377 _arc_to_created_tensors_.insert(arc, new_pot);
1380 auto op = schedule.scheduleMultiDimCreator(new_pot);
1381 if (op !=
nullptr)
const_cast< ScheduleOperator*
>(op)->makeResultsPersistent(
true);
1385 _separator_tensors_[arc] = new_pot;
1386 _messages_computed_[arc] =
true;
1390 template < GUM_Numeric GUM_SCALAR >
1391 void ShaferShenoyMRFInference< GUM_SCALAR >::_produceMessage_(NodeId from_id, NodeId to_id) {
1393 _ScheduleMultiDimSet_ pot_list;
1394 if (_clique_ss_tensor_[from_id] !=
nullptr) pot_list.insert(_clique_ss_tensor_[from_id]);
1397 for (
const auto other_id: _JT_->neighbours(from_id)) {
1398 if (other_id != to_id) {
1399 const auto separator_pot = _separator_tensors_[Arc(other_id, from_id)];
1400 if (separator_pot !=
nullptr) pot_list.insert(separator_pot);
1405 const NodeSet& from_clique = _JT_->clique(from_id);
1406 const NodeSet& separator = _JT_->separator(from_id, to_id);
1409 const auto& mn = this->MRF();
1411 for (
const auto node: from_clique) {
1412 if (!separator.contains(node)) {
1413 del_vars.
insert(&(mn.variable(node)));
1415 kept_vars.
insert(&(mn.variable(node)));
1421 const IScheduleMultiDim* new_pot = _marginalizeOut_(pot_list, del_vars, kept_vars);
1424 const Arc arc(from_id, to_id);
1425 if (!pot_list.exists(new_pot)) {
1426 if (!_arc_to_created_tensors_.exists(arc)) { _arc_to_created_tensors_.insert(arc, new_pot); }
1429 _separator_tensors_[arc] = new_pot;
1430 _messages_computed_[arc] =
true;
1434 template < GUM_Numeric GUM_SCALAR >
1435 void ShaferShenoyMRFInference< GUM_SCALAR >::onModelChanged_(
const GraphicalModel* mn) {
1436 JointTargetedMRFInference< GUM_SCALAR >::onModelChanged_(mn);
1440 template < GUM_Numeric GUM_SCALAR >
1441 void ShaferShenoyMRFInference< GUM_SCALAR >::makeInference_() {
1442 if (_use_schedules_) {
1446 for (
const auto node: this->targets()) {
1450 if (_graph_.exists(node)) {
1451 _collectMessage_(schedule, _node_to_clique_[node], _node_to_clique_[node]);
1459 for (
const auto& set: _joint_target_to_clique_)
1460 _collectMessage_(schedule, set.second, set.second);
1463 this->scheduler().execute(schedule);
1466 for (
const auto node: this->targets()) {
1470 if (_graph_.exists(node)) {
1471 _collectMessage_(_node_to_clique_[node], _node_to_clique_[node]);
1479 for (
const auto& set: _joint_target_to_clique_)
1480 _collectMessage_(set.second, set.second);
1485 template < GUM_Numeric GUM_SCALAR >
1486 Tensor< GUM_SCALAR >*
1487 ShaferShenoyMRFInference< GUM_SCALAR >::unnormalizedJointPosterior_(NodeId
id) {
1488 if (_use_schedules_) {
1490 return _unnormalizedJointPosterior_(schedule,
id);
1492 return _unnormalizedJointPosterior_(
id);
1497 template < GUM_Numeric GUM_SCALAR >
1498 Tensor< GUM_SCALAR >*
1499 ShaferShenoyMRFInference< GUM_SCALAR >::_unnormalizedJointPosterior_(Schedule& schedule,
1501 const auto& mn = this->MRF();
1505 if (this->hardEvidenceNodes().
contains(
id)) {
1506 return new Tensor< GUM_SCALAR >(*(this->evidence()[
id]));
1509 auto& scheduler = this->scheduler();
1513 const NodeId clique_of_id = _node_to_clique_[id];
1514 _collectMessage_(schedule, clique_of_id, clique_of_id);
1519 _ScheduleMultiDimSet_ pot_list;
1520 if (_clique_ss_tensor_[clique_of_id] !=
nullptr)
1521 pot_list.insert(_clique_ss_tensor_[clique_of_id]);
1524 for (
const auto other: _JT_->neighbours(clique_of_id))
1525 pot_list.insert(_separator_tensors_[Arc(other, clique_of_id)]);
1528 const NodeSet& nodes = _JT_->clique(clique_of_id);
1531 for (
const auto node: nodes) {
1532 if (node !=
id) del_vars.
insert(&(mn.variable(node)));
1537 auto resulting_pot =
const_cast< ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
1538 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
1539 _marginalizeOut_(schedule, pot_list, del_vars, kept_vars)));
1540 Tensor< GUM_SCALAR >* joint =
nullptr;
1542 scheduler.execute(schedule);
1546 if (pot_list.exists(resulting_pot)) {
1547 joint =
new Tensor< GUM_SCALAR >(resulting_pot->multiDim());
1549 joint = resulting_pot->exportMultiDim();
1555 bool nonzero_found =
false;
1556 for (Instantiation inst(*joint); !inst.end(); ++inst) {
1557 if (joint->get(inst)) {
1558 nonzero_found =
true;
1562 if (!nonzero_found) {
1566 "some evidence entered into the Markov "
1567 "net are incompatible (their joint proba = 0)");
1573 template < GUM_Numeric GUM_SCALAR >
1574 Tensor< GUM_SCALAR >*
1575 ShaferShenoyMRFInference< GUM_SCALAR >::_unnormalizedJointPosterior_(NodeId
id) {
1576 const auto& mn = this->MRF();
1580 if (this->hardEvidenceNodes().
contains(
id)) {
1581 return new Tensor< GUM_SCALAR >(*(this->evidence()[
id]));
1586 NodeId clique_of_id = _node_to_clique_[id];
1587 _collectMessage_(clique_of_id, clique_of_id);
1592 _ScheduleMultiDimSet_ pot_list;
1593 if (_clique_ss_tensor_[clique_of_id] !=
nullptr)
1594 pot_list.insert(_clique_ss_tensor_[clique_of_id]);
1597 for (
const auto other: _JT_->neighbours(clique_of_id))
1598 pot_list.insert(_separator_tensors_[Arc(other, clique_of_id)]);
1601 const NodeSet& nodes = _JT_->clique(clique_of_id);
1604 for (
const auto node: nodes) {
1605 if (node !=
id) del_vars.
insert(&(mn.variable(node)));
1610 auto resulting_pot =
const_cast< ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
1611 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
1612 _marginalizeOut_(pot_list, del_vars, kept_vars)));
1613 Tensor< GUM_SCALAR >* joint =
nullptr;
1617 if (pot_list.exists(resulting_pot)) {
1618 joint =
new Tensor< GUM_SCALAR >(resulting_pot->multiDim());
1620 joint = resulting_pot->exportMultiDim();
1621 delete resulting_pot;
1627 bool nonzero_found =
false;
1628 for (Instantiation inst(*joint); !inst.end(); ++inst) {
1629 if (joint->get(inst)) {
1630 nonzero_found =
true;
1634 if (!nonzero_found) {
1638 "some evidence entered into the Markov "
1639 "net are incompatible (their joint proba = 0)");
1645 template < GUM_Numeric GUM_SCALAR >
1646 const Tensor< GUM_SCALAR >& ShaferShenoyMRFInference< GUM_SCALAR >::posterior_(NodeId
id) {
1648 if (
auto p = _target_posteriors_.tryGet(
id)) {
return *(*p); }
1651 auto joint = unnormalizedJointPosterior_(
id);
1652 if (joint->sum() != 1)
1654 _target_posteriors_.insert(
id, joint);
1660 template < GUM_Numeric GUM_SCALAR >
1661 Tensor< GUM_SCALAR >*
1662 ShaferShenoyMRFInference< GUM_SCALAR >::unnormalizedJointPosterior_(
const NodeSet& set) {
1663 if (_use_schedules_) {
1665 return _unnormalizedJointPosterior_(schedule, set);
1667 return _unnormalizedJointPosterior_(set);
1672 template < GUM_Numeric GUM_SCALAR >
1673 Tensor< GUM_SCALAR >*
1674 ShaferShenoyMRFInference< GUM_SCALAR >::_unnormalizedJointPosterior_(Schedule& schedule,
1675 const NodeSet& set) {
1678 NodeSet targets = set, hard_ev_nodes;
1679 for (
const auto node: this->hardEvidenceNodes()) {
1680 if (targets.contains(node)) {
1681 targets.erase(node);
1682 hard_ev_nodes.insert(node);
1686 auto& scheduler = this->scheduler();
1690 const auto& evidence = this->evidence();
1691 if (targets.empty()) {
1692 if (set.size() == 1) {
1693 return new Tensor< GUM_SCALAR >(*evidence[*set.begin()]);
1695 _ScheduleMultiDimSet_ pot_list;
1696 for (
const auto node: set) {
1697 auto new_pot_ev = schedule.insertTable< Tensor< GUM_SCALAR > >(*evidence[node],
false);
1698 pot_list.insert(new_pot_ev);
1702 MultiDimCombinationDefault< Tensor< GUM_SCALAR > > fast_combination(_combination_op_);
1703 const IScheduleMultiDim* pot = fast_combination.schedule(schedule, pot_list);
1704 auto schedule_pot =
const_cast< ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
1705 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(pot));
1706 scheduler.execute(schedule);
1707 auto result = schedule_pot->exportMultiDim();
1717 NodeId clique_of_set;
1718 if (
auto p_clique = _joint_target_to_clique_.tryGet(set)) {
1719 clique_of_set = *p_clique;
1726 for (
const auto node: targets) {
1727 if (!_graph_.exists(node)) {
1729 "The variable " << this->MRF().variable(node).name() <<
"(" << node
1730 <<
") does not belong to this optimized inference.")
1736 const std::vector< NodeId >& JT_elim_order = _triangulation_->eliminationOrder();
1737 NodeProperty< int > elim_order(Size(JT_elim_order.size()));
1738 for (std::size_t i = std::size_t(0), size = JT_elim_order.size(); i < size; ++i)
1739 elim_order.insert(JT_elim_order[i], (
int)i);
1740 NodeId first_eliminated_node = *(targets.begin());
1741 int elim_number = elim_order[first_eliminated_node];
1742 for (
const auto node: targets) {
1743 if (elim_order[node] < elim_number) {
1744 elim_number = elim_order[node];
1745 first_eliminated_node = node;
1749 clique_of_set = _triangulation_->createdJunctionTreeClique(first_eliminated_node);
1753 const NodeSet& clique_nodes = _JT_->clique(clique_of_set);
1754 for (
const auto node: targets) {
1755 if (!clique_nodes.contains(node)) {
1757 this->MRF().names(set) <<
"(" << set <<
")"
1758 <<
" is not addressable in this optimized inference.")
1763 _joint_target_to_clique_.
insert(set, clique_of_set);
1767 _collectMessage_(schedule, clique_of_set, clique_of_set);
1772 _ScheduleMultiDimSet_ pot_list;
1773 if (_clique_ss_tensor_[clique_of_set] !=
nullptr) {
1774 auto pot = _clique_ss_tensor_[clique_of_set];
1775 if (!schedule.existsScheduleMultiDim(pot->id())) schedule.emplaceScheduleMultiDim(*pot);
1776 pot_list.insert(_clique_ss_tensor_[clique_of_set]);
1780 for (
const auto other: _JT_->neighbours(clique_of_set)) {
1781 const auto pot = _separator_tensors_[Arc(other, clique_of_set)];
1782 if (pot !=
nullptr) pot_list.insert(pot);
1787 const NodeSet& nodes = _JT_->clique(clique_of_set);
1790 const auto& mn = this->MRF();
1791 for (
const auto node: nodes) {
1792 if (!targets.contains(node)) {
1793 del_vars.
insert(&(mn.variable(node)));
1795 kept_vars.
insert(&(mn.variable(node)));
1801 const IScheduleMultiDim* new_pot = _marginalizeOut_(schedule, pot_list, del_vars, kept_vars);
1802 scheduler.execute(schedule);
1803 ScheduleMultiDim< Tensor< GUM_SCALAR > >* resulting_pot
1804 =
const_cast< ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
1805 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(new_pot));
1809 Tensor< GUM_SCALAR >* joint =
nullptr;
1810 if (pot_list.exists(resulting_pot)) {
1811 joint =
new Tensor< GUM_SCALAR >(resulting_pot->multiDim());
1813 joint = resulting_pot->exportMultiDim();
1818 bool nonzero_found =
false;
1819 for (Instantiation inst(*joint); !inst.end(); ++inst) {
1820 if ((*joint)[inst]) {
1821 nonzero_found =
true;
1825 if (!nonzero_found) {
1829 "some evidence entered into the Markov "
1830 "net are incompatible (their joint proba = 0)");
1837 template < GUM_Numeric GUM_SCALAR >
1838 Tensor< GUM_SCALAR >*
1839 ShaferShenoyMRFInference< GUM_SCALAR >::_unnormalizedJointPosterior_(
const NodeSet& set) {
1842 NodeSet targets = set, hard_ev_nodes;
1843 for (
const auto node: this->hardEvidenceNodes()) {
1844 if (targets.contains(node)) {
1845 targets.erase(node);
1846 hard_ev_nodes.insert(node);
1852 const auto& evidence = this->evidence();
1853 if (targets.empty()) {
1854 if (set.size() == 1) {
1855 return new Tensor< GUM_SCALAR >(*evidence[*set.begin()]);
1857 _TensorSet_ pot_list;
1858 for (
const auto node: set) {
1859 pot_list.insert(evidence[node]);
1863 MultiDimCombinationDefault< Tensor< GUM_SCALAR > > fast_combination(_combination_op_);
1864 const Tensor< GUM_SCALAR >* pot = fast_combination.execute(pot_list);
1866 return const_cast< Tensor< GUM_SCALAR >*
>(pot);
1874 NodeId clique_of_set;
1875 if (
auto p_clique = _joint_target_to_clique_.tryGet(set)) {
1876 clique_of_set = *p_clique;
1883 for (
const auto node: targets) {
1884 if (!_graph_.exists(node)) {
1886 "The variable " << this->MRF().variable(node).name() <<
"(" << node
1887 <<
") does not belong to this optimized inference.")
1893 const std::vector< NodeId >& JT_elim_order = _triangulation_->eliminationOrder();
1894 NodeProperty< int > elim_order(Size(JT_elim_order.size()));
1895 for (std::size_t i = std::size_t(0), size = JT_elim_order.size(); i < size; ++i)
1896 elim_order.insert(JT_elim_order[i], (
int)i);
1897 NodeId first_eliminated_node = *(targets.begin());
1898 int elim_number = elim_order[first_eliminated_node];
1899 for (
const auto node: targets) {
1900 if (elim_order[node] < elim_number) {
1901 elim_number = elim_order[node];
1902 first_eliminated_node = node;
1906 clique_of_set = _triangulation_->createdJunctionTreeClique(first_eliminated_node);
1910 const NodeSet& clique_nodes = _JT_->clique(clique_of_set);
1911 for (
const auto node: targets) {
1912 if (!clique_nodes.contains(node)) {
1918 _joint_target_to_clique_.
insert(set, clique_of_set);
1922 _collectMessage_(clique_of_set, clique_of_set);
1927 _ScheduleMultiDimSet_ pot_list;
1928 if (_clique_ss_tensor_[clique_of_set] !=
nullptr) {
1929 auto pot = _clique_ss_tensor_[clique_of_set];
1930 if (pot !=
nullptr) pot_list.insert(_clique_ss_tensor_[clique_of_set]);
1934 for (
const auto other: _JT_->neighbours(clique_of_set)) {
1935 const auto pot = _separator_tensors_[Arc(other, clique_of_set)];
1936 if (pot !=
nullptr) pot_list.insert(pot);
1941 const NodeSet& nodes = _JT_->clique(clique_of_set);
1944 const auto& mn = this->MRF();
1945 for (
const auto node: nodes) {
1946 if (!targets.contains(node)) {
1947 del_vars.
insert(&(mn.variable(node)));
1949 kept_vars.
insert(&(mn.variable(node)));
1955 const IScheduleMultiDim* new_pot = _marginalizeOut_(pot_list, del_vars, kept_vars);
1956 ScheduleMultiDim< Tensor< GUM_SCALAR > >* resulting_pot
1957 =
const_cast< ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(
1958 static_cast< const ScheduleMultiDim< Tensor< GUM_SCALAR >
>* >(new_pot));
1962 Tensor< GUM_SCALAR >* joint =
nullptr;
1963 if (pot_list.exists(resulting_pot)) {
1964 joint =
new Tensor< GUM_SCALAR >(resulting_pot->multiDim());
1966 joint = resulting_pot->exportMultiDim();
1972 bool nonzero_found =
false;
1973 for (Instantiation inst(*joint); !inst.end(); ++inst) {
1974 if ((*joint)[inst]) {
1975 nonzero_found =
true;
1979 if (!nonzero_found) {
1983 "some evidence entered into the Markov "
1984 "net are incompatible (their joint proba = 0)");
1991 template < GUM_Numeric GUM_SCALAR >
1992 const Tensor< GUM_SCALAR >&
1993 ShaferShenoyMRFInference< GUM_SCALAR >::jointPosterior_(
const NodeSet& set) {
1995 if (
auto p = _joint_target_posteriors_.tryGet(set)) {
return *(*p); }
1998 auto joint = unnormalizedJointPosterior_(set);
2000 _joint_target_posteriors_.insert(set, joint);
2006 template < GUM_Numeric GUM_SCALAR >
2007 const Tensor< GUM_SCALAR >&
2008 ShaferShenoyMRFInference< GUM_SCALAR >::jointPosterior_(
const NodeSet& wanted_target,
2009 const NodeSet& declared_target) {
2011 if (
auto p = _joint_target_posteriors_.tryGet(wanted_target))
return *(*p);
2017 if (!_joint_target_posteriors_.exists(declared_target)) {
2018 return jointPosterior_(declared_target);
2022 const auto& mn = this->MRF();
2024 for (
const auto node: declared_target)
2025 if (!wanted_target.contains(node)) del_vars.
insert(&(mn.variable(node)));
2027 =
new Tensor< GUM_SCALAR >(_joint_target_posteriors_[declared_target]->sumOut(del_vars));
2030 _joint_target_posteriors_.insert(wanted_target, pot);
2035 template < GUM_Numeric GUM_SCALAR >
2036 GUM_SCALAR ShaferShenoyMRFInference< GUM_SCALAR >::evidenceProbability() {
2038 this->makeInference();
2046 GUM_SCALAR prob_ev = 1;
2047 for (
const auto root: _roots_) {
2049 NodeId node = *(_JT_->clique(root).begin());
2050 Tensor< GUM_SCALAR >* tmp = unnormalizedJointPosterior_(node);
2051 prob_ev *= tmp->sum();
2055 for (
const auto& projected_cpt: _constants_)
2056 prob_ev *= projected_cpt.second;
2061 template < GUM_Numeric GUM_SCALAR >
2062 bool ShaferShenoyMRFInference< GUM_SCALAR >::isExactJointComputable_(
const NodeSet& vars) {
2063 if (JointTargetedMRFInference< GUM_SCALAR >::isExactJointComputable_(vars))
return true;
2065 this->prepareInference();
2067 for (
const auto& node: this->_JT_->nodes()) {
2068 const auto clique = _JT_->clique(node);
2069 if (vars == clique)
return true;
2074 template < GUM_Numeric GUM_SCALAR >
2075 NodeSet ShaferShenoyMRFInference< GUM_SCALAR >::superForJointComputable_(
const NodeSet& vars) {
2076 const auto superset = JointTargetedMRFInference< GUM_SCALAR >::superForJointComputable_(vars);
2077 if (!superset.empty())
return superset;
2079 this->prepareInference();
2081 for (
const auto& node: _JT_->nodes()) {
2082 const auto clique = _JT_->clique(node);
2083 if (vars.isStrictSubsetOf(clique))
return clique;
2091 template < GUM_Numeric GUM_SCALAR >
2092 static Tensor< GUM_SCALAR > SSNewMNmultiTensor(
const Tensor< GUM_SCALAR >& t1,
2093 const Tensor< GUM_SCALAR >& t2) {
2098 template < GUM_Numeric GUM_SCALAR >
2099 static Tensor< GUM_SCALAR > SSNewMNprojTensor(
const Tensor< GUM_SCALAR >& t1,
2101 return t1.sumOut(del_vars);
2104 template < GUM_Numeric GUM_SCALAR >
2105 void ShaferShenoyMRFInference< GUM_SCALAR >::onStateChanged_() {}
Implementation of Shafer-Shenoy's algorithm for inference in Markov random fields.
An algorithm for converting a join tree into a binary join tree.
Exception : a similar element already exists.
<agrum/MRF/inference/evidenceMRFInference.h>
Exception : fatal (unknown ?) error.
Class representing the minimal interface for Markov random field.
Exception : several evidence are incompatible together (proba=0).
<agrum/MRF/inference/jointTargetedMRFInference.h>
void insert(const Key &k)
Inserts a new element into the set.
ShaferShenoyMRFInference(const IMarkovRandomField< GUM_SCALAR > *MN, bool use_binary_join_tree=true)
default constructor
Exception : a looked-for element could not be found.
#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
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...