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