bnmf-algs
online_EM_funcs.hpp
Go to the documentation of this file.
1 #pragma once
2 
4 #include "defs.hpp"
5 #include "online_EM_defs.hpp"
6 #include "util/sampling.hpp"
7 #include <cmath>
8 #include <gsl/gsl_sf_gamma.h>
9 #include <utility>
10 #include <vector>
11 
12 namespace bnmf_algs {
13 namespace details {
17 namespace online_EM {
18 
28 template <typename T> size_t init_nan_values(matrix_t<T>& X) {
29  const auto x = static_cast<size_t>(X.rows());
30  const auto y = static_cast<size_t>(X.cols());
31 
32  T nonnan_sum = T();
33  size_t nonnan_count = 0;
34 
35  #pragma omp parallel for reduction(+:nonnan_sum,nonnan_count)
36  for (size_t i = 0; i < x; ++i) {
37  for (size_t j = 0; j < y; ++j) {
38  if (not std::isnan(X(i, j))) {
39  nonnan_sum += X(i, j);
40  ++nonnan_count;
41  }
42  }
43  }
44 
45  if (nonnan_count != 0) {
46  const T nonnan_mean = nonnan_sum / nonnan_count;
47 
48  #pragma omp parallel for schedule(static)
49  for (size_t i = 0; i < x; ++i) {
50  for (size_t j = 0; j < y; ++j) {
51  if (std::isnan(X(i, j))) {
52  X(i, j) = nonnan_mean;
53  }
54  }
55  }
56  }
57 
58  return nonnan_count;
59 }
60 
75 template <typename Scalar>
78  size_t y) {
79  const auto x = static_cast<size_t>(param_vec[0].alpha.size());
80  const auto z = static_cast<size_t>(param_vec.size());
81 
82  matrix_t<Scalar> alpha(x, z);
83  matrix_t<Scalar> beta(z, y);
84 
85  #pragma omp parallel for schedule(static)
86  for (size_t k = 0; k < z; ++k) {
87  for (size_t i = 0; i < x; ++i) {
88  alpha(i, k) = param_vec[k].alpha[i];
89  }
90 
91  for (size_t j = 0; j < y; ++j) {
92  beta(k, j) = param_vec[k].beta[j];
93  }
94  }
95 
96  return std::make_pair(alpha, beta);
97 }
98 
118 template <typename T>
120 init_S_xx(const matrix_t<T>& X_full, size_t z, const std::vector<size_t>& ii,
121  const std::vector<size_t>& jj) {
122  const auto x = static_cast<size_t>(X_full.rows());
123  const auto y = static_cast<size_t>(X_full.cols());
124 
125  // results
126  matrix_t<T> S_pjk = matrix_t<T>::Zero(y, z);
127  matrix_t<T> S_ipk = matrix_t<T>::Zero(x, z);
128  vector_t<T> S_ppk = vector_t<T>::Zero(z);
129 
130  util::gsl_rng_wrapper rnd_gen(gsl_rng_alloc(gsl_rng_taus), gsl_rng_free);
131 
132  vector_t<double> dirichlet_params = vector_t<double>::Constant(z, 1);
133 
134  #pragma omp parallel
135  {
136  // thread local variables
137  vector_t<T> fiber(z);
138  vector_t<double> fiber_double(z);
139  matrix_t<T> S_pjk_local = matrix_t<T>::Zero(y, z);
140  matrix_t<T> S_ipk_local = matrix_t<T>::Zero(x, z);
141  vector_t<T> S_ppk_local = vector_t<T>::Zero(z);
142 
143  // do in parallel
144  #pragma omp for
145  for (size_t t = 0; t < ii.size(); ++t) {
146  size_t i = ii[t], j = jj[t];
147 
148  gsl_ran_dirichlet(rnd_gen.get(), z, dirichlet_params.data(),
149  fiber_double.data());
150  fiber = fiber_double.template cast<T>();
151  fiber = fiber.array() * X_full(i, j);
152 
153  S_pjk_local.row(j) += fiber;
154  S_ipk_local.row(i) += fiber;
155  S_ppk_local += fiber;
156  }
157 
158  // reduce local variables into accumulators one thread at a time
159  #pragma omp critical
160  {
161  S_pjk += S_pjk_local;
162  S_ipk += S_ipk_local;
163  S_ppk += S_ppk_local;
164  }
165  }
166 
167  return std::make_tuple(S_pjk, S_ipk, S_ppk);
168 }
169 
185 template <typename T, typename Scalar, typename PsiFunction>
186 void update_logW(const matrix_t<Scalar>& alpha, const matrix_t<T>& S_ipk,
187  const vector_t<Scalar>& alpha_pk, const vector_t<T>& S_ppk,
188  const PsiFunction& psi_fn, matrix_t<double>& logW) {
189  const auto x = static_cast<size_t>(alpha.rows());
190  const auto z = static_cast<size_t>(alpha.cols());
191 
192  vector_t<T> psi_of_sums(z);
193  #pragma omp parallel for schedule(static)
194  for (size_t k = 0; k < z; ++k) {
195  psi_of_sums(k) = psi_fn(alpha_pk(k) + S_ppk(k));
196  }
197 
198  #pragma omp parallel for schedule(static)
199  for (size_t i = 0; i < x; ++i) {
200  for (size_t k = 0; k < z; ++k) {
201  logW(i, k) = psi_fn(alpha(i, k) + S_ipk(i, k)) - psi_of_sums(k);
202  }
203  }
204 }
205 
220 template <typename T, typename Scalar, typename PsiFunction>
221 void update_logH(const matrix_t<Scalar>& beta, const matrix_t<T>& S_pjk,
222  const Scalar b, const PsiFunction& psi_fn,
223  matrix_t<double>& logH) {
224  const auto z = static_cast<size_t>(beta.rows());
225  const auto y = static_cast<size_t>(beta.cols());
226 
227  const Scalar logb = std::log(b + 1);
228 
229  #pragma omp parallel for schedule(static)
230  for (size_t k = 0; k < z; ++k) {
231  for (size_t j = 0; j < y; ++j) {
232  logH(k, j) = psi_fn(beta(k, j) + S_pjk(j, k)) - logb;
233  }
234  }
235 }
236 
249 template <typename T>
251 find_nonzero(const matrix_t<T>& X) {
252  std::vector<size_t> ii, jj;
253  std::vector<T> xx;
254 
255  for (long i = 0; i < X.rows(); ++i) {
256  for (long j = 0; j < X.cols(); ++j) {
257  T x_ij = X(i, j);
258  if (std::isnan(x_ij) || x_ij > std::numeric_limits<T>::epsilon()) {
259  ii.push_back(static_cast<size_t>(i));
260  jj.push_back(static_cast<size_t>(j));
261  xx.push_back(x_ij);
262  }
263  }
264  }
265 
266  return std::make_tuple(ii, jj, xx);
267 }
268 
284 template <typename T>
285 double update_allocation(const std::vector<size_t>& ii,
286  const std::vector<size_t>& jj,
287  const std::vector<T>& xx, bld::EMResult<T>& res,
288  vector_t<T>& S_ppk) {
289 
290  const auto x = static_cast<size_t>(res.X_full.rows());
291  const auto y = static_cast<size_t>(res.X_full.cols());
292  const auto z = static_cast<size_t>(S_ppk.cols());
293 
294  double delta_log_PS = 0;
295 
296  #pragma omp parallel
297  {
298  matrix_t<T> S_pjk = matrix_t<T>::Zero(y, z);
299  matrix_t<T> S_ipk = matrix_t<T>::Zero(x, z);
300  vector_t<T> S_ppk_local = vector_t<T>::Zero(z);
301 
302  vector_t<T> gammaln_max_fiber(z);
303  vector_t<T> log_p(z);
304  vector_t<T> max_fiber(z);
305 
306  double delta_log_PS_local = 0;
307 
308  #pragma omp for
309  for (size_t t = 0; t < xx.size(); ++t) {
310  const size_t i = ii[t];
311  const size_t j = jj[t];
312  const T orig_x_ij = xx[t];
313 
314  log_p = res.logW.row(i) + res.logH.col(j).transpose();
315  const vector_t<T> log_p_exp = log_p.array().exp();
316 
317  // maximization step
318  if (std::isnan(orig_x_ij)) {
319  max_fiber = log_p_exp.array().floor();
320  res.X_full(i, j) = max_fiber.sum();
321  } else {
322  util::multinomial_mode(orig_x_ij, log_p_exp, max_fiber);
323  }
324 
325  S_pjk.row(j) += max_fiber;
326  S_ipk.row(i) += max_fiber;
327  S_ppk_local += max_fiber;
328 
329  for (size_t k = 0; k < z; ++k) {
330  gammaln_max_fiber(k) = gsl_sf_lngamma(max_fiber(k) + 1);
331  }
332  delta_log_PS_local -= gammaln_max_fiber.sum();
333  }
334 
335  #pragma omp critical
336  {
337  res.S_pjk += S_pjk;
338  res.S_ipk += S_ipk;
339  S_ppk += S_ppk_local;
340  delta_log_PS += delta_log_PS_local;
341  }
342  }
343 
344  return delta_log_PS;
345 }
346 
363 template <typename T, typename Scalar>
364 double delta_log_PS(const matrix_t<Scalar>& alpha, const matrix_t<Scalar>& beta,
365  const matrix_t<T>& S_ipk, const matrix_t<T>& S_pjk,
366  const vector_t<Scalar>& alpha_pk, const vector_t<T>& S_ppk,
367  Scalar b) {
368  double delta = -(std::log(b + 1) * S_ppk.sum());
369 
370  #pragma omp parallel for schedule(static) reduction(+:delta)
371  for (long k = 0; k < alpha.cols(); ++k) {
372  for (long i = 0; i < alpha.rows(); ++i) {
373  delta += gsl_sf_lngamma(alpha(i, k) + S_ipk(i, k));
374  }
375 
376  for (long j = 0; j < beta.cols(); ++j) {
377  delta += gsl_sf_lngamma(beta(k, j) + S_pjk(j, k));
378  }
379 
380  delta -= gsl_sf_lngamma(alpha_pk(k) + S_ppk(k));
381  }
382 
383  return delta;
384 }
385 
386 } // namespace EM
387 } // namespace details
388 } // namespace bnmf_algs
Structure to hold the parameters for the Allocation Model .
Definition: alloc_model_params.hpp:25
void update_logH(const matrix_t< Scalar > &beta, const matrix_t< T > &S_pjk, const Scalar b, const PsiFunction &psi_fn, matrix_t< double > &logH)
Perform an update on logW matrix.
Definition: online_EM_funcs.hpp:221
EMResult< T > online_EM(const matrix_t< T > &X, const std::vector< alloc_model::Params< Scalar >> &param_vec, const size_t max_iter=1000, const bool use_psi_appr=false)
Complete a matrix containing unobserved values given as NaN using an EM procedure according to the al...
Definition: online_EM.hpp:50
double delta_log_PS(const matrix_t< Scalar > &alpha, const matrix_t< Scalar > &beta, const matrix_t< T > &S_ipk, const matrix_t< T > &S_pjk, const vector_t< Scalar > &alpha_pk, const vector_t< T > &S_ppk, Scalar b)
Compute the difference in log_PS value computed using the given model variables.
Definition: online_EM_funcs.hpp:364
T log(T...args)
Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > matrix_t
Matrix type used in the computations.
Definition: defs.hpp:41
T make_tuple(T...args)
Structure holding the results of EM procedures.
Definition: online_EM_defs.hpp:13
matrix_t< double > logH
Matrix whose entry contains .
Definition: online_EM_defs.hpp:41
std::tuple< std::vector< size_t >, std::vector< size_t >, std::vector< T > > find_nonzero(const matrix_t< T > &X)
Find nonzero entries and their indices in the given matrix and return indices and values as vectors...
Definition: online_EM_funcs.hpp:251
matrix_t< T > X_full
Completed version of the incomplete matrix given as input to an EM algorithm.
Definition: online_EM_defs.hpp:33
matrix_t< T > S_ipk
Sum of the hidden tensor along its second dimension, i.e. .
Definition: online_EM_defs.hpp:24
T push_back(T...args)
T make_pair(T...args)
void multinomial_mode(Integer num_trials, const vector_t< Real > &prob, vector_t< Integer > &count, double eps=1e-50)
Find the mode of a multinomial distribution using Finucan&#39;s algorithm.
Definition: sampling.hpp:88
matrix_t< double > logW
Matrix whose entry contains .
Definition: online_EM_defs.hpp:37
matrix_t< T > S_pjk
Sum of the hidden tensor along its first dimension, i.e. .
Definition: online_EM_defs.hpp:19
T get(T...args)
size_t init_nan_values(matrix_t< T > &X)
Initialize all NaN values in the given matrix with the mean of the remaining values that are differen...
Definition: online_EM_funcs.hpp:28
T size(T...args)
T isnan(T...args)
std::tuple< matrix_t< T >, matrix_t< T >, vector_t< T > > init_S_xx(const matrix_t< T > &X_full, size_t z, const std::vector< size_t > &ii, const std::vector< size_t > &jj)
Initialize S_pjk, S_ipk matrices and S_ppk vector from a Dirichlet distribution with all parameters e...
Definition: online_EM_funcs.hpp:120
std::pair< matrix_t< Scalar >, matrix_t< Scalar > > init_alpha_beta(const std::vector< alloc_model::Params< Scalar >> &param_vec, size_t y)
Initialize each entry of alpha and beta matrices with the given model parameters. ...
Definition: online_EM_funcs.hpp:77
Eigen::Matrix< Scalar, 1, Eigen::Dynamic, Eigen::RowMajor > vector_t
Vector type used in the computations.
Definition: defs.hpp:27
Main namespace for bnmf-algs library.
Definition: alloc_model_funcs.hpp:12
double update_allocation(const std::vector< size_t > &ii, const std::vector< size_t > &jj, const std::vector< T > &xx, bld::EMResult< T > &res, vector_t< T > &S_ppk)
Update the current allocation by performing a maximization step for each nonzero entry of the origina...
Definition: online_EM_funcs.hpp:285
void update_logW(const matrix_t< Scalar > &alpha, const matrix_t< T > &S_ipk, const vector_t< Scalar > &alpha_pk, const vector_t< T > &S_ppk, const PsiFunction &psi_fn, matrix_t< double > &logW)
Perform an update on logW matrix.
Definition: online_EM_funcs.hpp:186