d6ba9354d3e45c47cf1990a76a74278dbd1c709e
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) 2006-2009 Joris Mooij [joris dot mooij at libdai dot org]
8 * Copyright (C) 2006-2007 Radboud University Nijmegen, The Netherlands
18 #include <dai/alldai.h>
27 const char *LC::Name
= "LC";
30 void LC::setProperties( const PropertySet
&opts
) {
31 DAI_ASSERT( opts
.hasKey("tol") );
32 DAI_ASSERT( opts
.hasKey("maxiter") );
33 DAI_ASSERT( opts
.hasKey("verbose") );
34 DAI_ASSERT( opts
.hasKey("cavity") );
35 DAI_ASSERT( opts
.hasKey("updates") );
37 props
.tol
= opts
.getStringAs
<double>("tol");
38 props
.maxiter
= opts
.getStringAs
<size_t>("maxiter");
39 props
.verbose
= opts
.getStringAs
<size_t>("verbose");
40 props
.cavity
= opts
.getStringAs
<Properties::CavityType
>("cavity");
41 props
.updates
= opts
.getStringAs
<Properties::UpdateType
>("updates");
42 if( opts
.hasKey("cavainame") )
43 props
.cavainame
= opts
.getStringAs
<string
>("cavainame");
44 if( opts
.hasKey("cavaiopts") )
45 props
.cavaiopts
= opts
.getStringAs
<PropertySet
>("cavaiopts");
46 if( opts
.hasKey("reinit") )
47 props
.reinit
= opts
.getStringAs
<bool>("reinit");
48 if( opts
.hasKey("damping") )
49 props
.damping
= opts
.getStringAs
<double>("damping");
55 PropertySet
LC::getProperties() const {
57 opts
.Set( "tol", props
.tol
);
58 opts
.Set( "maxiter", props
.maxiter
);
59 opts
.Set( "verbose", props
.verbose
);
60 opts
.Set( "cavity", props
.cavity
);
61 opts
.Set( "updates", props
.updates
);
62 opts
.Set( "cavainame", props
.cavainame
);
63 opts
.Set( "cavaiopts", props
.cavaiopts
);
64 opts
.Set( "reinit", props
.reinit
);
65 opts
.Set( "damping", props
.damping
);
70 string
LC::printProperties() const {
71 stringstream
s( stringstream::out
);
73 s
<< "tol=" << props
.tol
<< ",";
74 s
<< "maxiter=" << props
.maxiter
<< ",";
75 s
<< "verbose=" << props
.verbose
<< ",";
76 s
<< "cavity=" << props
.cavity
<< ",";
77 s
<< "updates=" << props
.updates
<< ",";
78 s
<< "cavainame=" << props
.cavainame
<< ",";
79 s
<< "cavaiopts=" << props
.cavaiopts
<< ",";
80 s
<< "reinit=" << props
.reinit
<< ",";
81 s
<< "damping=" << props
.damping
<< "]";
86 LC::LC( const FactorGraph
& fg
, const PropertySet
&opts
) : DAIAlgFG(fg
), _pancakes(), _cavitydists(), _phis(), _beliefs(), _maxdiff(0.0), _iters(0), props() {
87 setProperties( opts
);
90 _pancakes
.resize( nrVars() );
93 for( size_t i
=0; i
< nrVars(); i
++ )
94 _cavitydists
.push_back(Factor( delta(i
) ));
97 _phis
.reserve( nrVars() );
98 for( size_t i
= 0; i
< nrVars(); i
++ ) {
99 _phis
.push_back( vector
<Factor
>() );
100 _phis
[i
].reserve( nbV(i
).size() );
101 foreach( const Neighbor
&I
, nbV(i
) )
102 _phis
[i
].push_back( Factor( factor(I
).vars() / var(i
) ) );
106 _beliefs
.reserve( nrVars() );
107 for( size_t i
=0; i
< nrVars(); i
++ )
108 _beliefs
.push_back(Factor(var(i
)));
112 string
LC::identify() const {
113 return string(Name
) + printProperties();
117 void LC::CalcBelief (size_t i
) {
118 _beliefs
[i
] = _pancakes
[i
].marginal(var(i
));
122 double LC::CalcCavityDist (size_t i
, const std::string
&name
, const PropertySet
&opts
) {
126 if( props
.verbose
>= 2 )
127 cerr
<< "Initing cavity " << var(i
) << "(" << delta(i
).size() << " vars, " << delta(i
).nrStates() << " states)" << endl
;
129 if( props
.cavity
== Properties::CavityType::UNIFORM
)
130 Bi
= Factor(delta(i
));
132 InfAlg
*cav
= newInfAlg( name
, *this, opts
);
133 cav
->makeCavity( i
);
135 if( props
.cavity
== Properties::CavityType::FULL
)
136 Bi
= calcMarginal( *cav
, cav
->fg().delta(i
), props
.reinit
);
137 else if( props
.cavity
== Properties::CavityType::PAIR
)
138 Bi
= calcMarginal2ndO( *cav
, cav
->fg().delta(i
), props
.reinit
);
139 else if( props
.cavity
== Properties::CavityType::PAIR2
) {
140 vector
<Factor
> pairbeliefs
= calcPairBeliefsNew( *cav
, cav
->fg().delta(i
), props
.reinit
);
141 for( size_t ij
= 0; ij
< pairbeliefs
.size(); ij
++ )
142 Bi
*= pairbeliefs
[ij
];
144 maxdiff
= cav
->maxDiff();
148 _cavitydists
[i
] = Bi
;
154 double LC::InitCavityDists( const std::string
&name
, const PropertySet
&opts
) {
157 if( props
.verbose
>= 1 ) {
158 cerr
<< Name
<< "::InitCavityDists: ";
159 if( props
.cavity
== Properties::CavityType::UNIFORM
)
160 cerr
<< "Using uniform initial cavity distributions" << endl
;
161 else if( props
.cavity
== Properties::CavityType::FULL
)
162 cerr
<< "Using full " << name
<< opts
<< "...";
163 else if( props
.cavity
== Properties::CavityType::PAIR
)
164 cerr
<< "Using pairwise " << name
<< opts
<< "...";
165 else if( props
.cavity
== Properties::CavityType::PAIR2
)
166 cerr
<< "Using pairwise(new) " << name
<< opts
<< "...";
169 double maxdiff
= 0.0;
170 for( size_t i
= 0; i
< nrVars(); i
++ ) {
171 double md
= CalcCavityDist(i
, name
, opts
);
176 if( props
.verbose
>= 1 ) {
177 cerr
<< Name
<< "::InitCavityDists used " << toc() - tic
<< " seconds." << endl
;
184 long LC::SetCavityDists( std::vector
<Factor
> &Q
) {
185 if( props
.verbose
>= 1 )
186 cerr
<< Name
<< "::SetCavityDists: Setting initial cavity distributions" << endl
;
187 if( Q
.size() != nrVars() )
189 for( size_t i
= 0; i
< nrVars(); i
++ ) {
190 if( _cavitydists
[i
].vars() != Q
[i
].vars() ) {
193 _cavitydists
[i
] = Q
[i
];
200 for( size_t i
= 0; i
< nrVars(); ++i
)
201 foreach( const Neighbor
&I
, nbV(i
) )
202 if( props
.updates
== Properties::UpdateType::SEQRND
)
203 _phis
[i
][I
.iter
].randomize();
205 _phis
[i
][I
.iter
].fill(1.0);
209 Factor
LC::NewPancake (size_t i
, size_t _I
, bool & hasNaNs
) {
210 size_t I
= nbV(i
)[_I
];
211 Factor piet
= _pancakes
[i
];
213 // recalculate _pancake[i]
214 VarSet Ivars
= factor(I
).vars();
216 for( VarSet::const_iterator k
= Ivars
.begin(); k
!= Ivars
.end(); k
++ )
218 A_I
*= (_pancakes
[findVar(*k
)] * factor(I
).inverse()).marginal( Ivars
/ var(i
), false );
219 if( Ivars
.size() > 1 )
220 A_I
^= (1.0 / (Ivars
.size() - 1));
221 Factor A_Ii
= (_pancakes
[i
] * factor(I
).inverse() * _phis
[i
][_I
].inverse()).marginal( Ivars
/ var(i
), false );
222 Factor quot
= A_I
/ A_Ii
;
223 if( props
.damping
!= 0.0 )
224 quot
= (quot
^(1.0 - props
.damping
)) * (_phis
[i
][_I
]^props
.damping
);
226 piet
*= quot
/ _phis
[i
][_I
].normalized();
227 _phis
[i
][_I
] = quot
.normalized();
231 if( piet
.hasNaNs() ) {
232 cerr
<< Name
<< "::NewPancake(" << i
<< ", " << _I
<< "): has NaNs!" << endl
;
241 if( props
.verbose
>= 1 )
242 cerr
<< "Starting " << identify() << "...";
243 if( props
.verbose
>= 2 )
247 Diffs
diffs(nrVars(), 1.0);
249 double md
= InitCavityDists( props
.cavainame
, props
.cavaiopts
);
253 for( size_t i
= 0; i
< nrVars(); i
++ ) {
254 _pancakes
[i
] = _cavitydists
[i
];
256 foreach( const Neighbor
&I
, nbV(i
) ) {
257 _pancakes
[i
] *= factor(I
);
258 if( props
.updates
== Properties::UpdateType::SEQRND
)
259 _pancakes
[i
] *= _phis
[i
][I
.iter
];
262 _pancakes
[i
].normalize();
267 vector
<Factor
> old_beliefs
;
268 for(size_t i
=0; i
< nrVars(); i
++ )
269 old_beliefs
.push_back(belief(i
));
271 bool hasNaNs
= false;
272 for( size_t i
=0; i
< nrVars(); i
++ )
273 if( _pancakes
[i
].hasNaNs() ) {
278 cerr
<< Name
<< "::run: initial _pancakes has NaNs!" << endl
;
282 size_t nredges
= nrEdges();
283 vector
<Edge
> update_seq
;
284 update_seq
.reserve( nredges
);
285 for( size_t i
= 0; i
< nrVars(); ++i
)
286 foreach( const Neighbor
&I
, nbV(i
) )
287 update_seq
.push_back( Edge( i
, I
.iter
) );
289 // do several passes over the network until maximum number of iterations has
290 // been reached or until the maximum belief difference is smaller than tolerance
291 for( _iters
=0; _iters
< props
.maxiter
&& diffs
.maxDiff() > props
.tol
; _iters
++ ) {
292 // Sequential updates
293 if( props
.updates
== Properties::UpdateType::SEQRND
)
294 random_shuffle( update_seq
.begin(), update_seq
.end() );
296 for( size_t t
=0; t
< nredges
; t
++ ) {
297 size_t i
= update_seq
[t
].first
;
298 size_t _I
= update_seq
[t
].second
;
299 _pancakes
[i
] = NewPancake( i
, _I
, hasNaNs
);
305 // compare new beliefs with old ones
306 for(size_t i
=0; i
< nrVars(); i
++ ) {
307 diffs
.push( dist( belief(i
), old_beliefs
[i
], Prob::DISTLINF
) );
308 old_beliefs
[i
] = belief(i
);
311 if( props
.verbose
>= 3 )
312 cerr
<< Name
<< "::run: maxdiff " << diffs
.maxDiff() << " after " << _iters
+1 << " passes" << endl
;
315 if( diffs
.maxDiff() > _maxdiff
)
316 _maxdiff
= diffs
.maxDiff();
318 if( props
.verbose
>= 1 ) {
319 if( diffs
.maxDiff() > props
.tol
) {
320 if( props
.verbose
== 1 )
322 cerr
<< Name
<< "::run: WARNING: not converged within " << props
.maxiter
<< " passes (" << toc() - tic
<< " seconds)...final maxdiff:" << diffs
.maxDiff() << endl
;
324 if( props
.verbose
>= 2 )
325 cerr
<< Name
<< "::run: ";
326 cerr
<< "converged in " << _iters
<< " passes (" << toc() - tic
<< " seconds)." << endl
;
330 return diffs
.maxDiff();
334 } // end of namespace dai