Improved error messages of Evidence::addEvidenceTabFile
[libdai.git] / include / dai / emalg.h
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 Charles Vaske [cvaske at soe dot ucsc dot edu]
8 * Copyright (C) 2009 University of California, Santa Cruz
9 */
10
11
12 #ifndef __defined_libdai_emalg_h
13 #define __defined_libdai_emalg_h
14
15
16 #include <vector>
17 #include <map>
18
19 #include <dai/factor.h>
20 #include <dai/daialg.h>
21 #include <dai/evidence.h>
22 #include <dai/index.h>
23 #include <dai/properties.h>
24
25
26 /// \file
27 /// \brief Defines classes related to Expectation Maximization: EMAlg, ParameterEstimation, CondProbEstimation and SharedParameters
28
29
30 namespace dai {
31
32
33 /// Interface for a parameter estimation method.
34 /** This parameter estimation interface is based on sufficient statistics.
35 * Implementations are responsible for collecting data from a probability
36 * vector passed to it from a SharedParameters container object.
37 *
38 * Implementations of this interface should register a factory function
39 * via the static ParameterEstimation::registerMethod function.
40 * The default registry only contains CondProbEstimation, named
41 * "ConditionalProbEstimation".
42 *
43 * \author Charles Vaske
44 */
45 class ParameterEstimation {
46 public:
47 /// A pointer to a factory function.
48 typedef ParameterEstimation* (*ParamEstFactory)( const PropertySet& );
49
50 /// Virtual destructor for deleting pointers to derived classes.
51 virtual ~ParameterEstimation() {}
52 /// Virtual copy constructor.
53 virtual ParameterEstimation* clone() const = 0;
54
55 /// General factory method for construction of ParameterEstimation subclasses.
56 static ParameterEstimation* construct( const std::string &method, const PropertySet &p );
57
58 /// Register a subclass so that it can be used with ParameterEstimation::construct.
59 static void registerMethod( const std::string &method, const ParamEstFactory &f ) {
60 if( _registry == NULL )
61 loadDefaultRegistry();
62 (*_registry)[method] = f;
63 }
64
65 /// Estimate the factor using the accumulated sufficient statistics and reset.
66 virtual Prob estimate() = 0;
67
68 /// Accumulate the sufficient statistics for p.
69 virtual void addSufficientStatistics( const Prob &p ) = 0;
70
71 /// Returns the size of the Prob that should be passed to addSufficientStatistics.
72 virtual size_t probSize() const = 0;
73
74 private:
75 /// A static registry containing all methods registered so far.
76 static std::map<std::string, ParamEstFactory> *_registry;
77
78 /// Registers default ParameterEstimation subclasses (currently, only CondProbEstimation).
79 static void loadDefaultRegistry();
80 };
81
82
83 /// Estimates the parameters of a conditional probability table, using pseudocounts.
84 /** \author Charles Vaske
85 */
86 class CondProbEstimation : private ParameterEstimation {
87 private:
88 /// Number of states of the variable of interest
89 size_t _target_dim;
90 /// Current pseudocounts
91 Prob _stats;
92 /// Initial pseudocounts
93 Prob _initial_stats;
94
95 public:
96 /// Constructor
97 /** For a conditional probability \f$ P( X | Y ) \f$,
98 * \param target_dimension should equal \f$ | X | \f$
99 * \param pseudocounts has length \f$ |X| \cdot |Y| \f$
100 */
101 CondProbEstimation( size_t target_dimension, const Prob &pseudocounts );
102
103 /// Virtual constructor, using a PropertySet.
104 /** Some keys in the PropertySet are required.
105 * For a conditional probability \f$ P( X | Y ) \f$,
106 * - target_dimension should be equal to \f$ | X | \f$
107 * - total_dimension should be equal to \f$ |X| \cdot |Y| \f$
108 *
109 * An optional key is:
110 * - pseudo_count, which specifies the initial counts (defaults to 1)
111 */
112 static ParameterEstimation* factory( const PropertySet &p );
113
114 /// Virtual copy constructor
115 virtual ParameterEstimation* clone() const { return new CondProbEstimation( _target_dim, _initial_stats ); }
116
117 /// Virtual destructor
118 virtual ~CondProbEstimation() {}
119
120 /// Returns an estimate of the conditional probability distribution.
121 /** The format of the resulting Prob keeps all the values for
122 * \f$ P(X | Y=y) \f$ in sequential order in the array.
123 */
124 virtual Prob estimate();
125
126 /// Accumulate sufficient statistics from the expectations in p.
127 virtual void addSufficientStatistics( const Prob &p );
128
129 /// Returns the required size for arguments to addSufficientStatistics.
130 virtual size_t probSize() const { return _stats.size(); }
131 };
132
133
134 /// A single factor or set of factors whose parameters should be estimated.
135 /** To ensure that parameters can be shared between different factors during
136 * EM learning, each factor's values are reordered to match a desired variable
137 * ordering. The ordering of the variables in a factor may therefore differ
138 * from the canonical ordering used in libDAI. The SharedParameters
139 * class couples one or more factors (together with the specified orderings
140 * of the variables) with a ParameterEstimation object, taking care of the
141 * necessary permutations of the factor entries / parameters.
142 *
143 * \author Charles Vaske
144 */
145 class SharedParameters {
146 public:
147 /// Convenience label for an index into a factor in a FactorGraph.
148 typedef size_t FactorIndex;
149 /// Convenience label for a grouping of factor orientations.
150 typedef std::map<FactorIndex, std::vector<Var> > FactorOrientations;
151
152 private:
153 /// Maps factor indices to the corresponding VarSets
154 std::map<FactorIndex, VarSet> _varsets;
155 /// Maps factor indices to the corresponding Permute objects that permute the desired ordering into the canonical ordering
156 std::map<FactorIndex, Permute> _perms;
157 /// Maps factor indices to the corresponding desired variable orderings
158 FactorOrientations _varorders;
159 /// Parameter estimation method to be used
160 ParameterEstimation *_estimation;
161 /// Indicates whether the object pointed to by _estimation should be deleted upon destruction
162 bool _deleteEstimation;
163
164 /// Calculates the permutation that permutes the variables in varorder into the canonical ordering
165 static Permute calculatePermutation( const std::vector<Var> &varorder, VarSet &outVS );
166
167 /// Initializes _varsets and _perms from _varorders
168 void setPermsAndVarSetsFromVarOrders();
169
170 public:
171 /// Copy constructor
172 SharedParameters( const SharedParameters &sp );
173
174 /// Constructor
175 /** \param varorders all the factor orientations for this parameter
176 * \param estimation a pointer to the parameter estimation method
177 * \param deletePE whether the parameter estimation object should be deleted in the destructor
178 */
179 SharedParameters( const FactorOrientations &varorders, ParameterEstimation *estimation, bool deletePE=false );
180
181 /// Constructor for making an object from a stream and a factor graph
182 SharedParameters( std::istream &is, const FactorGraph &fg_varlookup );
183
184 /// Destructor
185 ~SharedParameters() {
186 if( _deleteEstimation )
187 delete _estimation;
188 }
189
190 /// Collect the necessary statistics from expected values
191 void collectSufficientStatistics( InfAlg &alg );
192
193 /// Estimate and set the shared parameters
194 void setParameters( FactorGraph &fg );
195
196 /// Returns the parameters
197 void collectParameters( const FactorGraph &fg, std::vector<Real> &outVals, std::vector<Var> &outVarOrder );
198 };
199
200
201 /// A MaximizationStep groups together several parameter estimation tasks into a single unit.
202 /** \author Charles Vaske
203 */
204 class MaximizationStep {
205 private:
206 std::vector<SharedParameters> _params;
207
208 public:
209 /// Default constructor
210 MaximizationStep() : _params() {}
211
212 /// Constructor from a vector of SharedParameters objects
213 MaximizationStep( std::vector<SharedParameters> &maximizations ) : _params(maximizations) {}
214
215 /// Constructor from an input stream and a corresponding factor graph
216 MaximizationStep( std::istream &is, const FactorGraph &fg_varlookup );
217
218 /// Collect the beliefs from this InfAlg as expectations for the next Maximization step.
219 void addExpectations( InfAlg &alg );
220
221 /// Using all of the currently added expectations, make new factors with maximized parameters and set them in the FactorGraph.
222 void maximize( FactorGraph &fg );
223
224 /// \name Iterator interface
225 //@{
226 typedef std::vector<SharedParameters>::iterator iterator;
227 typedef std::vector<SharedParameters>::const_iterator const_iterator;
228 iterator begin() { return _params.begin(); }
229 const_iterator begin() const { return _params.begin(); }
230 iterator end() { return _params.end(); }
231 const_iterator end() const { return _params.end(); }
232 //@}
233 };
234
235
236 /// EMAlg performs Expectation Maximization to learn factor parameters.
237 /** This requires specifying:
238 * - Evidence (instances of observations from the graphical model),
239 * - InfAlg for performing the E-step, which includes the factor graph,
240 * - a vector of MaximizationSteps steps to be performed.
241 *
242 * This implementation can perform incremental EM by using multiple
243 * MaximizationSteps. An expectation step is performed between execution
244 * of each MaximizationStep. A call to iterate() will cycle through all
245 * MaximizationSteps.
246 *
247 * Having multiple and separate maximization steps allows for maximizing some
248 * parameters, performing another E step, and then maximizing separate
249 * parameters, which may result in faster convergence in some cases.
250 *
251 * \author Charles Vaske
252 */
253 class EMAlg {
254 private:
255 /// All the data samples used during learning
256 const Evidence &_evidence;
257
258 /// How to do the expectation step
259 InfAlg &_estep;
260
261 /// The maximization steps to take
262 std::vector<MaximizationStep> _msteps;
263
264 /// Number of iterations done
265 size_t _iters;
266
267 /// History of likelihoods
268 std::vector<Real> _lastLogZ;
269
270 /// Maximum number of iterations
271 size_t _max_iters;
272
273 /// Convergence tolerance
274 Real _log_z_tol;
275
276 public:
277 /// Key for setting maximum iterations @see setTermConditions
278 static const std::string MAX_ITERS_KEY;
279 /// Default maximum iterations @see setTermConditions
280 static const size_t MAX_ITERS_DEFAULT;
281 /// Key for setting likelihood termination condition @see setTermConditions
282 static const std::string LOG_Z_TOL_KEY;
283 /// Default likelihood tolerance @see setTermConditions
284 static const Real LOG_Z_TOL_DEFAULT;
285
286 /// Construct an EMAlg from all these objects
287 EMAlg( const Evidence &evidence, InfAlg &estep, std::vector<MaximizationStep> &msteps, const PropertySet &termconditions )
288 : _evidence(evidence), _estep(estep), _msteps(msteps), _iters(0), _lastLogZ(), _max_iters(MAX_ITERS_DEFAULT), _log_z_tol(LOG_Z_TOL_DEFAULT)
289 {
290 setTermConditions( termconditions );
291 }
292
293 /// Construct an EMAlg from an Evidence object, an InfAlg object, and an input stream
294 EMAlg( const Evidence &evidence, InfAlg &estep, std::istream &mstep_file );
295
296 /// Change the conditions for termination
297 /** There are two possible parameters in the PropertySet
298 * - max_iters maximum number of iterations
299 * - log_z_tol proportion of increase in logZ
300 *
301 * \see hasSatisifiedTermConditions()
302 */
303 void setTermConditions( const PropertySet &p );
304
305 /// Determine if the termination conditions have been met.
306 /** There are two sufficient termination conditions:
307 * -# the maximum number of iterations has been performed
308 * -# the ratio of logZ increase over previous logZ is less than the
309 * tolerance, i.e.,
310 * \f$ \frac{\log(Z_t) - \log(Z_{t-1})}{| \log(Z_{t-1}) | } < \mathrm{tol} \f$.
311 */
312 bool hasSatisfiedTermConditions() const;
313
314 /// Return the last calculated log likelihood
315 Real getLogZ() const { return _lastLogZ.back(); }
316
317 /// Returns number of iterations done so far
318 size_t getCurrentIters() const { return _iters; }
319
320 /// Get the iteration method used
321 const InfAlg& eStep() const { return _estep; }
322
323 /// Perform an iteration over all maximization steps
324 Real iterate();
325
326 /// Perform an iteration over a single MaximizationStep
327 Real iterate( MaximizationStep &mstep );
328
329 /// Iterate until termination conditions are satisfied
330 void run();
331
332 /// \name Iterator interface
333 //@{
334 typedef std::vector<MaximizationStep>::iterator s_iterator;
335 typedef std::vector<MaximizationStep>::const_iterator const_s_iterator;
336 s_iterator s_begin() { return _msteps.begin(); }
337 const_s_iterator s_begin() const { return _msteps.begin(); }
338 s_iterator s_end() { return _msteps.end(); }
339 const_s_iterator s_end() const { return _msteps.end(); }
340 //@}
341 };
342
343
344 } // end of namespace dai
345
346
347 #endif