Small documentation improvements
[libdai.git] / src / bbp.cpp
1 /* Copyright (C) 2009 Frederik Eaton [frederik at ofb dot net]
2
3 This file is part of libDAI.
4
5 libDAI is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 libDAI is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with libDAI; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20
21 #include <dai/bp.h>
22 #include <dai/bbp.h>
23 #include <dai/gibbs.h>
24 #include <dai/util.h>
25 #include <dai/bipgraph.h>
26
27
28 namespace dai {
29
30
31 using namespace std;
32
33
34 /// Convenience typedef
35 typedef BipartiteGraph::Neighbor Neighbor;
36
37
38 Prob unnormAdjoint( const Prob &w, Real Z_w, const Prob &adj_w ) {
39 assert( w.size() == adj_w.size() );
40 Prob adj_w_unnorm( w.size(), 0.0 );
41 Real s = 0.0;
42 for( size_t i = 0; i < w.size(); i++ )
43 s += w[i] * adj_w[i];
44 for( size_t i = 0; i < w.size(); i++ )
45 adj_w_unnorm[i] = (adj_w[i] - s) / Z_w;
46 return adj_w_unnorm;
47 // THIS WOULD BE ABOUT 50% SLOWER: return (adj_w - (w * adj_w).sum()) / Z_w;
48 }
49
50
51 std::vector<size_t> getGibbsState( const InfAlg &ia, size_t iters ) {
52 PropertySet gibbsProps;
53 gibbsProps.Set("iters", iters);
54 gibbsProps.Set("verbose", size_t(0));
55 Gibbs gibbs( ia.fg(), gibbsProps );
56 gibbs.run();
57 return gibbs.state();
58 }
59
60
61 /// Returns the entry of the I'th factor corresponding to a global state
62 size_t getFactorEntryForState( const FactorGraph &fg, size_t I, const vector<size_t> &state ) {
63 size_t f_entry = 0;
64 for( int _j = fg.nbF(I).size() - 1; _j >= 0; _j-- ) {
65 // note that iterating over nbF(I) yields the same ordering
66 // of variables as iterating over factor(I).vars()
67 size_t j = fg.nbF(I)[_j];
68 f_entry *= fg.var(j).states();
69 f_entry += state[j];
70 }
71 return f_entry;
72 }
73
74
75 #define LOOP_ij(body) { \
76 size_t i_states = _fg->var(i).states(); \
77 size_t j_states = _fg->var(j).states(); \
78 if(_fg->var(i) > _fg->var(j)) { \
79 size_t xij=0; \
80 for(size_t xi=0; xi<i_states; xi++) { \
81 for(size_t xj=0; xj<j_states; xj++) { \
82 body; \
83 xij++; \
84 } \
85 } \
86 } else { \
87 size_t xij=0; \
88 for(size_t xj=0; xj<j_states; xj++) { \
89 for(size_t xi=0; xi<i_states; xi++) { \
90 body; \
91 xij++; \
92 } \
93 } \
94 } \
95 }
96
97
98 void BBP::RegenerateInds() {
99 // initialise _indices
100 // typedef std::vector<size_t> _ind_t;
101 // std::vector<std::vector<_ind_t> > _indices;
102 _indices.resize( _fg->nrVars() );
103 for( size_t i = 0; i < _fg->nrVars(); i++ ) {
104 _indices[i].clear();
105 _indices[i].reserve( _fg->nbV(i).size() );
106 foreach( const Neighbor &I, _fg->nbV(i) ) {
107 _ind_t index;
108 index.reserve( _fg->factor(I).states() );
109 for( IndexFor k(_fg->var(i), _fg->factor(I).vars()); k >= 0; ++k )
110 index.push_back( k );
111 _indices[i].push_back( index );
112 }
113 }
114 }
115
116
117 void BBP::RegenerateT() {
118 // _T[i][_I]
119 _T.resize( _fg->nrVars() );
120 for( size_t i = 0; i < _fg->nrVars(); i++ ) {
121 _T[i].resize( _fg->nbV(i).size() );
122 foreach( const Neighbor &I, _fg->nbV(i) ) {
123 Prob prod( _fg->var(i).states(), 1.0 );
124 foreach( const Neighbor &J, _fg->nbV(i) )
125 if( J.node != I.node )
126 prod *= _bp_dual.msgM( i, J.iter );
127 _T[i][I.iter] = prod;
128 }
129 }
130 }
131
132
133 void BBP::RegenerateU() {
134 // _U[I][_i]
135 _U.resize( _fg->nrFactors() );
136 for( size_t I = 0; I < _fg->nrFactors(); I++ ) {
137 _U[I].resize( _fg->nbF(I).size() );
138 foreach( const Neighbor &i, _fg->nbF(I) ) {
139 Prob prod( _fg->factor(I).states(), 1.0 );
140 foreach( const Neighbor &j, _fg->nbF(I) )
141 if( i.node != j.node ) {
142 Prob n_jI( _bp_dual.msgN( j, j.dual ) );
143 const _ind_t &ind = _index( j, j.dual );
144 // multiply prod by n_jI
145 for( size_t x_I = 0; x_I < prod.size(); x_I++ )
146 prod[x_I] *= n_jI[ind[x_I]];
147 }
148 _U[I][i.iter] = prod;
149 }
150 }
151 }
152
153
154 void BBP::RegenerateS() {
155 // _S[i][_I][_j]
156 _S.resize( _fg->nrVars() );
157 for( size_t i = 0; i < _fg->nrVars(); i++ ) {
158 _S[i].resize( _fg->nbV(i).size() );
159 foreach( const Neighbor &I, _fg->nbV(i) ) {
160 _S[i][I.iter].resize( _fg->nbF(I).size() );
161 foreach( const Neighbor &j, _fg->nbF(I) )
162 if( i != j ) {
163 Factor prod( _fg->factor(I) );
164 foreach( const Neighbor &k, _fg->nbF(I) ) {
165 if( k != i && k.node != j.node ) {
166 const _ind_t &ind = _index( k, k.dual );
167 Prob p( _bp_dual.msgN( k, k.dual ) );
168 for( size_t x_I = 0; x_I < prod.states(); x_I++ )
169 prod.p()[x_I] *= p[ind[x_I]];
170 }
171 }
172 // "Marginalize" onto i|j (unnormalized)
173 Prob marg;
174 marg = prod.marginal( VarSet(_fg->var(i), _fg->var(j)), false ).p();
175 _S[i][I.iter][j.iter] = marg;
176 }
177 }
178 }
179 }
180
181
182 void BBP::RegenerateR() {
183 // _R[I][_i][_J]
184 _R.resize( _fg->nrFactors() );
185 for( size_t I = 0; I < _fg->nrFactors(); I++ ) {
186 _R[I].resize( _fg->nbF(I).size() );
187 foreach( const Neighbor &i, _fg->nbF(I) ) {
188 _R[I][i.iter].resize( _fg->nbV(i).size() );
189 foreach( const Neighbor &J, _fg->nbV(i) ) {
190 if( I != J ) {
191 Prob prod( _fg->var(i).states(), 1.0 );
192 foreach( const Neighbor &K, _fg->nbV(i) )
193 if( K.node != I && K.node != J.node )
194 prod *= _bp_dual.msgM( i, K.iter );
195 _R[I][i.iter][J.iter] = prod;
196 }
197 }
198 }
199 }
200 }
201
202
203 void BBP::RegenerateInputs() {
204 _adj_b_V_unnorm.clear();
205 _adj_b_V_unnorm.reserve( _fg->nrVars() );
206 for( size_t i = 0; i < _fg->nrVars(); i++ )
207 _adj_b_V_unnorm.push_back( unnormAdjoint( _bp_dual.beliefV(i).p(), _bp_dual.beliefVZ(i), _adj_b_V[i] ) );
208 _adj_b_F_unnorm.clear();
209 _adj_b_F_unnorm.reserve( _fg->nrFactors() );
210 for( size_t I = 0; I < _fg->nrFactors(); I++ )
211 _adj_b_F_unnorm.push_back( unnormAdjoint( _bp_dual.beliefF(I).p(), _bp_dual.beliefFZ(I), _adj_b_F[I] ) );
212 }
213
214
215 void BBP::RegeneratePsiAdjoints() {
216 _adj_psi_V.clear();
217 _adj_psi_V.reserve( _fg->nrVars() );
218 for( size_t i = 0; i < _fg->nrVars(); i++ ) {
219 Prob p( _adj_b_V_unnorm[i] );
220 assert( p.size() == _fg->var(i).states() );
221 foreach( const Neighbor &I, _fg->nbV(i) )
222 p *= _bp_dual.msgM( i, I.iter );
223 p += _init_adj_psi_V[i];
224 _adj_psi_V.push_back( p );
225 }
226 _adj_psi_F.clear();
227 _adj_psi_F.reserve( _fg->nrFactors() );
228 for( size_t I = 0; I < _fg->nrFactors(); I++ ) {
229 Prob p( _adj_b_F_unnorm[I] );
230 assert( p.size() == _fg->factor(I).states() );
231 foreach( const Neighbor &i, _fg->nbF(I) ) {
232 Prob n_iI( _bp_dual.msgN( i, i.dual ) );
233 const _ind_t& ind = _index( i, i.dual );
234 // multiply prod with n_jI
235 for( size_t x_I = 0; x_I < p.size(); x_I++ )
236 p[x_I] *= n_iI[ind[x_I]];
237 }
238 p += _init_adj_psi_F[I];
239 _adj_psi_F.push_back( p );
240 }
241 }
242
243
244 void BBP::RegenerateParMessageAdjoints() {
245 size_t nv = _fg->nrVars();
246 _adj_n.resize( nv );
247 _adj_m.resize( nv );
248 _adj_n_unnorm.resize( nv );
249 _adj_m_unnorm.resize( nv );
250 _new_adj_n.resize( nv );
251 _new_adj_m.resize( nv );
252 for( size_t i = 0; i < _fg->nrVars(); i++ ) {
253 size_t n_i = _fg->nbV(i).size();
254 _adj_n[i].resize( n_i );
255 _adj_m[i].resize( n_i );
256 _adj_n_unnorm[i].resize( n_i );
257 _adj_m_unnorm[i].resize( n_i );
258 _new_adj_n[i].resize( n_i );
259 _new_adj_m[i].resize( n_i );
260 foreach( const Neighbor &I, _fg->nbV(i) ) {
261 { // calculate adj_n
262 Prob prod( _fg->factor(I).p() );
263 prod *= _adj_b_F_unnorm[I];
264 foreach( const Neighbor &j, _fg->nbF(I) )
265 if( i != j ) {
266 Prob n_jI( _bp_dual.msgN( j, j.dual ) );
267 const _ind_t &ind = _index( j, j.dual );
268 // multiply prod with n_jI
269 for( size_t x_I = 0; x_I < prod.size(); x_I++ )
270 prod[x_I] *= n_jI[ind[x_I]];
271 }
272 Prob marg( _fg->var(i).states(), 0.0 );
273 const _ind_t &ind = _index( i, I.iter );
274 for( size_t r = 0; r < prod.size(); r++ )
275 marg[ind[r]] += prod[r];
276 _new_adj_n[i][I.iter] = marg;
277 upMsgN( i, I.iter );
278 }
279
280 { // calculate adj_m
281 Prob prod( _adj_b_V_unnorm[i] );
282 assert( prod.size() == _fg->var(i).states() );
283 foreach( const Neighbor &J, _fg->nbV(i) )
284 if( J.node != I.node )
285 prod *= _bp_dual.msgM(i,J.iter);
286 _new_adj_m[i][I.iter] = prod;
287 upMsgM( i, I.iter );
288 }
289 }
290 }
291 }
292
293
294 void BBP::RegenerateSeqMessageAdjoints() {
295 size_t nv = _fg->nrVars();
296 _adj_m.resize( nv );
297 _adj_m_unnorm.resize( nv );
298 _new_adj_m.resize( nv );
299 for( size_t i = 0; i < _fg->nrVars(); i++ ) {
300 size_t n_i = _fg->nbV(i).size();
301 _adj_m[i].resize( n_i );
302 _adj_m_unnorm[i].resize( n_i );
303 _new_adj_m[i].resize( n_i );
304 foreach( const Neighbor &I, _fg->nbV(i) ) {
305 // calculate adj_m
306 Prob prod( _adj_b_V_unnorm[i] );
307 assert( prod.size() == _fg->var(i).states() );
308 foreach( const Neighbor &J, _fg->nbV(i) )
309 if( J.node != I.node )
310 prod *= _bp_dual.msgM( i, J.iter );
311 _adj_m[i][I.iter] = prod;
312 calcUnnormMsgM( i, I.iter );
313 _new_adj_m[i][I.iter] = Prob( _fg->var(i).states(), 0.0 );
314 }
315 }
316 for( size_t i = 0; i < _fg->nrVars(); i++ ) {
317 foreach( const Neighbor &I, _fg->nbV(i) ) {
318 // calculate adj_n
319 Prob prod( _fg->factor(I).p() );
320 prod *= _adj_b_F_unnorm[I];
321 foreach( const Neighbor &j, _fg->nbF(I) )
322 if( i != j ) {
323 Prob n_jI( _bp_dual.msgN( j, j.dual) );
324 const _ind_t& ind = _index( j, j.dual );
325 // multiply prod with n_jI
326 for( size_t x_I = 0; x_I < prod.size(); x_I++ )
327 prod[x_I] *= n_jI[ind[x_I]];
328 }
329 Prob marg( _fg->var(i).states(), 0.0 );
330 const _ind_t &ind = _index( i, I.iter );
331 for( size_t r = 0; r < prod.size(); r++ )
332 marg[ind[r]] += prod[r];
333 sendSeqMsgN( i, I.iter,marg );
334 }
335 }
336 }
337
338
339 void BBP::calcNewN( size_t i, size_t _I ) {
340 _adj_psi_V[i] += T(i,_I) * _adj_n_unnorm[i][_I];
341 Prob &new_adj_n_iI = _new_adj_n[i][_I];
342 new_adj_n_iI = Prob( _fg->var(i).states(), 0.0 );
343 size_t I = _fg->nbV(i)[_I];
344 foreach( const Neighbor &j, _fg->nbF(I) )
345 if( j != i ) {
346 const Prob &p = _S[i][_I][j.iter];
347 const Prob &_adj_m_unnorm_jI = _adj_m_unnorm[j][j.dual];
348 LOOP_ij(
349 new_adj_n_iI[xi] += p[xij] * _adj_m_unnorm_jI[xj];
350 );
351 /* THE FOLLOWING WOULD BE ABOUT TWICE AS SLOW:
352 Var vi = _fg->var(i);
353 Var vj = _fg->var(j);
354 new_adj_n_iI = (Factor(VarSet(vi, vj), p) * Factor(vj,_adj_m_unnorm_jI)).marginal(vi,false).p();
355 */
356 }
357 }
358
359
360 void BBP::calcNewM( size_t i, size_t _I ) {
361 const Neighbor &I = _fg->nbV(i)[_I];
362 Prob p( U(I, I.dual) );
363 const Prob &adj = _adj_m_unnorm[i][_I];
364 const _ind_t &ind = _index(i,_I);
365 for( size_t x_I = 0; x_I < p.size(); x_I++ )
366 p[x_I] *= adj[ind[x_I]];
367 _adj_psi_F[I] += p;
368 /* THE FOLLOWING WOULD BE SLIGHTLY SLOWER:
369 _adj_psi_F[I] += (Factor( _fg->factor(I).vars(), U(I, I.dual) ) * Factor( _fg->var(i), _adj_m_unnorm[i][_I] )).p();
370 */
371
372 _new_adj_m[i][_I] = Prob( _fg->var(i).states(), 0.0 );
373 foreach( const Neighbor &J, _fg->nbV(i) )
374 if( J != I )
375 _new_adj_m[i][_I] += _R[I][I.dual][J.iter] * _adj_n_unnorm[i][J.iter];
376 }
377
378
379 void BBP::calcUnnormMsgN( size_t i, size_t _I ) {
380 _adj_n_unnorm[i][_I] = unnormAdjoint( _bp_dual.msgN(i,_I), _bp_dual.zN(i,_I), _adj_n[i][_I] );
381 }
382
383
384 void BBP::calcUnnormMsgM( size_t i, size_t _I ) {
385 _adj_m_unnorm[i][_I] = unnormAdjoint( _bp_dual.msgM(i,_I), _bp_dual.zM(i,_I), _adj_m[i][_I] );
386 }
387
388
389 void BBP::upMsgN( size_t i, size_t _I ) {
390 _adj_n[i][_I] = _new_adj_n[i][_I];
391 calcUnnormMsgN( i, _I );
392 }
393
394
395 void BBP::upMsgM( size_t i, size_t _I ) {
396 _adj_m[i][_I] = _new_adj_m[i][_I];
397 calcUnnormMsgM( i, _I );
398 }
399
400
401 void BBP::doParUpdate() {
402 for( size_t i = 0; i < _fg->nrVars(); i++ )
403 foreach( const Neighbor &I, _fg->nbV(i) ) {
404 calcNewM( i, I.iter );
405 calcNewN( i, I.iter );
406 }
407 for( size_t i = 0; i < _fg->nrVars(); i++ )
408 foreach( const Neighbor &I, _fg->nbV(i) ) {
409 upMsgM( i, I.iter );
410 upMsgN( i, I.iter );
411 }
412 }
413
414
415 void BBP::incrSeqMsgM( size_t i, size_t _I, const Prob &p ) {
416 /* if( props.clean_updates )
417 _new_adj_m[i][_I] += p;
418 else {*/
419 _adj_m[i][_I] += p;
420 calcUnnormMsgM(i, _I);
421 // }
422 }
423
424
425 #if 0
426 Real pv_thresh=1000;
427 #endif
428
429
430 /*
431 void BBP::updateSeqMsgM( size_t i, size_t _I ) {
432 if( props.clean_updates ) {
433 #if 0
434 if(_new_adj_m[i][_I].sumAbs() > pv_thresh ||
435 _adj_m[i][_I].sumAbs() > pv_thresh) {
436
437 DAI_DMSG("in updateSeqMsgM");
438 DAI_PV(i);
439 DAI_PV(_I);
440 DAI_PV(_adj_m[i][_I]);
441 DAI_PV(_new_adj_m[i][_I]);
442 }
443 #endif
444 _adj_m[i][_I] += _new_adj_m[i][_I];
445 calcUnnormMsgM( i, _I );
446 _new_adj_m[i][_I].fill( 0.0 );
447 }
448 }
449 */
450
451 void BBP::setSeqMsgM( size_t i, size_t _I, const Prob &p ) {
452 _adj_m[i][_I] = p;
453 calcUnnormMsgM( i, _I );
454 }
455
456
457 void BBP::sendSeqMsgN( size_t i, size_t _I, const Prob &f ) {
458 Prob f_unnorm = unnormAdjoint( _bp_dual.msgN(i,_I), _bp_dual.zN(i,_I), f );
459 const Neighbor &I = _fg->nbV(i)[_I];
460 assert( I.iter == _I );
461 _adj_psi_V[i] += f_unnorm * T( i, _I );
462 #if 0
463 if(f_unnorm.sumAbs() > pv_thresh) {
464 DAI_DMSG("in sendSeqMsgN");
465 DAI_PV(i);
466 DAI_PV(I);
467 DAI_PV(_I);
468 DAI_PV(_bp_dual.msgN(i,_I));
469 DAI_PV(_bp_dual.zN(i,_I));
470 DAI_PV(_bp_dual.msgM(i,_I));
471 DAI_PV(_bp_dual.zM(i,_I));
472 DAI_PV(_fg->factor(I).p());
473 }
474 #endif
475 foreach( const Neighbor &J, _fg->nbV(i) ) {
476 if( J.node != I.node ) {
477 #if 0
478 if(f_unnorm.sumAbs() > pv_thresh) {
479 DAI_DMSG("in sendSeqMsgN loop");
480 DAI_PV(J);
481 DAI_PV(f_unnorm);
482 DAI_PV(_R[J][J.dual][_I]);
483 DAI_PV(f_unnorm * _R[J][J.dual][_I]);
484 }
485 #endif
486 incrSeqMsgM( i, J.iter, f_unnorm * R(J, J.dual, _I) );
487 }
488 }
489 }
490
491
492 void BBP::sendSeqMsgM( size_t j, size_t _I ) {
493 const Neighbor &I = _fg->nbV(j)[_I];
494
495 // DAI_PV(j);
496 // DAI_PV(I);
497 // DAI_PV(_adj_m_unnorm_jI);
498 // DAI_PV(_adj_m[j][_I]);
499 // DAI_PV(_bp_dual.zM(j,_I));
500
501 size_t _j = I.dual;
502 const Prob &_adj_m_unnorm_jI = _adj_m_unnorm[j][_I];
503 Prob um( U(I, _j) );
504 const _ind_t &ind = _index(j, _I);
505 for( size_t x_I = 0; x_I < um.size(); x_I++ )
506 um[x_I] *= _adj_m_unnorm_jI[ind[x_I]];
507 um *= 1 - props.damping;
508 _adj_psi_F[I] += um;
509
510 /* THE FOLLOWING WOULD BE SLIGHTLY SLOWER:
511 _adj_psi_F[I] += (Factor( _fg->factor(I).vars(), U(I, _j) ) * Factor( _fg->var(j), _adj_m_unnorm[j][_I] )).p() * (1.0 - props.damping);
512 */
513
514 // DAI_DMSG("in sendSeqMsgM");
515 // DAI_PV(j);
516 // DAI_PV(I);
517 // DAI_PV(_I);
518 // DAI_PV(_fg->nbF(I).size());
519 foreach( const Neighbor &i, _fg->nbF(I) ) {
520 if( i.node != j ) {
521 const Prob &S = _S[i][i.dual][_j];
522 Prob msg( _fg->var(i).states(), 0.0 );
523 LOOP_ij(
524 msg[xi] += S[xij] * _adj_m_unnorm_jI[xj];
525 );
526 msg *= 1.0 - props.damping;
527 /* THE FOLLOWING WOULD BE ABOUT TWICE AS SLOW:
528 Var vi = _fg->var(i);
529 Var vj = _fg->var(j);
530 msg = (Factor(VarSet(vi,vj), S) * Factor(vj,_adj_m_unnorm_jI)).marginal(vi,false).p() * (1.0 - props.damping);
531 */
532 #if 0
533 if(msg.sumAbs() > pv_thresh) {
534 DAI_DMSG("in sendSeqMsgM loop");
535
536 DAI_PV(j);
537 DAI_PV(I);
538 DAI_PV(_I);
539 DAI_PV(_fg->nbF(I).size());
540 DAI_PV(_fg->factor(I).p());
541 DAI_PV(_S[i][i.dual][_j]);
542
543 DAI_PV(i);
544 DAI_PV(i.dual);
545 DAI_PV(msg);
546 DAI_PV(_fg->nbV(i).size());
547 }
548 #endif
549 assert( _fg->nbV(i)[i.dual].node == I );
550 sendSeqMsgN( i, i.dual, msg );
551 }
552 }
553 setSeqMsgM( j, _I, _adj_m[j][_I] * props.damping );
554 }
555
556
557 Real BBP::getUnMsgMag() {
558 Real s = 0.0;
559 size_t e = 0;
560 for( size_t i = 0; i < _fg->nrVars(); i++ )
561 foreach( const Neighbor &I, _fg->nbV(i) ) {
562 s += _adj_m_unnorm[i][I.iter].sumAbs();
563 s += _adj_n_unnorm[i][I.iter].sumAbs();
564 e++;
565 }
566 return s / e;
567 }
568
569
570 void BBP::getMsgMags( Real &s, Real &new_s ) {
571 s = 0.0;
572 new_s = 0.0;
573 size_t e = 0;
574 for( size_t i = 0; i < _fg->nrVars(); i++ )
575 foreach( const Neighbor &I, _fg->nbV(i) ) {
576 s += _adj_m[i][I.iter].sumAbs();
577 s += _adj_n[i][I.iter].sumAbs();
578 new_s += _new_adj_m[i][I.iter].sumAbs();
579 new_s += _new_adj_n[i][I.iter].sumAbs();
580 e++;
581 }
582 s /= e;
583 new_s /= e;
584 }
585
586 // tuple<size_t,size_t,Real> BBP::getArgMaxPsi1Adj() {
587 // size_t argmax_var=0;
588 // size_t argmax_var_state=0;
589 // Real max_var=0;
590 // for( size_t i = 0; i < _fg->nrVars(); i++ ) {
591 // pair<size_t,Real> argmax_state = adj_psi_V(i).argmax();
592 // if(i==0 || argmax_state.second>max_var) {
593 // argmax_var = i;
594 // max_var = argmax_state.second;
595 // argmax_var_state = argmax_state.first;
596 // }
597 // }
598 // assert(/*0 <= argmax_var_state &&*/
599 // argmax_var_state < _fg->var(argmax_var).states());
600 // return tuple<size_t,size_t,Real>(argmax_var,argmax_var_state,max_var);
601 // }
602
603
604 void BBP::getArgmaxMsgM( size_t &out_i, size_t &out__I, Real &mag ) {
605 bool found = false;
606 for( size_t i = 0; i < _fg->nrVars(); i++ )
607 foreach( const Neighbor &I, _fg->nbV(i) ) {
608 Real thisMag = _adj_m[i][I.iter].sumAbs();
609 if( !found || mag < thisMag ) {
610 found = true;
611 mag = thisMag;
612 out_i = i;
613 out__I = I.iter;
614 }
615 }
616 assert( found );
617 }
618
619
620 Real BBP::getMaxMsgM() {
621 size_t dummy;
622 Real mag;
623 getArgmaxMsgM( dummy, dummy, mag );
624 return mag;
625 }
626
627
628 Real BBP::getTotalMsgM() {
629 Real mag = 0.0;
630 for( size_t i = 0; i < _fg->nrVars(); i++ )
631 foreach( const Neighbor &I, _fg->nbV(i) )
632 mag += _adj_m[i][I.iter].sumAbs();
633 return mag;
634 }
635
636
637 Real BBP::getTotalNewMsgM() {
638 Real mag = 0.0;
639 for( size_t i = 0; i < _fg->nrVars(); i++ )
640 foreach( const Neighbor &I, _fg->nbV(i) )
641 mag += _new_adj_m[i][I.iter].sumAbs();
642 return mag;
643 }
644
645
646 Real BBP::getTotalMsgN() {
647 Real mag = 0.0;
648 for( size_t i = 0; i < _fg->nrVars(); i++ )
649 foreach( const Neighbor &I, _fg->nbV(i) )
650 mag += _adj_n[i][I.iter].sumAbs();
651 return mag;
652 }
653
654
655 void BBP::Regenerate() {
656 RegenerateInds();
657 RegenerateT();
658 RegenerateU();
659 RegenerateS();
660 RegenerateR();
661 RegenerateInputs();
662 RegeneratePsiAdjoints();
663 if( props.updates == Properties::UpdateType::PAR )
664 RegenerateParMessageAdjoints();
665 else
666 RegenerateSeqMessageAdjoints();
667 _iters = 0;
668 }
669
670
671 std::vector<Prob> BBP::getZeroAdjF( const FactorGraph &fg ) {
672 vector<Prob> adj_2;
673 adj_2.reserve( fg.nrFactors() );
674 for( size_t I = 0; I < fg.nrFactors(); I++ )
675 adj_2.push_back( Prob( fg.factor(I).states(), 0.0 ) );
676 return adj_2;
677 }
678
679
680 std::vector<Prob> BBP::getZeroAdjV( const FactorGraph &fg ) {
681 vector<Prob> adj_1;
682 adj_1.reserve( fg.nrVars() );
683 for( size_t i = 0; i < fg.nrVars(); i++ )
684 adj_1.push_back( Prob( fg.var(i).states(), 0.0 ) );
685 return adj_1;
686 }
687
688
689 void BBP::run() {
690 typedef BBP::Properties::UpdateType UT;
691 Real &tol = props.tol;
692 UT &updates = props.updates;
693
694 Real tic = toc();
695 switch( (size_t)updates ) {
696 case UT::SEQ_MAX: {
697 size_t i, _I;
698 Real mag;
699 do {
700 _iters++;
701 getArgmaxMsgM( i, _I, mag );
702 sendSeqMsgM( i, _I );
703 } while( mag > tol && _iters < props.maxiter );
704
705 if( _iters >= props.maxiter )
706 if( props.verbose >= 1 )
707 cerr << "Warning: BBP didn't converge in " << _iters << " iterations (greatest message magnitude = " << mag << ")" << endl;
708 break;
709 } case UT::SEQ_FIX: {
710 Real mag;
711 do {
712 _iters++;
713 mag = getTotalMsgM();
714 if( mag < tol )
715 break;
716
717 for( size_t i = 0; i < _fg->nrVars(); i++ )
718 foreach( const Neighbor &I, _fg->nbV(i) )
719 sendSeqMsgM( i, I.iter );
720 /* for( size_t i = 0; i < _fg->nrVars(); i++ )
721 foreach( const Neighbor &I, _fg->nbV(i) )
722 updateSeqMsgM( i, I.iter );*/
723 } while( mag > tol && _iters < props.maxiter );
724
725 if( _iters >= props.maxiter )
726 if( props.verbose >= 1 )
727 cerr << "Warning: BBP didn't converge in " << _iters << " iterations (greatest message magnitude = " << mag << ")" << endl;
728 break;
729 } case UT::SEQ_BP_REV:
730 case UT::SEQ_BP_FWD: {
731 const BP *bp = static_cast<const BP*>(_ia);
732 vector<pair<size_t, size_t> > sentMessages = bp->getSentMessages();
733 size_t totalMessages = sentMessages.size();
734 if( totalMessages == 0 )
735 DAI_THROWE(INTERNAL_ERROR, "Asked for updates=" + std::string(updates) + " but no BP messages; did you forget to set recordSentMessages?");
736 if( updates==UT::SEQ_BP_FWD )
737 reverse( sentMessages.begin(), sentMessages.end() );
738 // DAI_PV(sentMessages.size());
739 // DAI_PV(_iters);
740 // DAI_PV(props.maxiter);
741 while( sentMessages.size() > 0 && _iters < props.maxiter ) {
742 // DAI_PV(sentMessages.size());
743 // DAI_PV(_iters);
744 _iters++;
745 pair<size_t, size_t> e = sentMessages.back();
746 sentMessages.pop_back();
747 size_t i = e.first, _I = e.second;
748 sendSeqMsgM( i, _I );
749 }
750 if( _iters >= props.maxiter )
751 if( props.verbose >= 1 )
752 cerr << "Warning: BBP updates limited to " << props.maxiter << " iterations, but using UpdateType " << updates << " with " << totalMessages << " messages" << endl;
753 break;
754 } case UT::PAR: {
755 do {
756 _iters++;
757 doParUpdate();
758 } while( (_iters < 2 || getUnMsgMag() > tol) && _iters < props.maxiter );
759 if( _iters == props.maxiter ) {
760 Real s, new_s;
761 getMsgMags( s, new_s );
762 if( props.verbose >= 1 )
763 cerr << "Warning: BBP didn't converge in " << _iters << " iterations (unnorm message magnitude = " << getUnMsgMag() << ", norm message mags = " << s << " -> " << new_s << ")" << endl;
764 }
765 break;
766 }
767 }
768 if( props.verbose >= 3 )
769 cerr << "BBP::run() took " << toc()-tic << " seconds " << doneIters() << " iterations" << endl;
770 }
771
772
773 double numericBBPTest( const InfAlg &bp, const vector<size_t> *state, const PropertySet &bbp_props, bbp_cfn_t cfn, double h ) {
774 // calculate the value of the unperturbed cost function
775 Real cf0 = getCostFn( bp, cfn, state );
776
777 // run BBP to estimate adjoints
778 BBP bbp( &bp, bbp_props );
779 initBBPCostFnAdj( bbp, bp, cfn, state );
780 bbp.run();
781
782 Real d = 0;
783 const FactorGraph& fg = bp.fg();
784
785 if( 1 ) {
786 // verify bbp.adj_psi_V
787
788 // for each variable i
789 for( size_t i = 0; i < fg.nrVars(); i++ ) {
790 vector<double> adj_est;
791 // for each value xi
792 for( size_t xi = 0; xi < fg.var(i).states(); xi++ ) {
793 // Clone 'bp' (which may be any InfAlg)
794 InfAlg *bp_prb = bp.clone();
795
796 // perturb it
797 size_t n = bp_prb->fg().var(i).states();
798 Prob psi_1_prb( n, 1.0 );
799 psi_1_prb[xi] += h;
800 // psi_1_prb.normalize();
801 size_t I = bp_prb->fg().nbV(i)[0]; // use first factor in list of neighbors of i
802 bp_prb->fg().factor(I) *= Factor( bp_prb->fg().var(i), psi_1_prb );
803
804 // call 'init' on the perturbed variables
805 bp_prb->init( bp_prb->fg().var(i) );
806
807 // run copy to convergence
808 bp_prb->run();
809
810 // calculate new value of cost function
811 Real cf_prb = getCostFn( *bp_prb, cfn, state );
812
813 // use to estimate adjoint for i
814 adj_est.push_back( (cf_prb - cf0) / h );
815
816 // free cloned InfAlg
817 delete bp_prb;
818 }
819 Prob p_adj_est( adj_est.begin(), adj_est.end() );
820 // compare this numerical estimate to the BBP estimate; sum the distances
821 cout << "i: " << i
822 << ", p_adj_est: " << p_adj_est
823 << ", bbp.adj_psi_V(i): " << bbp.adj_psi_V(i) << endl;
824 d += dist( p_adj_est, bbp.adj_psi_V(i), Prob::DISTL1 );
825 }
826 }
827 /* if(1) {
828 // verify bbp.adj_n and bbp.adj_m
829
830 // We actually want to check the responsiveness of objective
831 // function to changes in the final messages. But at the end of a
832 // BBP run, the message adjoints are for the initial messages (and
833 // they should be close to zero, see paper). So this resets the
834 // BBP adjoints to the refer to the desired final messages
835 bbp.RegenerateMessageAdjoints();
836
837 // for each variable i
838 for(size_t i=0; i<bp_dual.nrVars(); i++) {
839 // for each factor I ~ i
840 foreach(size_t I, bp_dual.nbV(i)) {
841 vector<double> adj_n_est;
842 // for each value xi
843 for(size_t xi=0; xi<bp_dual.var(i).states(); xi++) {
844 BP_dual bp_dual_prb(bp_dual);
845 // make h-sized change to newMsgN
846 bp_dual_prb.newMsgN(i,I)[xi] += h;
847 // recalculate beliefs
848 bp_dual_prb.CalcBeliefs();
849 // get cost function value
850 Real cf_prb = getCostFn(bp_dual_prb, cfn, &state);
851 // add it to list of adjoints
852 adj_n_est.push_back((cf_prb-cf0)/h);
853 }
854
855 vector<double> adj_m_est;
856 // for each value xi
857 for(size_t xi=0; xi<bp_dual.var(i).states(); xi++) {
858 BP_dual bp_dual_prb(bp_dual);
859 // make h-sized change to newMsgM
860 bp_dual_prb.newMsgM(I,i)[xi] += h;
861 // recalculate beliefs
862 bp_dual_prb.CalcBeliefs();
863 // get cost function value
864 Real cf_prb = getCostFn(bp_dual_prb, cfn, &state);
865 // add it to list of adjoints
866 adj_m_est.push_back((cf_prb-cf0)/h);
867 }
868
869 Prob p_adj_n_est(adj_n_est.begin(), adj_n_est.end());
870 // compare this numerical estimate to the BBP estimate; sum the distances
871 cerr << "i: " << i << ", I: " << I
872 << ", adj_n_est: " << p_adj_n_est
873 << ", bbp.adj_n(i,I): " << bbp.adj_n(i,I) << endl;
874 d += dist(p_adj_n_est, bbp.adj_n(i,I), Prob::DISTL1);
875
876 Prob p_adj_m_est(adj_m_est.begin(), adj_m_est.end());
877 // compare this numerical estimate to the BBP estimate; sum the distances
878 cerr << "i: " << i << ", I: " << I
879 << ", adj_m_est: " << p_adj_m_est
880 << ", bbp.adj_m(I,i): " << bbp.adj_m(I,i) << endl;
881 d += dist(p_adj_m_est, bbp.adj_m(I,i), Prob::DISTL1);
882 }
883 }
884 }
885 */
886 /* if(0) {
887 // verify bbp.adj_b_V
888 for(size_t i=0; i<bp_dual.nrVars(); i++) {
889 vector<double> adj_b_V_est;
890 // for each value xi
891 for(size_t xi=0; xi<bp_dual.var(i).states(); xi++) {
892 BP_dual bp_dual_prb(bp_dual);
893
894 // make h-sized change to b_1(i)[x_i]
895 bp_dual_prb._beliefs.b1[i][xi] += h;
896
897 // get cost function value
898 Real cf_prb = getCostFn(bp_dual_prb, cfn, &state);
899
900 // add it to list of adjoints
901 adj_b_V_est.push_back((cf_prb-cf0)/h);
902 }
903 Prob p_adj_b_V_est(adj_b_V_est.begin(), adj_b_V_est.end());
904 // compare this numerical estimate to the BBP estimate; sum the distances
905 cerr << "i: " << i
906 << ", adj_b_V_est: " << p_adj_b_V_est
907 << ", bbp.adj_b_V(i): " << bbp.adj_b_V(i) << endl;
908 d += dist(p_adj_b_V_est, bbp.adj_b_V(i), Prob::DISTL1);
909 }
910 }
911 */
912
913 // return total of distances
914 return d;
915 }
916
917
918 bool needGibbsState( bbp_cfn_t cfn ) {
919 switch( (size_t)cfn ) {
920 case bbp_cfn_t::CFN_GIBBS_B:
921 case bbp_cfn_t::CFN_GIBBS_B2:
922 case bbp_cfn_t::CFN_GIBBS_EXP:
923 case bbp_cfn_t::CFN_GIBBS_B_FACTOR:
924 case bbp_cfn_t::CFN_GIBBS_B2_FACTOR:
925 case bbp_cfn_t::CFN_GIBBS_EXP_FACTOR:
926 return true;
927 default:
928 return false;
929 }
930 }
931
932
933 void initBBPCostFnAdj( BBP &bbp, const InfAlg &ia, bbp_cfn_t cfn_type, const vector<size_t> *stateP ) {
934 const FactorGraph &fg = ia.fg();
935
936 switch( (size_t)cfn_type ) {
937 case bbp_cfn_t::CFN_BETHE_ENT: {
938 vector<Prob> b1_adj;
939 vector<Prob> b2_adj;
940 vector<Prob> psi1_adj;
941 vector<Prob> psi2_adj;
942 b1_adj.reserve( fg.nrVars() );
943 psi1_adj.reserve( fg.nrVars() );
944 b2_adj.reserve( fg.nrFactors() );
945 psi2_adj.reserve( fg.nrFactors() );
946 for( size_t i = 0; i < fg.nrVars(); i++ ) {
947 size_t dim = fg.var(i).states();
948 int c = fg.nbV(i).size();
949 Prob p(dim,0.0);
950 for( size_t xi = 0; xi < dim; xi++ )
951 p[xi] = (1 - c) * (1 + log( ia.beliefV(i)[xi] ));
952 b1_adj.push_back( p );
953
954 for( size_t xi = 0; xi < dim; xi++ )
955 p[xi] = -ia.beliefV(i)[xi];
956 psi1_adj.push_back( p );
957 }
958 for( size_t I = 0; I < fg.nrFactors(); I++ ) {
959 size_t dim = fg.factor(I).states();
960 Prob p( dim, 0.0 );
961 for( size_t xI = 0; xI < dim; xI++ )
962 p[xI] = 1 + log( ia.beliefF(I)[xI] / fg.factor(I).p()[xI] );
963 b2_adj.push_back( p );
964
965 for( size_t xI = 0; xI < dim; xI++ )
966 p[xI] = -ia.beliefF(I)[xI] / fg.factor(I).p()[xI];
967 psi2_adj.push_back( p );
968 }
969 bbp.init( b1_adj, b2_adj, psi1_adj, psi2_adj );
970 break;
971 } case bbp_cfn_t::CFN_FACTOR_ENT: {
972 vector<Prob> b2_adj;
973 b2_adj.reserve( fg.nrFactors() );
974 for( size_t I = 0; I < fg.nrFactors(); I++ ) {
975 size_t dim = fg.factor(I).states();
976 Prob p( dim, 0.0 );
977 for( size_t xI = 0; xI < dim; xI++ ) {
978 double bIxI = ia.beliefF(I)[xI];
979 if( bIxI < 1.0e-15 )
980 p[xI] = -1.0e10;
981 else
982 p[xI] = 1 + log( bIxI );
983 }
984 b2_adj.push_back(p);
985 }
986 bbp.init( bbp.getZeroAdjV(fg), b2_adj );
987 break;
988 } case bbp_cfn_t::CFN_VAR_ENT: {
989 vector<Prob> b1_adj;
990 b1_adj.reserve( fg.nrVars() );
991 for( size_t i = 0; i < fg.nrVars(); i++ ) {
992 size_t dim = fg.var(i).states();
993 Prob p( dim, 0.0 );
994 for( size_t xi = 0; xi < fg.var(i).states(); xi++ ) {
995 double bixi = ia.beliefV(i)[xi];
996 if( bixi < 1.0e-15 )
997 p[xi] = -1.0e10;
998 else
999 p[xi] = 1 + log( bixi );
1000 }
1001 b1_adj.push_back( p );
1002 }
1003 bbp.init( b1_adj );
1004 break;
1005 } case bbp_cfn_t::CFN_GIBBS_B:
1006 case bbp_cfn_t::CFN_GIBBS_B2:
1007 case bbp_cfn_t::CFN_GIBBS_EXP: {
1008 // cost functions that use Gibbs sample, summing over variable marginals
1009 vector<size_t> state;
1010 if( stateP == NULL )
1011 state = getGibbsState( ia, 2*ia.Iterations() );
1012 else
1013 state = *stateP;
1014 assert( state.size() == fg.nrVars() );
1015
1016 vector<Prob> b1_adj;
1017 b1_adj.reserve(fg.nrVars());
1018 for( size_t i = 0; i < state.size(); i++ ) {
1019 size_t n = fg.var(i).states();
1020 Prob delta( n, 0.0 );
1021 assert(/*0<=state[i] &&*/ state[i] < n);
1022 double b = ia.beliefV(i)[state[i]];
1023 switch( (size_t)cfn_type ) {
1024 case bbp_cfn_t::CFN_GIBBS_B:
1025 delta[state[i]] = 1.0;
1026 break;
1027 case bbp_cfn_t::CFN_GIBBS_B2:
1028 delta[state[i]] = b;
1029 break;
1030 case bbp_cfn_t::CFN_GIBBS_EXP:
1031 delta[state[i]] = exp(b);
1032 break;
1033 default:
1034 DAI_THROW(UNKNOWN_ENUM_VALUE);
1035 }
1036 b1_adj.push_back( delta );
1037 }
1038 bbp.init( b1_adj );
1039 break;
1040 } case bbp_cfn_t::CFN_GIBBS_B_FACTOR:
1041 case bbp_cfn_t::CFN_GIBBS_B2_FACTOR:
1042 case bbp_cfn_t::CFN_GIBBS_EXP_FACTOR: {
1043 // cost functions that use Gibbs sample, summing over factor marginals
1044 vector<size_t> state;
1045 if( stateP == NULL )
1046 state = getGibbsState( ia, 2*ia.Iterations() );
1047 else
1048 state = *stateP;
1049 assert( state.size() == fg.nrVars() );
1050
1051 vector<Prob> b2_adj;
1052 b2_adj.reserve( fg.nrVars() );
1053 for( size_t I = 0; I < fg.nrFactors(); I++ ) {
1054 size_t n = fg.factor(I).states();
1055 Prob delta( n, 0.0 );
1056
1057 size_t x_I = getFactorEntryForState( fg, I, state );
1058 assert(/*0<=x_I &&*/ x_I < n);
1059
1060 double b = ia.beliefF(I)[x_I];
1061 switch( (size_t)cfn_type ) {
1062 case bbp_cfn_t::CFN_GIBBS_B_FACTOR:
1063 delta[x_I] = 1.0;
1064 break;
1065 case bbp_cfn_t::CFN_GIBBS_B2_FACTOR:
1066 delta[x_I] = b;
1067 break;
1068 case bbp_cfn_t::CFN_GIBBS_EXP_FACTOR:
1069 delta[x_I] = exp( b );
1070 break;
1071 default:
1072 DAI_THROW(UNKNOWN_ENUM_VALUE);
1073 }
1074 b2_adj.push_back( delta );
1075 }
1076 bbp.init( bbp.getZeroAdjV(fg), b2_adj );
1077 break;
1078 } default:
1079 DAI_THROW(UNKNOWN_ENUM_VALUE);
1080 }
1081 }
1082
1083
1084 Real getCostFn( const InfAlg &ia, bbp_cfn_t cfn_type, const vector<size_t> *stateP ) {
1085 double cf = 0.0;
1086 const FactorGraph &fg = ia.fg();
1087
1088 switch( (size_t)cfn_type ) {
1089 case bbp_cfn_t::CFN_BETHE_ENT: // ignores state
1090 cf = -ia.logZ();
1091 break;
1092 case bbp_cfn_t::CFN_VAR_ENT: // ignores state
1093 for( size_t i = 0; i < fg.nrVars(); i++ )
1094 cf += -ia.beliefV(i).entropy();
1095 break;
1096 case bbp_cfn_t::CFN_FACTOR_ENT: // ignores state
1097 for( size_t I = 0; I < fg.nrFactors(); I++ )
1098 cf += -ia.beliefF(I).entropy();
1099 break;
1100 case bbp_cfn_t::CFN_GIBBS_B:
1101 case bbp_cfn_t::CFN_GIBBS_B2:
1102 case bbp_cfn_t::CFN_GIBBS_EXP: {
1103 assert( stateP != NULL );
1104 vector<size_t> state = *stateP;
1105 assert( state.size() == fg.nrVars() );
1106 for( size_t i = 0; i < fg.nrVars(); i++ ) {
1107 double b = ia.beliefV(i)[state[i]];
1108 switch( (size_t)cfn_type ) {
1109 case bbp_cfn_t::CFN_GIBBS_B:
1110 cf += b;
1111 break;
1112 case bbp_cfn_t::CFN_GIBBS_B2:
1113 cf += b * b / 2.0;
1114 break;
1115 case bbp_cfn_t::CFN_GIBBS_EXP:
1116 cf += exp( b );
1117 break;
1118 default:
1119 DAI_THROW(UNKNOWN_ENUM_VALUE);
1120 }
1121 }
1122 break;
1123 } case bbp_cfn_t::CFN_GIBBS_B_FACTOR:
1124 case bbp_cfn_t::CFN_GIBBS_B2_FACTOR:
1125 case bbp_cfn_t::CFN_GIBBS_EXP_FACTOR: {
1126 assert( stateP != NULL );
1127 vector<size_t> state = *stateP;
1128 assert( state.size() == fg.nrVars() );
1129 for( size_t I = 0; I < fg.nrFactors(); I++ ) {
1130 size_t x_I = getFactorEntryForState( fg, I, state );
1131 double b = ia.beliefF(I)[x_I];
1132 switch( (size_t)cfn_type ) {
1133 case bbp_cfn_t::CFN_GIBBS_B_FACTOR:
1134 cf += b;
1135 break;
1136 case bbp_cfn_t::CFN_GIBBS_B2_FACTOR:
1137 cf += b * b / 2.0;
1138 break;
1139 case bbp_cfn_t::CFN_GIBBS_EXP_FACTOR:
1140 cf += exp( b );
1141 break;
1142 default:
1143 DAI_THROW(UNKNOWN_ENUM_VALUE);
1144 }
1145 }
1146 break;
1147 } default:
1148 DAI_THROWE(UNKNOWN_ENUM_VALUE, "Unknown cost function " + std::string(cfn_type));
1149 }
1150 return cf;
1151 }
1152
1153
1154 } // end of namespace dai
1155
1156
1157 /* {{{ GENERATED CODE: DO NOT EDIT. Created by
1158 ./scripts/regenerate-properties include/dai/bbp.h src/bbp.cpp
1159 */
1160 namespace dai {
1161
1162 void BBP::Properties::set(const PropertySet &opts)
1163 {
1164 const std::set<PropertyKey> &keys = opts.allKeys();
1165 std::set<PropertyKey>::const_iterator i;
1166 for(i=keys.begin(); i!=keys.end(); i++) {
1167 if(*i == "verbose") continue;
1168 if(*i == "maxiter") continue;
1169 if(*i == "tol") continue;
1170 if(*i == "damping") continue;
1171 if(*i == "updates") continue;
1172 DAI_THROWE(UNKNOWN_PROPERTY_TYPE, "BBP: Unknown property " + *i);
1173 }
1174 if(!opts.hasKey("verbose"))
1175 DAI_THROWE(NOT_ALL_PROPERTIES_SPECIFIED,"BBP: Missing property \"verbose\" for method \"BBP\"");
1176 if(!opts.hasKey("maxiter"))
1177 DAI_THROWE(NOT_ALL_PROPERTIES_SPECIFIED,"BBP: Missing property \"maxiter\" for method \"BBP\"");
1178 if(!opts.hasKey("tol"))
1179 DAI_THROWE(NOT_ALL_PROPERTIES_SPECIFIED,"BBP: Missing property \"tol\" for method \"BBP\"");
1180 if(!opts.hasKey("damping"))
1181 DAI_THROWE(NOT_ALL_PROPERTIES_SPECIFIED,"BBP: Missing property \"damping\" for method \"BBP\"");
1182 if(!opts.hasKey("updates"))
1183 DAI_THROWE(NOT_ALL_PROPERTIES_SPECIFIED,"BBP: Missing property \"updates\" for method \"BBP\"");
1184 verbose = opts.getStringAs<size_t>("verbose");
1185 maxiter = opts.getStringAs<size_t>("maxiter");
1186 tol = opts.getStringAs<double>("tol");
1187 damping = opts.getStringAs<double>("damping");
1188 updates = opts.getStringAs<UpdateType>("updates");
1189 }
1190 PropertySet BBP::Properties::get() const {
1191 PropertySet opts;
1192 opts.Set("verbose", verbose);
1193 opts.Set("maxiter", maxiter);
1194 opts.Set("tol", tol);
1195 opts.Set("damping", damping);
1196 opts.Set("updates", updates);
1197 return opts;
1198 }
1199 string BBP::Properties::toString() const {
1200 stringstream s(stringstream::out);
1201 s << "[";
1202 s << "verbose=" << verbose << ",";
1203 s << "maxiter=" << maxiter << ",";
1204 s << "tol=" << tol << ",";
1205 s << "damping=" << damping << ",";
1206 s << "updates=" << updates;
1207 s << "]";
1208 return s.str();
1209 }
1210 } // end of namespace dai
1211 /* }}} END OF GENERATED CODE */