1 /**
2  * @file methods/hmm/hmm_model.hpp
3  * @author Ryan Curtin
4  *
5  * A serializable HMM model that also stores the type.
6  *
7  * mlpack is free software; you may redistribute it and/or modify it under the
8  * terms of the 3-clause BSD license.  You should have received a copy of the
9  * 3-clause BSD license along with mlpack.  If not, see
10  * http://www.opensource.org/licenses/BSD-3-Clause for more information.
11  */
12 #ifndef MLPACK_METHODS_HMM_HMM_MODEL_HPP
13 #define MLPACK_METHODS_HMM_HMM_MODEL_HPP
14 
15 #include "hmm.hpp"
16 #include <mlpack/methods/gmm/gmm.hpp>
17 #include <mlpack/methods/gmm/diagonal_gmm.hpp>
18 
19 namespace mlpack {
20 namespace hmm {
21 
22 enum HMMType : char
23 {
24   DiscreteHMM = 0,
25   GaussianHMM,
26   GaussianMixtureModelHMM,
27   DiagonalGaussianMixtureModelHMM
28 };
29 
30 /**
31  * A serializable HMM model that also stores the type.
32  */
33 class HMMModel
34 {
35  private:
36   //! The type of the HMM.
37   HMMType type;
38   //! Not used if type is not DiscreteHMM.
39   HMM<distribution::DiscreteDistribution>* discreteHMM;
40   //! Not used if type is not GaussianHMM.
41   HMM<distribution::GaussianDistribution>* gaussianHMM;
42   //! Not used if type is not GaussianMixtureModelHMM.
43   HMM<gmm::GMM>* gmmHMM;
44   //! Not used if type is not DiagonalGaussianMixtureModelHMM.
45   HMM<gmm::DiagonalGMM>* diagGMMHMM;
46 
47  public:
48   //! Construct a model of the given type.
HMMModel(const HMMType type=HMMType::DiscreteHMM)49   HMMModel(const HMMType type = HMMType::DiscreteHMM) :
50       type(type),
51       discreteHMM(NULL),
52       gaussianHMM(NULL),
53       gmmHMM(NULL),
54       diagGMMHMM(NULL)
55   {
56     if (type == HMMType::DiscreteHMM)
57       discreteHMM = new HMM<distribution::DiscreteDistribution>();
58     else if (type == HMMType::GaussianHMM)
59       gaussianHMM = new HMM<distribution::GaussianDistribution>();
60     else if (type == HMMType::GaussianMixtureModelHMM)
61       gmmHMM = new HMM<gmm::GMM>();
62     else if (type == HMMType::DiagonalGaussianMixtureModelHMM)
63       diagGMMHMM = new HMM<gmm::DiagonalGMM>();
64   }
65 
66   //! Copy another model.
HMMModel(const HMMModel & other)67   HMMModel(const HMMModel& other) :
68       type(other.type),
69       discreteHMM(NULL),
70       gaussianHMM(NULL),
71       gmmHMM(NULL),
72       diagGMMHMM(NULL)
73   {
74     if (type == HMMType::DiscreteHMM)
75       discreteHMM =
76           new HMM<distribution::DiscreteDistribution>(*other.discreteHMM);
77     else if (type == HMMType::GaussianHMM)
78       gaussianHMM =
79           new HMM<distribution::GaussianDistribution>(*other.gaussianHMM);
80     else if (type == HMMType::GaussianMixtureModelHMM)
81       gmmHMM = new HMM<gmm::GMM>(*other.gmmHMM);
82     else if (type == HMMType::DiagonalGaussianMixtureModelHMM)
83       diagGMMHMM = new HMM<gmm::DiagonalGMM>(*other.diagGMMHMM);
84   }
85 
86   //! Take ownership of another model.
HMMModel(HMMModel && other)87   HMMModel(HMMModel&& other) :
88       type(other.type),
89       discreteHMM(other.discreteHMM),
90       gaussianHMM(other.gaussianHMM),
91       gmmHMM(other.gmmHMM),
92       diagGMMHMM(other.diagGMMHMM)
93   {
94     other.type = HMMType::DiscreteHMM;
95     other.discreteHMM = new HMM<distribution::DiscreteDistribution>();
96     other.gaussianHMM = NULL;
97     other.gmmHMM = NULL;
98     other.diagGMMHMM = NULL;
99   }
100 
101   //! Copy assignment operator.
operator =(const HMMModel & other)102   HMMModel& operator=(const HMMModel& other)
103   {
104     if (this == &other)
105       return *this;
106 
107     delete discreteHMM;
108     delete gaussianHMM;
109     delete gmmHMM;
110     delete diagGMMHMM;
111 
112     discreteHMM = NULL;
113     gaussianHMM = NULL;
114     gmmHMM = NULL;
115     diagGMMHMM = NULL;
116 
117     type = other.type;
118     if (type == HMMType::DiscreteHMM)
119       discreteHMM =
120           new HMM<distribution::DiscreteDistribution>(*other.discreteHMM);
121     else if (type == HMMType::GaussianHMM)
122       gaussianHMM =
123           new HMM<distribution::GaussianDistribution>(*other.gaussianHMM);
124     else if (type == HMMType::GaussianMixtureModelHMM)
125       gmmHMM = new HMM<gmm::GMM>(*other.gmmHMM);
126     else if (type == HMMType::DiagonalGaussianMixtureModelHMM)
127       diagGMMHMM = new HMM<gmm::DiagonalGMM>(*other.diagGMMHMM);
128 
129     return *this;
130   }
131 
132   //! Clean memory.
~HMMModel()133   ~HMMModel()
134   {
135     delete discreteHMM;
136     delete gaussianHMM;
137     delete gmmHMM;
138     delete diagGMMHMM;
139   }
140 
141   /**
142    * Given a functor type, perform that functor with the optional extra info on
143    * the HMM.
144    */
145   template<typename ActionType,
146            typename ExtraInfoType>
PerformAction(ExtraInfoType * x)147   void PerformAction(ExtraInfoType* x)
148   {
149     if (type == HMMType::DiscreteHMM)
150       ActionType::Apply(*discreteHMM, x);
151     else if (type == HMMType::GaussianHMM)
152       ActionType::Apply(*gaussianHMM, x);
153     else if (type == HMMType::GaussianMixtureModelHMM)
154       ActionType::Apply(*gmmHMM, x);
155     else if (type == HMMType::DiagonalGaussianMixtureModelHMM)
156       ActionType::Apply(*diagGMMHMM, x);
157   }
158 
159   //! Serialize the model.
160   template<typename Archive>
serialize(Archive & ar,const unsigned int version)161   void serialize(Archive& ar, const unsigned int version)
162   {
163     ar & BOOST_SERIALIZATION_NVP(type);
164 
165     // If necessary, clean memory.
166     if (Archive::is_loading::value)
167     {
168       delete discreteHMM;
169       delete gaussianHMM;
170       delete gmmHMM;
171       delete diagGMMHMM;
172 
173       discreteHMM = NULL;
174       gaussianHMM = NULL;
175       gmmHMM = NULL;
176       diagGMMHMM = NULL;
177     }
178 
179     if (type == HMMType::DiscreteHMM)
180       ar & BOOST_SERIALIZATION_NVP(discreteHMM);
181     else if (type == HMMType::GaussianHMM)
182       ar & BOOST_SERIALIZATION_NVP(gaussianHMM);
183     else if (type == HMMType::GaussianMixtureModelHMM)
184       ar & BOOST_SERIALIZATION_NVP(gmmHMM);
185 
186     // Backward compatibility: new versions of HMM has a Diagonal GMM type.
187     if (version > 0)
188     {
189       if (type == HMMType::DiagonalGaussianMixtureModelHMM)
190         ar & BOOST_SERIALIZATION_NVP(diagGMMHMM);
191     }
192   }
193 
194   // Accessor method for type of HMM
Type()195   HMMType Type() { return type; }
196 
197   /**
198    * Accessor methods for discreteHMM, gaussianHMM, gmmHMM, and diagGMMHMM.
199    * Note that an instatiation of this class will only contain one type of HMM
200    * (as indicated by the "type" instance variable) - the other two pointers
201    * will be NULL.
202    *
203    * For instance, if the HMMModel object holds a discrete HMM, then:
204    * type         --> DiscreteHMM
205    * gaussianHMM  --> NULL
206    * gmmHMM       --> NULL
207    * diagGMMHMM   --> NULL
208    * discreteHMM  --> HMM<DiscreteDistribution> object
209    * and hence, calls to GMMHMM(), DiagGMMHMM() and GaussianHMM() will return
210    * NULL. Only the call to DiscreteHMM() will return a non NULL pointer.
211    *
212    * Hence, in practice, a user should be careful to first check the type of HMM
213    * (by calling the Type() accessor) and then perform subsequent actions, to
214    * avoid null pointer dereferences.
215    */
DiscreteHMM()216   HMM<distribution::DiscreteDistribution>* DiscreteHMM() { return discreteHMM; }
GaussianHMM()217   HMM<distribution::GaussianDistribution>* GaussianHMM() { return gaussianHMM; }
GMMHMM()218   HMM<gmm::GMM>* GMMHMM() { return gmmHMM; }
DiagGMMHMM()219   HMM<gmm::DiagonalGMM>* DiagGMMHMM() { return diagGMMHMM; }
220 };
221 
222 } // namespace hmm
223 } // namespace mlpack
224 
225 //! Set the serialization version of the HMMModel class.
226 BOOST_CLASS_VERSION(mlpack::hmm::HMMModel, 1);
227 
228 #endif
229