26 #ifndef DOXYGEN_SHOULD_SKIP_THIS 32 # define LBP_DEFAULT_MAXITER 100
33 # define LBP_DEFAULT_EPSILON 1e-8
34 # define LBP_DEFAULT_MIN_EPSILON_RATE 1e-10
35 # define LBP_DEFAULT_PERIOD_SIZE 1
36 # define LBP_DEFAULT_VERBOSITY false 40 # include <agrum/BN/inference/loopyBeliefPropagation.h> 46 template <
typename GUM_SCALAR >
47 LoopyBeliefPropagation< GUM_SCALAR >::LoopyBeliefPropagation(
const IBayesNet< GUM_SCALAR >* bn) :
48 ApproximateInference< GUM_SCALAR >(bn) {
50 GUM_CONSTRUCTOR(LoopyBeliefPropagation);
52 this->setEpsilon(LBP_DEFAULT_EPSILON);
53 this->setMinEpsilonRate(LBP_DEFAULT_MIN_EPSILON_RATE);
54 this->setMaxIter(LBP_DEFAULT_MAXITER);
55 this->setVerbosity(LBP_DEFAULT_VERBOSITY);
56 this->setPeriodSize(LBP_DEFAULT_PERIOD_SIZE);
62 template <
typename GUM_SCALAR >
63 INLINE LoopyBeliefPropagation< GUM_SCALAR >::~LoopyBeliefPropagation() {
64 GUM_DESTRUCTOR(LoopyBeliefPropagation);
68 template <
typename GUM_SCALAR >
69 void LoopyBeliefPropagation< GUM_SCALAR >::_init_messages_() {
71 for (
const auto& tail:
this->BN().nodes()) {
72 Potential< GUM_SCALAR > p;
73 p.add(
this->BN().variable(tail));
74 p.fill(
static_cast< GUM_SCALAR >(1));
76 for (
const auto& head:
this->BN().children(tail)) {
77 _messages_.insert(Arc(head, tail), p);
78 _messages_.insert(Arc(tail, head), p);
83 template <
typename GUM_SCALAR >
84 void LoopyBeliefPropagation< GUM_SCALAR >::updateOutdatedStructure_() {
89 template <
typename GUM_SCALAR >
90 Potential< GUM_SCALAR > LoopyBeliefPropagation< GUM_SCALAR >::_computeProdPi_(NodeId X) {
91 const auto& varX =
this->BN().variable(X);
93 auto piX =
this->BN().cpt(X);
94 for (
const auto& U:
this->BN().parents(X)) {
95 piX *= _messages_[Arc(U, X)];
97 piX = piX.margSumIn({&varX});
102 template <
typename GUM_SCALAR >
103 Potential< GUM_SCALAR > LoopyBeliefPropagation< GUM_SCALAR >::_computeProdPi_(NodeId X,
105 const auto& varX =
this->BN().variable(X);
106 const auto& varExcept =
this->BN().variable(except);
107 auto piXexcept =
this->BN().cpt(X);
108 for (
const auto& U:
this->BN().parents(X)) {
109 if (U != except) { piXexcept *= _messages_[Arc(U, X)]; }
111 piXexcept = piXexcept.margSumIn({&varX, &varExcept});
116 template <
typename GUM_SCALAR >
117 Potential< GUM_SCALAR > LoopyBeliefPropagation< GUM_SCALAR >::_computeProdLambda_(NodeId X) {
118 Potential< GUM_SCALAR > lamX;
119 if (
this->hasEvidence(X)) {
120 lamX = *(
this->evidence()[X]);
122 lamX.add(
this->BN().variable(X));
125 for (
const auto& Y:
this->BN().children(X)) {
126 lamX *= _messages_[Arc(Y, X)];
132 template <
typename GUM_SCALAR >
133 Potential< GUM_SCALAR > LoopyBeliefPropagation< GUM_SCALAR >::_computeProdLambda_(NodeId X,
135 Potential< GUM_SCALAR > lamXexcept;
136 if (
this->hasEvidence(X)) {
137 lamXexcept = *
this->evidence()[X];
139 lamXexcept.add(
this->BN().variable(X));
142 for (
const auto& Y:
this->BN().children(X)) {
143 if (Y != except) { lamXexcept *= _messages_[Arc(Y, X)]; }
150 template <
typename GUM_SCALAR >
151 GUM_SCALAR LoopyBeliefPropagation< GUM_SCALAR >::_updateNodeMessage_(NodeId X) {
152 auto piX = _computeProdPi_(X);
153 auto lamX = _computeProdLambda_(X);
159 for (
const auto& U:
this->BN().parents(X)) {
160 auto newLambda = (_computeProdPi_(X, U) * lamX).margSumIn({&
this->BN().variable(U)});
161 newLambda.normalize();
162 auto ekl =
static_cast< GUM_SCALAR >(0);
164 ekl = _messages_[Arc(X, U)].KL(newLambda);
165 }
catch (InvalidArgument&) {
166 GUM_ERROR(InvalidArgument,
"Not compatible pi during computation")
167 }
catch (FatalError&) {
168 ekl = std::numeric_limits< GUM_SCALAR >::infinity();
174 _messages_.set(Arc(X, U), newLambda);
178 for (
const auto& Y:
this->BN().children(X)) {
179 auto newPi = (piX * _computeProdLambda_(X, Y));
183 ekl = _messages_[Arc(X, Y)].KL(newPi);
184 }
catch (InvalidArgument&) {
185 GUM_ERROR(InvalidArgument,
"Not compatible pi during computation")
186 }
catch (FatalError&) {
187 ekl = std::numeric_limits< GUM_SCALAR >::infinity();
193 _messages_.set(Arc(X, Y), newPi);
199 template <
typename GUM_SCALAR >
200 INLINE
void LoopyBeliefPropagation< GUM_SCALAR >::_initStats_() {
202 for (
const auto& node:
this->BN().topologicalOrder()) {
203 _updateNodeMessage_(node);
209 template <
typename GUM_SCALAR >
210 void LoopyBeliefPropagation< GUM_SCALAR >::makeInference_() {
212 this->initApproximationScheme();
214 std::vector< NodeId > shuffleIds;
215 for (
const auto& node:
this->BN().nodes())
216 shuffleIds.push_back(node);
218 auto engine = std::default_random_engine{};
220 GUM_SCALAR error = 0.0;
222 std::shuffle(std::begin(shuffleIds), std::end(shuffleIds), engine);
223 this->updateApproximationScheme();
224 for (
const auto& node: shuffleIds) {
225 GUM_SCALAR e = _updateNodeMessage_(node);
226 if (e > error) error = e;
228 }
while (
this->continueApproximationScheme(error));
233 template <
typename GUM_SCALAR >
234 INLINE
const Potential< GUM_SCALAR >&
235 LoopyBeliefPropagation< GUM_SCALAR >::posterior_(NodeId id) {
236 auto p = _computeProdPi_(id) * _computeProdLambda_(id);
238 _posteriors_.set(id, p);
240 return _posteriors_[id];