1 /* This file is part of libDAI - http://www.libdai.org/
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.
7 * Copyright (C) 2009 Frederik Eaton [frederik at ofb dot net]
18 #include <dai/properties.h>
29 using boost::shared_ptr
;
32 const char *CBP::Name
= "CBP";
35 void CBP::setBeliefs( const std::vector
<Factor
> &bs
, double logZ
) {
38 _beliefsV
.reserve( nrVars() );
40 _beliefsF
.reserve( nrFactors() );
41 for( i
= 0; i
< nrVars(); i
++ )
42 _beliefsV
.push_back( bs
[i
] );
43 for( ; i
< nrVars() + nrFactors(); i
++ )
44 _beliefsF
.push_back( bs
[i
] );
49 void CBP::construct() {
51 _beliefsV
.reserve(nrVars());
52 for( size_t i
= 0; i
< nrVars(); i
++ )
53 _beliefsV
.push_back( Factor(var(i
)).normalized() );
56 _beliefsF
.reserve(nrFactors());
57 for( size_t I
= 0; I
< nrFactors(); I
++ ) {
59 f
.fill(1); f
.normalize();
60 _beliefsF
.push_back( f
);
63 // to compute average level
70 if( props
.clamp_outfile
.length() > 0 ) {
71 _clamp_ofstream
= shared_ptr
<ofstream
>(new ofstream( props
.clamp_outfile
.c_str(), ios_base::out
|ios_base::trunc
));
72 *_clamp_ofstream
<< "# COUNT LEVEL VAR STATE" << endl
;
77 /// Calculates a vector of mixtures p * b + (1-p) * c
78 static vector
<Factor
> mixBeliefs( Real p
, const vector
<Factor
> &b
, const vector
<Factor
> &c
) {
80 DAI_ASSERT( b
.size() == c
.size() );
81 out
.reserve( b
.size() );
83 for( size_t i
= 0; i
< b
.size(); i
++ )
84 // probably already normalized, but do it again just in case
85 out
.push_back( b
[i
].normalized() * p
+ c
[i
].normalized() * pc
);
91 size_t seed
= props
.rand_seed
;
95 InfAlg
*bp
= getInfAlg();
98 _iters
+= bp
->Iterations();
100 vector
<Factor
> beliefs_out
;
102 size_t choose_count
=0;
103 runRecurse( bp
, bp
->logZ(), vector
<size_t>(0), _num_leaves
, choose_count
, _sum_level
, lz_out
, beliefs_out
);
104 if( props
.verbose
>= 1 )
105 cerr
<< "CBP average levels = " << (_sum_level
/ _num_leaves
) << ", leaves = " << _num_leaves
<< endl
;
106 setBeliefs( beliefs_out
, lz_out
);
111 /// \todo Eventually this allow other inference algorithms that BP to be selected, via a property
112 InfAlg
* CBP::getInfAlg() {
114 bpProps
.Set("updates", props
.updates
);
115 bpProps
.Set("tol", props
.tol
);
116 bpProps
.Set("maxiter", props
.maxiter
);
117 bpProps
.Set("verbose", props
.verbose
);
118 bpProps
.Set("logdomain", false);
119 bpProps
.Set("damping", 0.0);
120 BP
*bp
= new BP( *this, bpProps
);
121 bp
->recordSentMessages
= true;
127 void CBP::runRecurse( InfAlg
*bp
, double orig_logZ
, vector
<size_t> clamped_vars_list
, size_t &num_leaves
,
128 size_t &choose_count
, double &sum_level
, Real
&lz_out
, vector
<Factor
>& beliefs_out
) {
129 // choose a variable/states to clamp:
134 bool clampingVar
= (Clamping() == Properties::ClampType::CLAMP_VAR
);
136 if( Recursion() == Properties::RecurseType::REC_LOGZ
&& recTol() > 0 && exp( bp
->logZ() - orig_logZ
) < recTol() )
139 found
= chooseNextClampVar( bp
, clamped_vars_list
, i
, xis
, &maxVar
);
143 sum_level
+= clamped_vars_list
.size();
144 beliefs_out
= bp
->beliefs();
150 if( props
.clamp_outfile
.length() > 0 )
151 *_clamp_ofstream
<< choose_count
<< "\t" << clamped_vars_list
.size() << "\t" << i
<< "\t" << xis
[0] << endl
;
154 foreach( size_t xi
, xis
)
155 DAI_ASSERT(/*0<=xi &&*/ xi
< var(i
).states() );
157 foreach( size_t xI
, xis
)
158 DAI_ASSERT(/*0<=xI &&*/ xI
< factor(i
).states() );
159 // - otherwise, clamp and recurse, saving margin estimates for each
160 // clamp setting. afterwards, combine estimates.
162 // compute complement of 'xis'
163 vector
<size_t> cmp_xis
= complement( xis
, clampingVar
? var(i
).states() : factor(i
).states() );
165 /// \todo could do this more efficiently with a nesting version of saveProbs/undoProbs
168 InfAlg
*bp_c
= bp
->clone();
170 bp_c
->fg().clampVar( i
, xis
);
171 bp_c
->init( var(i
) );
173 bp_c
->fg().clampFactor( i
, xis
);
174 bp_c
->init( factor(i
).vars() );
177 _iters
+= bp_c
->Iterations();
183 vector
<Factor
> cmp_b
;
184 InfAlg
*cmp_bp_c
= bp
->clone();
186 cmp_bp_c
->fg().clampVar( i
, cmp_xis
);
187 cmp_bp_c
->init(var(i
));
189 cmp_bp_c
->fg().clampFactor( i
, cmp_xis
);
190 cmp_bp_c
->init( factor(i
).vars() );
193 _iters
+= cmp_bp_c
->Iterations();
195 cmp_lz
= cmp_bp_c
->logZ();
196 cmp_b
= cmp_bp_c
->beliefs();
198 double p
= unSoftMax( lz
, cmp_lz
);
201 if( Recursion() == Properties::RecurseType::REC_BDIFF
&& recTol() > 0 ) {
202 vector
<Factor
> combined_b( mixBeliefs( p
, b
, cmp_b
) );
203 Real new_lz
= logSumExp( lz
,cmp_lz
);
204 bp__d
= dist( bp
->beliefs(), combined_b
, nrVars() );
205 if( exp( new_lz
- orig_logZ
) * bp__d
< recTol() ) {
207 sum_level
+= clamped_vars_list
.size();
208 beliefs_out
= combined_b
;
214 // either we are not doing REC_BDIFF or the distance was large
215 // enough to recurse:
216 runRecurse( bp_c
, orig_logZ
, clamped_vars_list
, num_leaves
, choose_count
, sum_level
, lz
, b
);
217 runRecurse( cmp_bp_c
, orig_logZ
, clamped_vars_list
, num_leaves
, choose_count
, sum_level
, cmp_lz
, cmp_b
);
219 p
= unSoftMax( lz
, cmp_lz
);
221 beliefs_out
= mixBeliefs( p
, b
, cmp_b
);
222 lz_out
= logSumExp( lz
, cmp_lz
);
224 if( props
.verbose
>= 2 ) {
225 Real d
= dist( bp
->beliefs(), beliefs_out
, nrVars() );
226 cerr
<< "Distance (clamping " << i
<< "): " << d
;
227 if( Recursion() == Properties::RecurseType::REC_BDIFF
)
228 cerr
<< "; bp_dual predicted " << bp__d
;
229 cerr
<< "; max_adjoint = " << maxVar
<< "; logZ = " << lz_out
<< " (in " << bp
->logZ() << ") (orig " << orig_logZ
<< "); p = " << p
<< "; level = " << clamped_vars_list
.size() << endl
;
237 // 'xis' must be sorted
238 bool CBP::chooseNextClampVar( InfAlg
*bp
, vector
<size_t> &clamped_vars_list
, size_t &i
, vector
<size_t> &xis
, Real
*maxVarOut
) {
240 if( props
.verbose
>= 3 )
241 cerr
<< "clamped_vars_list" << clamped_vars_list
<< endl
;
242 if( clamped_vars_list
.size() >= maxClampLevel() )
244 if( ChooseMethod() == Properties::ChooseMethodType::CHOOSE_RANDOM
) {
245 if( Clamping() == Properties::ClampType::CLAMP_VAR
) {
250 } while( abs( bp
->beliefV(i
).p().max() - 1) < tiny
&& t
< t1
);
253 // die("Too many levels requested in CBP");
255 // only pick probable values for variable
258 xi
= rnd( var(i
).states() );
260 } while( bp
->beliefV(i
).p()[xi
] < tiny
&& t
< t1
);
261 DAI_ASSERT( t
< t1
);
263 // DAI_ASSERT(!_clamped_vars.count(i)); // not true for >2-ary variables
264 DAI_IFVERB(2, "CHOOSE_RANDOM at level " << clamped_vars_list
.size() << " chose variable " << i
<< " state " << xis
[0] << endl
);
268 i
= rnd( nrFactors() );
270 } while( abs( bp
->beliefF(i
).p().max() - 1) < tiny
&& t
< t1
);
273 // die("Too many levels requested in CBP");
274 // only pick probable values for variable
277 xi
= rnd( factor(i
).states() );
279 } while( bp
->beliefF(i
).p()[xi
] < tiny
&& t
< t1
);
280 DAI_ASSERT( t
< t1
);
282 // DAI_ASSERT(!_clamped_vars.count(i)); // not true for >2-ary variables
283 DAI_IFVERB(2, endl
<<"CHOOSE_RANDOM chose factor "<<i
<<" state "<<xis
[0]<<endl
);
285 } else if( ChooseMethod() == Properties::ChooseMethodType::CHOOSE_MAXENT
) {
286 if( Clamping() == Properties::ClampType::CLAMP_VAR
) {
288 int win_k
= -1, win_xk
= -1;
289 for( size_t k
= 0; k
< nrVars(); k
++ ) {
290 Real ent
=bp
->beliefV(k
).entropy();
291 if( max_ent
< ent
) {
294 win_xk
= bp
->beliefV(k
).p().argmax().first
;
297 DAI_ASSERT( win_k
>= 0 );
298 DAI_ASSERT( win_xk
>= 0 );
300 xis
.resize( 1, win_xk
);
301 DAI_IFVERB(2, endl
<<"CHOOSE_MAXENT chose variable "<<i
<<" state "<<xis
[0]<<endl
);
302 if( bp
->beliefV(i
).p()[xis
[0]] < tiny
) {
303 DAI_IFVERB(2, "Warning: CHOOSE_MAXENT found unlikely state, not recursing");
308 int win_k
= -1, win_xk
= -1;
309 for( size_t k
= 0; k
< nrFactors(); k
++ ) {
310 Real ent
= bp
->beliefF(k
).entropy();
311 if( max_ent
< ent
) {
314 win_xk
= bp
->beliefF(k
).p().argmax().first
;
317 DAI_ASSERT( win_k
>= 0 );
318 DAI_ASSERT( win_xk
>= 0 );
320 xis
.resize( 1, win_xk
);
321 DAI_IFVERB(2, endl
<<"CHOOSE_MAXENT chose factor "<<i
<<" state "<<xis
[0]<<endl
);
322 if( bp
->beliefF(i
).p()[xis
[0]] < tiny
) {
323 DAI_IFVERB(2, "Warning: CHOOSE_MAXENT found unlikely state, not recursing");
327 } else if( ChooseMethod()==Properties::ChooseMethodType::CHOOSE_BP_L1
||
328 ChooseMethod()==Properties::ChooseMethodType::CHOOSE_BP_CFN
) {
329 bool doL1
= (ChooseMethod() == Properties::ChooseMethodType::CHOOSE_BP_L1
);
330 vector
<size_t> state
;
331 if( !doL1
&& needGibbsState( BBP_cost_function() ) )
332 state
= getGibbsState( *bp
, 2*bp
->Iterations() );
333 // try clamping each variable manually
334 DAI_ASSERT( Clamping() == Properties::ClampType::CLAMP_VAR
);
336 int win_k
= -1, win_xk
= -1;
337 for( size_t k
= 0; k
< nrVars(); k
++ ) {
338 for( size_t xk
= 0; xk
< var(k
).states(); xk
++ ) {
339 if( bp
->beliefV(k
)[xk
] < tiny
)
341 InfAlg
*bp1
= bp
->clone();
347 for( size_t j
= 0; j
< nrVars(); j
++ )
348 cost
+= dist( bp
->beliefV(j
), bp1
->beliefV(j
), Prob::DISTL1
);
350 cost
= getCostFn( *bp1
, BBP_cost_function(), &state
);
351 if( cost
> max_cost
|| win_k
== -1 ) {
359 DAI_ASSERT( win_k
>= 0 );
360 DAI_ASSERT( win_xk
>= 0 );
362 xis
.resize( 1, win_xk
);
363 } else if( ChooseMethod() == Properties::ChooseMethodType::CHOOSE_BBP
) {
367 bool clampingVar
= (Clamping() == Properties::ClampType::CLAMP_VAR
);
368 pair
<size_t, size_t> cv
= bbpFindClampVar( *bp
, clampingVar
, props
.bbp_props
, BBP_cost_function(), &mvo
);
370 // if slope isn't big enough then don't clamp
371 if( mvo
< minMaxAdj() )
374 size_t xi
= cv
.second
;
376 #define VAR_INFO (clampingVar?"variable ":"factor ") \
377 << i << " state " << xi \
378 << " (p=" << (clampingVar?bp->beliefV(i)[xi]:bp->beliefF(i)[xi]) \
379 << ", entropy = " << (clampingVar?bp->beliefV(i):bp->beliefF(i)).entropy() \
380 << ", maxVar = "<< mvo << ")"
381 Prob b
= ( clampingVar
? bp
->beliefV(i
).p() : bp
->beliefF(i
).p());
383 cerr
<< "Warning, at level " << clamped_vars_list
.size() << ", bbpFindClampVar found unlikely " << VAR_INFO
<< endl
;
386 if( abs(b
[xi
] - 1) < tiny
) {
387 cerr
<< "Warning at level " << clamped_vars_list
.size() << ", bbpFindClampVar found overly likely " << VAR_INFO
<< endl
;
393 DAI_ASSERT(/*0<=xi &&*/ xi
< var(i
).states() );
395 DAI_ASSERT(/*0<=xi &&*/ xi
< factor(i
).states() );
396 DAI_IFVERB(2, "CHOOSE_BBP (num clamped = " << clamped_vars_list
.size() << ") chose " << i
<< " state " << xi
<< endl
);
398 DAI_THROW(UNKNOWN_ENUM_VALUE
);
399 clamped_vars_list
.push_back( i
);
404 void CBP::printDebugInfo() {
411 //----------------------------------------------------------------
414 /** Function which takes a factor graph as input, runs Gibbs and BP_dual,
415 * creates and runs a BBP object, finds best variable, returns
416 * (variable,state) pair for clamping
418 pair
<size_t, size_t> bbpFindClampVar( const InfAlg
&in_bp
, bool clampingVar
, const PropertySet
&bbp_props
, bbp_cfn_t cfn
, Real
*maxVarOut
) {
419 BBP
bbp( &in_bp
, bbp_props
);
420 initBBPCostFnAdj( bbp
, in_bp
, cfn
, NULL
);
423 // find and return the (variable,state) with the largest adj_psi_V
424 size_t argmax_var
= 0;
425 size_t argmax_var_state
= 0;
428 for( size_t i
= 0; i
< in_bp
.fg().nrVars(); i
++ ) {
429 Prob adj_psi_V
= bbp
.adj_psi_V(i
);
431 // helps to account for amount of movement possible in variable
432 // i's beliefs? seems not..
433 adj_psi_V
*= in_bp
.beliefV(i
).entropy();
436 // adj_psi_V *= Prob(in_bp.fg().var(i).states(),1.0)-in_bp.beliefV(i).p();
437 adj_psi_V
*= in_bp
.beliefV(i
).p();
439 // try to compensate for effect on same variable (doesn't work)
440 // adj_psi_V[gibbs.state()[i]] -= bp_dual.beliefV(i)[gibbs.state()[i]]/10;
441 pair
<size_t,Real
> argmax_state
= adj_psi_V
.argmax();
443 if( i
== 0 || argmax_state
.second
> max_var
) {
445 max_var
= argmax_state
.second
;
446 argmax_var_state
= argmax_state
.first
;
449 DAI_ASSERT(/*0 <= argmax_var_state &&*/
450 argmax_var_state
< in_bp
.fg().var(argmax_var
).states() );
452 for( size_t I
= 0; I
< in_bp
.fg().nrFactors(); I
++ ) {
453 Prob adj_psi_F
= bbp
.adj_psi_F(I
);
455 // helps to account for amount of movement possible in variable
456 // i's beliefs? seems not..
457 adj_psi_F
*= in_bp
.beliefF(I
).entropy();
459 // try to compensate for effect on same variable (doesn't work)
460 // adj_psi_V[gibbs.state()[i]] -= bp_dual.beliefV(i)[gibbs.state()[i]]/10;
461 pair
<size_t,Real
> argmax_state
= adj_psi_F
.argmax();
463 if( I
== 0 || argmax_state
.second
> max_var
) {
465 max_var
= argmax_state
.second
;
466 argmax_var_state
= argmax_state
.first
;
469 DAI_ASSERT(/*0 <= argmax_var_state &&*/
470 argmax_var_state
< in_bp
.fg().factor(argmax_var
).states() );
473 *maxVarOut
= max_var
;
474 return make_pair( argmax_var
, argmax_var_state
);
478 vector
<size_t> complement( vector
<size_t> &xis
, size_t n_states
) {
479 vector
<size_t> cmp_xis( 0 );
481 for( size_t xi
= 0; xi
< n_states
; xi
++ ) {
482 while( j
< xis
.size() && xis
[j
] < xi
)
484 if( j
>= xis
.size() || xis
[j
] > xi
)
485 cmp_xis
.push_back(xi
);
487 DAI_ASSERT( xis
.size()+cmp_xis
.size() == n_states
);
492 Real
unSoftMax( Real a
, Real b
) {
494 return 1.0 / (1.0 + exp(b
-a
));
496 return exp(a
-b
) / (exp(a
-b
) + 1.0);
500 Real
logSumExp( Real a
, Real b
) {
502 return a
+ log1p( exp( b
-a
) );
504 return b
+ log1p( exp( a
-b
) );
508 Real
dist( const vector
<Factor
> &b1
, const vector
<Factor
> &b2
, size_t nv
) {
510 for( size_t k
= 0; k
< nv
; k
++ )
511 d
+= dist( b1
[k
], b2
[k
], Prob::DISTLINF
);
516 } // end of namespace dai
519 /* {{{ GENERATED CODE: DO NOT EDIT. Created by
520 ./scripts/regenerate-properties include/dai/cbp.h src/cbp.cpp
524 void CBP::Properties::set(const PropertySet
&opts
)
526 const std::set
<PropertyKey
> &keys
= opts
.allKeys();
527 std::set
<PropertyKey
>::const_iterator i
;
528 for(i
=keys
.begin(); i
!=keys
.end(); i
++) {
529 if(*i
== "verbose") continue;
530 if(*i
== "tol") continue;
531 if(*i
== "updates") continue;
532 if(*i
== "maxiter") continue;
533 if(*i
== "rec_tol") continue;
534 if(*i
== "max_levels") continue;
535 if(*i
== "min_max_adj") continue;
536 if(*i
== "choose") continue;
537 if(*i
== "recursion") continue;
538 if(*i
== "clamp") continue;
539 if(*i
== "bbp_props") continue;
540 if(*i
== "bbp_cfn") continue;
541 if(*i
== "rand_seed") continue;
542 if(*i
== "clamp_outfile") continue;
543 DAI_THROWE(UNKNOWN_PROPERTY_TYPE
, "CBP: Unknown property " + *i
);
545 if(!opts
.hasKey("tol"))
546 DAI_THROWE(NOT_ALL_PROPERTIES_SPECIFIED
,"CBP: Missing property \"tol\" for method \"CBP\"");
547 if(!opts
.hasKey("updates"))
548 DAI_THROWE(NOT_ALL_PROPERTIES_SPECIFIED
,"CBP: Missing property \"updates\" for method \"CBP\"");
549 if(!opts
.hasKey("maxiter"))
550 DAI_THROWE(NOT_ALL_PROPERTIES_SPECIFIED
,"CBP: Missing property \"maxiter\" for method \"CBP\"");
551 if(!opts
.hasKey("rec_tol"))
552 DAI_THROWE(NOT_ALL_PROPERTIES_SPECIFIED
,"CBP: Missing property \"rec_tol\" for method \"CBP\"");
553 if(!opts
.hasKey("min_max_adj"))
554 DAI_THROWE(NOT_ALL_PROPERTIES_SPECIFIED
,"CBP: Missing property \"min_max_adj\" for method \"CBP\"");
555 if(!opts
.hasKey("choose"))
556 DAI_THROWE(NOT_ALL_PROPERTIES_SPECIFIED
,"CBP: Missing property \"choose\" for method \"CBP\"");
557 if(!opts
.hasKey("recursion"))
558 DAI_THROWE(NOT_ALL_PROPERTIES_SPECIFIED
,"CBP: Missing property \"recursion\" for method \"CBP\"");
559 if(!opts
.hasKey("clamp"))
560 DAI_THROWE(NOT_ALL_PROPERTIES_SPECIFIED
,"CBP: Missing property \"clamp\" for method \"CBP\"");
561 if(!opts
.hasKey("bbp_props"))
562 DAI_THROWE(NOT_ALL_PROPERTIES_SPECIFIED
,"CBP: Missing property \"bbp_props\" for method \"CBP\"");
563 if(!opts
.hasKey("bbp_cfn"))
564 DAI_THROWE(NOT_ALL_PROPERTIES_SPECIFIED
,"CBP: Missing property \"bbp_cfn\" for method \"CBP\"");
565 if(opts
.hasKey("verbose")) {
566 verbose
= opts
.getStringAs
<size_t>("verbose");
570 tol
= opts
.getStringAs
<double>("tol");
571 updates
= opts
.getStringAs
<UpdateType
>("updates");
572 maxiter
= opts
.getStringAs
<size_t>("maxiter");
573 rec_tol
= opts
.getStringAs
<double>("rec_tol");
574 if(opts
.hasKey("max_levels")) {
575 max_levels
= opts
.getStringAs
<size_t>("max_levels");
579 min_max_adj
= opts
.getStringAs
<double>("min_max_adj");
580 choose
= opts
.getStringAs
<ChooseMethodType
>("choose");
581 recursion
= opts
.getStringAs
<RecurseType
>("recursion");
582 clamp
= opts
.getStringAs
<ClampType
>("clamp");
583 bbp_props
= opts
.getStringAs
<PropertySet
>("bbp_props");
584 bbp_cfn
= opts
.getStringAs
<bbp_cfn_t
>("bbp_cfn");
585 if(opts
.hasKey("rand_seed")) {
586 rand_seed
= opts
.getStringAs
<size_t>("rand_seed");
590 if(opts
.hasKey("clamp_outfile")) {
591 clamp_outfile
= opts
.getStringAs
<std::string
>("clamp_outfile");
596 PropertySet
CBP::Properties::get() const {
598 opts
.Set("verbose", verbose
);
599 opts
.Set("tol", tol
);
600 opts
.Set("updates", updates
);
601 opts
.Set("maxiter", maxiter
);
602 opts
.Set("rec_tol", rec_tol
);
603 opts
.Set("max_levels", max_levels
);
604 opts
.Set("min_max_adj", min_max_adj
);
605 opts
.Set("choose", choose
);
606 opts
.Set("recursion", recursion
);
607 opts
.Set("clamp", clamp
);
608 opts
.Set("bbp_props", bbp_props
);
609 opts
.Set("bbp_cfn", bbp_cfn
);
610 opts
.Set("rand_seed", rand_seed
);
611 opts
.Set("clamp_outfile", clamp_outfile
);
614 string
CBP::Properties::toString() const {
615 stringstream
s(stringstream::out
);
617 s
<< "verbose=" << verbose
<< ",";
618 s
<< "tol=" << tol
<< ",";
619 s
<< "updates=" << updates
<< ",";
620 s
<< "maxiter=" << maxiter
<< ",";
621 s
<< "rec_tol=" << rec_tol
<< ",";
622 s
<< "max_levels=" << max_levels
<< ",";
623 s
<< "min_max_adj=" << min_max_adj
<< ",";
624 s
<< "choose=" << choose
<< ",";
625 s
<< "recursion=" << recursion
<< ",";
626 s
<< "clamp=" << clamp
<< ",";
627 s
<< "bbp_props=" << bbp_props
<< ",";
628 s
<< "bbp_cfn=" << bbp_cfn
<< ",";
629 s
<< "rand_seed=" << rand_seed
<< ",";
630 s
<< "clamp_outfile=" << clamp_outfile
;
634 } // end of namespace dai
635 /* }}} END OF GENERATED CODE */