1 /* This file is part of libDAI - http://www.libdai.org/
3 * Copyright (c) 2006-2011, The libDAI authors. All rights reserved.
5 * Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
9 #include <dai/dai_config.h>
20 #include <dai/properties.h>
21 #include <dai/gibbs.h>
31 using boost::shared_ptr
;
34 /// Given a sorted vector of states \a xis and total state count \a n_states, return a vector of states not in \a xis
35 vector
<size_t> complement( vector
<size_t> &xis
, size_t n_states
) {
36 vector
<size_t> cmp_xis( 0 );
38 for( size_t xi
= 0; xi
< n_states
; xi
++ ) {
39 while( j
< xis
.size() && xis
[j
] < xi
)
41 if( j
>= xis
.size() || xis
[j
] > xi
)
42 cmp_xis
.push_back(xi
);
44 DAI_ASSERT( xis
.size()+cmp_xis
.size() == n_states
);
49 /// Computes \f$\frac{\exp(a)}{\exp(a)+\exp(b)}\f$
50 Real
unSoftMax( Real a
, Real b
) {
52 return 1.0 / (1.0 + exp(b
-a
));
54 return exp(a
-b
) / (exp(a
-b
) + 1.0);
58 /// Computes log of sum of exponents, i.e., \f$\log\left(\exp(a) + \exp(b)\right)\f$
59 Real
logSumExp( Real a
, Real b
) {
61 return a
+ log1p( exp( b
-a
) );
63 return b
+ log1p( exp( a
-b
) );
67 /// Compute sum of pairwise L-infinity distances of the first \a nv factors in each vector
68 Real
dist( const vector
<Factor
> &b1
, const vector
<Factor
> &b2
, size_t nv
) {
70 for( size_t k
= 0; k
< nv
; k
++ )
71 d
+= dist( b1
[k
], b2
[k
], DISTLINF
);
76 void CBP::setBeliefs( const std::vector
<Factor
> &bs
, Real logZ
) {
79 _beliefsV
.reserve( nrVars() );
81 _beliefsF
.reserve( nrFactors() );
82 for( i
= 0; i
< nrVars(); i
++ )
83 _beliefsV
.push_back( bs
[i
] );
84 for( ; i
< nrVars() + nrFactors(); i
++ )
85 _beliefsF
.push_back( bs
[i
] );
90 void CBP::construct() {
92 _beliefsV
.reserve(nrVars());
93 for( size_t i
= 0; i
< nrVars(); i
++ )
94 _beliefsV
.push_back( Factor(var(i
)).normalized() );
97 _beliefsF
.reserve(nrFactors());
98 for( size_t I
= 0; I
< nrFactors(); I
++ ) {
100 f
.fill(1); f
.normalize();
101 _beliefsF
.push_back( f
);
104 // to compute average level
111 if( props
.clamp_outfile
.length() > 0 ) {
112 _clamp_ofstream
= shared_ptr
<ofstream
>(new ofstream( props
.clamp_outfile
.c_str(), ios_base::out
|ios_base::trunc
));
113 *_clamp_ofstream
<< "# COUNT LEVEL VAR STATE" << endl
;
118 /// Calculates a vector of mixtures p * b + (1-p) * c
119 static vector
<Factor
> mixBeliefs( Real p
, const vector
<Factor
> &b
, const vector
<Factor
> &c
) {
121 DAI_ASSERT( b
.size() == c
.size() );
122 out
.reserve( b
.size() );
124 for( size_t i
= 0; i
< b
.size(); i
++ )
125 // probably already normalized, but do it again just in case
126 out
.push_back( b
[i
].normalized() * p
+ c
[i
].normalized() * pc
);
132 size_t seed
= props
.rand_seed
;
136 InfAlg
*bp
= getInfAlg();
139 _iters
+= bp
->Iterations();
141 vector
<Factor
> beliefs_out
;
143 size_t choose_count
=0;
144 runRecurse( bp
, bp
->logZ(), vector
<size_t>(0), _num_leaves
, choose_count
, _sum_level
, lz_out
, beliefs_out
);
145 if( props
.verbose
>= 1 )
146 cerr
<< "CBP average levels = " << (_sum_level
/ _num_leaves
) << ", leaves = " << _num_leaves
<< endl
;
147 setBeliefs( beliefs_out
, lz_out
);
153 InfAlg
* CBP::getInfAlg() {
155 bpProps
.set("updates", props
.updates
);
156 bpProps
.set("tol", props
.tol
);
157 bpProps
.set("maxiter", props
.maxiter
);
158 bpProps
.set("verbose", props
.verbose
);
159 bpProps
.set("logdomain", false);
160 bpProps
.set("damping", (Real
)0.0);
161 BP
*bp
= new BP( *this, bpProps
);
162 bp
->recordSentMessages
= true;
168 void CBP::runRecurse( InfAlg
*bp
, Real orig_logZ
, vector
<size_t> clamped_vars_list
, size_t &num_leaves
,
169 size_t &choose_count
, Real
&sum_level
, Real
&lz_out
, vector
<Factor
>& beliefs_out
) {
170 // choose a variable/states to clamp:
175 bool clampingVar
= (props
.clamp
== Properties::ClampType::CLAMP_VAR
);
177 if( props
.recursion
== Properties::RecurseType::REC_LOGZ
&& props
.rec_tol
> 0 && exp( bp
->logZ() - orig_logZ
) < props
.rec_tol
)
180 found
= chooseNextClampVar( bp
, clamped_vars_list
, i
, xis
, &maxVar
);
184 sum_level
+= clamped_vars_list
.size();
185 beliefs_out
= bp
->beliefs();
191 if( props
.clamp_outfile
.length() > 0 )
192 *_clamp_ofstream
<< choose_count
<< "\t" << clamped_vars_list
.size() << "\t" << i
<< "\t" << xis
[0] << endl
;
195 bforeach( size_t xi
, xis
)
196 DAI_ASSERT(/*0<=xi &&*/ xi
< var(i
).states() );
198 bforeach( size_t xI
, xis
)
199 DAI_ASSERT(/*0<=xI &&*/ xI
< factor(i
).nrStates() );
200 // - otherwise, clamp and recurse, saving margin estimates for each
201 // clamp setting. afterwards, combine estimates.
203 // compute complement of 'xis'
204 vector
<size_t> cmp_xis
= complement( xis
, clampingVar
? var(i
).states() : factor(i
).nrStates() );
206 /// \idea dai::CBP::runRecurse() could be implemented more efficiently with a nesting version of backupFactors/restoreFactors
207 // this improvement could also be done locally: backup the clamped factor in a local variable,
208 // and restore it just before we return.
211 InfAlg
*bp_c
= bp
->clone();
213 bp_c
->fg().clampVar( i
, xis
);
214 bp_c
->init( var(i
) );
216 bp_c
->fg().clampFactor( i
, xis
);
217 bp_c
->init( factor(i
).vars() );
220 _iters
+= bp_c
->Iterations();
226 vector
<Factor
> cmp_b
;
227 InfAlg
*cmp_bp_c
= bp
->clone();
229 cmp_bp_c
->fg().clampVar( i
, cmp_xis
);
230 cmp_bp_c
->init(var(i
));
232 cmp_bp_c
->fg().clampFactor( i
, cmp_xis
);
233 cmp_bp_c
->init( factor(i
).vars() );
236 _iters
+= cmp_bp_c
->Iterations();
238 cmp_lz
= cmp_bp_c
->logZ();
239 cmp_b
= cmp_bp_c
->beliefs();
241 Real p
= unSoftMax( lz
, cmp_lz
);
244 if( props
.recursion
== Properties::RecurseType::REC_BDIFF
&& props
.rec_tol
> 0 ) {
245 vector
<Factor
> combined_b( mixBeliefs( p
, b
, cmp_b
) );
246 Real new_lz
= logSumExp( lz
,cmp_lz
);
247 bp__d
= dist( bp
->beliefs(), combined_b
, nrVars() );
248 if( exp( new_lz
- orig_logZ
) * bp__d
< props
.rec_tol
) {
250 sum_level
+= clamped_vars_list
.size();
251 beliefs_out
= combined_b
;
257 // either we are not doing REC_BDIFF or the distance was large
258 // enough to recurse:
259 runRecurse( bp_c
, orig_logZ
, clamped_vars_list
, num_leaves
, choose_count
, sum_level
, lz
, b
);
260 runRecurse( cmp_bp_c
, orig_logZ
, clamped_vars_list
, num_leaves
, choose_count
, sum_level
, cmp_lz
, cmp_b
);
262 p
= unSoftMax( lz
, cmp_lz
);
264 beliefs_out
= mixBeliefs( p
, b
, cmp_b
);
265 lz_out
= logSumExp( lz
, cmp_lz
);
267 if( props
.verbose
>= 2 ) {
268 Real d
= dist( bp
->beliefs(), beliefs_out
, nrVars() );
269 cerr
<< "Distance (clamping " << i
<< "): " << d
;
270 if( props
.recursion
== Properties::RecurseType::REC_BDIFF
)
271 cerr
<< "; bp_dual predicted " << bp__d
;
272 cerr
<< "; max_adjoint = " << maxVar
<< "; logZ = " << lz_out
<< " (in " << bp
->logZ() << ") (orig " << orig_logZ
<< "); p = " << p
<< "; level = " << clamped_vars_list
.size() << endl
;
280 // 'xis' must be sorted
281 bool CBP::chooseNextClampVar( InfAlg
*bp
, vector
<size_t> &clamped_vars_list
, size_t &i
, vector
<size_t> &xis
, Real
*maxVarOut
) {
283 if( props
.verbose
>= 3 )
284 cerr
<< "clamped_vars_list" << clamped_vars_list
<< endl
;
285 if( clamped_vars_list
.size() >= props
.max_levels
)
287 if( props
.choose
== Properties::ChooseMethodType::CHOOSE_RANDOM
) {
288 if( props
.clamp
== Properties::ClampType::CLAMP_VAR
) {
293 } while( abs( bp
->beliefV(i
).p().max() - 1) < tiny
&& t
< t1
);
296 // die("Too many levels requested in CBP");
298 // only pick probable values for variable
301 xi
= rnd( var(i
).states() );
303 } while( bp
->beliefV(i
).p()[xi
] < tiny
&& t
< t1
);
304 DAI_ASSERT( t
< t1
);
306 // DAI_ASSERT(!_clamped_vars.count(i)); // not true for >2-ary variables
307 DAI_IFVERB(2, "CHOOSE_RANDOM at level " << clamped_vars_list
.size() << " chose variable " << i
<< " state " << xis
[0] << endl
);
311 i
= rnd( nrFactors() );
313 } while( abs( bp
->beliefF(i
).p().max() - 1) < tiny
&& t
< t1
);
316 // die("Too many levels requested in CBP");
317 // only pick probable values for variable
320 xi
= rnd( factor(i
).nrStates() );
322 } while( bp
->beliefF(i
).p()[xi
] < tiny
&& t
< t1
);
323 DAI_ASSERT( t
< t1
);
325 // DAI_ASSERT(!_clamped_vars.count(i)); // not true for >2-ary variables
326 DAI_IFVERB(2, endl
<<"CHOOSE_RANDOM chose factor "<<i
<<" state "<<xis
[0]<<endl
);
328 } else if( props
.choose
== Properties::ChooseMethodType::CHOOSE_MAXENT
) {
329 if( props
.clamp
== Properties::ClampType::CLAMP_VAR
) {
331 int win_k
= -1, win_xk
= -1;
332 for( size_t k
= 0; k
< nrVars(); k
++ ) {
333 Real ent
=bp
->beliefV(k
).entropy();
334 if( max_ent
< ent
) {
337 win_xk
= bp
->beliefV(k
).p().argmax().first
;
340 DAI_ASSERT( win_k
>= 0 );
341 DAI_ASSERT( win_xk
>= 0 );
343 xis
.resize( 1, win_xk
);
344 DAI_IFVERB(2, endl
<<"CHOOSE_MAXENT chose variable "<<i
<<" state "<<xis
[0]<<endl
);
345 if( bp
->beliefV(i
).p()[xis
[0]] < tiny
) {
346 DAI_IFVERB(2, "Warning: CHOOSE_MAXENT found unlikely state, not recursing");
351 int win_k
= -1, win_xk
= -1;
352 for( size_t k
= 0; k
< nrFactors(); k
++ ) {
353 Real ent
= bp
->beliefF(k
).entropy();
354 if( max_ent
< ent
) {
357 win_xk
= bp
->beliefF(k
).p().argmax().first
;
360 DAI_ASSERT( win_k
>= 0 );
361 DAI_ASSERT( win_xk
>= 0 );
363 xis
.resize( 1, win_xk
);
364 DAI_IFVERB(2, endl
<<"CHOOSE_MAXENT chose factor "<<i
<<" state "<<xis
[0]<<endl
);
365 if( bp
->beliefF(i
).p()[xis
[0]] < tiny
) {
366 DAI_IFVERB(2, "Warning: CHOOSE_MAXENT found unlikely state, not recursing");
370 } else if( props
.choose
==Properties::ChooseMethodType::CHOOSE_BP_L1
||
371 props
.choose
==Properties::ChooseMethodType::CHOOSE_BP_CFN
) {
372 bool doL1
= (props
.choose
== Properties::ChooseMethodType::CHOOSE_BP_L1
);
373 vector
<size_t> state
;
374 if( !doL1
&& props
.bbp_cfn
.needGibbsState() )
375 state
= getGibbsState( bp
->fg(), 2*bp
->Iterations() );
376 // try clamping each variable manually
377 DAI_ASSERT( props
.clamp
== Properties::ClampType::CLAMP_VAR
);
379 int win_k
= -1, win_xk
= -1;
380 for( size_t k
= 0; k
< nrVars(); k
++ ) {
381 for( size_t xk
= 0; xk
< var(k
).states(); xk
++ ) {
382 if( bp
->beliefV(k
)[xk
] < tiny
)
384 InfAlg
*bp1
= bp
->clone();
390 for( size_t j
= 0; j
< nrVars(); j
++ )
391 cost
+= dist( bp
->beliefV(j
), bp1
->beliefV(j
), DISTL1
);
393 cost
= props
.bbp_cfn
.evaluate( *bp1
, &state
);
394 if( cost
> max_cost
|| win_k
== -1 ) {
402 DAI_ASSERT( win_k
>= 0 );
403 DAI_ASSERT( win_xk
>= 0 );
405 xis
.resize( 1, win_xk
);
406 } else if( props
.choose
== Properties::ChooseMethodType::CHOOSE_BBP
) {
410 bool clampingVar
= (props
.clamp
== Properties::ClampType::CLAMP_VAR
);
411 pair
<size_t, size_t> cv
= BBPFindClampVar( *bp
, clampingVar
, props
.bbp_props
, props
.bbp_cfn
, &mvo
);
413 // if slope isn't big enough then don't clamp
414 if( mvo
< props
.min_max_adj
)
417 size_t xi
= cv
.second
;
419 #define VAR_INFO (clampingVar?"variable ":"factor ") \
420 << i << " state " << xi \
421 << " (p=" << (clampingVar?bp->beliefV(i)[xi]:bp->beliefF(i)[xi]) \
422 << ", entropy = " << (clampingVar?bp->beliefV(i):bp->beliefF(i)).entropy() \
423 << ", maxVar = "<< mvo << ")"
424 Prob b
= ( clampingVar
? bp
->beliefV(i
).p() : bp
->beliefF(i
).p());
426 cerr
<< "Warning, at level " << clamped_vars_list
.size() << ", BBPFindClampVar found unlikely " << VAR_INFO
<< endl
;
429 if( abs(b
[xi
] - 1) < tiny
) {
430 cerr
<< "Warning at level " << clamped_vars_list
.size() << ", BBPFindClampVar found overly likely " << VAR_INFO
<< endl
;
436 DAI_ASSERT(/*0<=xi &&*/ xi
< var(i
).states() );
438 DAI_ASSERT(/*0<=xi &&*/ xi
< factor(i
).nrStates() );
439 DAI_IFVERB(2, "CHOOSE_BBP (num clamped = " << clamped_vars_list
.size() << ") chose " << i
<< " state " << xi
<< endl
);
441 DAI_THROW(UNKNOWN_ENUM_VALUE
);
442 clamped_vars_list
.push_back( i
);
447 void CBP::printDebugInfo() {
454 std::pair
<size_t, size_t> BBPFindClampVar( const InfAlg
&in_bp
, bool clampingVar
, const PropertySet
&bbp_props
, const BBPCostFunction
&cfn
, Real
*maxVarOut
) {
455 BBP
bbp( &in_bp
, bbp_props
);
456 bbp
.initCostFnAdj( cfn
, NULL
);
459 // find and return the (variable,state) with the largest adj_psi_V
460 size_t argmax_var
= 0;
461 size_t argmax_var_state
= 0;
464 for( size_t i
= 0; i
< in_bp
.fg().nrVars(); i
++ ) {
465 Prob adj_psi_V
= bbp
.adj_psi_V(i
);
467 // helps to account for amount of movement possible in variable
468 // i's beliefs? seems not..
469 adj_psi_V
*= in_bp
.beliefV(i
).entropy();
472 // adj_psi_V *= Prob(in_bp.fg().var(i).states(),1.0)-in_bp.beliefV(i).p();
473 adj_psi_V
*= in_bp
.beliefV(i
).p();
475 // try to compensate for effect on same variable (doesn't work)
476 // adj_psi_V[gibbs.state()[i]] -= bp_dual.beliefV(i)[gibbs.state()[i]]/10;
477 pair
<size_t,Real
> argmax_state
= adj_psi_V
.argmax();
479 if( i
== 0 || argmax_state
.second
> max_var
) {
481 max_var
= argmax_state
.second
;
482 argmax_var_state
= argmax_state
.first
;
485 DAI_ASSERT(/*0 <= argmax_var_state &&*/
486 argmax_var_state
< in_bp
.fg().var(argmax_var
).states() );
488 for( size_t I
= 0; I
< in_bp
.fg().nrFactors(); I
++ ) {
489 Prob adj_psi_F
= bbp
.adj_psi_F(I
);
491 // helps to account for amount of movement possible in variable
492 // i's beliefs? seems not..
493 adj_psi_F
*= in_bp
.beliefF(I
).entropy();
495 // try to compensate for effect on same variable (doesn't work)
496 // adj_psi_V[gibbs.state()[i]] -= bp_dual.beliefV(i)[gibbs.state()[i]]/10;
497 pair
<size_t,Real
> argmax_state
= adj_psi_F
.argmax();
499 if( I
== 0 || argmax_state
.second
> max_var
) {
501 max_var
= argmax_state
.second
;
502 argmax_var_state
= argmax_state
.first
;
505 DAI_ASSERT(/*0 <= argmax_var_state &&*/
506 argmax_var_state
< in_bp
.fg().factor(argmax_var
).nrStates() );
509 *maxVarOut
= max_var
;
510 return make_pair( argmax_var
, argmax_var_state
);
514 } // end of namespace dai
517 /* {{{ GENERATED CODE: DO NOT EDIT. Created by
518 ./scripts/regenerate-properties include/dai/cbp.h src/cbp.cpp
522 void CBP::Properties::set(const PropertySet
&opts
)
524 const std::set
<PropertyKey
> &keys
= opts
.keys();
525 std::string errormsg
;
526 for( std::set
<PropertyKey
>::const_iterator i
= keys
.begin(); i
!= keys
.end(); i
++ ) {
527 if( *i
== "verbose" ) continue;
528 if( *i
== "tol" ) continue;
529 if( *i
== "updates" ) continue;
530 if( *i
== "maxiter" ) continue;
531 if( *i
== "rec_tol" ) continue;
532 if( *i
== "max_levels" ) continue;
533 if( *i
== "min_max_adj" ) continue;
534 if( *i
== "choose" ) continue;
535 if( *i
== "recursion" ) continue;
536 if( *i
== "clamp" ) continue;
537 if( *i
== "bbp_props" ) continue;
538 if( *i
== "bbp_cfn" ) continue;
539 if( *i
== "rand_seed" ) continue;
540 if( *i
== "clamp_outfile" ) continue;
541 errormsg
= errormsg
+ "CBP: Unknown property " + *i
+ "\n";
543 if( !errormsg
.empty() )
544 DAI_THROWE(UNKNOWN_PROPERTY
, errormsg
);
545 if( !opts
.hasKey("tol") )
546 errormsg
= errormsg
+ "CBP: Missing property \"tol\" for method \"CBP\"\n";
547 if( !opts
.hasKey("updates") )
548 errormsg
= errormsg
+ "CBP: Missing property \"updates\" for method \"CBP\"\n";
549 if( !opts
.hasKey("maxiter") )
550 errormsg
= errormsg
+ "CBP: Missing property \"maxiter\" for method \"CBP\"\n";
551 if( !opts
.hasKey("rec_tol") )
552 errormsg
= errormsg
+ "CBP: Missing property \"rec_tol\" for method \"CBP\"\n";
553 if( !opts
.hasKey("min_max_adj") )
554 errormsg
= errormsg
+ "CBP: Missing property \"min_max_adj\" for method \"CBP\"\n";
555 if( !opts
.hasKey("choose") )
556 errormsg
= errormsg
+ "CBP: Missing property \"choose\" for method \"CBP\"\n";
557 if( !opts
.hasKey("recursion") )
558 errormsg
= errormsg
+ "CBP: Missing property \"recursion\" for method \"CBP\"\n";
559 if( !opts
.hasKey("clamp") )
560 errormsg
= errormsg
+ "CBP: Missing property \"clamp\" for method \"CBP\"\n";
561 if( !opts
.hasKey("bbp_props") )
562 errormsg
= errormsg
+ "CBP: Missing property \"bbp_props\" for method \"CBP\"\n";
563 if( !opts
.hasKey("bbp_cfn") )
564 errormsg
= errormsg
+ "CBP: Missing property \"bbp_cfn\" for method \"CBP\"\n";
565 if( !errormsg
.empty() )
566 DAI_THROWE(NOT_ALL_PROPERTIES_SPECIFIED
,errormsg
);
567 if( opts
.hasKey("verbose") ) {
568 verbose
= opts
.getStringAs
<size_t>("verbose");
572 tol
= opts
.getStringAs
<Real
>("tol");
573 updates
= opts
.getStringAs
<UpdateType
>("updates");
574 maxiter
= opts
.getStringAs
<size_t>("maxiter");
575 rec_tol
= opts
.getStringAs
<Real
>("rec_tol");
576 if( opts
.hasKey("max_levels") ) {
577 max_levels
= opts
.getStringAs
<size_t>("max_levels");
581 min_max_adj
= opts
.getStringAs
<Real
>("min_max_adj");
582 choose
= opts
.getStringAs
<ChooseMethodType
>("choose");
583 recursion
= opts
.getStringAs
<RecurseType
>("recursion");
584 clamp
= opts
.getStringAs
<ClampType
>("clamp");
585 bbp_props
= opts
.getStringAs
<PropertySet
>("bbp_props");
586 bbp_cfn
= opts
.getStringAs
<BBPCostFunction
>("bbp_cfn");
587 if( opts
.hasKey("rand_seed") ) {
588 rand_seed
= opts
.getStringAs
<size_t>("rand_seed");
592 if( opts
.hasKey("clamp_outfile") ) {
593 clamp_outfile
= opts
.getStringAs
<std::string
>("clamp_outfile");
598 PropertySet
CBP::Properties::get() const {
600 opts
.set("verbose", verbose
);
601 opts
.set("tol", tol
);
602 opts
.set("updates", updates
);
603 opts
.set("maxiter", maxiter
);
604 opts
.set("rec_tol", rec_tol
);
605 opts
.set("max_levels", max_levels
);
606 opts
.set("min_max_adj", min_max_adj
);
607 opts
.set("choose", choose
);
608 opts
.set("recursion", recursion
);
609 opts
.set("clamp", clamp
);
610 opts
.set("bbp_props", bbp_props
);
611 opts
.set("bbp_cfn", bbp_cfn
);
612 opts
.set("rand_seed", rand_seed
);
613 opts
.set("clamp_outfile", clamp_outfile
);
616 string
CBP::Properties::toString() const {
617 stringstream
s(stringstream::out
);
619 s
<< "verbose=" << verbose
<< ",";
620 s
<< "tol=" << tol
<< ",";
621 s
<< "updates=" << updates
<< ",";
622 s
<< "maxiter=" << maxiter
<< ",";
623 s
<< "rec_tol=" << rec_tol
<< ",";
624 s
<< "max_levels=" << max_levels
<< ",";
625 s
<< "min_max_adj=" << min_max_adj
<< ",";
626 s
<< "choose=" << choose
<< ",";
627 s
<< "recursion=" << recursion
<< ",";
628 s
<< "clamp=" << clamp
<< ",";
629 s
<< "bbp_props=" << bbp_props
<< ",";
630 s
<< "bbp_cfn=" << bbp_cfn
<< ",";
631 s
<< "rand_seed=" << rand_seed
<< ",";
632 s
<< "clamp_outfile=" << clamp_outfile
;
636 } // end of namespace dai
637 /* }}} END OF GENERATED CODE */