GCOP
1.0
|
00001 // This file is part of libgcop, a library for Geometric Control, Optimization, and Planning (GCOP) 00002 // 00003 // Copyright (C) 2004-2014 Marin Kobilarov <marin(at)jhu.edu> 00004 // 00005 // This Source Code Form is subject to the terms of the Mozilla 00006 // Public License v. 2.0. If a copy of the MPL was not distributed 00007 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 00008 00009 #ifndef GCOP_GMM_H 00010 #define GCOP_GMM_H 00011 00012 #include "normal.h" 00013 00014 namespace gcop { 00015 using namespace Eigen; 00016 using namespace std; 00017 00023 template<int _n = Dynamic> 00024 class Gmm { 00025 public: 00026 typedef Matrix<double, _n, 1> Vectornd; 00027 typedef Matrix<double, _n, _n> Matrixnd; 00028 00034 Gmm(int n, int k = 1); 00035 00036 virtual ~Gmm(); 00037 00038 bool Update(); 00039 00040 double L(const Vectornd &x) const; 00041 00042 double Sample(Vectornd &x); 00043 00044 void Init(const Vectornd &xlb, const Vectornd &xub); 00045 00054 void Fit(const vector<pair<Vectornd, double> > &xps, double a = 1, 00055 int iter = 50, const Matrixnd *S = 0); 00056 00057 int k; 00058 vector<Normal<_n>> ns; 00059 vector<double> ws; 00060 vector<double> cdf; 00061 00062 double tol; 00063 00064 protected: 00065 Vectornd t2; 00066 Matrixnd t3; 00067 }; 00068 00069 00070 template<int _n> 00071 Gmm<_n>::Gmm(int n, int k) : 00072 k(k), ns(k, Normal<_n>(n)), ws(k), cdf(k) 00073 { 00074 assert(n > 0); 00075 assert(k > 0); 00076 00077 // ns = new Normal*[k]; 00078 // ws = new double[k]; 00079 // cdf = new double[k]; 00080 00081 for (int i = 0; i < k; ++i) { 00082 // ns[i] = new Normal(n); 00083 ws[i] = 1/((double)k); 00084 cdf[i] = (i+1)/((double)k); 00085 00086 ns[i].mu.setZero(); 00087 ns[i].P = VectorXd::Constant(n, 1).asDiagonal(); 00088 } 00089 00090 tol = .01; 00091 00092 if (_n == Dynamic) { 00093 t2.resize(n); 00094 t3.resize(n, n); 00095 } 00096 } 00097 00098 00099 template<int _n> 00100 Gmm<_n>::~Gmm() 00101 { 00102 /* 00103 for (int i = 0; i < k; ++i) 00104 delete ns[i]; 00105 delete cdf; 00106 delete ws; 00107 delete ns; 00108 */ 00109 } 00110 00111 00112 template<int _n> 00113 bool Gmm<_n>::Update() 00114 { 00115 if (k == 1) { 00116 ws[0] = 1; 00117 cdf[0] = 1; 00118 return ns[0].Update(); 00119 00120 } else { 00121 /* 00122 bool ok = true; 00123 double wn = 0; 00124 for (int i = 0; i < k; ++i) { 00125 ok = ok && ns[i]->Update(); 00126 wn += ws[i]; 00127 } 00128 00129 for (int i = 0; i < k; ++i) { 00130 ws[i] /= wn; 00131 cdf[i] = (i ? cdf[i - 1] + ws[i] : ws[i]); 00132 } 00133 return ok; 00134 */ 00135 00136 00137 // No need to call update since it is called after EM 00138 00139 bool ok = true; 00140 for (int i = 0; i < k; ++i) { 00141 cdf[i] = (i ? cdf[i - 1] + ws[i] : ws[i]); 00142 ok = ok && ns[i].pd; 00143 } 00144 return ok; 00145 } 00146 } 00147 00148 template<int _n> 00149 double Gmm<_n>::L(const Vectornd &x) const 00150 { 00151 if (k == 1) { 00152 return ns[0].L(x); 00153 } else { 00154 00155 double l = 0; 00156 for (int i = 0; i < k; ++i) 00157 l += ws[i]*ns[i].L(x); 00158 return l; 00159 } 00160 } 00161 00162 00163 template<int _n> 00164 double Gmm<_n>::Sample(Vectornd &x) 00165 { 00166 if (k == 1) { 00167 return ns[0].Sample(x); 00168 00169 } else { 00170 // for now this is unefficient if k is big 00171 // TODO: implement as binary search 00172 double uc = rand()/(double)RAND_MAX; 00173 int i = 0; 00174 while (uc > cdf[i]) 00175 ++i; 00176 00177 assert(i < k); 00178 return ns[i].Sample(x); 00179 } 00180 } 00181 00182 template<int _n> 00183 void Gmm<_n>::Init(const Vectornd &xlb, const Vectornd &xub) 00184 { 00185 Vectornd dx = xub - xlb; // range 00186 Vectornd r = dx/pow(k, 1.0/dx.size())/2; // radius 00187 Matrixnd P = (r.cwiseProduct(r)).asDiagonal(); 00188 for (int i = 0; i < k; ++i) { 00189 ns[i].mu = xlb + dx.cwiseProduct(VectorXd::Random(xlb.size())); 00190 ns[i].P = P; 00191 ws[i] = 1.0/k; 00192 ns[i].Update(); 00193 } 00194 00195 Update(); 00196 } 00197 00198 00199 template<int _n> 00200 void Gmm<_n>::Fit(const vector<pair<Vectornd, double> > &xps, double a, int iter, const Matrixnd *S) 00201 { 00202 int N = xps.size(); 00203 00204 if (k == 1) { 00205 ns[0].Fit(xps, a); 00206 if (S) 00207 ns[0].P += *S; 00208 return; 00209 } 00210 00211 assert(N > 0); 00212 00213 double ps[N][k]; 00214 00215 for (int l = 0; l < iter; ++l) { 00216 00217 // E-step 00218 00219 for (int j = 0; j < N; ++j) { 00220 00221 const Vectornd &x = xps[j].first; 00222 double p = xps[j].second; 00223 00224 double norm = 0; 00225 double *psj = ps[j]; 00226 00227 for (int i = 0; i < k; ++i) { 00228 psj[i] = p*ns[i].L(x); // likelihood of each sample 00229 norm += psj[i]; 00230 } 00231 00232 // assert(norm > 1e-10); 00233 // cout << " normalized: ps[" << j << "]="; 00234 for (int i = 0; i < k; ++i) { 00235 psj[i] /= norm; 00236 // cout << psj[i] << " "; 00237 } 00238 // cout << endl; 00239 } 00240 00241 00242 // M-step 00243 double maxd = 0; 00244 00245 for (int i = 0; i < k; ++i) { 00246 double t1 = 0; 00247 // Vectornd t2; = VectorXd::Zero(ns[0].mu.size()); 00248 t2.setZero();//Redundancy 00249 //MatrixXd t3 = MatrixXd::Zero(ns[0].mu.size(), ns[0].mu.size()); 00250 t3.setZero(); 00251 00252 for (int j = 0; j < N; ++j) { 00253 const VectorXd &x = xps[j].first; 00254 t1 += ps[j][i]; 00255 t2 += ps[j][i]*x; 00256 t3 += (ps[j][i]*x)*x.transpose(); 00257 } 00258 00259 ws[i] = t1; 00260 00261 VectorXd mu = t2/t1; 00262 00263 double d = (mu - ns[i].mu).norm(); 00264 if (maxd < d) 00265 maxd = d; 00266 00267 ns[i].mu = mu; 00268 ns[i].P = (t3 - t2*(t2.transpose()/t1))/t1; 00269 if (S) 00270 ns[i].P += *S; 00271 00272 00273 if (!ns[i].Update()) // set Pinv, det, norm 00274 return; 00275 } 00276 00277 if (maxd < tol) { 00278 //cout << "[W] Gmm::Fit: tolerance " << maxd << " reached after " << l << " iterations!" << endl; 00279 break; 00280 } 00281 } 00282 } 00283 00284 00285 } 00286 00287 #endif