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(
48 const IBayesNet< GUM_SCALAR >* bn) :
49 ApproximateInference< GUM_SCALAR >(bn) {
51 GUM_CONSTRUCTOR(LoopyBeliefPropagation);
53 this->setEpsilon(LBP_DEFAULT_EPSILON);
54 this->setMinEpsilonRate(LBP_DEFAULT_MIN_EPSILON_RATE);
55 this->setMaxIter(LBP_DEFAULT_MAXITER);
56 this->setVerbosity(LBP_DEFAULT_VERBOSITY);
57 this->setPeriodSize(LBP_DEFAULT_PERIOD_SIZE);
63 template <
typename GUM_SCALAR >
64 INLINE LoopyBeliefPropagation< GUM_SCALAR >::~LoopyBeliefPropagation() {
65 GUM_DESTRUCTOR(LoopyBeliefPropagation);
69 template <
typename GUM_SCALAR >
70 void LoopyBeliefPropagation< GUM_SCALAR >::init_messages__() {
72 for (
const auto& tail:
this->BN().nodes()) {
73 Potential< GUM_SCALAR > p;
74 p.add(
this->BN().variable(tail));
75 p.fill(
static_cast< GUM_SCALAR >(1));
77 for (
const auto& head:
this->BN().children(tail)) {
78 messages__.insert(Arc(head, tail), p);
79 messages__.insert(Arc(tail, head), p);
84 template <
typename GUM_SCALAR >
85 void LoopyBeliefPropagation< GUM_SCALAR >::updateOutdatedStructure_() {
90 template <
typename GUM_SCALAR >
91 Potential< GUM_SCALAR >
92 LoopyBeliefPropagation< GUM_SCALAR >::computeProdPi__(NodeId X) {
93 const auto& varX =
this->BN().variable(X);
95 auto piX =
this->BN().cpt(X);
96 for (
const auto& U:
this->BN().parents(X)) {
97 piX *= messages__[Arc(U, X)];
99 piX = piX.margSumIn({&varX});
104 template <
typename GUM_SCALAR >
105 Potential< GUM_SCALAR >
106 LoopyBeliefPropagation< GUM_SCALAR >::computeProdPi__(NodeId X,
108 const auto& varX =
this->BN().variable(X);
109 const auto& varExcept =
this->BN().variable(except);
110 auto piXexcept =
this->BN().cpt(X);
111 for (
const auto& U:
this->BN().parents(X)) {
112 if (U != except) { piXexcept *= messages__[Arc(U, X)]; }
114 piXexcept = piXexcept.margSumIn({&varX, &varExcept});
119 template <
typename GUM_SCALAR >
120 Potential< GUM_SCALAR >
121 LoopyBeliefPropagation< GUM_SCALAR >::computeProdLambda__(NodeId X) {
122 Potential< GUM_SCALAR > lamX;
123 if (
this->hasEvidence(X)) {
124 lamX = *(
this->evidence()[X]);
126 lamX.add(
this->BN().variable(X));
129 for (
const auto& Y:
this->BN().children(X)) {
130 lamX *= messages__[Arc(Y, X)];
136 template <
typename GUM_SCALAR >
137 Potential< GUM_SCALAR >
138 LoopyBeliefPropagation< GUM_SCALAR >::computeProdLambda__(NodeId X,
140 Potential< GUM_SCALAR > lamXexcept;
141 if (
this->hasEvidence(X)) {
142 lamXexcept = *
this->evidence()[X];
144 lamXexcept.add(
this->BN().variable(X));
147 for (
const auto& Y:
this->BN().children(X)) {
148 if (Y != except) { lamXexcept *= messages__[Arc(Y, X)]; }
155 template <
typename GUM_SCALAR >
156 GUM_SCALAR LoopyBeliefPropagation< GUM_SCALAR >::updateNodeMessage__(NodeId X) {
157 auto piX = computeProdPi__(X);
158 auto lamX = computeProdLambda__(X);
164 for (
const auto& U:
this->BN().parents(X)) {
166 = (computeProdPi__(X, U) * lamX).margSumIn({&
this->BN().variable(U)});
167 newLambda.normalize();
168 auto ekl =
static_cast< GUM_SCALAR >(0);
170 ekl = messages__[Arc(X, U)].KL(newLambda);
171 }
catch (InvalidArgument&) {
172 GUM_ERROR(InvalidArgument,
"Not compatible pi during computation");
173 }
catch (FatalError&) {
174 ekl = std::numeric_limits< GUM_SCALAR >::infinity();
180 messages__.set(Arc(X, U), newLambda);
184 for (
const auto& Y:
this->BN().children(X)) {
185 auto newPi = (piX * computeProdLambda__(X, Y));
189 ekl = messages__[Arc(X, Y)].KL(newPi);
190 }
catch (InvalidArgument&) {
191 GUM_ERROR(InvalidArgument,
"Not compatible pi during computation");
192 }
catch (FatalError&) {
193 ekl = std::numeric_limits< GUM_SCALAR >::infinity();
199 messages__.set(Arc(X, Y), newPi);
205 template <
typename GUM_SCALAR >
206 INLINE
void LoopyBeliefPropagation< GUM_SCALAR >::initStats__() {
208 for (
const auto& node:
this->BN().topologicalOrder()) {
209 updateNodeMessage__(node);
215 template <
typename GUM_SCALAR >
216 void LoopyBeliefPropagation< GUM_SCALAR >::makeInference_() {
218 this->initApproximationScheme();
220 std::vector< NodeId > shuffleIds;
221 for (
const auto& node:
this->BN().nodes())
222 shuffleIds.push_back(node);
224 auto engine = std::default_random_engine{};
226 GUM_SCALAR error = 0.0;
228 std::shuffle(std::begin(shuffleIds), std::end(shuffleIds), engine);
229 this->updateApproximationScheme();
230 for (
const auto& node: shuffleIds) {
231 GUM_SCALAR e = updateNodeMessage__(node);
232 if (e > error) error = e;
234 }
while (
this->continueApproximationScheme(error));
239 template <
typename GUM_SCALAR >
240 INLINE
const Potential< GUM_SCALAR >&
241 LoopyBeliefPropagation< GUM_SCALAR >::posterior_(NodeId id) {
242 auto p = computeProdPi__(id) * computeProdLambda__(id);
244 posteriors__.set(id, p);
246 return posteriors__[id];